mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-28 14:13:59 +08:00
Compare commits
242 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a64ab7df4e | ||
|
|
82cd64cb50 | ||
|
|
065bfb8694 | ||
|
|
6db335cf87 | ||
|
|
155f4670e9 | ||
|
|
50522b571a | ||
|
|
8e138863ce | ||
|
|
7d8dfe628d | ||
|
|
8baed5b306 | ||
|
|
41a5ffd214 | ||
|
|
c6aec3a1af | ||
|
|
22e30f7a62 | ||
|
|
57711b8ab5 | ||
|
|
90ff1259ea | ||
|
|
d88fc5ccd7 | ||
|
|
5aaca2aa9c | ||
|
|
8b9ca56e17 | ||
|
|
e4f3772e6d | ||
|
|
d58ec81d20 | ||
|
|
415aae44b6 | ||
|
|
a533286658 | ||
|
|
e59f91cd82 | ||
|
|
5f8b85d8a4 | ||
|
|
47c7b88436 | ||
|
|
90006782f2 | ||
|
|
c3d49cbe70 | ||
|
|
112323a360 | ||
|
|
9d08c90fda | ||
|
|
602d24deec | ||
|
|
a2b9f66785 | ||
|
|
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 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -363,4 +363,6 @@ MigrationBackup/
|
|||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
|
||||||
|
/framework/*pro*
|
||||||
/framework/*Pro*
|
/framework/*Pro*
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
**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**
|
||||||
|
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|||||||
@@ -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.1.0.7</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>
|
|
||||||
101
framework/ThingsGateway - Admin.sln
Normal file
101
framework/ThingsGateway - Admin.sln
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.6.33927.249
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{4E66C22C-0636-4949-BF6A-9E3BBE1550BA}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
admin\Directory.Build.props = admin\Directory.Build.props
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "admin\ThingsGateway.Components\ThingsGateway.Components.csproj", "{0A891D8E-23B3-46AD-8D30-565EE5004F93}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "admin\ThingsGateway.Core\ThingsGateway.Core.csproj", "{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "admin\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{5DA3D2BD-6768-4479-B52F-49E022EFF310}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "admin\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "admin\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{D6685A42-2712-417A-92C5-5EFF90B9FA94}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "admin\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web", "web", "{F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
web\Directory.Build.props = web\Directory.Build.props
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "web\ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{D37EC028-EA46-4510-8261-6E780A906314}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "web\ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{C5F662EB-991F-438D-BF61-EF87E7371C04}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{97B23D8B-C6C0-4746-A21F-C7B49354B284}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\.gitignore = ..\.gitignore
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj", "{6961511A-8787-42AF-827D-B630B2AF4791}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "foundation", "foundation", "{268A1A81-2685-47E1-9986-5934A58A31A4}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{6961511A-8787-42AF-827D-B630B2AF4791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6961511A-8787-42AF-827D-B630B2AF4791}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6961511A-8787-42AF-827D-B630B2AF4791}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6961511A-8787-42AF-827D-B630B2AF4791}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{0A891D8E-23B3-46AD-8D30-565EE5004F93} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
|
||||||
|
{A712EAEE-94F2-4F01-8C1C-2EC802280DD7} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
|
||||||
|
{5DA3D2BD-6768-4479-B52F-49E022EFF310} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
|
||||||
|
{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
|
||||||
|
{D6685A42-2712-417A-92C5-5EFF90B9FA94} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
|
||||||
|
{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
|
||||||
|
{D37EC028-EA46-4510-8261-6E780A906314} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
|
||||||
|
{C5F662EB-991F-438D-BF61-EF87E7371C04} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
|
||||||
|
{6961511A-8787-42AF-827D-B630B2AF4791} = {268A1A81-2685-47E1-9986-5934A58A31A4}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -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,42 +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
|
|
||||||
@inherits BaseComponentBase
|
|
||||||
@using BlazorComponent;
|
|
||||||
@using Masa.Blazor;
|
|
||||||
<div class="d-flex align-center py-1">
|
|
||||||
<MBreadcrumbs Routable @key="@(Guid.NewGuid())" Class="pa-0">
|
|
||||||
<DividerContent>
|
|
||||||
<MIcon Class="ma-0 pa-0">mdi-chevron-right</MIcon>
|
|
||||||
</DividerContent>
|
|
||||||
<ChildContent>
|
|
||||||
<div style="white-space: nowrap !important;overflow: hidden !important; text-overflow: ellipsis !important;">
|
|
||||||
<MBreadcrumbsItem Href="javascript:history.back(-1)">
|
|
||||||
<MIcon Size=20>mdi-arrow-left</MIcon>
|
|
||||||
</MBreadcrumbsItem>
|
|
||||||
@for (var i = 0; i < BreadcrumbItems.Count; i++)
|
|
||||||
{
|
|
||||||
var item = BreadcrumbItems[i];
|
|
||||||
var isLast = i == BreadcrumbItems.Count - 1;
|
|
||||||
<MBreadcrumbsItem Href="@item.Href">
|
|
||||||
<span class="@(isLast ? "text-subtitle2" : "text-body2")">@item.Text</span>
|
|
||||||
</MBreadcrumbsItem>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</ChildContent>
|
|
||||||
</MBreadcrumbs>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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,62 +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.Core;
|
|
||||||
using ThingsGateway.Admin.Core.JsonExtensions;
|
|
||||||
|
|
||||||
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("注销失败", AlertTypes.Error);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await PopupService.EnqueueSnackbarAsync("注销成功", AlertTypes.Success);
|
|
||||||
await Task.Delay(500);
|
|
||||||
NavigationManager.NavigateTo(NavigationManager.Uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +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 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");
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
options.Locale = new Locale("zh-CN", "en-US");
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
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.3" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.10" />
|
|
||||||
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Update="wwwroot\**">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,894 +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>
|
|
||||||
<member name="T:BlazorComponent.I18n.CookieStorage">
|
|
||||||
<summary>
|
|
||||||
CookieStorage
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:BlazorComponent.I18n.CookieStorage.#ctor(Microsoft.JSInterop.IJSRuntime)">
|
|
||||||
<summary>
|
|
||||||
CookieStorage
|
|
||||||
</summary>
|
|
||||||
<param name="jsRuntime"></param>
|
|
||||||
</member>
|
|
||||||
<member name="M:BlazorComponent.I18n.CookieStorage.GetCookieAsync(System.String)">
|
|
||||||
<summary>
|
|
||||||
GetCookieAsync
|
|
||||||
</summary>
|
|
||||||
<param name="key"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:BlazorComponent.I18n.CookieStorage.GetCookie(System.String)">
|
|
||||||
<summary>
|
|
||||||
GetCookie
|
|
||||||
</summary>
|
|
||||||
<param name="key"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:BlazorComponent.I18n.CookieStorage.SetItemAsync``1(System.String,``0)">
|
|
||||||
<summary>
|
|
||||||
SetItemAsync
|
|
||||||
</summary>
|
|
||||||
<typeparam name="T"></typeparam>
|
|
||||||
<param name="key"></param>
|
|
||||||
<param name="value"></param>
|
|
||||||
</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,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 App.GetService<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 App.GetService<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;
|
|
||||||
|
|
||||||
@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,114 +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; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private string SearchKey { get; set; }
|
|
||||||
|
|
||||||
private Task AddCallAsync(RoleAddInput input)
|
|
||||||
{
|
|
||||||
return App.GetService<RoleService>().AddAsync(input);
|
|
||||||
}
|
|
||||||
private async Task DeleteCallAsync(IEnumerable<SysRole> sysRoles)
|
|
||||||
{
|
|
||||||
await App.GetService<RoleService>().DeleteAsync(sysRoles.Select(a => a.Id).ToArray());
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task EditCallAsync(RoleEditInput input)
|
|
||||||
{
|
|
||||||
await App.GetService<RoleService>().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 App.GetService<RoleService>().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 App.GetService<RoleService>().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 App.GetService<RoleService>().PageAsync(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ResuorceInitAsync()
|
|
||||||
{
|
|
||||||
ResTreeSelectors = (await App.GetService<ResourceService>().GetRoleGrantResourceMenusAsync());
|
|
||||||
RoleHasResuorces = (await App.GetService<RoleService>().OwnResourceAsync(ChoiceRoleId))?.GrantInfoList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<List<UserSelectorOutput>> UserInitAsync()
|
|
||||||
{
|
|
||||||
AllUsers = await App.GetService<SysUserService>().UserSelectorAsync(SearchKey);
|
|
||||||
var data = await App.GetService<RoleService>().OwnUserAsync(ChoiceRoleId);
|
|
||||||
UsersChoice = AllUsers.Where(a => data.Contains(a.Id)).ToList();
|
|
||||||
return AllUsers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,104 +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; }
|
|
||||||
|
|
||||||
|
|
||||||
private Task AddCallAsync(UserAddInput input)
|
|
||||||
{
|
|
||||||
return App.GetService<SysUserService>().AddAsync(input);
|
|
||||||
}
|
|
||||||
private async Task DeleteCallAsync(IEnumerable<SysUser> users)
|
|
||||||
{
|
|
||||||
await App.GetService<SysUserService>().DeleteAsync(users.Select(a => a.Id).ToArray());
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task EditCallAsync(UserEditInput users)
|
|
||||||
{
|
|
||||||
await App.GetService<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 App.GetService<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 App.GetService<SysUserService>().PageAsync(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ResetPasswordAsync(SysUser sysUser)
|
|
||||||
{
|
|
||||||
await App.GetService<SysUserService>().ResetPasswordAsync(sysUser.Id);
|
|
||||||
await PopupService.EnqueueSnackbarAsync(new("成功", AlertTypes.Success));
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RoleInitAsync()
|
|
||||||
{
|
|
||||||
AllRoles = await App.GetService<RoleService>().RoleSelectorAsync();
|
|
||||||
var data = await App.GetService<RoleService>().GetRoleIdListByUserIdAsync(ChoiceUserId);
|
|
||||||
RolesChoice = AllRoles.Where(a => data.Contains(a.Id)).ToList();
|
|
||||||
}
|
|
||||||
private async Task UserStatusChangeAsync(SysUser context, bool enable)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (enable)
|
|
||||||
await App.GetService<SysUserService>().EnableUserAsync(context.Id);
|
|
||||||
else
|
|
||||||
await App.GetService<SysUserService>().DisableUserAsync(context.Id);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await _datatable?.QueryClickAsync();
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,143 +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;
|
|
||||||
using ThingsGateway.Admin.Core.JsonExtensions;
|
|
||||||
|
|
||||||
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; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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 App.GetService<SysUserService>().GetIdByAccountAsync(loginModel.Account);
|
|
||||||
var data = await App.GetService<UserCenterService>().GetLoginDefaultRazorAsync(userId);
|
|
||||||
var sameLevelMenus = await App.GetService<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 = App.GetService<AuthService>().GetCaptchaInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task<string> RefreshCode()
|
|
||||||
{
|
|
||||||
CaptchaInfo = App.GetService<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,95 +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;
|
|
||||||
|
|
||||||
using ThingsGateway.Admin.Core.JsonExtensions;
|
|
||||||
|
|
||||||
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.42" />
|
|
||||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.8.42" />
|
|
||||||
<PackageReference Include="Furion.Pure" Version="4.8.8.42" />
|
|
||||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.104" />
|
|
||||||
<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,187 +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";
|
|
||||||
using var httpClient = new HttpClient();
|
|
||||||
using var stream = await httpClient.GetStreamAsync(url);
|
|
||||||
using 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,144 +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>
|
|
||||||
/// ManageGatewayConfig
|
|
||||||
/// </summary>
|
|
||||||
public class ManageGatewayConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 启用
|
|
||||||
/// </summary>
|
|
||||||
public bool Enable { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// MqttBrokerIP
|
|
||||||
/// </summary>
|
|
||||||
public string MqttBrokerIP { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// MqttBrokerPort
|
|
||||||
/// </summary>
|
|
||||||
public int MqttBrokerPort { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UserName
|
|
||||||
/// </summary>
|
|
||||||
public string UserName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Password
|
|
||||||
/// </summary>
|
|
||||||
public string Password { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DBDownTopic,Rpc返回为{DBDownTopic}/Return
|
|
||||||
/// </summary>
|
|
||||||
public string DBDownTopic { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DBUploadTopic,Rpc返回为{DBUploadTopic}/Return
|
|
||||||
/// </summary>
|
|
||||||
public string DBUploadTopic { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// WriteRpcTopic,Rpc返回为{WriteRpcTopic}/Return
|
|
||||||
/// </summary>
|
|
||||||
public string WriteRpcTopic { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用于Mqtt Json传输,上传/下载配置信息
|
|
||||||
/// </summary>
|
|
||||||
public class MqttDB
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 标识
|
|
||||||
/// </summary>
|
|
||||||
public string GatewayId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 采集设备
|
|
||||||
/// </summary>
|
|
||||||
public List<CollectDevice> CollectDevices { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// true=>删除全部后增加
|
|
||||||
/// </summary>
|
|
||||||
public bool IsCollectDevicesFullUp { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 上传设备
|
|
||||||
/// </summary>
|
|
||||||
public List<UploadDevice> UploadDevices { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// true=>删除全部后增加
|
|
||||||
/// </summary>
|
|
||||||
public bool IsUploadDevicesFullUp { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 变量
|
|
||||||
/// </summary>
|
|
||||||
public List<DeviceVariable> DeviceVariables { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// true=>删除全部后增加
|
|
||||||
/// </summary>
|
|
||||||
public bool IsDeviceVariablesFullUp { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 配置项
|
|
||||||
/// </summary>
|
|
||||||
public List<SysConfig> SysConfigs { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// MqttRpc传入
|
|
||||||
/// </summary>
|
|
||||||
public class ManageMqttRpcFrom
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 标识
|
|
||||||
/// </summary>
|
|
||||||
public string GatewayId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 标识
|
|
||||||
/// </summary>
|
|
||||||
public string RpcId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// "WriteInfos":{"test":"1"}
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, string> WriteInfos { get; set; } = new();
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// MqttRpc输出
|
|
||||||
/// </summary>
|
|
||||||
public class ManageMqttRpcResult
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 标识
|
|
||||||
/// </summary>
|
|
||||||
public string GatewayId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 标识
|
|
||||||
/// </summary>
|
|
||||||
public string RpcId { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 消息
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, OperResult> Message { get; set; } = new();
|
|
||||||
/// <summary>
|
|
||||||
/// 是否成功
|
|
||||||
/// </summary>
|
|
||||||
public bool Success { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,289 +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.Extensions;
|
|
||||||
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
using MQTTnet;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
using ThingsGateway.Foundation;
|
|
||||||
|
|
||||||
using TouchSocket.Core;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Application;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设备采集报警后台服务
|
|
||||||
/// </summary>
|
|
||||||
public class ManageGatewayWorker : BackgroundService
|
|
||||||
{
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly ILogger _manageLogger;
|
|
||||||
private readonly ILogger _clientLogger;
|
|
||||||
/// <inheritdoc cref="ManageGatewayWorker"/>
|
|
||||||
public ManageGatewayWorker(ILoggerFactory loggerFactory)
|
|
||||||
{
|
|
||||||
_logger = loggerFactory.CreateLogger("ManageGatewayWorker");
|
|
||||||
_manageLogger = loggerFactory.CreateLogger("管理网关(mqttBroker)");
|
|
||||||
_clientLogger = loggerFactory.CreateLogger("子网关(mqttClient)");
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 服务状态
|
|
||||||
/// </summary>
|
|
||||||
public OperResult RealAlarmStatuString { get; set; } = new OperResult("初始化");
|
|
||||||
/// <summary>
|
|
||||||
/// 服务状态
|
|
||||||
/// </summary>
|
|
||||||
public OperResult HisAlarmStatuString { get; set; } = new OperResult("初始化");
|
|
||||||
/// <summary>
|
|
||||||
/// 服务状态
|
|
||||||
/// </summary>
|
|
||||||
public OperResult ReadAlarmStatuString { get; set; } = new OperResult("初始化");
|
|
||||||
private MqttServer _mqttServer;
|
|
||||||
private IMqttClient _mqttClient;
|
|
||||||
|
|
||||||
|
|
||||||
#region worker服务
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override async Task StartAsync(CancellationToken token)
|
|
||||||
{
|
|
||||||
_logger?.LogInformation("ManageGatewayWorker启动");
|
|
||||||
await RestartAsync();
|
|
||||||
await base.StartAsync(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override Task StopAsync(CancellationToken token)
|
|
||||||
{
|
|
||||||
_logger?.LogInformation("ManageGatewayWorker停止");
|
|
||||||
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>
|
|
||||||
private readonly EasyLock restartLock = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 重启
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task RestartAsync()
|
|
||||||
{
|
|
||||||
await StopAsync();
|
|
||||||
await StartAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task StartAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//重启操作在未完全之前直接取消
|
|
||||||
if (restartLock.IsWaitting)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await restartLock.WaitAsync();
|
|
||||||
|
|
||||||
await InitAsync();
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "启动错误");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
restartLock.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal async Task StopAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//重启操作在未完全之前直接取消
|
|
||||||
if (restartLock.IsWaitting)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await restartLock.WaitAsync();
|
|
||||||
_mqttClient?.SafeDispose();
|
|
||||||
_mqttServer?.SafeDispose();
|
|
||||||
_mqttClient = null;
|
|
||||||
_mqttServer = null;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "停止错误");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
restartLock.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化
|
|
||||||
/// </summary>
|
|
||||||
private async Task InitAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var manageGatewayConfig = App.GetConfig<ManageGatewayConfig>("ManageGatewayConfig");
|
|
||||||
if (manageGatewayConfig?.Enable != true)
|
|
||||||
{
|
|
||||||
HisAlarmStatuString = new OperResult($"已退出:不启用管理功能");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var mqttFactory = new MqttFactory(new MqttNetLogger(_manageLogger));
|
|
||||||
var mqttServerOptions = mqttFactory.CreateServerOptionsBuilder()
|
|
||||||
.WithDefaultEndpointBoundIPAddress(string.IsNullOrEmpty(manageGatewayConfig.MqttBrokerIP) ? null : IPAddress.Parse(manageGatewayConfig.MqttBrokerIP))
|
|
||||||
.WithDefaultEndpointPort(manageGatewayConfig.MqttBrokerPort)
|
|
||||||
.WithDefaultEndpoint()
|
|
||||||
.Build();
|
|
||||||
_mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions);
|
|
||||||
if (_mqttServer != null)
|
|
||||||
{
|
|
||||||
_mqttServer.ValidatingConnectionAsync += MqttServer_ValidatingConnectionAsync;
|
|
||||||
_mqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync;
|
|
||||||
_mqttServer.LoadingRetainedMessageAsync += MqttServer_LoadingRetainedMessageAsync;
|
|
||||||
_mqttServer.InterceptingSubscriptionAsync += MqttServer_InterceptingSubscriptionAsync; ;
|
|
||||||
await _mqttServer.StartAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_manageLogger.LogError(ex, "初始化失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var clientGatewayConfig = App.GetConfig<ManageGatewayConfig>("ClientGatewayConfig");
|
|
||||||
if (clientGatewayConfig?.Enable != true)
|
|
||||||
{
|
|
||||||
RealAlarmStatuString = new OperResult($"已退出:不启用子网关功能");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var mqttFactory = new MqttFactory(new MqttNetLogger(_clientLogger));
|
|
||||||
var _mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
|
||||||
.WithCredentials(clientGatewayConfig.UserName, clientGatewayConfig.Password)//账密
|
|
||||||
.WithTcpServer(clientGatewayConfig.MqttBrokerIP, clientGatewayConfig.MqttBrokerPort)//服务器
|
|
||||||
.WithCleanSession(true)
|
|
||||||
.WithKeepAlivePeriod(TimeSpan.FromSeconds(120.0))
|
|
||||||
.WithoutThrowOnNonSuccessfulConnectResponse()
|
|
||||||
.Build();
|
|
||||||
var _mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
|
||||||
.WithTopicFilter(
|
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic(clientGatewayConfig.WriteRpcTopic);
|
|
||||||
f.WithAtMostOnceQoS();
|
|
||||||
})
|
|
||||||
.WithTopicFilter(
|
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic(clientGatewayConfig.DBDownTopic);
|
|
||||||
f.WithAtMostOnceQoS();
|
|
||||||
})
|
|
||||||
.WithTopicFilter(
|
|
||||||
f =>
|
|
||||||
{
|
|
||||||
f.WithTopic(clientGatewayConfig.DBUploadTopic);
|
|
||||||
f.WithAtMostOnceQoS();
|
|
||||||
})
|
|
||||||
.Build();
|
|
||||||
_mqttClient = mqttFactory.CreateMqttClient();
|
|
||||||
_mqttClient.ConnectedAsync += MqttClient_ConnectedAsync;
|
|
||||||
_mqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_clientLogger.LogError(ex, "初始化失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs args)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MqttClient_ConnectedAsync(MqttClientConnectedEventArgs args)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MqttServer_InterceptingSubscriptionAsync(InterceptingSubscriptionEventArgs args)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MqttServer_LoadingRetainedMessageAsync(LoadingRetainedMessagesEventArgs args)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs args)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task MqttServer_ValidatingConnectionAsync(ValidatingConnectionEventArgs args)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,50 +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 MQTTnet.Diagnostics;
|
|
||||||
|
|
||||||
|
|
||||||
namespace ThingsGateway.Application;
|
|
||||||
|
|
||||||
internal class MqttNetLogger : IMqttNetLogger
|
|
||||||
{
|
|
||||||
readonly ILogger LogMessage;
|
|
||||||
public MqttNetLogger(ILogger logger)
|
|
||||||
{
|
|
||||||
LogMessage = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsEnabled => true;
|
|
||||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
|
||||||
{
|
|
||||||
switch (logLevel)
|
|
||||||
{
|
|
||||||
case MqttNetLogLevel.Verbose:
|
|
||||||
LogMessage?.Log(LogLevel.Trace, source, message != null ? (parameters != null ? message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty : message) : string.Empty, exception);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MqttNetLogLevel.Info:
|
|
||||||
LogMessage?.Log(LogLevel.Information, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MqttNetLogLevel.Warning:
|
|
||||||
LogMessage?.Log(LogLevel.Warning, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MqttNetLogLevel.Error:
|
|
||||||
LogMessage?.Log(LogLevel.Warning, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,256 +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 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>
|
|
||||||
/// 一般底层驱动,也有可能为null
|
|
||||||
/// </summary>
|
|
||||||
protected abstract IReadWriteDevice PLC { 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 serialSession = new SerialsSession();
|
|
||||||
(serialSession).Setup(FoundataionConfig);
|
|
||||||
return OperResult.CreateSuccessResult((object)serialSession);
|
|
||||||
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, isOnline: false);
|
|
||||||
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 virtual async Task<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<DeviceVariableRunTime, JToken> writeInfoLists, CancellationToken token)
|
|
||||||
{
|
|
||||||
if (PLC == null)
|
|
||||||
throw new("未初始化成功");
|
|
||||||
Dictionary<string, OperResult> operResults = new();
|
|
||||||
foreach (var writeInfo in writeInfoLists)
|
|
||||||
{
|
|
||||||
var result = await PLC.WriteAsync(writeInfo.Key.VariableAddress, writeInfo.Key.DataType, writeInfo.Value.ToString(), token);
|
|
||||||
await Task.Delay(10, token); //防止密集写入
|
|
||||||
operResults.Add(writeInfo.Key.Name, result);
|
|
||||||
}
|
|
||||||
return operResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void NewMessage(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4)
|
|
||||||
{
|
|
||||||
if (IsSaveLog)
|
|
||||||
{
|
|
||||||
if (arg3.StartsWith(FoundationConst.LogMessageHeader))
|
|
||||||
{
|
|
||||||
var customLevel = App.GetConfig<Microsoft.Extensions.Logging.LogLevel?>("Logging:LogLevel:BackendLog") ?? Microsoft.Extensions.Logging.LogLevel.Trace;
|
|
||||||
if ((byte)arg1 < (byte)customLevel)
|
|
||||||
{
|
|
||||||
var logRuntime = new BackendLog
|
|
||||||
{
|
|
||||||
LogLevel = (Microsoft.Extensions.Logging.LogLevel)arg1,
|
|
||||||
LogMessage = arg3,
|
|
||||||
LogSource = "采集设备:" + CurDevice.Name,
|
|
||||||
LogTime = SysDateTimeExtensions.CurrentDateTime,
|
|
||||||
Exception = null,
|
|
||||||
};
|
|
||||||
_logQueues.Enqueue(logRuntime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
base.NewMessage(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,150 +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 System.Collections.Concurrent;
|
|
||||||
|
|
||||||
using ThingsGateway.Foundation;
|
|
||||||
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
|
|
||||||
|
|
||||||
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));
|
|
||||||
Task.Factory.StartNew(LogInsertAsync);
|
|
||||||
}
|
|
||||||
/// <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 bool IsSaveLog { 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>
|
|
||||||
protected ConcurrentQueue<BackendLog> _logQueues = new();
|
|
||||||
/// <summary>
|
|
||||||
/// 设备报文
|
|
||||||
/// </summary>
|
|
||||||
internal virtual 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
private async Task LogInsertAsync()
|
|
||||||
{
|
|
||||||
var db = DbContext.Db.CopyNew();
|
|
||||||
while (!DisposedValue)
|
|
||||||
{
|
|
||||||
if (_logQueues.Count > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var data = _logQueues.ToListWithDequeue();
|
|
||||||
await db.InsertableWithAttr(data).ExecuteCommandAsync();//入库
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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,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 Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
using ThingsGateway.Foundation;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void NewMessage(TouchSocket.Core.LogLevel arg1, object arg2, string arg3, Exception arg4)
|
|
||||||
{
|
|
||||||
if (IsSaveLog)
|
|
||||||
{
|
|
||||||
if (arg3.StartsWith(FoundationConst.LogMessageHeader))
|
|
||||||
{
|
|
||||||
var customLevel = App.GetConfig<Microsoft.Extensions.Logging.LogLevel?>("Logging:LogLevel:BackendLog") ?? Microsoft.Extensions.Logging.LogLevel.Trace;
|
|
||||||
if ((byte)arg1 < (byte)customLevel)
|
|
||||||
{
|
|
||||||
var logRuntime = new BackendLog
|
|
||||||
{
|
|
||||||
LogLevel = (Microsoft.Extensions.Logging.LogLevel)arg1,
|
|
||||||
LogMessage = arg3,
|
|
||||||
LogSource = "上传设备:" + CurDevice.Name,
|
|
||||||
LogTime = SysDateTimeExtensions.CurrentDateTime,
|
|
||||||
Exception = null,
|
|
||||||
};
|
|
||||||
_logQueues.Enqueue(logRuntime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
base.NewMessage(arg1, arg2, arg3, arg4);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,237 +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.Admin.Core.JsonExtensions;
|
|
||||||
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="items">指定键为变量名称,值为附带方法参数或写入值</param>
|
|
||||||
/// <param name="isBlazor">如果是true,不检查<see cref="MemoryVariable.RpcWriteEnable"/>字段</param>
|
|
||||||
/// <param name="token"><see cref="CancellationToken"/> 取消源</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<Dictionary<string, OperResult>> InvokeDeviceMethodAsync(string sourceDes, Dictionary<string, string> items, bool isBlazor = false, CancellationToken token = default)
|
|
||||||
{
|
|
||||||
Dictionary<CollectDeviceCore, Dictionary<DeviceVariableRunTime, JToken>> WriteVariables = new();
|
|
||||||
Dictionary<CollectDeviceCore, Dictionary<DeviceVariableRunTime, string>> WriteMethods = new();
|
|
||||||
Dictionary<string, OperResult> results = new();
|
|
||||||
foreach (var item in items)
|
|
||||||
{
|
|
||||||
|
|
||||||
OperResult data = new();
|
|
||||||
var tag = _globalDeviceData.AllVariables.FirstOrDefault(it => it.Name == item.Key);
|
|
||||||
if (tag == null)
|
|
||||||
results.Add(item.Key, new("不存在变量:" + item.Key));
|
|
||||||
if (tag.ProtectTypeEnum == ProtectTypeEnum.ReadOnly)
|
|
||||||
results.Add(item.Key, new("只读变量:" + item.Key));
|
|
||||||
if (!tag.RpcWriteEnable && !isBlazor)
|
|
||||||
results.Add(item.Key, new("不允许远程写入:" + item.Key));
|
|
||||||
|
|
||||||
if (tag.IsMemoryVariable == true)
|
|
||||||
{
|
|
||||||
results.Add(item.Key, tag.SetValue(item.Value));
|
|
||||||
}
|
|
||||||
var dev = _collectDeviceHostService.CollectDeviceCores.FirstOrDefault(it => it.Device.Id == tag.DeviceId);
|
|
||||||
if (dev == null)
|
|
||||||
results.Add(item.Key, new OperResult("系统错误,不存在对应采集设备,请稍候重试"));
|
|
||||||
if (dev.Device.DeviceStatus == DeviceStatusEnum.OffLine)
|
|
||||||
results.Add(item.Key, new OperResult("设备已离线"));
|
|
||||||
if (dev.Device.DeviceStatus == DeviceStatusEnum.Pause)
|
|
||||||
results.Add(item.Key, new OperResult("设备已暂停"));
|
|
||||||
|
|
||||||
if (!results.ContainsKey(item.Key))
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(tag.OtherMethod))
|
|
||||||
{
|
|
||||||
//写入变量
|
|
||||||
JToken tagValue;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
tagValue = JToken.Parse(item.Value);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
tagValue = JToken.Parse("\"" + item.Value + "\"");
|
|
||||||
}
|
|
||||||
if (WriteVariables.ContainsKey(dev))
|
|
||||||
{
|
|
||||||
|
|
||||||
WriteVariables[dev].Add(tag, tagValue);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriteVariables.Add(dev, new());
|
|
||||||
WriteVariables[dev].Add(tag, tagValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (WriteMethods.ContainsKey(dev))
|
|
||||||
{
|
|
||||||
|
|
||||||
WriteMethods[dev].Add(tag, item.Value);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriteVariables.Add(dev, new());
|
|
||||||
WriteVariables[dev].Add(tag, item.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in WriteVariables)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var result = await item.Key.InVokeWriteAsync(item.Value, token);
|
|
||||||
foreach (var resultItem in result)
|
|
||||||
{
|
|
||||||
string operObj;
|
|
||||||
string parJson;
|
|
||||||
if (resultItem.Key.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
operObj = items.Select(x => x.Key).ToJsonString();
|
|
||||||
parJson = items.Select(x => x.Value).ToJsonString();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
operObj = resultItem.Key;
|
|
||||||
parJson = items[resultItem.Key];
|
|
||||||
|
|
||||||
}
|
|
||||||
_logQueues.Enqueue(
|
|
||||||
new RpcLog()
|
|
||||||
{
|
|
||||||
LogTime = SysDateTimeExtensions.CurrentDateTime,
|
|
||||||
OperateMessage = resultItem.Value.Exception,
|
|
||||||
IsSuccess = resultItem.Value.IsSuccess,
|
|
||||||
OperateMethod = WriteVariable,
|
|
||||||
OperateObject = operObj,
|
|
||||||
OperateSource = sourceDes,
|
|
||||||
ParamJson = parJson,
|
|
||||||
ResultJson = resultItem.Value.Message
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!resultItem.Value.IsSuccess)
|
|
||||||
{
|
|
||||||
_logger.LogWarning($"写入变量[{resultItem.Key}]失败:{resultItem.Value.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
results.AddRange(result);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning($"写入变量异常:{ex.Message + Environment.NewLine + ex.StackTrace}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (var item in WriteMethods)
|
|
||||||
{
|
|
||||||
foreach (var writeMethod in item.Value)
|
|
||||||
{
|
|
||||||
|
|
||||||
//执行变量附带的方法
|
|
||||||
var method = item.Key.DeviceVariableMethodSources.FirstOrDefault(it => it.DeviceVariable == writeMethod.Key);
|
|
||||||
OperResult<string> result;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result = await item.Key.InvokeMethodAsync(method, false, writeMethod.Value, token);
|
|
||||||
results.Add(writeMethod.Key.Name, result);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
result = new OperResult<string>(ex);
|
|
||||||
results.Add(writeMethod.Key.Name, result);
|
|
||||||
}
|
|
||||||
_logQueues.Enqueue(
|
|
||||||
new RpcLog()
|
|
||||||
{
|
|
||||||
LogTime = SysDateTimeExtensions.CurrentDateTime,
|
|
||||||
OperateMessage = result.Exception,
|
|
||||||
IsSuccess = result.IsSuccess,
|
|
||||||
OperateMethod = writeMethod.Key.OtherMethod,
|
|
||||||
OperateObject = writeMethod.Key.Name,
|
|
||||||
OperateSource = sourceDes,
|
|
||||||
ParamJson = writeMethod.Value?.ToString(),
|
|
||||||
ResultJson = result.Message
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (!result.IsSuccess)
|
|
||||||
{
|
|
||||||
_logger.LogWarning($"执行变量[{writeMethod.Key.Name}]方法[{writeMethod.Key.OtherMethod}]失败:{result.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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,30 +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")
|
|
||||||
.Concat(SeedDataUtil.GetSeedData<DriverPlugin>("pro_driver_plugin.json"))
|
|
||||||
.Concat(SeedDataUtil.GetSeedData<DriverPlugin>("custom_driver_plugin.json"))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,357 +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
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"Id": 442505,
|
|
||||||
"FileName": "ThingsGateway.DLT645",
|
|
||||||
"AssembleName": "DLT645_2007",
|
|
||||||
"DriverTypeEnum": "Collect",
|
|
||||||
"FilePath": "Plugins/ThingsGateway.DLT645/ThingsGateway.DLT645.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": 442506,
|
|
||||||
"FileName": "ThingsGateway.DLT645",
|
|
||||||
"AssembleName": "DLT645_2007OverTcp",
|
|
||||||
"DriverTypeEnum": "Collect",
|
|
||||||
"FilePath": "Plugins/ThingsGateway.DLT645/ThingsGateway.DLT645.dll",
|
|
||||||
"CreateTime": "2023/8/6 18:22:33",
|
|
||||||
"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,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,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,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 newDevs = input.Adapt<List<UploadDevice>>();
|
|
||||||
newDevs.ForEach(a =>
|
|
||||||
{
|
|
||||||
var newId = YitIdHelper.NextId();
|
|
||||||
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,49 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="SeedData\Json\driver_plugin.json" />
|
|
||||||
<None Remove="SeedData\Json\gateway_openapi_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\gateway_openapi_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.7.0" />
|
|
||||||
<PackageReference Include="MQTTnet" Version="4.2.1.781" />
|
|
||||||
</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.FromJsonString<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.ToJsonString(), 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,780 +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
|
|
||||||
{
|
|
||||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime);
|
|
||||||
|
|
||||||
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;
|
|
||||||
Device.SetDeviceStatus(SysDateTimeExtensions.CurrentDateTime);
|
|
||||||
|
|
||||||
_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 (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.ToJsonString());
|
|
||||||
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, errorCount: 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Device.SetDeviceStatus(errorCount: 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()?.TrimEnd(';').Split(';');
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int index = 0;
|
|
||||||
for (int i = 0; i < ps.Length; i++)
|
|
||||||
{
|
|
||||||
if (typeof(CancellationToken).IsAssignableFrom(ps[i].ParameterType))
|
|
||||||
{
|
|
||||||
methodResult.HasTokenObj = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (strs.Length <= index)
|
|
||||||
continue;
|
|
||||||
//得到对于的方法参数值
|
|
||||||
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()?.TrimEnd(';').Split(';');
|
|
||||||
string[] strs2 = value?.Trim()?.TrimEnd(';').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", ""));
|
|
||||||
if (isRead)
|
|
||||||
{
|
|
||||||
var operResult = deviceVariableMethodSource.DeviceVariable.SetValue(content);
|
|
||||||
if (!operResult.IsSuccess)
|
|
||||||
{
|
|
||||||
_logger?.LogWarning(operResult.Message, ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isRead && !result.IsSuccess)
|
|
||||||
{
|
|
||||||
var operResult = deviceVariableMethodSource.DeviceVariable.SetValue(null, isOnline: false);
|
|
||||||
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<Dictionary<string, OperResult>> InVokeWriteAsync(Dictionary<DeviceVariableRunTime, JToken> writeInfoLists, CancellationToken token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await easyLock.WaitAsync();
|
|
||||||
if (IsShareChannel) _driver.InitDataAdapter();
|
|
||||||
Dictionary<string, OperResult> results = new();
|
|
||||||
foreach (var deviceVariable in writeInfoLists.Keys)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(deviceVariable.WriteExpressions))
|
|
||||||
{
|
|
||||||
var jToken = writeInfoLists[deviceVariable];
|
|
||||||
object rawdata;
|
|
||||||
if (jToken is JValue jValue)
|
|
||||||
{
|
|
||||||
rawdata = jValue.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rawdata = jToken.ToString();
|
|
||||||
}
|
|
||||||
object data;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
data = deviceVariable.WriteExpressions.GetExpressionsResult(rawdata);
|
|
||||||
writeInfoLists[deviceVariable] = JToken.FromObject(data);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
results.Add(deviceVariable.Name, new OperResult(deviceVariable.Name + " 转换写入表达式失败:" + ex.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
var result = await _driver.WriteValuesAsync(writeInfoLists.
|
|
||||||
Where(a => !results.Any(b => b.Key == a.Key.Name)).
|
|
||||||
ToDictionary(item => item.Key, item => item.Value),
|
|
||||||
token);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
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,570 +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 StartOtherHostService();
|
|
||||||
|
|
||||||
//开始全部采集线程
|
|
||||||
await StartAllDeviceThreadsAsync();
|
|
||||||
|
|
||||||
}
|
|
||||||
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.FromJsonString<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.ToJsonString(), 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.ToJsonString(), 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:
|
|
||||||
<SerialSessionPage @ref=SerialSessionPage></SerialSessionPage>
|
|
||||||
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
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人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 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>
|
|
||||||
/// SerialSessionPage
|
|
||||||
/// </summary>
|
|
||||||
public SerialSessionPage SerialSessionPage;
|
|
||||||
/// <summary>
|
|
||||||
/// TcpClientPage
|
|
||||||
/// </summary>
|
|
||||||
public TcpClientPage TcpClientPage;
|
|
||||||
/// <summary>
|
|
||||||
/// TcpServerPage
|
|
||||||
/// </summary>
|
|
||||||
public TcpServerPage TcpServerPage;
|
|
||||||
/// <summary>
|
|
||||||
/// UdpSessionPage
|
|
||||||
/// </summary>
|
|
||||||
public UdpSessionPage UdpSessionPage;
|
|
||||||
/// <summary>
|
|
||||||
/// 选择,1-TCPCLIENT,2-串口,3-UDP,4-TCPServer
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public ChannelEnum Channel { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// </summary>
|
|
||||||
public override ThingsGateway.Foundation.IReadWriteDevice Plc { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 模板
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义模板
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment OtherContent { get; set; }
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override void Dispose()
|
|
||||||
{
|
|
||||||
Plc?.SafeDispose();
|
|
||||||
TcpClientPage?.SafeDispose();
|
|
||||||
SerialSessionPage?.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 (SerialSessionPage != null)
|
|
||||||
SerialSessionPage.LogAction = LogOut;
|
|
||||||
if (TcpServerPage != null)
|
|
||||||
TcpServerPage.LogAction = LogOut;
|
|
||||||
if (UdpSessionPage != null)
|
|
||||||
UdpSessionPage.LogAction = LogOut;
|
|
||||||
//载入配置
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
base.OnAfterRender(firstRender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,257 +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.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 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 App.GetService<CollectDeviceService>().AddAsync(data);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
isDownExport = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 导入变量
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task DeviceVariableImportAsync(List<DeviceVariable> data)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
isDownExport = true;
|
|
||||||
StateHasChanged();
|
|
||||||
await App.GetService<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 App.GetService<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 App.GetService<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,304 +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 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.Application;
|
|
||||||
|
|
||||||
@attribute [Authorize]
|
|
||||||
@inherits BaseComponentBase
|
|
||||||
@inject UserResoures UserResoures
|
|
||||||
|
|
||||||
@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>
|
|
||||||
@(
|
|
||||||
App.GetService<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 App.GetService<CollectDeviceService>().AddAsync(input);
|
|
||||||
CollectDevices = App.GetService<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 App.GetService<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 App.GetService<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 App.GetService<CollectDeviceService>().DeleteAsync(input.Select(a => a.Id).ToArray());
|
|
||||||
CollectDevices = App.GetService<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 App.GetService<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 App.GetService<CollectDeviceService>().EditAsync(input);
|
|
||||||
CollectDevices = App.GetService<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 App.GetService<CollectDeviceService>().PageAsync(input);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task SaveDeviceImportAsync(Dictionary<string, ImportPreviewOutputBase> data)
|
|
||||||
{
|
|
||||||
await App.GetService<CollectDeviceService>().ImportAsync(data);
|
|
||||||
await DatatableQueryAsync();
|
|
||||||
ImportExcel.IsShowImport = false;
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +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;
|
|
||||||
|
|
||||||
@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 App.GetService<ConfigService>().EditBatchAsync(_alarmConfig);
|
|
||||||
await AlarmHostService.RestartAsync();
|
|
||||||
await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnHisSaveAsync()
|
|
||||||
{
|
|
||||||
var confirm = await PopupService.OpenConfirmDialogAsync("确认", "保存配置后将重启历史服务,是否确定?");
|
|
||||||
if (confirm)
|
|
||||||
{
|
|
||||||
await App.GetService<ConfigService>().EditBatchAsync(_hisConfig);
|
|
||||||
await HistoryValueHostService.RestartAsync();
|
|
||||||
await PopupService.EnqueueSnackbarAsync("成功", AlertTypes.Success);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,647 +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;
|
|
||||||
|
|
||||||
@attribute [Authorize]
|
|
||||||
@inherits BaseComponentBase
|
|
||||||
@inject UserResoures UserResoures
|
|
||||||
@inject NavigationManager NavigationManager
|
|
||||||
|
|
||||||
@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 Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicepause")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
|
||||||
OnClick=@(()=>
|
|
||||||
{
|
|
||||||
if(collectDeviceInfoItem.Driver!=null)
|
|
||||||
collectDeviceInfoItem.Driver.IsSaveLog=! collectDeviceInfoItem.Driver.IsSaveLog;
|
|
||||||
}
|
|
||||||
)>
|
|
||||||
<MIcon>@((collectDeviceInfoItem.Driver?.IsSaveLog == true) ? "mdi-pause" : "mdi-play")</MIcon>
|
|
||||||
</MButton>
|
|
||||||
</ActivatorContent>
|
|
||||||
<ChildContent>
|
|
||||||
<span>@((collectDeviceInfoItem.Driver?.IsSaveLog != true) ? "存入数据库,注意若交互频繁,可能导致数据库太大" : "不存入数据库")</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 Disabled=@(!UserResoures.IsHasButtonWithRole("gatewaydevicepause")) Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
|
||||||
OnClick=@(()=>
|
|
||||||
{
|
|
||||||
if(uploadDeviceInfoItem.Driver!=null)
|
|
||||||
uploadDeviceInfoItem.Driver.IsSaveLog=! uploadDeviceInfoItem.Driver.IsSaveLog;
|
|
||||||
}
|
|
||||||
)>
|
|
||||||
<MIcon>@((uploadDeviceInfoItem.Driver?.IsSaveLog == true) ? "mdi-pause" : "mdi-play")</MIcon>
|
|
||||||
</MButton>
|
|
||||||
</ActivatorContent>
|
|
||||||
<ChildContent>
|
|
||||||
<span>@((uploadDeviceInfoItem.Driver?.IsSaveLog != true) ? "存入数据库,注意若交互频繁,可能导致数据库太大" : "不存入数据库")</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,274 +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 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>
|
|
||||||
/// 设备状态页面
|
|
||||||
/// </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; }
|
|
||||||
|
|
||||||
/// <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("重启", "确定重启?");
|
|
||||||
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 ? "启动" : "暂停";
|
|
||||||
var confirm = await PopupService.OpenConfirmDialogAsync(str, $"确定{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", $"报文导出{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.txt", streamRef);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
isDownExport = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//去除单个采集重启
|
|
||||||
//async Task RestartAsync(long devId)
|
|
||||||
//{
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// var confirm = await PopupService.OpenConfirmDialogAsync("重启", "确定重启?");
|
|
||||||
// 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 ? "启动" : "暂停";
|
|
||||||
var confirm = await PopupService.OpenConfirmDialogAsync(str, $"确定{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("重启", "确定重启?");
|
|
||||||
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,67 +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 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();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// </summary>
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
DriverPlugins = App.GetService<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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +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/hisalarm"
|
|
||||||
@namespace ThingsGateway.Blazor
|
|
||||||
@using System.Linq.Expressions;
|
|
||||||
@using BlazorComponent;
|
|
||||||
@using Furion.DataValidation;
|
|
||||||
@using Mapster;
|
|
||||||
@using Masa.Blazor.Presets;
|
|
||||||
@using System.IO;
|
|
||||||
@using Masa.Blazor;
|
|
||||||
@using Microsoft.AspNetCore.Authorization;
|
|
||||||
@using ThingsGateway.Admin.Blazor.Core;
|
|
||||||
@using ThingsGateway.Admin.Blazor;
|
|
||||||
@using ThingsGateway.Admin.Core;
|
|
||||||
@using ThingsGateway.Application;
|
|
||||||
@using TouchSocket.Core;
|
|
||||||
@attribute [Authorize]
|
|
||||||
@inherits BaseComponentBase
|
|
||||||
@inject UserResoures UserResoures
|
|
||||||
@layout MainLayout
|
|
||||||
<AppDataTable @ref="_datatable" TItem="HistoryAlarm" SearchItem="HisPageInput"
|
|
||||||
AddItem="object" EditItem="object" IsShowSelect=false
|
|
||||||
IsMenuOperTemplate=false SearchModel="SearchModel"
|
|
||||||
QueryCallAsync="QueryCallAsync"
|
|
||||||
IsShowDetailButton
|
|
||||||
IsShowQueryButton>
|
|
||||||
<SearchTemplate>
|
|
||||||
|
|
||||||
<MMenu CloseOnContentClick="false" OffsetY Context="menu">
|
|
||||||
<ActivatorContent>
|
|
||||||
<MTextField Dense Readonly Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 "
|
|
||||||
Value="context.StartTime.Value.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)"
|
|
||||||
@attributes="menu.Attrs" Outlined Label=@context.Description(x => x.StartTime) />
|
|
||||||
</ActivatorContent>
|
|
||||||
<ChildContent>
|
|
||||||
<AppDateTimePicker @bind-Value="context.StartTime"></AppDateTimePicker>
|
|
||||||
</ChildContent>
|
|
||||||
</MMenu>
|
|
||||||
|
|
||||||
<MMenu CloseOnContentClick="false" OffsetY Context="menu">
|
|
||||||
<ActivatorContent>
|
|
||||||
<MTextField Dense Readonly Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 "
|
|
||||||
Value="context.EndTime.Value.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)" Clearable
|
|
||||||
@attributes="menu.Attrs" Outlined Label=@context.Description(x => x.EndTime) />
|
|
||||||
</ActivatorContent>
|
|
||||||
<ChildContent>
|
|
||||||
<AppDateTimePicker @bind-Value="context.EndTime"></AppDateTimePicker>
|
|
||||||
</ChildContent>
|
|
||||||
</MMenu>
|
|
||||||
|
|
||||||
|
|
||||||
<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.DeviceName" Clearable
|
|
||||||
Outlined Label=@context.Description(x => x.DeviceName) />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</SearchTemplate>
|
|
||||||
|
|
||||||
|
|
||||||
<ItemColTemplate>
|
|
||||||
@switch (context.Header.Value)
|
|
||||||
{
|
|
||||||
case nameof(context.Item.IsOnline):
|
|
||||||
<EnableChip Value="context.Item.IsOnline" DisabledLabel="离线" EnabledLabel="在线">
|
|
||||||
</EnableChip>
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
@if (context.Header.CellClass?.Contains("text-truncate") == true)
|
|
||||||
{
|
|
||||||
<span title=@context.Value>
|
|
||||||
@context.Value
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@context.Value
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
</ItemColTemplate>
|
|
||||||
|
|
||||||
</AppDataTable>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,103 +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 SqlSugar;
|
|
||||||
|
|
||||||
using ThingsGateway.Admin.Blazor.Core;
|
|
||||||
using ThingsGateway.Admin.Core;
|
|
||||||
using ThingsGateway.Application;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Blazor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 历史报警页面
|
|
||||||
/// </summary>
|
|
||||||
public partial class HistoryAlarmPage
|
|
||||||
{
|
|
||||||
private IAppDataTable _datatable;
|
|
||||||
|
|
||||||
|
|
||||||
AlarmWorker AlarmHostService { get; set; }
|
|
||||||
[Inject]
|
|
||||||
InitTimezone InitTimezone { get; set; }
|
|
||||||
|
|
||||||
HisPageInput SearchModel { get; set; } = new();
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
AlarmHostService = ServiceHelper.GetBackgroundService<AlarmWorker>();
|
|
||||||
await base.OnInitializedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DatatableQuery()
|
|
||||||
{
|
|
||||||
await _datatable?.QueryClickAsync();
|
|
||||||
}
|
|
||||||
private async Task<SqlSugarPagedList<HistoryAlarm>> QueryCallAsync(HisPageInput input)
|
|
||||||
{
|
|
||||||
var result = await AlarmHostService.GetAlarmDbAsync();
|
|
||||||
if (result.IsSuccess)
|
|
||||||
{
|
|
||||||
return await Task.Run(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var query = result.Content.CopyNew().Queryable<HistoryAlarm>().
|
|
||||||
WhereIF(!input.DeviceName.IsNullOrEmpty(), a => a.DeviceName.Contains(input.DeviceName))
|
|
||||||
.WhereIF(!input.Name.IsNullOrEmpty(), a => a.Name.Contains(input.Name))
|
|
||||||
.WhereIF(input.StartTime != null, a => a.EventTime >= input.StartTime.Value.ToLocalTime())
|
|
||||||
.WhereIF(input.EndTime != null, a => a.EventTime <= input.EndTime.Value.ToLocalTime());
|
|
||||||
|
|
||||||
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")}");
|
|
||||||
}
|
|
||||||
var data = await query.ToPagedListAsync(input.Current, input.Size);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await InvokeAsync(async () => await PopupService.EnqueueSnackbarAsync("查询失败,请检查网络连接:" + ex.Message, AlertTypes.Warning));
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Current = 1,
|
|
||||||
Size = 10,
|
|
||||||
Pages = 0,
|
|
||||||
Records = new List<HistoryAlarm>(),
|
|
||||||
Total = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await InvokeAsync(async () => await PopupService.EnqueueSnackbarAsync(result.Message, AlertTypes.Warning));
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Current = 1,
|
|
||||||
Size = 10,
|
|
||||||
Pages = 0,
|
|
||||||
Records = new List<HistoryAlarm>(),
|
|
||||||
Total = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +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/historyvalue"
|
|
||||||
@namespace ThingsGateway.Blazor
|
|
||||||
@using System.Linq.Expressions;
|
|
||||||
@using BlazorComponent;
|
|
||||||
@using Furion.DataValidation;
|
|
||||||
@using Mapster;
|
|
||||||
@using Masa.Blazor.Presets;
|
|
||||||
@using System.IO;
|
|
||||||
@using Masa.Blazor;
|
|
||||||
@using Microsoft.AspNetCore.Authorization;
|
|
||||||
@using ThingsGateway.Admin.Blazor.Core;
|
|
||||||
@using ThingsGateway.Admin.Blazor;
|
|
||||||
@using ThingsGateway.Admin.Core;
|
|
||||||
@using ThingsGateway.Application;
|
|
||||||
@using TouchSocket.Core;
|
|
||||||
@attribute [Authorize]
|
|
||||||
@inject MasaBlazor MasaBlazor
|
|
||||||
@inherits BaseComponentBase
|
|
||||||
@inject UserResoures UserResoures
|
|
||||||
@layout MainLayout
|
|
||||||
@inject InitTimezone InitTimezone
|
|
||||||
<AppDataTable @ref="_datatable" TItem="HistoryValue" SearchItem="HisPageInput"
|
|
||||||
AddItem="object" EditItem="object" IsShowSelect=false
|
|
||||||
IsMenuOperTemplate=false
|
|
||||||
QueryCallAsync="QueryCallAsync"
|
|
||||||
IsShowDetailButton
|
|
||||||
IsShowQueryButton>
|
|
||||||
<SearchTemplate>
|
|
||||||
|
|
||||||
<MMenu CloseOnContentClick="false" OffsetY Context="menu">
|
|
||||||
<ActivatorContent>
|
|
||||||
<MTextField Dense Readonly Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 "
|
|
||||||
Value="context.StartTime.Value.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)"
|
|
||||||
Clearable
|
|
||||||
@attributes="menu.Attrs" Outlined Label=@context.Description(x => x.StartTime) />
|
|
||||||
</ActivatorContent>
|
|
||||||
<ChildContent>
|
|
||||||
<AppDateTimePicker @bind-Value="context.StartTime"></AppDateTimePicker>
|
|
||||||
</ChildContent>
|
|
||||||
</MMenu>
|
|
||||||
|
|
||||||
<MMenu CloseOnContentClick="false" OffsetY Context="menu">
|
|
||||||
<ActivatorContent>
|
|
||||||
<MTextField Dense Readonly Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 "
|
|
||||||
Value="context.EndTime.Value.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)" Clearable
|
|
||||||
@attributes="menu.Attrs" Outlined Label=@context.Description(x => x.EndTime) />
|
|
||||||
</ActivatorContent>
|
|
||||||
<ChildContent>
|
|
||||||
<AppDateTimePicker @bind-Value="context.EndTime"></AppDateTimePicker>
|
|
||||||
</ChildContent>
|
|
||||||
</MMenu>
|
|
||||||
|
|
||||||
|
|
||||||
<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) />
|
|
||||||
|
|
||||||
|
|
||||||
</SearchTemplate>
|
|
||||||
|
|
||||||
<ItemColWithDTTemplate>
|
|
||||||
@if (context?.Value?.GetType() == typeof(DateTime))
|
|
||||||
{
|
|
||||||
<span>
|
|
||||||
@((DateTime.SpecifyKind((DateTime)context.Value, DateTimeKind.Utc)).ToLocalTime().ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset))
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@switch (context.Header.Value)
|
|
||||||
{
|
|
||||||
case nameof(context.Item.IsOnline):
|
|
||||||
<EnableChip Value="context.Item.IsOnline" DisabledLabel="离线" EnabledLabel="在线">
|
|
||||||
</EnableChip>
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
@if (context.Header.CellClass?.Contains("text-truncate") == true)
|
|
||||||
{
|
|
||||||
<span title=@context.Value>
|
|
||||||
@context.Value
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@context.Value
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</ItemColWithDTTemplate>
|
|
||||||
|
|
||||||
</AppDataTable>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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 BlazorComponent;
|
|
||||||
|
|
||||||
using Masa.Blazor;
|
|
||||||
|
|
||||||
using SqlSugar;
|
|
||||||
|
|
||||||
using ThingsGateway.Admin.Blazor.Core;
|
|
||||||
using ThingsGateway.Admin.Core;
|
|
||||||
using ThingsGateway.Application;
|
|
||||||
|
|
||||||
|
|
||||||
namespace ThingsGateway.Blazor;
|
|
||||||
/// <summary>
|
|
||||||
/// 时序库页面
|
|
||||||
/// </summary>
|
|
||||||
public partial class HistoryValuePage
|
|
||||||
{
|
|
||||||
|
|
||||||
HisPageInput SearchModel { get; set; } = new();
|
|
||||||
|
|
||||||
private IAppDataTable _datatable;
|
|
||||||
HistoryValueWorker HistoryValueHostService { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected override async Task OnInitializedAsync()
|
|
||||||
{
|
|
||||||
HistoryValueHostService = ServiceHelper.GetBackgroundService<HistoryValueWorker>();
|
|
||||||
await base.OnInitializedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DatatableQuery()
|
|
||||||
{
|
|
||||||
await _datatable?.QueryClickAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private async Task<SqlSugarPagedList<HistoryValue>> QueryCallAsync(HisPageInput input)
|
|
||||||
{
|
|
||||||
var result = await HistoryValueHostService.GetHisDbAsync();
|
|
||||||
if (result.IsSuccess)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await Task.Run(async () =>
|
|
||||||
{
|
|
||||||
var query = result.Content.CopyNew().Queryable<HistoryValue>()
|
|
||||||
.WhereIF(!input.Name.IsNullOrEmpty(), a => a.Name.Contains(input.Name))
|
|
||||||
.WhereIF(input.StartTime != null, a => a.CollectTime >= input.StartTime.Value)
|
|
||||||
.WhereIF(input.EndTime != null, a => a.CollectTime <= input.EndTime.Value);
|
|
||||||
|
|
||||||
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")}");
|
|
||||||
}
|
|
||||||
var data = await query.ToPagedListAsync(input.Current, input.Size);
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await InvokeAsync(async () => await PopupService.EnqueueSnackbarAsync("查询失败,请检查网络连接:" + ex.Message, AlertTypes.Warning));
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Current = 1,
|
|
||||||
Size = 10,
|
|
||||||
Pages = 0,
|
|
||||||
Records = new List<HistoryValue>(),
|
|
||||||
Total = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await InvokeAsync(async () => await PopupService.EnqueueSnackbarAsync(result.Message, AlertTypes.Warning));
|
|
||||||
return new()
|
|
||||||
{
|
|
||||||
Current = 1,
|
|
||||||
Size = 10,
|
|
||||||
Pages = 0,
|
|
||||||
Records = new List<HistoryValue>(),
|
|
||||||
Total = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,480 +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/memoryvariable"
|
|
||||||
@namespace ThingsGateway.Blazor
|
|
||||||
@using System.Linq.Expressions;
|
|
||||||
@using BlazorComponent;
|
|
||||||
@using Furion.DataValidation;
|
|
||||||
@using Mapster;
|
|
||||||
@using Masa.Blazor.Presets;
|
|
||||||
@using System.IO;
|
|
||||||
@using Masa.Blazor;
|
|
||||||
@using Microsoft.AspNetCore.Authorization;
|
|
||||||
@using ThingsGateway.Admin.Blazor.Core;
|
|
||||||
@using ThingsGateway.Admin.Blazor;
|
|
||||||
@using ThingsGateway.Admin.Core;
|
|
||||||
@using ThingsGateway.Application;
|
|
||||||
@using TouchSocket.Core;
|
|
||||||
@attribute [Authorize]
|
|
||||||
@inherits BaseComponentBase
|
|
||||||
@inject UserResoures UserResoures
|
|
||||||
@layout MainLayout
|
|
||||||
|
|
||||||
|
|
||||||
<AppDataTable @ref="_datatable"
|
|
||||||
StyleString=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+10}px)")
|
|
||||||
TItem="DeviceVariable" SearchItem="MemoryVariablePageInput"
|
|
||||||
AddItem="MemoryVariableAddInput" EditItem="MemoryVariableAddInput"
|
|
||||||
IsMenuOperTemplate=false SearchModel="search"
|
|
||||||
FilterHeaders="FilterHeaders"
|
|
||||||
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.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>
|
|
||||||
@GetRenderFragment(context)
|
|
||||||
</AddTemplate>
|
|
||||||
<EditTemplate>
|
|
||||||
@GetRenderFragment(context)
|
|
||||||
</EditTemplate>
|
|
||||||
</AppDataTable>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<ImportExcel @ref=ImportExcel Import="SaveDeviceImportAsync" Preview="DeviceImportAsync" />
|
|
||||||
|
|
||||||
|
|
||||||
@code {
|
|
||||||
|
|
||||||
RenderFragment GetRenderFragment(MemoryVariableAddInput context)
|
|
||||||
{
|
|
||||||
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.Unit)) </MSubheader>
|
|
||||||
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.Unit />
|
|
||||||
</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.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>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|
||||||
|
|
||||||
</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,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 BlazorComponent;
|
|
||||||
|
|
||||||
using Furion;
|
|
||||||
|
|
||||||
using Mapster;
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using Microsoft.AspNetCore.Components.Forms;
|
|
||||||
|
|
||||||
using ThingsGateway.Admin.Blazor.Core;
|
|
||||||
using ThingsGateway.Admin.Core;
|
|
||||||
using ThingsGateway.Application;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Blazor;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 内存变量页面
|
|
||||||
/// </summary>
|
|
||||||
public partial class MemoryVariablePage
|
|
||||||
{
|
|
||||||
private IAppDataTable _datatable;
|
|
||||||
|
|
||||||
long choiceUploadDeviceId;
|
|
||||||
|
|
||||||
ImportExcel ImportExcel;
|
|
||||||
|
|
||||||
private readonly MemoryVariablePageInput search = new();
|
|
||||||
|
|
||||||
StringNumber tab;
|
|
||||||
|
|
||||||
List<UploadDevice> UploadDevices = new();
|
|
||||||
|
|
||||||
[Inject]
|
|
||||||
AjaxService AjaxService { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected override async Task OnParametersSetAsync()
|
|
||||||
{
|
|
||||||
UploadDevices = App.GetService<IUploadDeviceService>().GetCacheList();
|
|
||||||
await base.OnParametersSetAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FilterHeaders(List<DataTableHeader<DeviceVariable>> datas)
|
|
||||||
{
|
|
||||||
datas.RemoveWhere(it => it.Value == nameof(DeviceVariable.DeviceId));
|
|
||||||
datas.RemoveWhere(it => it.Value == nameof(DeviceVariable.VariableAddress));
|
|
||||||
datas.RemoveWhere(it => it.Value == nameof(DeviceVariable.OtherMethod));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private async Task AddCallAsync(MemoryVariableAddInput input)
|
|
||||||
{
|
|
||||||
await App.GetService<VariableService>().AddAsync(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ClearAsync()
|
|
||||||
{
|
|
||||||
var confirm = await PopupService.OpenConfirmDialogAsync("确认", "清空?");
|
|
||||||
if (confirm)
|
|
||||||
{
|
|
||||||
await App.GetService<VariableService>().ClearMemoryVariableAsync();
|
|
||||||
}
|
|
||||||
await DatatableQueryAsync();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DatatableQueryAsync()
|
|
||||||
{
|
|
||||||
await _datatable.QueryClickAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteCallAsync(IEnumerable<DeviceVariable> input)
|
|
||||||
{
|
|
||||||
await App.GetService<VariableService>().DeleteAsync(input.Select(a => a.Id).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
Task<Dictionary<string, ImportPreviewOutputBase>> DeviceImportAsync(IBrowserFile file)
|
|
||||||
{
|
|
||||||
return App.GetService<VariableService>().MemoryVariablePreviewAsync(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async Task DownExportAsync(MemoryVariablePageInput input = null)
|
|
||||||
{
|
|
||||||
await AjaxService.DownFileAsync("gatewayFile/memoryVariable", SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat(), input.Adapt<MemoryVariableInput>());
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task EditCallAsync(MemoryVariableAddInput input)
|
|
||||||
{
|
|
||||||
await App.GetService<VariableService>().EditAsync(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
List<DependencyProperty> GetDriverProperties(long driverId, List<DependencyProperty> dependencyProperties)
|
|
||||||
{
|
|
||||||
return ServiceHelper.GetBackgroundService<UploadDeviceWorker>().GetVariablePropertys(driverId, dependencyProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<SqlSugarPagedList<DeviceVariable>> QueryCallAsync(MemoryVariablePageInput input)
|
|
||||||
{
|
|
||||||
var data = await App.GetService<VariableService>().PageAsync(input);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
async Task SaveDeviceImportAsync(Dictionary<string, ImportPreviewOutputBase> data)
|
|
||||||
{
|
|
||||||
await App.GetService<VariableService>().ImportAsync(data);
|
|
||||||
await DatatableQueryAsync();
|
|
||||||
ImportExcel.IsShowImport = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,264 +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/uploaddevice"
|
|
||||||
@namespace ThingsGateway.Blazor
|
|
||||||
@using System.Linq.Expressions;
|
|
||||||
@using BlazorComponent;
|
|
||||||
@using Furion;
|
|
||||||
@using Mapster;
|
|
||||||
@using Masa.Blazor.Presets;
|
|
||||||
@using System.IO;
|
|
||||||
@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
|
|
||||||
@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="string" TKey="string" ActiveChanged=@(async a=>
|
|
||||||
{
|
|
||||||
if(search.DeviceGroup!=a.FirstOrDefault())
|
|
||||||
{
|
|
||||||
search.DeviceGroup=a.FirstOrDefault();
|
|
||||||
await DatatableQuery();
|
|
||||||
}
|
|
||||||
} )
|
|
||||||
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" TItem="UploadDevice" SearchItem="UploadDevicePageInput"
|
|
||||||
StyleString=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight+10}px)")
|
|
||||||
AddItem="UploadDeviceAddInput" EditItem="UploadDeviceEditInput"
|
|
||||||
IsMenuOperTemplate=false SearchModel="search"
|
|
||||||
QueryCallAsync="QueryCallAsync" AddCallAsync="AddCallAsync"
|
|
||||||
EditCallAsync="EditCallAsync" DeleteCallAsync="DeleteCallAsync"
|
|
||||||
IsShowDetailButton
|
|
||||||
IsShowQueryButton
|
|
||||||
IsShowAddButton=@UserResoures.IsHasButtonWithRole("gatewayuploaddeviceadd")
|
|
||||||
IsShowDeleteButton=@UserResoures.IsHasButtonWithRole("gatewayuploaddevicedelete")
|
|
||||||
IsShowEditButton=@UserResoures.IsHasButtonWithRole("gatewayuploaddeviceedit")>
|
|
||||||
<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) />
|
|
||||||
</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="()=>CopyDevice(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("gatewayuploaddeviceedit")) Class="my-1 mx-2" OnClick="()=>{ ImportExcel.Step=1; ImportExcel.IsShowImport=true;}" Color="primary">
|
|
||||||
导入
|
|
||||||
</MButton>
|
|
||||||
</OtherToolbarTemplate>
|
|
||||||
<AddTemplate>
|
|
||||||
@GetRenderFragment(context)
|
|
||||||
</AddTemplate>
|
|
||||||
|
|
||||||
<EditTemplate>
|
|
||||||
@GetRenderFragment(context)
|
|
||||||
|
|
||||||
</EditTemplate>
|
|
||||||
|
|
||||||
|
|
||||||
<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>
|
|
||||||
@(
|
|
||||||
App.GetService<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;
|
|
||||||
}
|
|
||||||
RenderFragment GetRenderFragment(UploadDeviceEditInput context)
|
|
||||||
{
|
|
||||||
RenderFragment renderFragment = null;
|
|
||||||
renderFragment +=
|
|
||||||
@<div>
|
|
||||||
<MTabs @bind-Value="tab">
|
|
||||||
<MTab Value="1" Style="height:50px;"> 基本信息 </MTab>
|
|
||||||
<MTab Value="2"> 扩展属性 </MTab>
|
|
||||||
</MTabs>
|
|
||||||
|
|
||||||
<MTabsItems Value="tab">
|
|
||||||
<MTabItem Value="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(UploadDeviceRunTime).GetDescription(nameof(UploadDeviceRunTime.PluginName))) </MSubheader>
|
|
||||||
<MCascader Value="context.PluginId" Class="mt-3 mr-3" Clearable TValue=long TItem="DriverPluginCategory"
|
|
||||||
ValueChanged=@(a=>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 />
|
|
||||||
|
|
||||||
</MCard>
|
|
||||||
</MTabItem>
|
|
||||||
<MTabItem Value="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.IsNullOrEmpty() Bottom Context="tip">
|
|
||||||
<ActivatorContent>
|
|
||||||
@if (item.PropertyName.Contains("BigText"))
|
|
||||||
{
|
|
||||||
<MTextarea AutoGrow Dense @attributes="@tip.Attrs" Outlined HideDetails="@("auto")" @bind-Value=@item.Value />
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<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,149 +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 UploadDevicePage
|
|
||||||
{
|
|
||||||
private readonly UploadDevicePageInput search = new();
|
|
||||||
private IAppDataTable _datatable;
|
|
||||||
List<string> _deviceGroups = new();
|
|
||||||
string _searchName;
|
|
||||||
List<DriverPluginCategory> DriverPlugins;
|
|
||||||
ImportExcel ImportExcel;
|
|
||||||
StringNumber tab;
|
|
||||||
[Inject]
|
|
||||||
AjaxService AjaxService { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
[CascadingParameter]
|
|
||||||
MainLayout MainLayout { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected override async Task OnParametersSetAsync()
|
|
||||||
{
|
|
||||||
DriverPlugins = App.GetService<IDriverPluginService>().GetDriverPluginChildrenList(DriverEnum.Upload);
|
|
||||||
_deviceGroups = App.GetService<IUploadDeviceService>().GetCacheList()?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
|
|
||||||
await base.OnParametersSetAsync();
|
|
||||||
}
|
|
||||||
private async Task AddCallAsync(UploadDeviceAddInput input)
|
|
||||||
{
|
|
||||||
await App.GetService<UploadDeviceService>().AddAsync(input);
|
|
||||||
_deviceGroups = App.GetService<UploadDeviceService>().GetCacheList()?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task CopyDevice(IEnumerable<UploadDevice> data)
|
|
||||||
{
|
|
||||||
if (!data.Any())
|
|
||||||
{
|
|
||||||
await PopupService.EnqueueSnackbarAsync("需选择一项或多项", AlertTypes.Warning);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await App.GetService<UploadDeviceService>().CopyDevAsync(data);
|
|
||||||
await DatatableQuery();
|
|
||||||
await PopupService.EnqueueSnackbarAsync("复制成功", AlertTypes.Success);
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DatatableQuery()
|
|
||||||
{
|
|
||||||
await _datatable?.QueryClickAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task DeleteCallAsync(IEnumerable<UploadDevice> input)
|
|
||||||
{
|
|
||||||
await App.GetService<UploadDeviceService>().DeleteAsync(input.Select(a => a.Id).ToArray());
|
|
||||||
_deviceGroups = App.GetService<UploadDeviceService>().GetCacheList()?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
Task<Dictionary<string, ImportPreviewOutputBase>> DeviceImportAsync(IBrowserFile file)
|
|
||||||
{
|
|
||||||
return App.GetService<UploadDeviceService>().PreviewAsync(file);
|
|
||||||
}
|
|
||||||
async Task DownExportAsync(UploadDevicePageInput input = null)
|
|
||||||
{
|
|
||||||
await AjaxService.DownFileAsync("gatewayFile/uploadDevice", SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat(), input.Adapt<CollectDeviceInput>());
|
|
||||||
}
|
|
||||||
private async Task DriverValueChangedAsync(UploadDeviceEditInput context, long pluginId)
|
|
||||||
{
|
|
||||||
if (pluginId <= 0)
|
|
||||||
return;
|
|
||||||
if (context.DevicePropertys == null || context.DevicePropertys?.Count == 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
context.PluginId = pluginId;
|
|
||||||
context.DevicePropertys = GetDriverProperties(pluginId, context.Id);
|
|
||||||
await PopupService.EnqueueSnackbarAsync("插件附加属性已更新", AlertTypes.Success);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await PopupService.EnqueueSnackbarAsync(ex.Message, AlertTypes.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
private async Task EditCallAsync(UploadDeviceEditInput input)
|
|
||||||
{
|
|
||||||
await App.GetService<UploadDeviceService>().EditAsync(input);
|
|
||||||
_deviceGroups = App.GetService<UploadDeviceService>().GetCacheList()?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
List<DependencyProperty> GetDriverProperties(long driverId, long devId)
|
|
||||||
{
|
|
||||||
return ServiceHelper.GetBackgroundService<UploadDeviceWorker>().GetDevicePropertys(driverId, devId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<SqlSugarPagedList<UploadDevice>> QueryCallAsync(UploadDevicePageInput input)
|
|
||||||
{
|
|
||||||
var data = await App.GetService<UploadDeviceService>().PageAsync(input);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task SaveDeviceImportAsync(Dictionary<string, ImportPreviewOutputBase> data)
|
|
||||||
{
|
|
||||||
await App.GetService<UploadDeviceService>().ImportAsync(data);
|
|
||||||
await DatatableQuery();
|
|
||||||
ImportExcel.IsShowImport = false;
|
|
||||||
await MainLayout.StateHasChangedAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj" />
|
|
||||||
<ProjectReference Include="..\ThingsGateway.Application\ThingsGateway.Application.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
|
|
||||||
@@ -1,469 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<doc>
|
|
||||||
<assembly>
|
|
||||||
<name>ThingsGateway.Blazor</name>
|
|
||||||
</assembly>
|
|
||||||
<members>
|
|
||||||
<member name="T:ThingsGateway.Blazor.ImportExcel">
|
|
||||||
<summary>
|
|
||||||
导入excel
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.ImportExcel.Import">
|
|
||||||
<summary>
|
|
||||||
导入
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.ImportExcel.IsShowImport">
|
|
||||||
<summary>
|
|
||||||
是否显示
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.ImportExcel.Preview">
|
|
||||||
<summary>
|
|
||||||
预览
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.ImportExcel.Step">
|
|
||||||
<summary>
|
|
||||||
当前步数
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.HisPageInput">
|
|
||||||
<summary>
|
|
||||||
历史查询条件
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.HisPageInput.StartTime">
|
|
||||||
<summary>
|
|
||||||
开始时间
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.HisPageInput.EndTime">
|
|
||||||
<summary>
|
|
||||||
结束时间
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.ChannelEnum">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.ChannelEnum.None">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.ChannelEnum.TcpClientEx">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.ChannelEnum.SerialPort">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.ChannelEnum.UdpSession">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.ChannelEnum.TcpServer">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.DefalutDebugDriverPage">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.DefalutDebugDriverPage.SerialSessionPage">
|
|
||||||
<summary>
|
|
||||||
SerialSessionPage
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.DefalutDebugDriverPage.TcpClientPage">
|
|
||||||
<summary>
|
|
||||||
TcpClientPage
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.DefalutDebugDriverPage.TcpServerPage">
|
|
||||||
<summary>
|
|
||||||
TcpServerPage
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.DefalutDebugDriverPage.UdpSessionPage">
|
|
||||||
<summary>
|
|
||||||
UdpSessionPage
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.DefalutDebugDriverPage.Channel">
|
|
||||||
<summary>
|
|
||||||
选择,1-TCPCLIENT,2-串口,3-UDP,4-TCPServer
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.DefalutDebugDriverPage.Plc">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.DefalutDebugDriverPage.ChildContent">
|
|
||||||
<summary>
|
|
||||||
模板
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.DefalutDebugDriverPage.OtherContent">
|
|
||||||
<summary>
|
|
||||||
自定义模板
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DefalutDebugDriverPage.Dispose">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DefalutDebugDriverPage.OnAfterRender(System.Boolean)">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
<param name="firstRender"></param>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.SerialSessionPage">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.SerialSessionPage.LogAction">
|
|
||||||
<summary>
|
|
||||||
日志输出
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.SerialSessionPage.Dispose">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.SerialSessionPage.GetSerialSession">
|
|
||||||
<summary>
|
|
||||||
获取对象
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.SerialSessionPage.OnInitialized">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.TcpClientPage">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.TcpClientPage.LogAction">
|
|
||||||
<summary>
|
|
||||||
日志输出
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.TcpClientPage.IP">
|
|
||||||
<summary>
|
|
||||||
IP
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.TcpClientPage.Port">
|
|
||||||
<summary>
|
|
||||||
端口
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.TcpClientPage.Dispose">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.TcpClientPage.GetTcpClient">
|
|
||||||
<summary>
|
|
||||||
获取对象
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.TcpClientPage.OnInitialized">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.TcpServerPage">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.TcpServerPage.LogAction">
|
|
||||||
<summary>
|
|
||||||
日志输出
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.TcpServerPage.Dispose">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.TcpServerPage.GetTcpServer">
|
|
||||||
<summary>
|
|
||||||
获取对象
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.TcpServerPage.OnInitialized">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.UdpSessionPage">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Blazor.UdpSessionPage.LogAction">
|
|
||||||
<summary>
|
|
||||||
日志输出
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.UdpSessionPage.Dispose">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.UdpSessionPage.GetUdpSession">
|
|
||||||
<summary>
|
|
||||||
获取对象
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.UdpSessionPage.OnInitialized">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.BackendLogPage">
|
|
||||||
<summary>
|
|
||||||
后台日志页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.CollectDevicePage">
|
|
||||||
<summary>
|
|
||||||
采集设备页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.CollectDevicePage.OnParametersSetAsync">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.ConfigPage">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.ConfigPage.#ctor">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.ConfigPage.OnParametersSetAsync">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.DeviceStatusPage">
|
|
||||||
<summary>
|
|
||||||
设备状态页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DeviceStatusPage.Dispose">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DeviceStatusPage.OnInitialized">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DeviceStatusPage.OnParametersSet">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.DeviceVariablePage">
|
|
||||||
<summary>
|
|
||||||
采集变量页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DeviceVariablePage.OnParametersSetAsync">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.DeviceVariableRunTimePage">
|
|
||||||
<summary>
|
|
||||||
实时数据页
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.DeviceVariableRunTimePage.DeviceName">
|
|
||||||
<summary>
|
|
||||||
设备名称
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Blazor.DeviceVariableRunTimePage.UploadDeviceName">
|
|
||||||
<summary>
|
|
||||||
上传设备名称
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DeviceVariableRunTimePage.Dispose">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DeviceVariableRunTimePage.OnInitialized">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DeviceVariableRunTimePage.OnParametersSetAsync">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.DriverDebugPage">
|
|
||||||
<summary>
|
|
||||||
调试页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DriverDebugPage.OnInitialized">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.DriverDebugPage.ImportVaiableAsync(System.Int64)">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.DriverPluginPage">
|
|
||||||
<summary>
|
|
||||||
插件管理页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.HardwareInfoPage">
|
|
||||||
<summary>
|
|
||||||
硬件信息页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.HardwareInfoPage.OnInitialized">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.HistoryAlarmPage">
|
|
||||||
<summary>
|
|
||||||
历史报警页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.HistoryAlarmPage.OnInitializedAsync">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.HistoryValuePage">
|
|
||||||
<summary>
|
|
||||||
时序库页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.HistoryValuePage.OnInitializedAsync">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.MemoryVariablePage">
|
|
||||||
<summary>
|
|
||||||
内存变量页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.MemoryVariablePage.OnParametersSetAsync">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.RealAlarmPage">
|
|
||||||
<summary>
|
|
||||||
实时报警
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.RealAlarmPage.Dispose">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.RealAlarmPage.OnInitialized">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.RpcLogPage">
|
|
||||||
<summary>
|
|
||||||
RPC日志页面
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Blazor.UploadDevicePage">
|
|
||||||
<summary>
|
|
||||||
上传设备页
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Blazor.UploadDevicePage.OnParametersSetAsync">
|
|
||||||
<summary>
|
|
||||||
<inheritdoc/>
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="T:ThingsGateway.Application.DriverDebugUIBase">
|
|
||||||
<summary>
|
|
||||||
调试UI
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Application.DriverDebugUIBase.isDownExport">
|
|
||||||
<summary>
|
|
||||||
导出提示
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="F:ThingsGateway.Application.DriverDebugUIBase.Messages">
|
|
||||||
<summary>
|
|
||||||
日志缓存
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Application.DriverDebugUIBase.Plc">
|
|
||||||
<summary>
|
|
||||||
默认读写设备
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Application.DriverDebugUIBase.Address">
|
|
||||||
<summary>
|
|
||||||
变量地址
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Application.DriverDebugUIBase.DataTypeEnum">
|
|
||||||
<summary>
|
|
||||||
数据类型
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Application.DriverDebugUIBase.JS">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="P:ThingsGateway.Application.DriverDebugUIBase.WriteValue">
|
|
||||||
<summary>
|
|
||||||
写入值
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.Dispose">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.ReadAsync">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.WriteAsync">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DeviceImportAsync(ThingsGateway.Application.CollectDevice)">
|
|
||||||
<summary>
|
|
||||||
导入设备
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DeviceVariableImportAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})">
|
|
||||||
<summary>
|
|
||||||
导入变量
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceMessageExportAsync(System.Collections.Generic.IEnumerable{System.String})">
|
|
||||||
<summary>
|
|
||||||
导出
|
|
||||||
</summary>
|
|
||||||
<param name="values"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceExportAsync(ThingsGateway.Application.CollectDevice)">
|
|
||||||
<summary>
|
|
||||||
导出到excel
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceVariableExportAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable},System.String)">
|
|
||||||
<summary>
|
|
||||||
导出到excel
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.LogOut(TouchSocket.Core.LogLevel,System.Object,System.String,System.Exception)">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
<member name="M:ThingsGateway.Application.DriverDebugUIBase.OnInitialized">
|
|
||||||
<inheritdoc/>
|
|
||||||
</member>
|
|
||||||
</members>
|
|
||||||
</doc>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user