Compare commits
	
		
			177 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 947cd712e1 | ||
|   | edc208f96b | ||
|   | 1fb0296ee7 | ||
|   | 6488d3df87 | ||
|   | 56189d78e0 | ||
|   | bff18127b8 | ||
|   | 363206e0ba | ||
|   | fd3e378501 | ||
|   | 4ba2fe4c9d | ||
|   | 2c499626ad | ||
|   | 2b581a03c3 | ||
|   | 450c15210a | ||
|   | 65fed8cc93 | ||
|   | 4b64771ea2 | ||
|   | f39977a6ff | ||
|   | 933b535caa | ||
|   | 8abc5d2f20 | ||
|   | d8783cd994 | ||
|   | d5d087feb5 | ||
|   | 6ba3399df7 | ||
|   | 65124b3aa8 | ||
|   | 98597f4726 | ||
|   | e7981f0d8e | ||
|   | cf654427c3 | ||
|   | ff2f628282 | ||
|   | ae818ca265 | ||
|   | 0f2aed458e | ||
|   | d486c44ff6 | ||
|   | ca7b9980bf | ||
|   | 3c71e6a8e3 | ||
|   | 542442864c | ||
|   | 5edb64fa85 | ||
|   | 8dc1c898a3 | ||
|   | 1ed35726b0 | ||
|   | 27fae9ebaa | ||
|   | b103f25c94 | ||
|   | abff450274 | ||
|   | c260736a11 | ||
|   | 166ac2307a | ||
|   | b21a4e1a4d | ||
|   | f7dc943fa3 | ||
|   | bfbd2693ec | ||
|   | 819e71c993 | ||
|   | 9fd0b489a2 | ||
|   | f5fe9f8dae | ||
|   | f9ffc18145 | ||
|   | 08db5b983a | ||
|   | 5b3b4c8c50 | ||
|   | 73f914ffc4 | ||
|   | d6bdd73ed6 | ||
|   | 7370ee7349 | ||
|   | 4574596bac | ||
|   | 4d16855e36 | ||
|   | 13a0d4d282 | ||
|   | b9cd06b829 | ||
|   | 5b460e8fa2 | ||
|   | 41087edf17 | ||
|   | 2afcc38e38 | ||
|   | e59ccce25f | ||
|   | d7425890e8 | ||
|   | a989a837fb | ||
|   | db1221da50 | ||
|   | cf794569ed | ||
|   | 51e5bbab0d | ||
|   | 2c197ed2b2 | ||
|   | d8fc6665b3 | ||
|   | c671a79822 | ||
|   | 9d93ce4c41 | ||
|   | a6d99fe227 | ||
|   | 923b8bca31 | ||
|   | e2c30d1c88 | ||
|   | b6d9f2a04e | ||
|   | 57306ea664 | ||
|   | cd7f3fd02f | ||
|   | 0482e077a8 | ||
|   | 5f986a45ca | ||
|   | ca7b49c0d5 | ||
|   | 52dd555e6c | ||
|   | 579b1a59f9 | ||
|   | 5299c5c4be | ||
|   | f7756bccef | ||
|   | a6b874d160 | ||
|   | 3e5fb3ddcf | ||
|   | 5e6bcb12d3 | ||
|   | 14303f1429 | ||
|   | 96711ba022 | ||
|   | cbfc0fdbdc | ||
|   | 6e81886c0e | ||
|   | 2d976bc132 | ||
|   | 57f6a476af | ||
|   | 8491ed296e | ||
|   | cd1288afdc | ||
|   | ec6c830cb0 | ||
|   | 2f86ccc4bf | ||
|   | 8ca445aec0 | ||
|   | 1e1f27c8a5 | ||
|   | 2b84bde367 | ||
|   | b52e58551d | ||
|   | 9aceed00bf | ||
|   | 58814f7f74 | ||
|   | 6a70ef9f31 | ||
|   | 82cc4ca500 | ||
|   | 4567fa04ed | ||
|   | 8b98b5d818 | ||
|   | 176d0351af | ||
|   | d63dc3384b | ||
|   | 1ccd704e30 | ||
|   | f5d23dbe79 | ||
|   | 75bfe53ac3 | ||
|   | 3308f916dd | ||
|   | e7140279ca | ||
|   | 1034719f5e | ||
|   | 2c00043a7f | ||
|   | 65c695d9ce | ||
|   | 57253fe46a | ||
|   | 4e5c443440 | ||
|   | 0b3b73d8ec | ||
|   | 921eabc134 | ||
|   | 0faa428751 | ||
|   | f71a2fdd63 | ||
|   | 4eb9ed8aba | ||
|   | d7b549abb8 | ||
|   | 95d723c578 | ||
|   | 2fcd853e86 | ||
|   | 07eef7c812 | ||
|   | b01e0757fa | ||
|   | 32844a20c6 | ||
|   | 5b6532c601 | ||
|   | 2c5b4b4027 | ||
|   | 72d7ecf195 | ||
|   | 2cfa6b4306 | ||
|   | 6f6ffde0ab | ||
|   | 1694739a16 | ||
|   | 95d1e8bfca | ||
|   | 60dec08e3c | ||
|   | a99d71be93 | ||
|   | f1331b6a0c | ||
|   | 10d66b642b | ||
|   | cd2310e4a8 | ||
|   | 1b399cf6b0 | ||
|   | 877445bc0a | ||
|   | 9a5b345bde | ||
|   | fc9e8ea7b3 | ||
|   | 32be6fcfc1 | ||
|   | 49847236c2 | ||
|   | d8424443e6 | ||
|   | f3b571ec3f | ||
|   | 99318bb5d7 | ||
|   | 1aa154c9aa | ||
|   | c65d8a445b | ||
|   | 80f4f85570 | ||
|   | 5beee43a6b | ||
|   | 8d6ae203a0 | ||
|   | 4353479a5c | ||
|   | 34d7687f9e | ||
|   | b1dc3cf4af | ||
|   | 6a58b95933 | ||
|   | d3badfd02b | ||
|   | 0098be057b | ||
|   | 6f972aa515 | ||
|   | 7407ba6313 | ||
|   | 1c79de207b | ||
|   | 257c79db92 | ||
|   | 9d1934a308 | ||
|   | d70f959902 | ||
|   | e4d810222f | ||
|   | bc1af4ae07 | ||
|   | 6e688ef43f | ||
|   | f0fe1b23dc | ||
|   | aaf2006401 | ||
|   | b821e26935 | ||
|   | 7ae4287157 | ||
|   | c6fcc38a65 | ||
|   | ab2d5c8853 | ||
|   | 5e557ff0bc | ||
|   | 918ca449a1 | ||
|   | 8e73368008 | 
| @@ -5,10 +5,9 @@ | ||||
|  | ||||
|  **NetCore** 跨平台边缘采集网关(工业设备采集) | ||||
|  | ||||
|  **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/profiles/KimDiego) | ||||
|  | ||||
|  **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 [**ThingsGateway.Admin**](https://gitee.com/dotnetchina/ThingsGateway/blob/master/framework/ThingsGateway.Admin.sln) | ||||
|  **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | ||||
|  | ||||
|  **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin** | ||||
|  | ||||
|  | ||||
| ## 文档 | ||||
| @@ -23,7 +22,9 @@ | ||||
|  | ||||
| [ThingsGateway演示地址](http://120.24.62.140:5000/) | ||||
|  | ||||
| 账户	:  **superAdmin**	密码 : **111111** | ||||
| 账户	:  **superAdmin**	 | ||||
|  | ||||
| 密码 : **111111** | ||||
|  | ||||
| ## 赞助 | ||||
|  | ||||
|   | ||||
| @@ -1,140 +1,7 @@ | ||||
| [*.cs] | ||||
|  | ||||
| # CA1822: 将成员标记为 static | ||||
| dotnet_diagnostic.CA1822.severity = none | ||||
|  | ||||
| # CA1816: Dispose 方法应调用 SuppressFinalize | ||||
| dotnet_diagnostic.CA1816.severity = none | ||||
| # CA1848: 使用 LoggerMessage 委托 | ||||
| dotnet_diagnostic.CA1848.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 | ||||
|  | ||||
| [*.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 | ||||
| dotnet_diagnostic.CA2254.severity = suggestion | ||||
|   | ||||
							
								
								
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| ############################################################################### | ||||
| # Set default behavior to automatically normalize line endings. | ||||
| ############################################################################### | ||||
| * text=auto | ||||
|  | ||||
| ############################################################################### | ||||
| # Set default behavior for command prompt diff. | ||||
| # | ||||
| # This is need for earlier builds of msysgit that does not have it on by | ||||
| # default for csharp files. | ||||
| # Note: This is only used by command line | ||||
| ############################################################################### | ||||
| #*.cs     diff=csharp | ||||
|  | ||||
| ############################################################################### | ||||
| # Set the merge driver for project and solution files | ||||
| # | ||||
| # Merging from the command prompt will add diff markers to the files if there | ||||
| # are conflicts (Merging from VS is not affected by the settings below, in VS | ||||
| # the diff markers are never inserted). Diff markers may cause the following  | ||||
| # file extensions to fail to load in VS. An alternative would be to treat | ||||
| # these files as binary and thus will always conflict and require user | ||||
| # intervention with every merge. To do so, just uncomment the entries below | ||||
| ############################################################################### | ||||
| #*.sln       merge=binary | ||||
| #*.csproj    merge=binary | ||||
| #*.vbproj    merge=binary | ||||
| #*.vcxproj   merge=binary | ||||
| #*.vcproj    merge=binary | ||||
| #*.dbproj    merge=binary | ||||
| #*.fsproj    merge=binary | ||||
| #*.lsproj    merge=binary | ||||
| #*.wixproj   merge=binary | ||||
| #*.modelproj merge=binary | ||||
| #*.sqlproj   merge=binary | ||||
| #*.wwaproj   merge=binary | ||||
|  | ||||
| ############################################################################### | ||||
| # behavior for image files | ||||
| # | ||||
| # image files are treated as binary by default. | ||||
| ############################################################################### | ||||
| #*.jpg   binary | ||||
| #*.png   binary | ||||
| #*.gif   binary | ||||
|  | ||||
| ############################################################################### | ||||
| # diff behavior for common document formats | ||||
| #  | ||||
| # Convert binary document formats to text before diffing them. This feature | ||||
| # is only available from the command line. Turn it on by uncommenting the  | ||||
| # entries below. | ||||
| ############################################################################### | ||||
| #*.doc   diff=astextplain | ||||
| #*.DOC   diff=astextplain | ||||
| #*.docx  diff=astextplain | ||||
| #*.DOCX  diff=astextplain | ||||
| #*.dot   diff=astextplain | ||||
| #*.DOT   diff=astextplain | ||||
| #*.pdf   diff=astextplain | ||||
| #*.PDF   diff=astextplain | ||||
| #*.rtf   diff=astextplain | ||||
| #*.RTF   diff=astextplain | ||||
							
								
								
									
										364
									
								
								framework/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								framework/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,364 @@ | ||||
| ## Ignore Visual Studio temporary files, build results, and | ||||
| ## files generated by popular Visual Studio add-ons. | ||||
| ## | ||||
| ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore | ||||
|  | ||||
| # User-specific files | ||||
| *.rsuser | ||||
| *.suo | ||||
| *.user | ||||
| *.userosscache | ||||
| *.sln.docstates | ||||
|  | ||||
| # User-specific files (MonoDevelop/Xamarin Studio) | ||||
| *.userprefs | ||||
|  | ||||
| # Mono auto generated files | ||||
| mono_crash.* | ||||
|  | ||||
| # Build results | ||||
| [Dd]ebug/ | ||||
| [Dd]ebugPublic/ | ||||
| [Rr]elease/ | ||||
| [Rr]eleases/ | ||||
| x64/ | ||||
| x86/ | ||||
| [Ww][Ii][Nn]32/ | ||||
| [Aa][Rr][Mm]/ | ||||
| [Aa][Rr][Mm]64/ | ||||
| bld/ | ||||
| [Bb]in/ | ||||
| [Oo]bj/ | ||||
| [Oo]ut/ | ||||
| [Ll]og/ | ||||
| [Ll]ogs/ | ||||
|  | ||||
| # Visual Studio 2015/2017 cache/options directory | ||||
| .vs/ | ||||
| # Uncomment if you have tasks that create the project's static files in wwwroot | ||||
| #wwwroot/ | ||||
|  | ||||
| # Visual Studio 2017 auto generated files | ||||
| Generated\ Files/ | ||||
|  | ||||
| # MSTest test Results | ||||
| [Tt]est[Rr]esult*/ | ||||
| [Bb]uild[Ll]og.* | ||||
|  | ||||
| # NUnit | ||||
| *.VisualState.xml | ||||
| TestResult.xml | ||||
| nunit-*.xml | ||||
|  | ||||
| # Build Results of an ATL Project | ||||
| [Dd]ebugPS/ | ||||
| [Rr]eleasePS/ | ||||
| dlldata.c | ||||
|  | ||||
| # Benchmark Results | ||||
| BenchmarkDotNet.Artifacts/ | ||||
|  | ||||
| # .NET Core | ||||
| project.lock.json | ||||
| project.fragment.lock.json | ||||
| artifacts/ | ||||
|  | ||||
| # ASP.NET Scaffolding | ||||
| ScaffoldingReadMe.txt | ||||
|  | ||||
| # StyleCop | ||||
| StyleCopReport.xml | ||||
|  | ||||
| # Files built by Visual Studio | ||||
| *_i.c | ||||
| *_p.c | ||||
| *_h.h | ||||
| *.ilk | ||||
| *.meta | ||||
| *.obj | ||||
| *.iobj | ||||
| *.pch | ||||
| *.pdb | ||||
| *.ipdb | ||||
| *.pgc | ||||
| *.pgd | ||||
| *.rsp | ||||
| *.sbr | ||||
| *.tlb | ||||
| *.tli | ||||
| *.tlh | ||||
| *.tmp | ||||
| *.tmp_proj | ||||
| *_wpftmp.csproj | ||||
| *.log | ||||
| *.vspscc | ||||
| *.vssscc | ||||
| .builds | ||||
| *.pidb | ||||
| *.svclog | ||||
| *.scc | ||||
|  | ||||
| # Chutzpah Test files | ||||
| _Chutzpah* | ||||
|  | ||||
| # Visual C++ cache files | ||||
| ipch/ | ||||
| *.aps | ||||
| *.ncb | ||||
| *.opendb | ||||
| *.opensdf | ||||
| *.sdf | ||||
| *.cachefile | ||||
| *.VC.db | ||||
| *.VC.VC.opendb | ||||
|  | ||||
| # Visual Studio profiler | ||||
| *.psess | ||||
| *.vsp | ||||
| *.vspx | ||||
| *.sap | ||||
|  | ||||
| # Visual Studio Trace Files | ||||
| *.e2e | ||||
|  | ||||
| # TFS 2012 Local Workspace | ||||
| $tf/ | ||||
|  | ||||
| # Guidance Automation Toolkit | ||||
| *.gpState | ||||
|  | ||||
| # ReSharper is a .NET coding add-in | ||||
| _ReSharper*/ | ||||
| *.[Rr]e[Ss]harper | ||||
| *.DotSettings.user | ||||
|  | ||||
| # TeamCity is a build add-in | ||||
| _TeamCity* | ||||
|  | ||||
| # DotCover is a Code Coverage Tool | ||||
| *.dotCover | ||||
|  | ||||
| # AxoCover is a Code Coverage Tool | ||||
| .axoCover/* | ||||
| !.axoCover/settings.json | ||||
|  | ||||
| # Coverlet is a free, cross platform Code Coverage Tool | ||||
| coverage*.json | ||||
| coverage*.xml | ||||
| coverage*.info | ||||
|  | ||||
| # Visual Studio code coverage results | ||||
| *.coverage | ||||
| *.coveragexml | ||||
|  | ||||
| # NCrunch | ||||
| _NCrunch_* | ||||
| .*crunch*.local.xml | ||||
| nCrunchTemp_* | ||||
|  | ||||
| # MightyMoose | ||||
| *.mm.* | ||||
| AutoTest.Net/ | ||||
|  | ||||
| # Web workbench (sass) | ||||
| .sass-cache/ | ||||
|  | ||||
| # Installshield output folder | ||||
| [Ee]xpress/ | ||||
|  | ||||
| # DocProject is a documentation generator add-in | ||||
| DocProject/buildhelp/ | ||||
| DocProject/Help/*.HxT | ||||
| DocProject/Help/*.HxC | ||||
| DocProject/Help/*.hhc | ||||
| DocProject/Help/*.hhk | ||||
| DocProject/Help/*.hhp | ||||
| DocProject/Help/Html2 | ||||
| DocProject/Help/html | ||||
|  | ||||
| # Click-Once directory | ||||
| publish/ | ||||
|  | ||||
| # Publish Web Output | ||||
| *.[Pp]ublish.xml | ||||
| *.azurePubxml | ||||
| # Note: Comment the next line if you want to checkin your web deploy settings, | ||||
| # but database connection strings (with potential passwords) will be unencrypted | ||||
| *.pubxml | ||||
| *.publishproj | ||||
|  | ||||
| # Microsoft Azure Web App publish settings. Comment the next line if you want to | ||||
| # checkin your Azure Web App publish settings, but sensitive information contained | ||||
| # in these scripts will be unencrypted | ||||
| PublishScripts/ | ||||
|  | ||||
| # NuGet Packages | ||||
| *.nupkg | ||||
| # NuGet Symbol Packages | ||||
| *.snupkg | ||||
| # The packages folder can be ignored because of Package Restore | ||||
| **/[Pp]ackages/* | ||||
| # except build/, which is used as an MSBuild target. | ||||
| !**/[Pp]ackages/build/ | ||||
| # Uncomment if necessary however generally it will be regenerated when needed | ||||
| #!**/[Pp]ackages/repositories.config | ||||
| # NuGet v3's project.json files produces more ignorable files | ||||
| *.nuget.props | ||||
| *.nuget.targets | ||||
|  | ||||
| # Microsoft Azure Build Output | ||||
| csx/ | ||||
| *.build.csdef | ||||
|  | ||||
| # Microsoft Azure Emulator | ||||
| ecf/ | ||||
| rcf/ | ||||
|  | ||||
| # Windows Store app package directories and files | ||||
| AppPackages/ | ||||
| BundleArtifacts/ | ||||
| Package.StoreAssociation.xml | ||||
| _pkginfo.txt | ||||
| *.appx | ||||
| *.appxbundle | ||||
| *.appxupload | ||||
|  | ||||
| # Visual Studio cache files | ||||
| # files ending in .cache can be ignored | ||||
| *.[Cc]ache | ||||
| # but keep track of directories ending in .cache | ||||
| !?*.[Cc]ache/ | ||||
|  | ||||
| # Others | ||||
| ClientBin/ | ||||
| ~$* | ||||
| *~ | ||||
| *.dbmdl | ||||
| *.dbproj.schemaview | ||||
| *.jfm | ||||
| *.pfx | ||||
| *.publishsettings | ||||
| orleans.codegen.cs | ||||
|  | ||||
| # Including strong name files can present a security risk | ||||
| # (https://github.com/github/gitignore/pull/2483#issue-259490424) | ||||
| #*.snk | ||||
|  | ||||
| # Since there are multiple workflows, uncomment next line to ignore bower_components | ||||
| # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) | ||||
| #bower_components/ | ||||
|  | ||||
| # RIA/Silverlight projects | ||||
| Generated_Code/ | ||||
|  | ||||
| # Backup & report files from converting an old project file | ||||
| # to a newer Visual Studio version. Backup files are not needed, | ||||
| # because we have git ;-) | ||||
| _UpgradeReport_Files/ | ||||
| Backup*/ | ||||
| UpgradeLog*.XML | ||||
| UpgradeLog*.htm | ||||
| ServiceFabricBackup/ | ||||
| *.rptproj.bak | ||||
|  | ||||
| # SQL Server files | ||||
| *.mdf | ||||
| *.ldf | ||||
| *.ndf | ||||
|  | ||||
| # Business Intelligence projects | ||||
| *.rdl.data | ||||
| *.bim.layout | ||||
| *.bim_*.settings | ||||
| *.rptproj.rsuser | ||||
| *- [Bb]ackup.rdl | ||||
| *- [Bb]ackup ([0-9]).rdl | ||||
| *- [Bb]ackup ([0-9][0-9]).rdl | ||||
|  | ||||
| # Microsoft Fakes | ||||
| FakesAssemblies/ | ||||
|  | ||||
| # GhostDoc plugin setting file | ||||
| *.GhostDoc.xml | ||||
|  | ||||
| # Node.js Tools for Visual Studio | ||||
| .ntvs_analysis.dat | ||||
| node_modules/ | ||||
|  | ||||
| # Visual Studio 6 build log | ||||
| *.plg | ||||
|  | ||||
| # Visual Studio 6 workspace options file | ||||
| *.opt | ||||
|  | ||||
| # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) | ||||
| *.vbw | ||||
|  | ||||
| # Visual Studio LightSwitch build output | ||||
| **/*.HTMLClient/GeneratedArtifacts | ||||
| **/*.DesktopClient/GeneratedArtifacts | ||||
| **/*.DesktopClient/ModelManifest.xml | ||||
| **/*.Server/GeneratedArtifacts | ||||
| **/*.Server/ModelManifest.xml | ||||
| _Pvt_Extensions | ||||
|  | ||||
| # Paket dependency manager | ||||
| .paket/paket.exe | ||||
| paket-files/ | ||||
|  | ||||
| # FAKE - F# Make | ||||
| .fake/ | ||||
|  | ||||
| # CodeRush personal settings | ||||
| .cr/personal | ||||
|  | ||||
| # Python Tools for Visual Studio (PTVS) | ||||
| __pycache__/ | ||||
| *.pyc | ||||
|  | ||||
| # Cake - Uncomment if you are using it | ||||
| # tools/** | ||||
| # !tools/packages.config | ||||
|  | ||||
| # Tabs Studio | ||||
| *.tss | ||||
|  | ||||
| # Telerik's JustMock configuration file | ||||
| *.jmconfig | ||||
|  | ||||
| # BizTalk build output | ||||
| *.btp.cs | ||||
| *.btm.cs | ||||
| *.odx.cs | ||||
| *.xsd.cs | ||||
|  | ||||
| # OpenCover UI analysis results | ||||
| OpenCover/ | ||||
|  | ||||
| # Azure Stream Analytics local run output | ||||
| ASALocalRun/ | ||||
|  | ||||
| # MSBuild Binary and Structured Log | ||||
| *.binlog | ||||
|  | ||||
| # NVidia Nsight GPU debugger configuration file | ||||
| *.nvuser | ||||
|  | ||||
| # MFractors (Xamarin productivity tool) working folder | ||||
| .mfractor/ | ||||
|  | ||||
| # Local History for Visual Studio | ||||
| .localhistory/ | ||||
|  | ||||
| # BeatPulse healthcheck temp database | ||||
| healthchecksdb | ||||
|  | ||||
| # Backup folder for Package Reference Convert tool in Visual Studio 2017 | ||||
| MigrationBackup/ | ||||
|  | ||||
| # Ionide (cross platform F# VS Code tools) working folder | ||||
| .ionide/ | ||||
|  | ||||
| # Fody - auto-generated XML schema | ||||
| FodyWeavers.xsd | ||||
|  | ||||
							
								
								
									
										15
									
								
								framework/Demo/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								framework/Demo/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <Project> | ||||
| 	<PropertyGroup> | ||||
| 		<Version>3.0.0.3</Version> | ||||
| 		<LangVersion>latest</LangVersion> | ||||
| 		<TargetFrameworks>net6.0;net7.0</TargetFrameworks> | ||||
| 		<Authors>Diego</Authors> | ||||
| 		<Product>ThingsGateway</Product> | ||||
| 		<Copyright>© 2023-present Diego</Copyright> | ||||
| 		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl> | ||||
| 		<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages> | ||||
| 		<GenerateDocumentationFile>False</GenerateDocumentationFile> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
|  | ||||
| </Project> | ||||
| @@ -12,4 +12,4 @@ | ||||
| 
 | ||||
| global using System; | ||||
| 
 | ||||
| global using TouchSocket.Core; | ||||
| global using ThingsGateway.Components; | ||||
| @@ -0,0 +1,38 @@ | ||||
| #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 Photino.Blazor; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
| internal class Program | ||||
| { | ||||
|     [STAThread] | ||||
|     private static void Main(string[] args) | ||||
|     { | ||||
|         System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory); | ||||
|  | ||||
|         var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args); | ||||
|  | ||||
|         appBuilder.RootComponents.Add<App>("#app"); | ||||
|  | ||||
|         appBuilder.Services.ThingsGatewayComponentsConfigureServices(); | ||||
|         var app = appBuilder.Build(); | ||||
|         app.MainWindow.SetTitle("ThingsGateway.Foundation.Demo"); | ||||
|         app.MainWindow.SetIconFile("wwwroot/favicon.ico"); | ||||
|         AppDomain.CurrentDomain.UnhandledException += (sender, error) => | ||||
|         { | ||||
|         }; | ||||
|  | ||||
|         app.Run(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>WinExe</OutputType> | ||||
| 		<ApplicationIcon>favicon.ico</ApplicationIcon> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <None Remove="favicon.ico" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <Content Include="favicon.ico"> | ||||
| 	    <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 	    <ExcludeFromSingleFile>true</ExcludeFromSingleFile> | ||||
| 	    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
| 	  </Content> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Photino.Blazor" Version="2.6.0" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
|  | ||||
| </Project> | ||||
							
								
								
									
										
											BIN
										
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Photino/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Photino/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										26
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Rcl/App.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Rcl/App.razor
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
|  | ||||
| <Router AppAssembly="@typeof(App).Assembly"> | ||||
|     <Found Context="routeData"> | ||||
|         <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> | ||||
|         <FocusOnNavigate RouteData="@routeData" Selector="h1" /> | ||||
|     </Found> | ||||
|     <NotFound> | ||||
|         <LayoutView Layout="@typeof(MainLayout)"> | ||||
|             <p role="alert">Sorry, there's nothing at this address.</p> | ||||
|         </LayoutView> | ||||
|     </NotFound> | ||||
| </Router> | ||||
|  | ||||
| @@ -0,0 +1,146 @@ | ||||
| #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; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
| using LogLevel = Microsoft.Extensions.Logging.LogLevel; | ||||
| /// <summary> | ||||
| /// 调试UI | ||||
| /// </summary> | ||||
| public abstract class DriverDebugUIBase : ComponentBase, IDisposable | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志缓存 | ||||
|     /// </summary> | ||||
|     public ConcurrentLinkedList<(LogLevel level, string message)> Messages = new(); | ||||
|  | ||||
|     private PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(1)); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     ~DriverDebugUIBase() | ||||
|     { | ||||
|         this.SafeDispose(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 变量地址 | ||||
|     /// </summary> | ||||
|     public virtual string Address { get; set; } = "40001"; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 长度 | ||||
|     /// </summary> | ||||
|     public virtual int Length { get; set; } = 1; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 默认读写设备 | ||||
|     /// </summary> | ||||
|     public virtual IReadWrite Plc { get; set; } | ||||
|     /// <summary> | ||||
|     /// 写入值 | ||||
|     /// </summary> | ||||
|     public virtual string WriteValue { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 数据类型 | ||||
|     /// </summary> | ||||
|     protected virtual DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Int16; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     [Inject] | ||||
|     public InitTimezone InitTimezone { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public virtual void Dispose() | ||||
|     { | ||||
|         _periodicTimer?.Dispose(); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public void LogOut(ThingsGateway.Foundation.LogLevel logLevel, object source, string message, Exception exception) | ||||
|     { | ||||
|         Messages.Add(((LogLevel)logLevel, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {message} {exception}")); | ||||
|         if (Messages.Count > 2500) | ||||
|         { | ||||
|             Messages.Clear(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public virtual async Task ReadAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var data = await Plc.ReadAsync(Address, Length, DataTypeEnum); | ||||
|             if (data.IsSuccess) | ||||
|             { | ||||
|                 Messages.Add((LogLevel.Information, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 对应类型值:{Environment.NewLine}{data.Content.ToJsonString(true)} ")); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 Messages.Add((LogLevel.Warning, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}")); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Messages.Add((LogLevel.Error, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex.Message}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public virtual async Task WriteAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var data = await Plc.WriteAsync(Address, WriteValue, Length, DataTypeEnum); | ||||
|             if (data.IsSuccess) | ||||
|             { | ||||
|                 Messages.Add((LogLevel.Information, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}")); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 Messages.Add((LogLevel.Warning, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}")); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Messages.Add((LogLevel.Error, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex.Message}")); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         _ = RunTimerAsync(); | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|     private async Task RunTimerAsync() | ||||
|     { | ||||
|         while (await _periodicTimer.WaitForNextTickAsync()) | ||||
|         { | ||||
|             await InvokeAsync(StateHasChanged); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,205 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Microsoft.JSInterop; | ||||
| @using ThingsGateway.Foundation; | ||||
| @using Masa.Blazor; | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @inherits DriverDebugUIBase | ||||
|  | ||||
| <MCard Elevation="1" Rounded="false" Class=" pa-2" Style="width:100%"> | ||||
|     <MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters> | ||||
|  | ||||
|         <MCol Md="5"> | ||||
|             <MTabs @bind-Value="tab" Class="ma-2"> | ||||
|                 <MTab Value=1>   读写测试    </MTab> | ||||
|                 <MTab Value=2>    特殊功能    </MTab> | ||||
|                 <MTab Value=3>    代码示例    </MTab> | ||||
|             </MTabs> | ||||
|  | ||||
|             <MTabsItems Value="tab"> | ||||
|                 <MTabItem Value="1"> | ||||
|                     @if (tab == 1) | ||||
|                     { | ||||
|                         @if (ReadWriteContent == null) | ||||
|                         { | ||||
|  | ||||
|                             <div class="my-1 py-1"> | ||||
|                                 <MTooltip Bottom Context="tip"> | ||||
|                                     <ActivatorContent> | ||||
|                                         <MTextarea 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> | ||||
|                                 <MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters> | ||||
|                                 <MTextField Class="mx-1 my-1" Style="max-width:200px" Label="读取长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@Length /> | ||||
|                                     <MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="DataTypeEnum" Outlined Label="数据类型" | ||||
|                                              Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar()) | ||||
|                                              MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                                              ItemText=@((u) =>u.Description) | ||||
|                                              ItemValue=@(u =>(DataTypeEnum)u.Value) | ||||
|                                              HideDetails=@("auto") Height="30" | ||||
|                                               Dense> | ||||
|                                 </MSelect> | ||||
|                             </MRow> | ||||
|  | ||||
|                             <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> | ||||
|  | ||||
|                             </div> | ||||
|  | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             @ReadWriteContent | ||||
|                         } | ||||
|                     } | ||||
|                 </MTabItem> | ||||
|  | ||||
|                 <MTabItem Value="2"> | ||||
|                     @if (tab == 2) | ||||
|                     { | ||||
|                         @if (ShowDefaultOtherContent) | ||||
|                         { | ||||
|                             <MSubheader> | ||||
|                                 连读打包 | ||||
|                             </MSubheader> | ||||
|                             <MContainer> | ||||
|  | ||||
|                                 @foreach (var item in DeviceVariableRunTimes) | ||||
|                                 { | ||||
|                                     <MRow Dense Align="AlignTypes.Center"> | ||||
|                                         <MTextField Class="ma-1" Outlined Style="min-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.VariableAddress)) Dense HideDetails="@("auto")" @bind-Value=@item.VariableAddress></MTextField> | ||||
|                                         <MSelect Class="mx-1 my-1" Style="max-width:120px" @bind-Value="item.DataTypeEnum" Outlined Label=@(item.DescriptionWithOutSugar(x => x.DataTypeEnum)) | ||||
|                                                  Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar()) | ||||
|                                                  MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                                                  ItemText=@((u) =>u.Description) | ||||
|                                                  ItemValue=@(u =>(DataTypeEnum)u.Value) | ||||
|                                                  HideDetails=@("auto") Height="30" | ||||
|                                               Dense> | ||||
|                                     </MSelect> | ||||
|  | ||||
|                                     <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.IntervalTime)) Dense HideDetails="@("auto")" @bind-Value=@item.IntervalTime></MTextField> | ||||
|  | ||||
|                                         <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=实时值 Readonly ClearIcon="" Dense HideDetails="@("auto")" Value=item.Value?.ToJsonString()></MTextField> | ||||
|  | ||||
|                                     </MRow> | ||||
|                                 } | ||||
|                                 <MRow Dense> | ||||
|                                 <MTextField Class="ma-1" Outlined Style="max-width:100px" Label="打包长度" Dense HideDetails="@("auto")" @bind-Value=@MaxPack></MTextField> | ||||
|                                     <MButton Class="ma-1" Color="primary" OnClick="MulReadAsync"> | ||||
|                                         读取 | ||||
|                                     </MButton> | ||||
|                                 </MRow> | ||||
|                             </MContainer> | ||||
|  | ||||
|                         } | ||||
|  | ||||
|                         @if (OtherContent != null) | ||||
|                         { | ||||
|                             <MSheet Style="height:100%;overflow-y:auto"> | ||||
|                                 @OtherContent | ||||
|                             </MSheet> | ||||
|                         } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|                     } | ||||
|                 </MTabItem> | ||||
|  | ||||
|  | ||||
|                 <MTabItem Value="3"> | ||||
|                     @if (tab == 3) | ||||
|                     { | ||||
|                         @if (CodeContent != null) | ||||
|                             @CodeContent | ||||
|                         else | ||||
|                         { | ||||
|  | ||||
|                             <MRow Align="AlignTypes.Center"> | ||||
|                                 <MContainer> | ||||
|  | ||||
|                                     <MItemGroup @bind-Value="_selected" Class="shrink mr-6" Mandatory> | ||||
|                                     @{ | ||||
|  | ||||
|                                             int index = 0; | ||||
|  | ||||
|                                         } | ||||
|                                         <MRow> | ||||
|  | ||||
|                                             @foreach (var item in Sections) | ||||
|                                             { | ||||
|                                                 <MItem Value="@(index++)"> | ||||
|                                                     <div> | ||||
|                                                         <MButton IsActive="@context.Active" Icon OnClick="@context.Toggle"> | ||||
|                                                             <MIcon>mdi-record</MIcon> | ||||
|                                                         </MButton> | ||||
|                                                     </div> | ||||
|                                                 </MItem> | ||||
|                                             } | ||||
|                                         </MRow> | ||||
|                                     </MItemGroup> | ||||
|                                 </MContainer> | ||||
|  | ||||
|                                 <MCol> | ||||
|                                     <MWindow Value="_selected" Vertical Class="elevation-1 grey lighten-5 rounded-b" Style=@($"height:450px;overflow:auto")> | ||||
|                                         @{ | ||||
|                                             int index = 0; | ||||
|                                         } | ||||
|                                         @foreach (var item in Sections) | ||||
|                                         { | ||||
|                                             <MWindowItem Value="@(index++)"> | ||||
|                                                 <AppCode RoundedTop0 Code="@item.Code" Language="@item.Language" /> | ||||
|                                             </MWindowItem> | ||||
|                                         } | ||||
|                                     </MWindow> | ||||
|                                 </MCol> | ||||
|                             </MRow> | ||||
|  | ||||
|  | ||||
|                         } | ||||
|  | ||||
|  | ||||
|                     } | ||||
|                 </MTabItem> | ||||
|             </MTabsItems> | ||||
|  | ||||
|         </MCol> | ||||
|  | ||||
|         <MCol Md="7"> | ||||
|             <MCard Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4"> | ||||
|                 <ConsoleTxt Messages="Messages" Height=500></ConsoleTxt> | ||||
|             </MCard> | ||||
|         </MCol> | ||||
|  | ||||
|     </MRow> | ||||
|  | ||||
| </MCard> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     StringNumber tab; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,236 @@ | ||||
| #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 Microsoft.AspNetCore.Components; | ||||
|  | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class DriverDebugUIPage : DriverDebugUIBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// DeviceVariableRunTimes | ||||
|     /// </summary> | ||||
|     public List<DeviceVariableRunTime> DeviceVariableRunTimes; | ||||
|     /// <summary> | ||||
|     /// MaxPack | ||||
|     /// </summary> | ||||
|     public int MaxPack = 100; | ||||
|     /// <summary> | ||||
|     /// MulReadAsync | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task MulReadAsync() | ||||
|     { | ||||
|         var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack); | ||||
|         foreach (var item in deviceVariableSourceReads) | ||||
|         { | ||||
|             var result = await Plc.ReadAsync(item.VariableAddress, item.Length); | ||||
|             if (result.IsSuccess) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     item.DeviceVariableRunTimes.PraseStructContent(Plc, result.Content); | ||||
|                     Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Content.ToHexString(' '))); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex.Message)); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|                 Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Message)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private StringNumber _selected = 0; | ||||
|     /// <summary> | ||||
|     /// Sections | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public List<(string Code, string Language)> Sections { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ShowDefaultOtherContent | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public bool ShowDefaultOtherContent { get; set; } = true; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         DeviceVariableRunTimes = new() | ||||
|             { | ||||
|                                 new DeviceVariableRunTime() | ||||
|                                 { | ||||
|                                     DataTypeEnum=DataTypeEnum.Int16, | ||||
|                                     VariableAddress="40001", | ||||
|                                     IntervalTime=1000, | ||||
|                                 }, | ||||
|                                    new DeviceVariableRunTime() | ||||
|                                 { | ||||
|                                     DataTypeEnum=DataTypeEnum.Int16, | ||||
|                                     VariableAddress="40011", | ||||
|                                     IntervalTime=1000, | ||||
|                                 }, | ||||
|                                    new DeviceVariableRunTime() | ||||
|                                 { | ||||
|                                     DataTypeEnum=DataTypeEnum.Int16, | ||||
|                                     VariableAddress="40031", | ||||
|                                     IntervalTime=1000, | ||||
|                                 }, | ||||
|                                    new DeviceVariableRunTime() | ||||
|                                 { | ||||
|                                     DataTypeEnum=DataTypeEnum.Int16, | ||||
|                                     VariableAddress="40101", | ||||
|                                     IntervalTime=1000, | ||||
|                                 }, | ||||
|             }; | ||||
|         Sections.Add(( | ||||
| """ | ||||
|                 /// <inheritdoc/> | ||||
|                 public class DeviceVariableSourceRead : IDeviceVariableSourceRead<DeviceVariableRunTime> | ||||
|                 { | ||||
|                     /// <inheritdoc/> | ||||
|                     public TimerTick TimerTick { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     public string VariableAddress { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     public int Length { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     public List<DeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<DeviceVariableRunTime>(); | ||||
|                 } | ||||
|                 /// <inheritdoc/> | ||||
|                 public class DeviceVariableRunTime : IDeviceVariableRunTime | ||||
|                 { | ||||
|                     /// <inheritdoc/> | ||||
|                     [Description("读取间隔")] | ||||
|                     public int IntervalTime { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     [Description("变量地址")] | ||||
|                     public string VariableAddress { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     public int Index { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     [Description("数据类型")] | ||||
|                     public DataTypeEnum DataTypeEnum { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     [Description("实时值")] | ||||
|                     public object Value { get; set; } | ||||
|                     /// <inheritdoc/> | ||||
|                     public OperResult SetValue(object value) | ||||
|                     { | ||||
|                         Value = value; | ||||
|                         return OperResult.CreateSuccessResult(); | ||||
|                     } | ||||
|                 } | ||||
|                 public List<DeviceVariableRunTime> DeviceVariableRunTimes; | ||||
|                                  | ||||
|                 private static async Task ModbusClientAsync(IReadWrite plc) | ||||
|                 { | ||||
|                 DeviceVariableRunTimes = new() | ||||
|                 { | ||||
|                                 new DeviceVariableRunTime() | ||||
|                                 { | ||||
|                                     DataTypeEnum=DataTypeEnum.Int16, | ||||
|                                     VariableAddress="40001", | ||||
|                                     IntervalTime=1000, | ||||
|                                 }, | ||||
|                                    new DeviceVariableRunTime() | ||||
|                                 { | ||||
|                                     DataTypeEnum=DataTypeEnum.Int16, | ||||
|                                     VariableAddress="40011", | ||||
|                                     IntervalTime=1000, | ||||
|                                 }, | ||||
|                                    new DeviceVariableRunTime() | ||||
|                                 { | ||||
|                                     DataTypeEnum=DataTypeEnum.Int16, | ||||
|                                     VariableAddress="40031", | ||||
|                                     IntervalTime=1000, | ||||
|                                 }, | ||||
|                                    new DeviceVariableRunTime() | ||||
|                                 { | ||||
|                                     DataTypeEnum=DataTypeEnum.Int16, | ||||
|                                     VariableAddress="40101", | ||||
|                                     IntervalTime=1000, | ||||
|                                 }, | ||||
|                 }; | ||||
|  | ||||
|                     #region 连读 | ||||
|                 var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack); | ||||
|                 foreach (var item in deviceVariableSourceReads) | ||||
|                 { | ||||
|                     var result = await Plc.ReadAsync(item.VariableAddress, item.Length); | ||||
|                     if (result.IsSuccess) | ||||
|                     { | ||||
|                         item.DeviceVariableRunTimes.PraseStructContent(result.Content); | ||||
|                     } | ||||
|                 } | ||||
|                     #endregion | ||||
|  | ||||
|                 } | ||||
|                  | ||||
| """, "csharp")); | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 自定义模板 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public RenderFragment ReadWriteContent { get; set; } | ||||
|     /// <summary> | ||||
|     /// 自定义模板 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public RenderFragment OtherContent { get; set; } | ||||
|     /// <summary> | ||||
|     /// 自定义模板 | ||||
|     /// </summary> | ||||
|     [Parameter] | ||||
|     public RenderFragment CodeContent { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     ~DriverDebugUIPage() | ||||
|     { | ||||
|         this.SafeDispose(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public override IReadWrite Plc { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void Dispose() | ||||
|     { | ||||
|         Plc?.SafeDispose(); | ||||
|         base.Dispose(); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,47 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using System.IO.Ports; | ||||
| @using Masa.Blazor | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">通道配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
|         <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.PortName)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.PortName /> | ||||
|         <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.BaudRate)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.BaudRate /> | ||||
|         <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.DataBits)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.DataBits /> | ||||
|         <MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.Parity" Label="@(serialProperty.DescriptionWithOutSugar(x => x.Parity))" | ||||
|                  Items=@(typeof(Parity).GetEnumListWithOutSugar()) | ||||
|                  MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                  ItemText=@((u) =>u.Description) | ||||
|                  ItemValue=@(u =>(Parity)u.Value) | ||||
|                  HideDetails=@("auto") Height="30" | ||||
|                  Dense> | ||||
|         </MSelect> | ||||
|         <MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.StopBits" Label="@(serialProperty.DescriptionWithOutSugar(x => x.StopBits))" | ||||
|                  Items=@(typeof(StopBits).GetEnumListWithOutSugar()) | ||||
|                  MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                  ItemText=@((u) =>u.Description) | ||||
|                  ItemValue=@(u =>(StopBits)u.Value) | ||||
|                  HideDetails=@("auto") Height="30" | ||||
|                  Dense> | ||||
|         </MSelect> | ||||
|         <MButton Class="ma-1" OnClick=@ConnectAsync Color="primary"> | ||||
|             连接 | ||||
|         </MButton> | ||||
|         <MButton Class="ma-1" OnClick=@DisConnect Color="red"> | ||||
|             断开 | ||||
|         </MButton> | ||||
|     </MRow> | ||||
| </MCard> | ||||
| @@ -0,0 +1,106 @@ | ||||
| #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.Foundation.Demo; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class SerialSessionPage : IDisposable | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志输出 | ||||
|     /// </summary> | ||||
|     public Action<LogLevel, object, string, Exception> LogAction; | ||||
|  | ||||
|     private TouchSocketConfig config; | ||||
|  | ||||
|     private readonly SerialProperty serialProperty = new(); | ||||
|  | ||||
|     private SerialSession SerialSession { get; set; } = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取对象 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public SerialSession GetSerialSession() | ||||
|     { | ||||
|         config ??= new TouchSocketConfig(); | ||||
|         var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|         config.SetSerialProperty(serialProperty); | ||||
|         //载入配置 | ||||
|         SerialSession.Setup(config); | ||||
|         return SerialSession; | ||||
|     } | ||||
|     private async Task ConnectAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             SerialSession.Close(); | ||||
|             await GetSerialSession().ConnectAsync(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private void DisConnect() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             SerialSession.Close(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         config ??= new TouchSocketConfig(); | ||||
|  | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|             config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|             SerialSession.Setup(config); | ||||
|         } | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|  | ||||
|     private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception); | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         SerialSession.SafeDispose(); | ||||
|     } | ||||
|     internal void StateHasChangedAsync() | ||||
|     { | ||||
|         StateHasChanged(); | ||||
|     } | ||||
| } | ||||
| @@ -10,21 +10,19 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
| 
 | ||||
| @namespace ThingsGateway.Blazor | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using System.IO.Ports; | ||||
| @using System.Collections.Concurrent; | ||||
| @using ThingsGateway.Foundation; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
| @using Masa.Blazor | ||||
| @using TouchSocket.Core; | ||||
| @using TouchSocket.Sockets; | ||||
| @implements IDisposable | ||||
| <MCard Class="pa-4" Flat Elevation="0" Rounded="false"> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start"> | ||||
|         <MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP /> | ||||
|         <MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port /> | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">通道配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
| 
 | ||||
|         <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP /> | ||||
|         <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port /> | ||||
| 
 | ||||
|         <MButton Class="ma-1" OnClick=@ConnectAsync Color="primary"> | ||||
|             连接 | ||||
| @@ -33,7 +31,4 @@ | ||||
|             断开 | ||||
|         </MButton> | ||||
|     </MRow> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| </MCard> | ||||
| @@ -0,0 +1,116 @@ | ||||
| #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.Foundation.Demo; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class TcpClientPage : IDisposable | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志输出 | ||||
|     /// </summary> | ||||
|     public Action<LogLevel, object, string, Exception> LogAction; | ||||
|  | ||||
|     private TouchSocketConfig config; | ||||
|     /// <summary> | ||||
|     /// IP | ||||
|     /// </summary> | ||||
|     private string IP = "127.0.0.1"; | ||||
|     /// <summary> | ||||
|     /// Port | ||||
|     /// </summary> | ||||
|     public int Port { get; set; } = 502; | ||||
|  | ||||
|     private TcpClient TcpClient { get; set; } = new(); | ||||
|  | ||||
|     private async Task ConnectAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             TcpClient.Close(); | ||||
|             await GetTcpClient().ConnectAsync(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|  | ||||
|             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private void DisConnect() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             TcpClient.Close(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|  | ||||
|             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 获取对象 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public TcpClient GetTcpClient() | ||||
|     { | ||||
|         config ??= new TouchSocketConfig(); | ||||
|         var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|         config.SetRemoteIPHost(new IPHost(IP + ":" + Port)); | ||||
|         //载入配置 | ||||
|         TcpClient.Setup(config); | ||||
|         return TcpClient; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         config ??= new TouchSocketConfig(); | ||||
|  | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|             config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|             config.SetRemoteIPHost(new IPHost(IP + ":" + Port)); | ||||
|             TcpClient.Setup(config); | ||||
|         } | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|  | ||||
|     private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         TcpClient.SafeDispose(); | ||||
|     } | ||||
|  | ||||
|     internal void StateHasChangedAsync() | ||||
|     { | ||||
|         StateHasChanged(); | ||||
|     } | ||||
| } | ||||
| @@ -10,21 +10,20 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
| 
 | ||||
| @namespace ThingsGateway.Blazor | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using System.IO.Ports; | ||||
| @using System.Collections.Concurrent; | ||||
| @using ThingsGateway.Foundation; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
| @using Masa.Blazor | ||||
| @using TouchSocket.Core; | ||||
| @using TouchSocket.Sockets; | ||||
| @implements IDisposable | ||||
| <MCard Class="pa-4" Flat Elevation="0" Rounded="false"> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start"> | ||||
|         <MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip /> | ||||
|         <MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port /> | ||||
| 
 | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">通道配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
| 
 | ||||
|         <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip /> | ||||
|         <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port /> | ||||
| 
 | ||||
|         <MButton Class="ma-1" OnClick=@Connect Color="primary"> | ||||
|             连接 | ||||
| @@ -32,8 +31,8 @@ | ||||
|         <MButton Class="ma-1" OnClick=@DisConnect Color="red"> | ||||
|             断开 | ||||
|         </MButton> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     </MRow> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| </MCard> | ||||
| </MCard> | ||||
| @@ -1,25 +1,22 @@ | ||||
| #region copyright | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using TouchSocket.Core; | ||||
| using TouchSocket.Sockets; | ||||
| 
 | ||||
| namespace ThingsGateway.Blazor; | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
| 
 | ||||
| /// <inheritdoc/> | ||||
| public partial class TcpServerPage | ||||
| public partial class TcpServerPage : IDisposable | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志输出 | ||||
|     /// 日志输出 | ||||
|     /// </summary> | ||||
|     public Action<LogLevel, object, string, Exception> LogAction; | ||||
| 
 | ||||
| @@ -31,11 +28,6 @@ public partial class TcpServerPage | ||||
| 
 | ||||
|     private TcpService TcpServer { get; set; } = new(); | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         TcpServer.SafeDispose(); | ||||
|     } | ||||
|     private void Connect() | ||||
|     { | ||||
|         try | ||||
| @@ -62,39 +54,55 @@ public partial class TcpServerPage | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 获取对象 | ||||
|     /// 获取对象 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public TcpService GetTcpServer() | ||||
|     { | ||||
|         config?.Dispose(); | ||||
|         config = new TouchSocketConfig(); | ||||
|         var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace }; | ||||
|         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace }); | ||||
|         config ??= new TouchSocketConfig(); | ||||
|         var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|         config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) }); | ||||
|         config.SetBufferLength(300); | ||||
|         //载入配置 | ||||
|         //载入配置 | ||||
|         TcpServer.Setup(config); | ||||
|         return TcpServer; | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         config ??= new TouchSocketConfig(); | ||||
| 
 | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     protected override void OnInitialized() | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         config?.Dispose(); | ||||
|         config = new TouchSocketConfig(); | ||||
|         var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace }; | ||||
|         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace }); | ||||
|         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|         config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) }); | ||||
|         config.SetBufferLength(300); | ||||
|         TcpServer = new TcpService(); | ||||
|         TcpServer.Setup(config); | ||||
|         base.OnInitialized(); | ||||
|         if (firstRender) | ||||
|         { | ||||
|             var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|             config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|             config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) }); | ||||
|             TcpServer.Setup(config); | ||||
|         } | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
| 
 | ||||
|     private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         TcpServer.SafeDispose(); | ||||
|     } | ||||
|     internal void StateHasChangedAsync() | ||||
|     { | ||||
|         StateHasChanged(); | ||||
|     } | ||||
| } | ||||
| @@ -10,21 +10,20 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
| 
 | ||||
| @namespace ThingsGateway.Blazor | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using System.IO.Ports; | ||||
| @using System.Collections.Concurrent; | ||||
| @using ThingsGateway.Foundation; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
| @using Masa.Blazor | ||||
| @using TouchSocket.Core; | ||||
| @using TouchSocket.Sockets; | ||||
| @implements IDisposable | ||||
| <MCard Class="pa-4" Flat Elevation="0" Rounded="false"> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start"> | ||||
|         <MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip /> | ||||
|         <MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port /> | ||||
| 
 | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">通道配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
| 
 | ||||
|         <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP /> | ||||
|         <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port /> | ||||
| 
 | ||||
|         <MButton Class="ma-1" OnClick=Connect Color="primary"> | ||||
|             连接 | ||||
| @@ -32,8 +31,7 @@ | ||||
|         <MButton Class="ma-1" OnClick=DisConnect Color="red"> | ||||
|             断开 | ||||
|         </MButton> | ||||
| 
 | ||||
| 
 | ||||
|     </MRow> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| </MCard> | ||||
| @@ -0,0 +1,113 @@ | ||||
| #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.Foundation.Demo; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class UdpSessionPage : IDisposable | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志输出 | ||||
|     /// </summary> | ||||
|     public Action<LogLevel, object, string, Exception> LogAction; | ||||
|  | ||||
|     private TouchSocketConfig config; | ||||
|     /// <summary> | ||||
|     /// IP | ||||
|     /// </summary> | ||||
|     public string IP = "127.0.0.1"; | ||||
|     /// <summary> | ||||
|     /// Port | ||||
|     /// </summary> | ||||
|     public int Port = 502; | ||||
|  | ||||
|     private UdpSession UdpSession { get; set; } = new(); | ||||
|  | ||||
|     private void Connect() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             UdpSession.Stop(); | ||||
|             GetUdpSession().Start(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private void DisConnect() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             UdpSession.Stop(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|  | ||||
|             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 获取对象 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public UdpSession GetUdpSession() | ||||
|     { | ||||
|         config ??= new TouchSocketConfig(); | ||||
|         var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|         config.SetRemoteIPHost(new IPHost(IP + ":" + Port)); | ||||
|         config.SetBindIPHost(new IPHost(0)); | ||||
|         //载入配置 | ||||
|         UdpSession.Setup(config); | ||||
|         return UdpSession; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         config ??= new TouchSocketConfig(); | ||||
|  | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|             config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||
|             config.SetRemoteIPHost(new IPHost(IP + ":" + Port)); | ||||
|             config.SetBindIPHost(new IPHost(0)); | ||||
|             UdpSession.Setup(config); | ||||
|         } | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|     private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         UdpSession.SafeDispose(); | ||||
|     } | ||||
|     internal void StateHasChangedAsync() | ||||
|     { | ||||
|         StateHasChanged(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| #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.Foundation.Demo; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class DeviceVariableRunTime : IDeviceVariableRunTime | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     [Description("读取间隔")] | ||||
|     public int IntervalTime { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     [Description("变量地址")] | ||||
|     public string VariableAddress { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public int Index { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     [Description("数据类型")] | ||||
|     public DataTypeEnum DataTypeEnum { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     [Description("实时值")] | ||||
|     public object Value { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public OperResult SetValue(object value, DateTime dateTime = default, bool isOnline = true) | ||||
|     { | ||||
|         Value = value; | ||||
|         return OperResult.CreateSuccessResult(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| #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.Collections.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class DeviceVariableSourceRead : IDeviceVariableSourceRead<IDeviceVariableRunTime> | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public TimerTick TimerTick { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public string VariableAddress { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public int Length { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public List<IDeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<IDeviceVariableRunTime>(); | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| global using System; | ||||
| global using System.Threading; | ||||
| global using System.Threading.Tasks; | ||||
|  | ||||
| global using ThingsGateway.Components; | ||||
| global using ThingsGateway.Foundation.Serial; | ||||
| global using ThingsGateway.Foundation.Sockets; | ||||
| @@ -0,0 +1,36 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/" | ||||
| @layout BaseLayout | ||||
| @inject NavigationManager NavigationManager | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
|  | ||||
| @code { | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     /// <returns></returns> | ||||
|     protected override async Task OnAfterRenderAsync(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             NavigationManager.NavigateTo("index"); | ||||
|         } | ||||
|  | ||||
|         await base.OnAfterRenderAsync(firstRender); | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,57 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/index" | ||||
|  | ||||
| <div class="ml-2"> | ||||
|     <div class="my-6 "> | ||||
|         <MLabel Class="text-h3" Color="primary">ThingsGateway</MLabel> | ||||
|     </div> | ||||
|     <div> | ||||
|         <strong class="text--lighten-1 text-h5 my-1">文档</strong> | ||||
|     </div> | ||||
|     <div class="my-2 ml-4"> | ||||
|         <PCopyableText> | ||||
|             https://diego2098.gitee.io/thingsgateway-docs/ | ||||
|         </PCopyableText> | ||||
|     </div> | ||||
|     <div> | ||||
|         <strong class="text--lighten-1 text-h5 my-1">协议</strong> | ||||
|     </div> | ||||
|     <div class="my-2 ml-4"> | ||||
|         <PCopyableText Text="https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh"> | ||||
|             Apache-2.0开源协议 | ||||
|         </PCopyableText> | ||||
|     </div> | ||||
|  | ||||
|     <div> | ||||
|         <strong class="text--lighten-1 text-h5 my-1">赞助</strong> | ||||
|     </div> | ||||
|     <div class="my-2 ml-4"> | ||||
|         <PCopyableText Text="https://diego2098.gitee.io/thingsgateway-docs/docs/donate"> | ||||
|             ThingsGateway赞助途径 | ||||
|         </PCopyableText> | ||||
|     </div> | ||||
|  | ||||
|     <div> | ||||
|         <strong class="text--lighten-1 text-h5 my-1">社区</strong> | ||||
|     </div> | ||||
|     <div class="my-2 ml-4"> | ||||
|         <PCopyableText Text="605534569"> | ||||
|             QQ群:605534569 | ||||
|         </PCopyableText> | ||||
|     </div> | ||||
|  | ||||
|      | ||||
|  | ||||
| </div> | ||||
|  | ||||
| @@ -0,0 +1,42 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @inherits LayoutComponentBase | ||||
|  | ||||
| <CascadingValue Value="IsMobile" Name="IsMobile"> | ||||
|     <MApp> | ||||
|         <MErrorHandler> | ||||
|             @Body | ||||
|         </MErrorHandler> | ||||
|     </MApp> | ||||
| </CascadingValue> | ||||
|  | ||||
| @code { | ||||
|     public bool IsMobile { get; set; } | ||||
|     [Inject] | ||||
|     public MasaBlazor MasaBlazor { get; set; } | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         base.OnInitialized(); | ||||
|         MasaBlazor.BreakpointChanged += BreakpointOnOnUpdate; | ||||
|     } | ||||
|  | ||||
|     private void BreakpointOnOnUpdate(object sender, BreakpointChangedEventArgs e) | ||||
|     { | ||||
|         IsMobile = MasaBlazor.Breakpoint.Mobile; | ||||
|         if (e.MobileChanged) | ||||
|         { | ||||
|             StateHasChanged(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -10,34 +10,18 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
| 
 | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @using Masa.Blazor.Presets | ||||
| @using ThingsGateway.Admin.Blazor.Core | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using System.Text; | ||||
| @inherits LayoutComponentBase | ||||
| @layout BaseLayout | ||||
| @if (UserManager.UserId > 0) | ||||
| { | ||||
|     <SysSignalR></SysSignalR> | ||||
| } | ||||
| 
 | ||||
| <PPageTabsProvider> | ||||
| 
 | ||||
|     <CascadingValue Value="@this" IsFixed> | ||||
|         <CascadingValue Value="@Changed" Name="Changed"> | ||||
| 
 | ||||
|             <MNavigationDrawer Color="barcolor" @bind-Value="_drawerOpen" App Width="200"> | ||||
|                 @if (IsMobile) | ||||
|                 { | ||||
|                     <MSystemBar Color="barcolor" Height="@(BlazorResourceConst.PageTabsHeight)"> | ||||
|                         <MButton Icon OnClick=@(()=> _drawerOpen = !_drawerOpen)> | ||||
|                             <MIcon> | ||||
|                                 mdi-close-thick | ||||
|                             </MIcon> | ||||
|                         </MButton> | ||||
|                         <MSpacer /> | ||||
|                         <AppbarButtons /> | ||||
|                     </MSystemBar> | ||||
|                 } | ||||
|                 <Logo CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE HeightInt=@(IsMobile?BlazorResourceConst.AppBarHeight:BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight) /> | ||||
|                 <Logo CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT  CONFIG_TITLE=@CONFIG_TITLE HeightInt=@(IsMobile?BlazorResourceConst.AppBarHeight:BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight) /> | ||||
|                 <AppList ClassString="overflow-y-auto" Routable | ||||
|                          StyleString=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight}px);") | ||||
|                          Items="Navs" /> | ||||
| @@ -48,18 +32,16 @@ | ||||
|                 <MButton Class="mr-0" Icon Small=IsMobile OnClick=@(() => _drawerOpen = !_drawerOpen)> | ||||
|                     <MIcon>mdi-menu</MIcon> | ||||
|                 </MButton> | ||||
|                 <AppBarItems CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE> | ||||
|                 </AppBarItems> | ||||
| 
 | ||||
|             </MAppBar> | ||||
| 
 | ||||
|             <MMain Style=@($"{(!(IsMobile||_drawerOpen!=true)? "padding-left:200px;":"")}")> | ||||
|                 <div class="full-width"> | ||||
|                     <PageTabs @ref="_pageTabs" /> | ||||
|                     <PageTabs @ref="_pageTabs" PageTabItems="pageTabItems" /> | ||||
|                 </div> | ||||
|                 <MDivider Center></MDivider> | ||||
|                 <MCard Flat Class="overflow-y-auto overflow-x-hidden ma-auto pa-0 rounded-0" Style=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight+BlazorResourceConst.FooterHeight}px);")> | ||||
|                     <PPageContainer PageTabs="@_pageTabs?.PPageTabs" SelfPatterns="@selfPatterns"> | ||||
|                     <PPageContainer PageTabs="@_pageTabs?.PPageTabs"> | ||||
|                         @Body | ||||
|                     </PPageContainer> | ||||
|                 </MCard> | ||||
| @@ -71,5 +53,25 @@ | ||||
|     </CascadingValue> | ||||
| 
 | ||||
| </PPageTabsProvider> | ||||
| @code { | ||||
|     bool Changed { get; set; } | ||||
|     private bool? _drawerOpen = true; | ||||
| 
 | ||||
|     private PageTabs _pageTabs; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// IsMobile | ||||
|     /// </summary> | ||||
|     [CascadingParameter(Name = "IsMobile")] | ||||
|     public bool IsMobile { get; set; } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @code{ | ||||
|     private string CONFIG_COPYRIGHT = "Diego"; | ||||
|     private string CONFIG_COPYRIGHT_URL = "https://gitee.com/diego2098/ThingsGateway"; | ||||
|     private string CONFIG_TITLE = "ThingsGateway"; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|   | ||||
| @@ -0,0 +1,214 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD> | ||||
| //  Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD> | ||||
| //  GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<51><C8BA>605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
| public partial class MainLayout | ||||
| { | ||||
|  | ||||
|     private List<NavItem> Navs { get; set; } = new(); | ||||
|     private List<PageTabItem> pageTabItems { get; set; } = new(); | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         var dataString = | ||||
| """ | ||||
| [ | ||||
|   { | ||||
|     "Href": "/index", | ||||
|     "Title": "<22><>ҳ" | ||||
|   }, | ||||
|   { | ||||
|     "Title": "Modbus", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/ModbusRtu", | ||||
|         "Title": "ModbusRtu" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/ModbusTcp", | ||||
|         "Title": "ModbusTcp" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/ModbusRtuOverTcp", | ||||
|         "Title": "ModbusRtuOverTcp" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/ModbusRtuOverUdp", | ||||
|         "Title": "ModbusRtuOverUdp" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/ModbusUdp", | ||||
|         "Title": "ModbusUdp" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/ModbusTcpDtu", | ||||
|         "Title": "ModbusTcpDtu" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/ModbusTcpServer", | ||||
|         "Title": "ModbusTcpServer" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/ModbusSerialServer", | ||||
|         "Title": "ModbusSerialServer" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "Siemens", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/S7_1500", | ||||
|         "Title": "S7_1500" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/S7_1200", | ||||
|         "Title": "S7_1200" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/S7_200", | ||||
|         "Title": "S7_200" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/S7_200SMART", | ||||
|         "Title": "S7_200SMART" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/S7_300", | ||||
|         "Title": "S7_400" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/S7_400", | ||||
|         "Title": "S7_400" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "DLT645", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/DLT645_2007", | ||||
|         "Title": "DLT645_2007" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/DLT645_2007OverTcp", | ||||
|         "Title": "DLT645_2007OverTcp" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "OPCDA", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/OPCDAClient", | ||||
|         "Title": "OPCDAClient" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "OPCUA", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/OPCUAClient", | ||||
|         "Title": "OPCUAClient" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| ] | ||||
|  | ||||
|  | ||||
| """; | ||||
|         Navs = dataString.FromJsonString<List<NavItem>>(); | ||||
|  | ||||
| #if Pro | ||||
|         var dataStringPro = | ||||
| """ | ||||
| [ | ||||
|   { | ||||
|     "Title": "ABCIP", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/ABCIPTCP", | ||||
|         "Title": "ABCIPTCP" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "Omron", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/OmronFinsTcp", | ||||
|         "Title": "OmronFinsTcp" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/OmronFinsUdp", | ||||
|         "Title": "OmronFinsUdp" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "Secs", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/SecsTcp", | ||||
|         "Title": "SecsTcp" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "TS550", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/TS550", | ||||
|         "Title": "TS550" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "Vigor", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/VigorSerial", | ||||
|         "Title": "VigorSerial" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/VigorSerialOverTcp", | ||||
|         "Title": "VigorSerialOverTcp" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   { | ||||
|     "Title": "GasCustom", | ||||
|     "Children": [ | ||||
|       { | ||||
|         "Href": "/GasCustomSerial", | ||||
|         "Title": "GasCustomSerial" | ||||
|       }, | ||||
|       { | ||||
|         "Href": "/GasCustomSerialOverTcp", | ||||
|         "Title": "GasCustomSerialOverTcp" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
|  | ||||
| ] | ||||
|  | ||||
|  | ||||
| """; | ||||
|         Navs.AddRange(dataStringPro.FromJsonString<List<NavItem>>()); | ||||
| #endif | ||||
|         pageTabItems = Navs.PasePageTabItem(); | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,121 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'"> | ||||
| 		<DefineConstants>Pro</DefineConstants> | ||||
| 	</PropertyGroup> | ||||
| 	<ItemGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'"> | ||||
| 		 | ||||
| 		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor.cs" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor" /> | ||||
| 		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.AllenBradleyCip\ThingsGateway.Foundation.Adapter.AllenBradleyCip.csproj" /> | ||||
|  | ||||
| 		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor" /> | ||||
| 		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor" /> | ||||
| 		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Omron\ThingsGateway.Foundation.Adapter.Omron.csproj" /> | ||||
|  | ||||
| 		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor.cs" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor" /> | ||||
| 		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Secs\ThingsGateway.Foundation.Adapter.Secs.csproj" /> | ||||
|  | ||||
|  | ||||
| 		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor.cs" Link="Pages\TS550\TS550DebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor" Link="Pages\TS550\TS550DebugPage.razor" /> | ||||
| 		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.TS550\ThingsGateway.Foundation.Adapter.TS550.csproj" /> | ||||
|  | ||||
| 		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialDebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor" Link="Pages\Vigor\VigorSerialDebugPage.razor" /> | ||||
| 		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor" /> | ||||
| 		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Vigor\ThingsGateway.Foundation.Adapter.Vigor.csproj" /> | ||||
|  | ||||
|  | ||||
| 		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor" /> | ||||
| 		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor" /> | ||||
| 		<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.HZW_QTJC_01\ThingsGateway.Foundation.Adapter.HZW_QTJC_01.csproj" /> | ||||
|  | ||||
|  | ||||
| 		 | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007DebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor" Link="Pages\DLT645\DLT645_2007DebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor" /> | ||||
|  | ||||
| 		 | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor" Link="Pages\Modbus\ModbusRtuDebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor" Link="Pages\Modbus\ModbusTcpDebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusUdpDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor" Link="Pages\Modbus\ModbusUdpDebugPage.razor" /> | ||||
|  | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor.cs" Link="Pages\OPCDA\OPCDAClientDebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor.cs" Link="Pages\OPCDA\OPCDAClientPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor.cs" Link="Pages\OPCDA\OPCDAImportVariable.razor.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor" Link="Pages\OPCDA\OPCDAClientDebugPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor" Link="Pages\OPCDA\OPCDAClientPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor" Link="Pages\OPCDA\OPCDAImportVariable.razor" /> | ||||
|  | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor" Link="Pages\OPCUA\OPCUAClientDebugPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor" Link="Pages\OPCUA\OPCUAClientPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor" Link="Pages\OPCUA\OPCUAImportVariable.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor.cs" Link="Pages\OPCUA\OPCUAClientDebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor.cs" Link="Pages\OPCUA\OPCUAClientPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor.cs" Link="Pages\OPCUA\OPCUAImportVariable.razor.cs" /> | ||||
|  | ||||
|  | ||||
|  | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor" Link="Pages\Siemens\S7_1200DebugPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor" Link="Pages\Siemens\S7_1500DebugPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor" Link="Pages\Siemens\S7_200DebugPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor" Link="Pages\Siemens\S7_200SMARTDebugPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor" Link="Pages\Siemens\S7_300DebugPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor" Link="Pages\Siemens\S7_400DebugPage.razor" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor.cs" Link="Pages\Siemens\S7_1200DebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor.cs" Link="Pages\Siemens\S7_1500DebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor.cs" Link="Pages\Siemens\S7_200DebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor.cs" Link="Pages\Siemens\S7_200SMARTDebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor.cs" Link="Pages\Siemens\S7_300DebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor.cs" Link="Pages\Siemens\S7_400DebugPage.razor.cs" /> | ||||
|  | ||||
|  | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<Content Update="wwwroot\**"> | ||||
| 			<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 		</Content> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
|  | ||||
| </Project> | ||||
| @@ -0,0 +1,29 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @using System.Net.Http | ||||
| @using Microsoft.AspNetCore.Components.Forms | ||||
| @using Microsoft.AspNetCore.Components.Routing | ||||
| @using Microsoft.AspNetCore.Components.Web | ||||
| @using Microsoft.AspNetCore.Components.Web.Virtualization | ||||
| @using Microsoft.JSInterop | ||||
| @using BlazorComponent | ||||
| @using Masa.Blazor | ||||
| @using Masa.Blazor.Presets | ||||
| @using ThingsGateway.Foundation; | ||||
| @using ThingsGateway.Components; | ||||
| @using ThingsGateway.Core; | ||||
| @using System.Net.Http.Json | ||||
| @using System.IO; | ||||
| @using System.Text.Json; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
| @using ThingsGateway.Foundation.Sockets; | ||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.2 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 10 KiB | 
| @@ -0,0 +1,41 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="utf-8" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> | ||||
|     <title>ThingsGateway.Foundation.Demo</title> | ||||
|     <base href="/" /> | ||||
|  | ||||
|     <link rel="icon" href="favicon.ico" type="image/x-icon"> | ||||
|  | ||||
|  | ||||
|     <link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet" /> | ||||
|     <link href="_content/ThingsGateway.Components/css/materialdesign/v7.1.96/css/materialdesignicons.min.css" rel="stylesheet"> | ||||
|     <link href="_content/ThingsGateway.Components/css/material/icons.css" rel="stylesheet"> | ||||
|     <link href="_content/ThingsGateway.Components/css/fontawesome/v6.4.0/css/all.min.css" rel="stylesheet"> | ||||
|     <link href="_content/ThingsGateway.Components/style/custom.css" rel="stylesheet"> | ||||
|     <link href="_content/ThingsGateway.Components/prism/prism-material-dark-for-masa.css" rel="stylesheet"> | ||||
|     <link href="_content/ThingsGateway.Components/prism/prism-line-highlight.min.css" rel="stylesheet"> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     <div id="app"></div> | ||||
|  | ||||
|     <div id="blazor-error-ui"> | ||||
|         <span> | ||||
|             <environment include="Staging,Production"> | ||||
|                 An error has occurred. This application may no longer respond until reloaded. | ||||
|             </environment> | ||||
|             <environment include="Development"> | ||||
|                 An unhandled exception has occurred. See browser dev tools for details. | ||||
|             </environment> | ||||
|         </span> | ||||
|         <a href="" class="reload">Reload</a> | ||||
|         <a class="dismiss">🗙</a> | ||||
|     </div> | ||||
|  | ||||
|     <script src="_framework/blazor.webview.js" autostart="true"></script> | ||||
|     <script src="_content/ThingsGateway.Components/prism/prism.min.js"></script> | ||||
|     <script src="_content/BlazorComponent/js/blazor-component.js"></script> | ||||
| </body> | ||||
| </html> | ||||
| @@ -1,7 +1,9 @@ | ||||
| <Project> | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFrameworks>net6.0;net7.0</TargetFrameworks> | ||||
| 		<Version>2.0.3.0</Version> | ||||
| 		<Version>3.0.0.3</Version> | ||||
| 		<GenerateDocumentationFile>True</GenerateDocumentationFile> | ||||
| 		<LangVersion>latest</LangVersion> | ||||
| 		<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks> | ||||
| 		<Authors>Diego</Authors> | ||||
| 		<Product>ThingsGateway</Product> | ||||
| 		<Copyright>© 2023-present Diego</Copyright> | ||||
| @@ -11,34 +13,30 @@ | ||||
| 		<EmbedAllSources>true</EmbedAllSources> | ||||
| 		<RepositoryType>Gitee</RepositoryType> | ||||
| 		<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||||
| 		<PackageReadmeFile>./README.md</PackageReadmeFile> | ||||
| 		<PackageReadmeFile>README.md</PackageReadmeFile> | ||||
| 		<PackageIcon>icon.png</PackageIcon> | ||||
| 		<IncludeSymbols>true</IncludeSymbols> | ||||
| 		<SymbolPackageFormat>snupkg</SymbolPackageFormat> | ||||
| 		<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> | ||||
| 		<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl> | ||||
| 		<PackageIcon>icon.png</PackageIcon> | ||||
| 		<PackageTags>ThingsGateway;Diego;dotNET China;Blazor;设备采集;边缘网关</PackageTags> | ||||
| 		<PackageOutputPath>../../../nupkgs</PackageOutputPath> | ||||
| 		<SignAssembly>True</SignAssembly> | ||||
| 		<DelaySign>False</DelaySign> | ||||
| 		<AssemblyOriginatorKeyFile>..\..\..\..\snks\ThingsGateway.snk</AssemblyOriginatorKeyFile> | ||||
| 		<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages> | ||||
| 		<PackageOutputPath>../../nupkgs</PackageOutputPath> | ||||
| 		<AssemblyOriginatorKeyFile>../../../snks/ThingsGateway.snk</AssemblyOriginatorKeyFile> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<PropertyGroup> | ||||
| 		<LangVersion>latest</LangVersion> | ||||
| 	</PropertyGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<None Include="..\..\..\..\README.md" Pack="true" PackagePath="\" /> | ||||
| 		<None Include="..\..\..\README.md" Pack="true" PackagePath="\" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<None Include="..\..\..\..\icon.png"> | ||||
| 		<None Include="..\..\..\icon.png"> | ||||
| 			<Pack>True</Pack> | ||||
| 			<PackagePath></PackagePath> | ||||
| 		</None> | ||||
| @@ -10,66 +10,47 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using BlazorComponent; | ||||
| namespace ThingsGateway.Admin.Blazor.Core; | ||||
| namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// ListItem | ||||
| /// 控制码 | ||||
| /// </summary> | ||||
| /// <typeparam name="TItem"></typeparam> | ||||
| public interface IAppItem<TItem> | ||||
| public enum ControlCode : byte | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 子菜单 | ||||
|     /// 读数据 | ||||
|     /// </summary> | ||||
|     List<TItem> Children { get; } | ||||
| 
 | ||||
|     Read = 0x11, | ||||
|     /// <summary> | ||||
|     /// 是否启用下划线 | ||||
|     /// 读后续数据 | ||||
|     /// </summary> | ||||
|     bool Divider { get; set; } | ||||
| 
 | ||||
|     ReadSub = 0x12, | ||||
|     /// <summary> | ||||
|     /// 菜单头部标题 | ||||
|     /// 读站号 | ||||
|     /// </summary> | ||||
|     string Heading { get; } | ||||
| 
 | ||||
|     ReadStation = 0x13, | ||||
|     /// <summary> | ||||
|     /// 链接 | ||||
|     /// 写数据 | ||||
|     /// </summary> | ||||
|     string Href { get; set; } | ||||
| 
 | ||||
|     Write = 0x14, | ||||
|     /// <summary> | ||||
|     /// 图标 | ||||
|     /// 写站号 | ||||
|     /// </summary> | ||||
|     string Icon { get; set; } | ||||
| 
 | ||||
|     WriteStation = 0x15, | ||||
|     /// <summary> | ||||
|     /// 菜单副标题 | ||||
|     /// 广播校时 | ||||
|     /// </summary> | ||||
|     string SubTitle { get; set; } | ||||
| 
 | ||||
|     BroadcastTime = 0x08, | ||||
|     /// <summary> | ||||
|     /// 跳转方式 | ||||
|     /// 冻结 | ||||
|     /// </summary> | ||||
|     string Target { get; set; } | ||||
| 
 | ||||
|     Freeze = 0x16, | ||||
|     /// <summary> | ||||
|     /// 菜单标题 | ||||
|     /// 更新波特率 | ||||
|     /// </summary> | ||||
|     string Title { get; set; } | ||||
| 
 | ||||
|     WriteBaudRate = 0x17, | ||||
|     /// <summary> | ||||
|     /// 菜单值 | ||||
|     /// 更新密码 | ||||
|     /// </summary> | ||||
|     StringNumber Value { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 是否有子菜单 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     bool HasChildren() | ||||
|     { | ||||
|         return Children is not null && Children.Any(); | ||||
|     } | ||||
|     WritePassword = 0x18, | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,481 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||
| /// <summary> | ||||
| /// DLT645_2007 | ||||
| /// </summary> | ||||
| public class DLT645_2007 : ReadWriteDevicesSerialSessionBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// DLT645_2007 | ||||
|     /// </summary> | ||||
|     /// <param name="serialSession"></param> | ||||
|     public DLT645_2007(SerialSession serialSession) : base(serialSession) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 增加FE FE FE FE的报文头部 | ||||
|     /// </summary> | ||||
|     [Description("前导符报文头")] | ||||
|     public bool EnableFEHead { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入需操作员代码 | ||||
|     /// </summary> | ||||
|     [Description("操作员代码")] | ||||
|     public string OperCode { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入密码 | ||||
|     /// </summary> | ||||
|     [Description("写入密码")] | ||||
|     public string Password { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 通讯地址BCD码,一般应该是12个字符 | ||||
|     /// </summary> | ||||
|     [Description("通讯地址")] | ||||
|     public string Station { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|  | ||||
|         var str = """ | ||||
|             查看附带文档或者相关资料,下面列举一下常见的数据标识地址  | ||||
|              | ||||
|             地址                       说明                     | ||||
|             ----------------------------------------- | ||||
|             02010100    A相电压 | ||||
|             02020100    A相电流 | ||||
|             02030000    瞬时总有功功率 | ||||
|             00000000    (当前)组合有功总电能 | ||||
|             00010000    (当前)正向有功总电能 | ||||
|              | ||||
|             """; | ||||
|         return base.GetAddressDescription() + Environment.NewLine + str; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<byte[]>(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<byte[]>(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient = null) | ||||
|     { | ||||
|         var dataHandleAdapter = new DLT645_2007DataHandleAdapter | ||||
|         { | ||||
|             EnableFEHead = EnableFEHead | ||||
|         }; | ||||
|         SerialSession.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, string value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             Password ??= string.Empty; | ||||
|             OperCode ??= string.Empty; | ||||
|             if (Password.Length < 8) | ||||
|                 Password = Password.PadLeft(8, '0'); | ||||
|             if (OperCode.Length < 8) | ||||
|                 OperCode = OperCode.PadLeft(8, '0'); | ||||
|             var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes()); | ||||
|             string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             Password ??= string.Empty; | ||||
|             OperCode ??= string.Empty; | ||||
|             if (Password.Length < 8) | ||||
|                 Password = Password.PadLeft(8, '0'); | ||||
|             if (OperCode.Length < 8) | ||||
|                 OperCode = OperCode.PadLeft(8, '0'); | ||||
|             var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes()); | ||||
|             string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|  | ||||
|  | ||||
|     #region 其他方法 | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 广播校时 | ||||
|     /// </summary> | ||||
|     /// <param name="dateTime"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}"; | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().ToArray(), "999999999999".ByHexStringToBytes()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 SerialSession.Send(commandResult.Content); | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 冻结 | ||||
|     /// </summary> | ||||
|     /// <param name="dateTime"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}"; | ||||
|             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||
|             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 读取通信地址 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     var buffer = result1.Content.SelectMiddle(0, 6).BytesAdd(-0x33); | ||||
|                     return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString()); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult<string>(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<string>(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 修改波特率 | ||||
|     /// </summary> | ||||
|     /// <param name="baudRate"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             byte baudRateByte; | ||||
|             switch (baudRate) | ||||
|             { | ||||
|                 case 600: baudRateByte = 0x02; break; | ||||
|                 case 1200: baudRateByte = 0x04; break; | ||||
|                 case 2400: baudRateByte = 0x08; break; | ||||
|                 case 4800: baudRateByte = 0x10; break; | ||||
|                 case 9600: baudRateByte = 0x20; break; | ||||
|                 case 19200: baudRateByte = 0x40; break; | ||||
|                 default: return new OperResult<string>($"不支持此波特率:{baudRate}"); | ||||
|             } | ||||
|             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||
|             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 更新通信地址 | ||||
|     /// </summary> | ||||
|     /// <param name="station"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 修改密码 | ||||
|     /// </summary> | ||||
|     /// <param name="level"></param> | ||||
|     /// <param name="oldPassword"></param> | ||||
|     /// <param name="newPassword"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|  | ||||
|             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||
|             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||
|             string str = $"04000C{(level + 1):D2}"; | ||||
|  | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword, | ||||
|                 str.ByHexStringToBytes().Reverse().ToArray() | ||||
|                 .SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray()) | ||||
|                 .SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray()) | ||||
|                 , Station.ByHexStringToBytes().Reverse().ToArray()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     #endregion | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,109 @@ | ||||
| #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.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||
|  | ||||
| /// <summary> | ||||
| /// DLT645_2007Address | ||||
| /// </summary> | ||||
| public class DLT645_2007Address : DeviceAddressBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public DLT645_2007Address() | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 数据标识 | ||||
|     /// </summary> | ||||
|     public byte[] DataId { get; set; } = new byte[0]; | ||||
|     /// <summary> | ||||
|     /// 反转解析 | ||||
|     /// </summary> | ||||
|     public bool Reverse { get; set; } = true; | ||||
|     /// <summary> | ||||
|     /// 站号信息 | ||||
|     /// </summary> | ||||
|     public byte[] Station { get; set; } = new byte[0]; | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 解析地址 | ||||
|     /// </summary> | ||||
|     /// <param name="address"></param> | ||||
|     /// <returns></returns> | ||||
|     public static DLT645_2007Address ParseFrom(string address) | ||||
|     { | ||||
|         DLT645_2007Address dLT645_2007Address = new(); | ||||
|         byte[] array; | ||||
|         array = new byte[0]; | ||||
|         if (address.IndexOf(';') < 0) | ||||
|         { | ||||
|             array = address.ByHexStringToBytes().Reverse().ToArray(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||
|  | ||||
|             for (int index = 0; index < strArray.Length; ++index) | ||||
|             { | ||||
|                 if (strArray[index].ToUpper().StartsWith("S=")) | ||||
|                 { | ||||
|                     var station = strArray[index].Substring(2); | ||||
|                     if (station.IsNullOrEmpty()) station = string.Empty; | ||||
|                     if (station.Length < 12) | ||||
|                         station = station.PadLeft(12, '0'); | ||||
|                     dLT645_2007Address.Station = station.ByHexStringToBytes().Reverse().ToArray(); | ||||
|                 } | ||||
|                 else if (strArray[index].Contains("r=")) | ||||
|                 { | ||||
|                     dLT645_2007Address.Reverse = strArray[index].Substring(2).GetBoolValue(); | ||||
|                 } | ||||
|                 else if (!strArray[index].Contains("=")) | ||||
|                 { | ||||
|                     array = strArray[index].ByHexStringToBytes().Reverse().ToArray(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         dLT645_2007Address.DataId = array; | ||||
|         return dLT645_2007Address; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string ToString() | ||||
|     { | ||||
|         StringBuilder stringGeter = new(); | ||||
|         if (Station.Length > 0) | ||||
|         { | ||||
|             stringGeter.Append("s=" + Station.Reverse().ToArray().ToHexString() + ";"); | ||||
|         } | ||||
|         if (DataId.Length > 0) | ||||
|         { | ||||
|             stringGeter.Append(DataId.Reverse().ToArray().ToHexString() + ";"); | ||||
|         } | ||||
|         if (!Reverse) | ||||
|         { | ||||
|             stringGeter.Append("s=" + Reverse.ToString() + ";"); | ||||
|         } | ||||
|         return stringGeter.ToString(); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,117 @@ | ||||
| #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.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||
| /// <summary> | ||||
| /// DLT645_2007 | ||||
| /// </summary> | ||||
| public class DLT645_2007BitConverter : ThingsGatewayBitConverter | ||||
| { | ||||
|     /// <summary> | ||||
|     /// DLT645_2007 | ||||
|     /// </summary> | ||||
|     public DLT645_2007BitConverter(EndianType endianType) : base(endianType) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DLT645协议转换double | ||||
|     /// </summary> | ||||
|     /// <param name="buffer">带数据项标识</param> | ||||
|     /// <param name="offset"></param> | ||||
|     /// <returns></returns> | ||||
|     public override double ToDouble(byte[] buffer, int offset) | ||||
|     { | ||||
|         return Convert.ToDouble(this.ToString(buffer, offset, buffer.Length)); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter CopyNew() | ||||
|     { | ||||
|         return new DLT645_2007BitConverter(EndianType) | ||||
|         { | ||||
|             DataFormat = DataFormat, | ||||
|             BcdFormat = BcdFormat, | ||||
|             Encoding = Encoding, | ||||
|             IsStringReverseByteWord = IsStringReverseByteWord, | ||||
|             Length = Length, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string ToString(byte[] buffer) | ||||
|     { | ||||
|         return this.ToString(buffer, 0, buffer.Length); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string ToString(byte[] buffer, int offset, int length) | ||||
|     { | ||||
|         buffer = buffer.RemoveBegin(offset); | ||||
|         buffer = buffer.BytesAdd(-0x33); | ||||
|         var dataInfos = DLT645Helper.GetDataInfos(buffer); | ||||
|         StringBuilder stringBuilder = new(); | ||||
|         foreach (var dataInfo in dataInfos) | ||||
|         { | ||||
|             //实际数据 | ||||
|             var content = buffer.SelectMiddle(4, dataInfo.ByteLength).Reverse().ToArray(); | ||||
|             if (dataInfo.IsSigned)//可能为负数 | ||||
|             { | ||||
|                 if (content[0] > 0x80)//最高位是表示正负 | ||||
|                 { | ||||
|                     content[0] = (byte)(content[0] - 0x80); | ||||
|                     if (dataInfo.Digtal == 0)//无小数点 | ||||
|                     { | ||||
|                         stringBuilder.Append($"-{content.ToHexString()}"); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         stringBuilder.Append((-(Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString()); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ToString(stringBuilder, dataInfo, content); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ToString(stringBuilder, dataInfo, content); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return stringBuilder.ToString(); | ||||
|  | ||||
|         static void ToString(StringBuilder stringBuilder, DataInfo dataInfo, byte[] content) | ||||
|         { | ||||
|             if (dataInfo.Digtal < 0) | ||||
|             { | ||||
|                 stringBuilder.Append($"{Encoding.ASCII.GetString(content)}"); | ||||
|             } | ||||
|             else if (dataInfo.Digtal == 0)//无小数点 | ||||
|             { | ||||
|                 stringBuilder.Append($"{content.ToHexString()}"); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 stringBuilder.Append(((Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,191 @@ | ||||
| #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 ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||
|  | ||||
| /// <summary> | ||||
| /// DLT645_2007DataHandleAdapter | ||||
| /// </summary> | ||||
| public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<DLT645_2007Message> | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 增加FE FE FE FE的报文头部 | ||||
|     /// </summary> | ||||
|     [Description("前导符报文头")] | ||||
|     public bool EnableFEHead { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override byte[] PackCommand(byte[] command) | ||||
|     { | ||||
|         //打包时加上4个FE字节 | ||||
|         if (EnableFEHead) | ||||
|         { | ||||
|             return DataTransUtil.SpliceArray(new byte[4] { 0xFE, 0xFE, 0xFE, 0xFE }, command); | ||||
|         } | ||||
|         return command; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override DLT645_2007Message GetInstance() | ||||
|     { | ||||
|         return new DLT645_2007Message(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override FilterResult UnpackResponse(DLT645_2007Message request, byte[] send, byte[] body, byte[] response) | ||||
|     { | ||||
|         //因为设备可能带有FE前导符开头,这里找到0x68的位置 | ||||
|         int headCodeIndex = -1; | ||||
|         if (response != null) | ||||
|         { | ||||
|             for (int index = 0; index < response.Length; index++) | ||||
|             { | ||||
|                 if (response[index] == 0x68) | ||||
|                 { | ||||
|                     headCodeIndex = index; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         int sendHeadCodeIndex = 0; | ||||
|         if (send != null) | ||||
|         { | ||||
|             for (int index = 0; index < send.Length; index++) | ||||
|             { | ||||
|                 if (send[index] == 0x68) | ||||
|                 { | ||||
|                     sendHeadCodeIndex = index; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         //帧起始符 地址域  帧起始符 控制码 数据域长度共10个字节 | ||||
|         if (headCodeIndex < 0 || headCodeIndex + 10 > response.Length) | ||||
|             return FilterResult.Cache; | ||||
|  | ||||
|  | ||||
|         var len = 10 + response[headCodeIndex + 9] + 2; | ||||
|  | ||||
|         if (response.Length - headCodeIndex < len) | ||||
|         { | ||||
|             return FilterResult.Cache; | ||||
|         } | ||||
|         if (response.Length - headCodeIndex >= len && response[len + headCodeIndex - 1] == 0x16) | ||||
|         { | ||||
|  | ||||
|             //检查校验码 | ||||
|             int sumCheck = 0; | ||||
|             for (int i = headCodeIndex; i < len + headCodeIndex - 2; i++) | ||||
|                 sumCheck += response[i]; | ||||
|             if ((byte)sumCheck != response[len + headCodeIndex - 2]) | ||||
|             { | ||||
|                 //校验错误 | ||||
|                 request.Message = "和校验错误"; | ||||
|                 request.ErrorCode = 999; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|  | ||||
|             if ( | ||||
|                 (response[headCodeIndex + 1] != send[sendHeadCodeIndex + 1]) || | ||||
|                 (response[headCodeIndex + 2] != send[sendHeadCodeIndex + 2]) || | ||||
|                 (response[headCodeIndex + 3] != send[sendHeadCodeIndex + 3]) || | ||||
|                 (response[headCodeIndex + 4] != send[sendHeadCodeIndex + 4]) || | ||||
|                 (response[headCodeIndex + 5] != send[sendHeadCodeIndex + 5]) || | ||||
|                 (response[headCodeIndex + 6] != send[sendHeadCodeIndex + 6]) | ||||
|                 )//设备地址不符合时,返回错误 | ||||
|             { | ||||
|  | ||||
|                 if ( | ||||
|                 (send[sendHeadCodeIndex + 1] == 0xAA) && | ||||
|                 (send[sendHeadCodeIndex + 2] == 0xAA) && | ||||
|                 (send[sendHeadCodeIndex + 3] == 0xAA) && | ||||
|                 (send[sendHeadCodeIndex + 4] == 0xAA) && | ||||
|                 (send[sendHeadCodeIndex + 5] == 0xAA) && | ||||
|                 (send[sendHeadCodeIndex + 6] == 0xAA) | ||||
|                 )//读写通讯地址例外 | ||||
|                 { | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     request.Message = "返回地址不符合规则"; | ||||
|                     request.ErrorCode = 999; | ||||
|                     return FilterResult.Success; | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|  | ||||
|  | ||||
|             if ((response[headCodeIndex + 8] != send[sendHeadCodeIndex + 8] + 0x80))//控制码不符合时,返回错误 | ||||
|             { | ||||
|                 request.Message = "返回控制码:" + $"0x{response[headCodeIndex + 8]:X2},请求控制码:" + $"0x{send[sendHeadCodeIndex + 8]:X2},不符合规则"; | ||||
|                 request.ErrorCode = 999; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|             if ((response[headCodeIndex + 8] & 0x40) == 0x40)//控制码bit6为1时,返回错误 | ||||
|             { | ||||
|                 byte byte1 = (byte)(response[headCodeIndex + 10] - 0x33); | ||||
|                 var error = DLT645Helper.Get2007ErrorMessage(byte1); | ||||
|                 request.Message = "异常控制码:" + $"0x{response[headCodeIndex + 8]:X2},错误信息:{error}"; | ||||
|                 request.ErrorCode = 999; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|  | ||||
|             if (send[sendHeadCodeIndex + 8] == (byte)ControlCode.Read || | ||||
|     send[sendHeadCodeIndex + 8] == (byte)ControlCode.Write | ||||
|     ) | ||||
|             { | ||||
|                 //数据标识不符合时,返回错误 | ||||
|                 if ( | ||||
|                 (response[headCodeIndex + 10] == send[sendHeadCodeIndex + 10]) && | ||||
|                 (response[headCodeIndex + 11] == send[sendHeadCodeIndex + 11]) && | ||||
|                 (response[headCodeIndex + 12] == send[sendHeadCodeIndex + 12]) && | ||||
|                 (response[headCodeIndex + 13] == send[sendHeadCodeIndex + 13]) | ||||
|                 ) | ||||
|                 { | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     request.Message = "返回数据标识不符合规则"; | ||||
|                     request.ErrorCode = 999; | ||||
|                     return FilterResult.Success; | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|  | ||||
|             request.Content = response.RemoveBegin(headCodeIndex + 10).RemoveLast(response.Length + 2 - len - headCodeIndex); | ||||
|             request.ErrorCode = 0; | ||||
|             return FilterResult.Success; | ||||
|  | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             request.ErrorCode = 999; | ||||
|             return FilterResult.Success; | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,34 @@ | ||||
| #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.Foundation.Adapter.DLT645; | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class DLT645_2007Message : MessageBase, IMessage | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public override int HeadBytesLength => -1; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool CheckHeadBytes(byte[] head) | ||||
|     { | ||||
|         BodyLength = -1; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void SendBytesThen() | ||||
|     { | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,481 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||
| /// <summary> | ||||
| /// DLT645_2007 | ||||
| /// </summary> | ||||
| public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// DLT645_2007 | ||||
|     /// </summary> | ||||
|     /// <param name="tcpClient"></param> | ||||
|     public DLT645_2007OverTcp(TcpClient tcpClient) : base(tcpClient) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 增加FE FE FE FE的报文头部 | ||||
|     /// </summary> | ||||
|     [Description("前导符报文头")] | ||||
|     public bool EnableFEHead { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入需操作员代码 | ||||
|     /// </summary> | ||||
|     [Description("操作员代码")] | ||||
|     public string OperCode { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 写入密码 | ||||
|     /// </summary> | ||||
|     [Description("写入密码")] | ||||
|     public string Password { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 通讯地址BCD码,一般应该是12个字符 | ||||
|     /// </summary> | ||||
|     [Description("通讯地址")] | ||||
|     public string Station { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|  | ||||
|         var str = """ | ||||
|             查看附带文档或者相关资料,下面列举一下常见的数据标识地址  | ||||
|              | ||||
|             地址                       说明                     | ||||
|             ----------------------------------------- | ||||
|             02010100    A相电压 | ||||
|             02020100    A相电流 | ||||
|             02030000    瞬时总有功功率 | ||||
|             00000000    (当前)组合有功总电能 | ||||
|             00010000    (当前)正向有功总电能 | ||||
|              | ||||
|             """; | ||||
|         return base.GetAddressDescription() + Environment.NewLine + str; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<byte[]>(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<byte[]>(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient = null) | ||||
|     { | ||||
|         var dataHandleAdapter = new DLT645_2007DataHandleAdapter | ||||
|         { | ||||
|             EnableFEHead = EnableFEHead | ||||
|         }; | ||||
|         TcpClient.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, string value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             Password ??= string.Empty; | ||||
|             OperCode ??= string.Empty; | ||||
|             if (Password.Length < 8) | ||||
|                 Password = Password.PadLeft(8, '0'); | ||||
|             if (OperCode.Length < 8) | ||||
|                 OperCode = OperCode.PadLeft(8, '0'); | ||||
|             var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes()); | ||||
|             string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             Password ??= string.Empty; | ||||
|             OperCode ??= string.Empty; | ||||
|             if (Password.Length < 8) | ||||
|                 Password = Password.PadLeft(8, '0'); | ||||
|             if (OperCode.Length < 8) | ||||
|                 OperCode = OperCode.PadLeft(8, '0'); | ||||
|             var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes()); | ||||
|             string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||
|  | ||||
|  | ||||
|     #region 其他方法 | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 广播校时 | ||||
|     /// </summary> | ||||
|     /// <param name="dateTime"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}"; | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().Reverse().ToArray(), "999999999999".ByHexStringToBytes()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 TcpClient.Send(commandResult.Content); | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 冻结 | ||||
|     /// </summary> | ||||
|     /// <param name="dateTime"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}"; | ||||
|             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||
|             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().Reverse().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 读取通信地址 | ||||
|     /// </summary> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     var buffer = result1.Content.SelectMiddle(0, 6).BytesAdd(-0x33); | ||||
|                     return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString()); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult<string>(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<string>(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 修改波特率 | ||||
|     /// </summary> | ||||
|     /// <param name="baudRate"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             byte baudRateByte; | ||||
|             switch (baudRate) | ||||
|             { | ||||
|                 case 600: baudRateByte = 0x02; break; | ||||
|                 case 1200: baudRateByte = 0x04; break; | ||||
|                 case 2400: baudRateByte = 0x08; break; | ||||
|                 case 4800: baudRateByte = 0x10; break; | ||||
|                 case 9600: baudRateByte = 0x20; break; | ||||
|                 case 19200: baudRateByte = 0x40; break; | ||||
|                 default: return new OperResult<string>($"不支持此波特率:{baudRate}"); | ||||
|             } | ||||
|             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||
|             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 更新通信地址 | ||||
|     /// </summary> | ||||
|     /// <param name="station"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 修改密码 | ||||
|     /// </summary> | ||||
|     /// <param name="level"></param> | ||||
|     /// <param name="oldPassword"></param> | ||||
|     /// <param name="newPassword"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|  | ||||
|             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||
|             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||
|             string str = $"04000C{level:D2}"; | ||||
|  | ||||
|             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword, | ||||
|                 str.ByHexStringToBytes().Reverse().ToArray() | ||||
|                 .SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray()) | ||||
|                 .SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray()) | ||||
|                 , Station.ByHexStringToBytes().Reverse().ToArray()); | ||||
|             if (commandResult.IsSuccess) | ||||
|             { | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||
|                 var result1 = ((MessageBase)result.RequestInfo); | ||||
|                 if (result1.IsSuccess) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result1); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult(commandResult); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<string>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     #endregion | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| #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.Collections.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||
|  | ||||
| internal static class PackHelper | ||||
| { | ||||
|     public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new() | ||||
|     { | ||||
|         var byteConverter = device.ThingsGatewayBitConverter; | ||||
|         var result = new List<T>(); | ||||
|         //需要先剔除额外信息,比如dataformat等 | ||||
|         foreach (var item in deviceVariables) | ||||
|         { | ||||
|             var address = item.VariableAddress; | ||||
|  | ||||
|             IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter); | ||||
|             item.ThingsGatewayBitConverter = transformParameter; | ||||
|             //item.VariableAddress = address; | ||||
|             item.Index = device.GetBitOffset(item.VariableAddress); | ||||
|  | ||||
|             result.Add(new() | ||||
|             { | ||||
|                 DeviceVariableRunTimes = new() { item }, | ||||
|                 VariableAddress = address, | ||||
|                 Length = 1, | ||||
|             }); | ||||
|         } | ||||
|         return result; | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| global using System; | ||||
| global using System.Linq; | ||||
| global using System.Threading; | ||||
| global using System.Threading.Tasks; | ||||
|  | ||||
| global using ThingsGateway.Foundation.Serial; | ||||
| global using ThingsGateway.Foundation.Sockets; | ||||
| @@ -0,0 +1,5 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" /> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
| @@ -0,0 +1,442 @@ | ||||
| <?xml version="1.0"?> | ||||
| <doc> | ||||
|     <assembly> | ||||
|         <name>ThingsGateway.Foundation.Adapter.DLT645</name> | ||||
|     </assembly> | ||||
|     <members> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DataInfo"> | ||||
|             <summary> | ||||
|             解析参数 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DataInfo.ByteLength"> | ||||
|             <summary> | ||||
|             解析长度 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DataInfo.Digtal"> | ||||
|             <summary> | ||||
|             小数位 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DataInfo.IsSigned"> | ||||
|             <summary> | ||||
|             有符号解析 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645Helper.GetDataInfos(System.Byte[])"> | ||||
|             <summary> | ||||
|             获取返回的解析信息 | ||||
|             </summary> | ||||
|             <param name="buffer"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645Helper.GetDLT645_2007Command(System.String,System.Int32,System.Byte,System.String,System.Byte[],System.String[])"> | ||||
|             <summary> | ||||
|             获取DLT645报文 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007"> | ||||
|             <summary> | ||||
|             DLT645_2007 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.#ctor(ThingsGateway.Foundation.Serial.SerialSession)"> | ||||
|             <summary> | ||||
|             DLT645_2007 | ||||
|             </summary> | ||||
|             <param name="serialSession"></param> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.EnableFEHead"> | ||||
|             <summary> | ||||
|             增加FE FE FE FE的报文头部 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.OperCode"> | ||||
|             <summary> | ||||
|             写入需操作员代码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.Password"> | ||||
|             <summary> | ||||
|             写入密码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.Station"> | ||||
|             <summary> | ||||
|             通讯地址BCD码,一般应该是12个字符 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.GetAddressDescription"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.SetDataAdapter"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.String,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.UInt32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Byte,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Double,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Single,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Int64,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.UInt64,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.UInt16,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Int16,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.BroadcastTime(System.DateTime,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             广播校时 | ||||
|             </summary> | ||||
|             <param name="dateTime"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.FreezeAsync(System.DateTime,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             冻结 | ||||
|             </summary> | ||||
|             <param name="dateTime"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.ReadDeviceStationAsync(System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             读取通信地址 | ||||
|             </summary> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteBaudRateAsync(System.Int32,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             修改波特率 | ||||
|             </summary> | ||||
|             <param name="baudRate"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteDeviceStationAsync(System.String,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             更新通信地址 | ||||
|             </summary> | ||||
|             <param name="station"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WritePasswordAsync(System.Byte,System.String,System.String,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             修改密码 | ||||
|             </summary> | ||||
|             <param name="level"></param> | ||||
|             <param name="oldPassword"></param> | ||||
|             <param name="newPassword"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.DLT645.ControlCode"> | ||||
|             <summary> | ||||
|             控制码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.Read"> | ||||
|             <summary> | ||||
|             读数据 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.ReadSub"> | ||||
|             <summary> | ||||
|             读后续数据 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.ReadStation"> | ||||
|             <summary> | ||||
|             读站号 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.Write"> | ||||
|             <summary> | ||||
|             写数据 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.WriteStation"> | ||||
|             <summary> | ||||
|             写站号 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.BroadcastTime"> | ||||
|             <summary> | ||||
|             广播校时 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.Freeze"> | ||||
|             <summary> | ||||
|             冻结 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.WriteBaudRate"> | ||||
|             <summary> | ||||
|             更新波特率 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.WritePassword"> | ||||
|             <summary> | ||||
|             更新密码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address"> | ||||
|             <summary> | ||||
|             DLT645_2007Address | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.#ctor"> | ||||
|             <summary> | ||||
|             <inheritdoc/> | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.#ctor(System.String,System.UInt16)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.#ctor(System.String,System.Byte[])"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.DataId"> | ||||
|             <summary> | ||||
|             数据标识 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.Reverse"> | ||||
|             <summary> | ||||
|             反转解析 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.Station"> | ||||
|             <summary> | ||||
|             站号信息 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.Parse(System.String,System.Int32)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.ParseFrom(System.String,System.Int32)"> | ||||
|             <summary> | ||||
|             解析地址 | ||||
|             </summary> | ||||
|             <param name="address"></param> | ||||
|             <param name="length"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.ToString"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter"> | ||||
|             <summary> | ||||
|             DLT645_2007 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter.#ctor(ThingsGateway.Foundation.EndianType)"> | ||||
|             <summary> | ||||
|             DLT645_2007 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter.ToDouble(System.Byte[],System.Int32)"> | ||||
|             <summary> | ||||
|             DLT645协议转换double | ||||
|             </summary> | ||||
|             <param name="buffer">带数据项标识</param> | ||||
|             <param name="offset"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter.ToString(System.Byte[])"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter.ToString(System.Byte[],System.Int32,System.Int32)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter"> | ||||
|             <summary> | ||||
|             DLT645_2007DataHandleAdapter | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter.EnableFEHead"> | ||||
|             <summary> | ||||
|             增加FE FE FE FE的报文头部 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter.PackCommand(System.Byte[])"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter.GetInstance"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message,System.Byte[],System.Byte[],System.Byte[])"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp"> | ||||
|             <summary> | ||||
|             DLT645_2007 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.#ctor(ThingsGateway.Foundation.TcpClient)"> | ||||
|             <summary> | ||||
|             DLT645_2007 | ||||
|             </summary> | ||||
|             <param name="tcpClient"></param> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.EnableFEHead"> | ||||
|             <summary> | ||||
|             增加FE FE FE FE的报文头部 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.OperCode"> | ||||
|             <summary> | ||||
|             写入需操作员代码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.Password"> | ||||
|             <summary> | ||||
|             写入密码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.Station"> | ||||
|             <summary> | ||||
|             通讯地址BCD码,一般应该是12个字符 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.GetAddressDescription"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.SetDataAdapter"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.String,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.UInt32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Byte,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Double,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Single,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Int64,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.UInt64,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.UInt16,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Int16,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.BroadcastTime(System.DateTime,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             广播校时 | ||||
|             </summary> | ||||
|             <param name="dateTime"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.FreezeAsync(System.DateTime,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             冻结 | ||||
|             </summary> | ||||
|             <param name="dateTime"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.ReadDeviceStationAsync(System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             读取通信地址 | ||||
|             </summary> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteBaudRateAsync(System.Int32,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             修改波特率 | ||||
|             </summary> | ||||
|             <param name="baudRate"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteDeviceStationAsync(System.String,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             更新通信地址 | ||||
|             </summary> | ||||
|             <param name="station"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WritePasswordAsync(System.Byte,System.String,System.String,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             修改密码 | ||||
|             </summary> | ||||
|             <param name="level"></param> | ||||
|             <param name="oldPassword"></param> | ||||
|             <param name="newPassword"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message"> | ||||
|             <summary> | ||||
|             <inheritdoc/> | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message.HeadBytesLength"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message.CheckHeadBytes(System.Byte[])"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message.SendBytesThen"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|     </members> | ||||
| </doc> | ||||
| @@ -0,0 +1,19 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| global using System; | ||||
| global using System.Linq; | ||||
| global using System.Threading; | ||||
| global using System.Threading.Tasks; | ||||
|  | ||||
| global using ThingsGateway.Foundation.Serial; | ||||
| global using ThingsGateway.Foundation.Sockets; | ||||
| @@ -12,6 +12,9 @@ | ||||
| 
 | ||||
| using System.Text; | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
| 
 | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| 
 | ||||
| /// <summary> | ||||
| @@ -26,22 +29,10 @@ public class ModbusAddress : DeviceAddressBase | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusAddress(string address, ushort len) | ||||
|     { | ||||
|         Station = 0; | ||||
|         AddressStart = 0; | ||||
|         Parse(address, len); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusAddress(string address, byte station) | ||||
|     { | ||||
|         Station = station; | ||||
|         AddressStart = 0; | ||||
|         Parse(address, 0); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 读取功能码 | ||||
|     /// </summary> | ||||
|     public ushort AddressStart => Address.ToUShort(); | ||||
|     /// <summary> | ||||
|     /// 读取功能码 | ||||
|     /// </summary> | ||||
| @@ -56,15 +47,44 @@ public class ModbusAddress : DeviceAddressBase | ||||
|     /// 写入功能码 | ||||
|     /// </summary> | ||||
|     public byte WriteFunction { get; set; } | ||||
|     /// <summary> | ||||
|     /// 打包临时写入,需要读取的字节长度 | ||||
|     /// </summary> | ||||
|     public int ByteLength { get; set; } | ||||
|     /// <summary> | ||||
|     /// BitIndex | ||||
|     /// </summary> | ||||
|     public int BitIndex => (int)(Address.SplitDot().LastOrDefault().ToInt()); | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override void Parse(string address, int length) | ||||
|     /// <summary> | ||||
|     /// 读取功能码 | ||||
|     /// </summary> | ||||
|     public ushort AddressEnd => (ushort)(AddressStart + (Math.Ceiling(ByteLength / 2.0) > 0 ? Math.Ceiling(ByteLength / 2.0) : 1)); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 作为Slave时需提供的SocketId,用于分辨Socket客户端,通常对比的是初始链接时的注册包 | ||||
|     /// </summary> | ||||
|     public string SocketId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 解析地址 | ||||
|     /// </summary> | ||||
|     public static ModbusAddress ParseFrom(string address, byte station) | ||||
|     { | ||||
|         Length = length; | ||||
|         ModbusAddress modbusAddress = new() | ||||
|         { | ||||
|             Station = station | ||||
|         }; | ||||
|         return ParseFrom(address, modbusAddress); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 解析地址 | ||||
|     /// </summary> | ||||
|     public static ModbusAddress ParseFrom(string address, ModbusAddress modbusAddress = null) | ||||
|     { | ||||
|         modbusAddress ??= new(); | ||||
|         if (address.IndexOf(';') < 0) | ||||
|         { | ||||
|             Address(address); | ||||
| 
 | ||||
|         } | ||||
|         else | ||||
|         { | ||||
| @@ -74,12 +94,16 @@ public class ModbusAddress : DeviceAddressBase | ||||
|                 if (strArray[index].ToUpper().StartsWith("S=")) | ||||
|                 { | ||||
|                     if (Convert.ToInt16(strArray[index].Substring(2)) > 0) | ||||
|                         Station = byte.Parse(strArray[index].Substring(2)); | ||||
|                         modbusAddress.Station = byte.Parse(strArray[index].Substring(2)); | ||||
|                 } | ||||
|                 else if (strArray[index].ToUpper().StartsWith("W=")) | ||||
|                 { | ||||
|                     if (Convert.ToInt16(strArray[index].Substring(2)) > 0) | ||||
|                         this.WriteFunction = byte.Parse(strArray[index].Substring(2)); | ||||
|                         modbusAddress.WriteFunction = byte.Parse(strArray[index].Substring(2)); | ||||
|                 } | ||||
|                 else if (strArray[index].ToUpper().StartsWith("ID=")) | ||||
|                 { | ||||
|                     modbusAddress.SocketId = strArray[index].Substring(3); | ||||
|                 } | ||||
|                 else if (!strArray[index].Contains("=")) | ||||
|                 { | ||||
| @@ -88,17 +112,35 @@ public class ModbusAddress : DeviceAddressBase | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return modbusAddress; | ||||
| 
 | ||||
|         void Address(string address) | ||||
|         { | ||||
|             var readF = ushort.Parse(address.Substring(0, 1)); | ||||
|             if (readF > 4) | ||||
|                 throw new("功能码错误"); | ||||
|             GetFunction(readF); | ||||
|             AddressStart = int.Parse(address.Substring(1)) - 1; | ||||
|             switch (readF) | ||||
|             { | ||||
|                 case 0: | ||||
|                     modbusAddress.ReadFunction = 1; | ||||
|                     break; | ||||
|                 case 1: | ||||
|                     modbusAddress.ReadFunction = 2; | ||||
|                     break; | ||||
|                 case 3: | ||||
|                     modbusAddress.ReadFunction = 4; | ||||
|                     break; | ||||
|                 case 4: | ||||
|                     modbusAddress.ReadFunction = 3; | ||||
|                     break; | ||||
|             } | ||||
|             modbusAddress.Address = (double.Parse(address.Substring(1)) - 1).ToString(); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override string ToString() | ||||
|     { | ||||
| @@ -111,28 +153,14 @@ public class ModbusAddress : DeviceAddressBase | ||||
|         { | ||||
|             stringGeter.Append("w=" + WriteFunction.ToString() + ";"); | ||||
|         } | ||||
|         if (!string.IsNullOrEmpty(SocketId)) | ||||
|         { | ||||
|             stringGeter.Append("id=" + SocketId + ";"); | ||||
|         } | ||||
|         stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString()); | ||||
|         return stringGeter.ToString(); | ||||
|     } | ||||
| 
 | ||||
|     private void GetFunction(ushort readF) | ||||
|     { | ||||
|         switch (readF) | ||||
|         { | ||||
|             case 0: | ||||
|                 ReadFunction = 1; | ||||
|                 break; | ||||
|             case 1: | ||||
|                 ReadFunction = 2; | ||||
|                 break; | ||||
|             case 3: | ||||
|                 ReadFunction = 4; | ||||
|                 break; | ||||
|             case 4: | ||||
|                 ReadFunction = 3; | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|     private string GetFunctionString(int readF) | ||||
|     { | ||||
|         return readF switch | ||||
| @@ -0,0 +1,321 @@ | ||||
| #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.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.Bool; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
|  | ||||
| internal class ModbusHelper | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 添加Crc16 | ||||
|     /// </summary> | ||||
|     internal static byte[] AddCrc(byte[] command) | ||||
|     { | ||||
|         return EasyCRC16.CRC16(command); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 添加ModbusTcp报文头 | ||||
|     /// </summary> | ||||
|     internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id) | ||||
|     { | ||||
|         byte[] tcp = new byte[modbus.Length + 6]; | ||||
|         tcp[0] = BitConverter.GetBytes(id)[1]; | ||||
|         tcp[1] = BitConverter.GetBytes(id)[0]; | ||||
|         tcp[4] = BitConverter.GetBytes(modbus.Length)[1]; | ||||
|         tcp[5] = BitConverter.GetBytes(modbus.Length)[0]; | ||||
|         modbus.CopyTo(tcp, 6); | ||||
|         return tcp; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// modbus地址格式说明 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     internal static string GetAddressDescription() | ||||
|     { | ||||
|         StringBuilder stringBuilder = new(); | ||||
|         stringBuilder.AppendLine("Modbus寄存器"); | ||||
|         stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。"); | ||||
|         stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。"); | ||||
|         stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。"); | ||||
|         stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。"); | ||||
|         stringBuilder.AppendLine("举例:"); | ||||
|         stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器"); | ||||
|         stringBuilder.AppendLine("额外格式:"); | ||||
|         stringBuilder.AppendLine("设备站号 ,比如40001;s=2; ,代表设备地址为2的保持寄存器第一个寄存器"); | ||||
|         stringBuilder.AppendLine("写入功能码 ,比如40001;w=16; ,代表保持寄存器第一个寄存器,写入值时采用0x10功能码,而不是默认的0x06功能码"); | ||||
|         return stringBuilder.ToString(); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 通过错误码来获取到对应的文本消息 | ||||
|     /// </summary> | ||||
|     internal static string GetDescriptionByErrorCode(byte code) | ||||
|     { | ||||
|         return code switch | ||||
|         { | ||||
|             1 => "不支持的功能码", | ||||
|             2 => "读取寄存器越界", | ||||
|             3 => "读取长度超限", | ||||
|             4 => "读写异常", | ||||
|             _ => "未知错误", | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头 | ||||
|     /// </summary> | ||||
|     /// <param name="send">发送数据</param> | ||||
|     /// <param name="response">返回数据</param> | ||||
|     /// <returns></returns> | ||||
|     internal static OperResult<byte[], FilterResult> GetModbusData(byte[] send, byte[] response) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (response.Length < 3) | ||||
|                 return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||
|  | ||||
|  | ||||
|             if (response[1] >= 0x80)//错误码 | ||||
|                 return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success }; | ||||
|             if ((response.Length < response[2] + 3)) | ||||
|                 return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||
|  | ||||
|  | ||||
|             if (send.Length == 0) | ||||
|             { | ||||
|                 var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success); | ||||
|                 result.Message = "接收数据正确,但主机并没有主动请求数据"; | ||||
|                 return result; | ||||
|             } | ||||
|             if (send[0] != response[0]) | ||||
|                 return new OperResult<byte[], FilterResult>(string.Format("站号不一致", send[0], response[0])) { Content2 = FilterResult.Success }; | ||||
|             if (send[1] != response[1]) | ||||
|                 return new OperResult<byte[], FilterResult>() { Message = "功能码不一致", Content2 = FilterResult.Success }; | ||||
|             return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[], FilterResult>(ex.Message) { Content2 = FilterResult.Success }; | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 去除Crc,返回modbus数据区 | ||||
|     /// </summary> | ||||
|     /// <param name="send"></param> | ||||
|     /// <param name="response"></param> | ||||
|     /// <param name="crcCheck"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static OperResult<byte[], FilterResult> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true) | ||||
|     { | ||||
|         if (response.Length < 3) | ||||
|             return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||
|  | ||||
|         if (response[1] >= 0x80)//错误码 | ||||
|             return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success }; | ||||
|         if ((response.Length < response[2] + 5)) | ||||
|             return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||
|  | ||||
|         if (response[2] == 0) | ||||
|         { | ||||
|             if ((response.Length < 8)) | ||||
|                 return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||
|         } | ||||
|  | ||||
|         var data = response.SelectMiddle(0, response[2] != 0 ? response[2] + 5 : 8); | ||||
|         if (crcCheck && !EasyCRC16.CheckCRC16(data)) | ||||
|             return new OperResult<byte[], FilterResult>("Crc校验失败" + DataTransUtil.ByteToHexString(data, ' ')) { Content2 = FilterResult.Success }; | ||||
|         return GetModbusData(send, data.RemoveLast(2)); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 获取读取报文 | ||||
|     /// </summary> | ||||
|     internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, station); | ||||
|             return OperResult.CreateSuccessResult(GetReadModbusCommand(mAddress, length)); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 获取写入布尔量报文,根据地址识别功能码 | ||||
|     /// </summary> | ||||
|     internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, station); | ||||
|  | ||||
|             //功能码或实际长度 | ||||
|             if (values?.Length > 1 || mAddress.WriteFunction == 15) | ||||
|                 return GetWriteBoolModbusCommand(mAddress, values, values.Length); | ||||
|             else | ||||
|                 return GetWriteBoolModbusCommand(address, values[0], station); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取写入字报文,根据地址识别功能码 | ||||
|     /// </summary> | ||||
|     internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, station); | ||||
|  | ||||
|             //功能码或实际长度 | ||||
|             if (value?.Length > 2 || mAddress.WriteFunction == 16) | ||||
|                 return OperResult.CreateSuccessResult(GetWriteModbusCommand(mAddress, value)); | ||||
|             else | ||||
|                 return OperResult.CreateSuccessResult(GetWriteOneModbusCommand(mAddress, value)); | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 获取读取报文 | ||||
|     /// </summary> | ||||
|     internal static byte[] GetReadModbusCommand(ModbusAddress mAddress, int length) | ||||
|     { | ||||
|         byte[] array = new byte[6] | ||||
|         { | ||||
|         (byte) mAddress.Station, | ||||
|         (byte) mAddress.ReadFunction, | ||||
|         BitConverter.GetBytes(mAddress.AddressStart)[1], | ||||
|         BitConverter.GetBytes(mAddress.AddressStart)[0], | ||||
|         BitConverter.GetBytes(length)[1], | ||||
|         BitConverter.GetBytes(length)[0] | ||||
|         }; | ||||
|         return array; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取05写入布尔量报文 | ||||
|     /// </summary> | ||||
|     internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (address.IndexOf('.') <= 0) | ||||
|             { | ||||
|                 var mAddress = ModbusAddress.ParseFrom(address, station); | ||||
|  | ||||
|                 return OperResult.CreateSuccessResult(GetWriteBoolModbusCommand(mAddress, value)); | ||||
|             } | ||||
|             return new("不支持写入字寄存器的某一位"); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取05写入布尔量报文 | ||||
|     /// </summary> | ||||
|     private static byte[] GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value) | ||||
|     { | ||||
|         byte[] array = new byte[6] | ||||
|         { | ||||
|     (byte) mAddress.Station, | ||||
|     (byte)5, | ||||
|     BitConverter.GetBytes(mAddress.AddressStart)[1], | ||||
|     BitConverter.GetBytes(mAddress.AddressStart)[0], | ||||
|      0, | ||||
|      0 | ||||
|         }; | ||||
|         if (value) | ||||
|         { | ||||
|             array[4] = 0xFF; | ||||
|             array[5] = 0; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             array[4] = 0; | ||||
|             array[5] = 0; | ||||
|         } | ||||
|         return array; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取15写入布尔量报文 | ||||
|     /// </summary> | ||||
|     internal static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             byte[] numArray1 = values.BoolArrayToByte(); | ||||
|             byte[] numArray2 = new byte[7 + numArray1.Length]; | ||||
|             numArray2[0] = (byte)mAddress.Station; | ||||
|             numArray2[1] = (byte)15; | ||||
|             numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1]; | ||||
|             numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0]; | ||||
|             numArray2[4] = (byte)(length / 256); | ||||
|             numArray2[5] = (byte)(length % 256); | ||||
|             numArray2[6] = (byte)numArray1.Length; | ||||
|             numArray1.CopyTo(numArray2, 7); | ||||
|             return OperResult.CreateSuccessResult(numArray2); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取16写入字报文 | ||||
|     /// </summary> | ||||
|     internal static byte[] GetWriteModbusCommand(ModbusAddress mAddress, byte[] values) | ||||
|     { | ||||
|         byte[] numArray = new byte[7 + values.Length]; | ||||
|         numArray[0] = (byte)mAddress.Station; | ||||
|         numArray[1] = (byte)16; | ||||
|         numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1]; | ||||
|         numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0]; | ||||
|         numArray[4] = (byte)(values.Length / 2 / 256); | ||||
|         numArray[5] = (byte)(values.Length / 2 % 256); | ||||
|         numArray[6] = (byte)values.Length; | ||||
|         values.CopyTo(numArray, 7); | ||||
|         return numArray; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取6写入字报文 | ||||
|     /// </summary> | ||||
|     internal static byte[] GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values) | ||||
|     { | ||||
|         byte[] numArray = new byte[4 + values.Length]; | ||||
|         numArray[0] = (byte)mAddress.Station; | ||||
|         numArray[1] = (byte)6; | ||||
|         numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1]; | ||||
|         numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0]; | ||||
|         values.CopyTo(numArray, 4); | ||||
|         return numArray; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,192 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| /// <summary> | ||||
| /// ModbusRtu | ||||
| /// </summary> | ||||
| public class ModbusRtu : ReadWriteDevicesSerialSessionBase | ||||
| { | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ModbusRtu | ||||
|     /// </summary> | ||||
|     /// <param name="serialSession"></param> | ||||
|     public ModbusRtu(SerialSession serialSession) : base(serialSession) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Crc校验 | ||||
|     /// </summary> | ||||
|     [Description("Crc校验")] | ||||
|     public bool Crc16CheckEnable { get; set; } = true; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 站号 | ||||
|     /// </summary> | ||||
|     [Description("站号")] | ||||
|     public byte Station { get; set; } = 1; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient = null) | ||||
|     { | ||||
|         ModbusRtuDataHandleAdapter dataHandleAdapter = new() | ||||
|         { | ||||
|             Crc16CheckEnable = Crc16CheckEnable, | ||||
|             CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||
|         }; | ||||
|         SerialSession.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             if (FrameTime != 0) | ||||
|                 Thread.Sleep(FrameTime); | ||||
|             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             await Task.Delay(FrameTime, cancellationToken); | ||||
|             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -11,6 +11,8 @@ | ||||
| #endregion | ||||
| 
 | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| 
 | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| 
 | ||||
| /// <summary> | ||||
| @@ -42,53 +44,43 @@ public class ModbusRtuDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<M | ||||
|     /// <inheritdoc/> | ||||
|     protected override FilterResult UnpackResponse(ModbusRtuMessage request, byte[] send, byte[] body, byte[] response) | ||||
|     { | ||||
|         //理想状态检测 | ||||
|         var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||
|         if (result.IsSuccess) | ||||
|         //链路干扰时需剔除前缀中的多于字节,初步按站号+功能码找寻初始字节 | ||||
|         if (send?.Length > 0) | ||||
|         { | ||||
|             request.ResultCode = result.ResultCode; | ||||
|             request.Message = result.Message; | ||||
|             request.Content = result.Content; | ||||
|             return FilterResult.Success; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (response.Length <= 1) | ||||
|             int index = -1; | ||||
|             for (int i = 0; i < response.Length - 1; i++) | ||||
|             { | ||||
|                 request.ResultCode = result.ResultCode; | ||||
|                 request.Message = result.Message; | ||||
|                 request.Content = result.Content; | ||||
|                 //如果长度不足,返回缓存 | ||||
|                 return FilterResult.Cache; | ||||
|                 if (response[i] == send[0] && (response[i + 1] == send[1] || response[i + 1] == (send[1] + 0x80))) | ||||
|                 { | ||||
|                     index = i; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             if (!(response[1] <= 0x10)) | ||||
|             if (index >= 0) | ||||
|             { | ||||
|                 request.ResultCode = result.ResultCode; | ||||
|                 response = response.RemoveBegin(index); | ||||
|             } | ||||
| 
 | ||||
|             //理想状态检测 | ||||
|             var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||
|             if (result.IsSuccess) | ||||
|             { | ||||
|                 request.ErrorCode = result.ErrorCode; | ||||
|                 request.Message = result.Message; | ||||
|                 request.Content = result.Content; | ||||
|                 //功能码不对,返回放弃 | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if ((response.Length > response[2] + 4)) | ||||
|                 { | ||||
|                     request.ResultCode = result.ResultCode; | ||||
|                     request.Message = result.Message; | ||||
|                     request.Content = result.Content; | ||||
|                     //如果长度已经超了,说明这段报文已经不能继续解析了,直接返回放弃 | ||||
|                     return FilterResult.Success; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     request.ResultCode = result.ResultCode; | ||||
|                     request.Message = result.Message; | ||||
|                     request.Content = result.Content; | ||||
|                     //否则返回缓存 | ||||
|                     return FilterResult.Cache; | ||||
|                 } | ||||
|                 request.ErrorCode = result.ErrorCode; | ||||
|                 request.Message = result.Message; | ||||
|             } | ||||
|             return result.Content2; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return FilterResult.Success; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @@ -22,13 +22,13 @@ public class ModbusRtuMessage : MessageBase, IMessage | ||||
|     /// <inheritdoc/> | ||||
|     public override bool CheckHeadBytes(byte[] head) | ||||
|     { | ||||
|         BodyLength = -1; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override void SendBytesThen() | ||||
|     { | ||||
|         BodyLength = -1; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -0,0 +1,186 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
|  | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| /// <inheritdoc/> | ||||
| public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase | ||||
| { | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusRtuOverTcp(TcpClient tcpClient) : base(tcpClient) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Crc校验 | ||||
|     /// </summary> | ||||
|     [Description("Crc校验")] | ||||
|     public bool Crc16CheckEnable { get; set; } = true; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 站号 | ||||
|     /// </summary> | ||||
|     [Description("站号")] | ||||
|     public byte Station { get; set; } = 1; | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient = null) | ||||
|     { | ||||
|         ModbusRtuDataHandleAdapter dataHandleAdapter = new() | ||||
|         { | ||||
|             Crc16CheckEnable = Crc16CheckEnable, | ||||
|             CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||
|         }; | ||||
|         TcpClient.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             if (FrameTime != 0) | ||||
|                 Thread.Sleep(FrameTime); | ||||
|             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             await Task.Delay(FrameTime, cancellationToken); | ||||
|             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,185 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class ModbusRtuOverUdp : ReadWriteDevicesUdpSessionBase | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusRtuOverUdp(UdpSession udpSession) : base(udpSession) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Crc校验 | ||||
|     /// </summary> | ||||
|     [Description("Crc校验")] | ||||
|     public bool Crc16CheckEnable { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 站号 | ||||
|     /// </summary> | ||||
|     [Description("站号")] | ||||
|     public byte Station { get; set; } = 1; | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient = null) | ||||
|     { | ||||
|         ModbusRtuOverUdpDataHandleAdapter dataHandleAdapter = new() | ||||
|         { | ||||
|             Crc16CheckEnable = Crc16CheckEnable, | ||||
|         }; | ||||
|         UdpSession.Config.SetUdpDataHandlingAdapter(() => | ||||
|         { | ||||
|             return dataHandleAdapter; | ||||
|         }); | ||||
|         UdpSession.Setup(UdpSession.Config); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             if (FrameTime != 0) | ||||
|                 Thread.Sleep(FrameTime); | ||||
|             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             await Task.Delay(FrameTime, cancellationToken); | ||||
|             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -35,9 +35,9 @@ public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAd | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override OperResult<byte[]> UnpackResponse( | ||||
|               byte[] send, byte[] response) | ||||
|     protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response) | ||||
|     { | ||||
|         return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||
|         var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,421 @@ | ||||
| #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.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.Bool; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||
|     /// </summary> | ||||
|     public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SerialSession, OperResult> WriteData; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 继电器 | ||||
|     /// </summary> | ||||
|     private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 开关输入 | ||||
|     /// </summary> | ||||
|     private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 输入寄存器 | ||||
|     /// </summary> | ||||
|     private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 保持寄存器 | ||||
|     /// </summary> | ||||
|     private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new(); | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusSerialServer(SerialSession serialSession) : base(serialSession) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 多站点 | ||||
|     /// </summary> | ||||
|     public bool MulStation { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 默认站点 | ||||
|     /// </summary> | ||||
|     public byte Station { get; set; } = 1; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void Dispose() | ||||
|     { | ||||
|         foreach (var item in ModbusServer01ByteBlocks) | ||||
|         { | ||||
|             item.Value.SafeDispose(); | ||||
|         } | ||||
|         foreach (var item in ModbusServer02ByteBlocks) | ||||
|         { | ||||
|             item.Value.SafeDispose(); | ||||
|         } | ||||
|         foreach (var item in ModbusServer03ByteBlocks) | ||||
|         { | ||||
|             item.Value.SafeDispose(); | ||||
|         } | ||||
|         foreach (var item in ModbusServer04ByteBlocks) | ||||
|         { | ||||
|             item.Value.SafeDispose(); | ||||
|         } | ||||
|         ModbusServer01ByteBlocks.Clear(); | ||||
|         ModbusServer02ByteBlocks.Clear(); | ||||
|         ModbusServer03ByteBlocks.Clear(); | ||||
|         ModbusServer04ByteBlocks.Clear(); | ||||
|         base.Dispose(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|  | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         ModbusAddress mAddress; | ||||
|         try | ||||
|         { | ||||
|             mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|         if (MulStation) | ||||
|         { | ||||
|             Init(mAddress); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (Station != mAddress.Station) | ||||
|             { | ||||
|                 return new OperResult<byte[]>("地址错误"); | ||||
|             } | ||||
|             Init(mAddress); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station]; | ||||
|         var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station]; | ||||
|         var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station]; | ||||
|         var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station]; | ||||
|         int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength; | ||||
|         switch (mAddress.ReadFunction) | ||||
|         { | ||||
|             case 1: | ||||
|                 byte[] bytes0 = new byte[len]; | ||||
|                 ModbusServer01ByteBlock.Pos = mAddress.AddressStart; | ||||
|                 ModbusServer01ByteBlock.Read(bytes0); | ||||
|                 return OperResult.CreateSuccessResult(bytes0); | ||||
|             case 2: | ||||
|                 byte[] bytes1 = new byte[len]; | ||||
|                 ModbusServer02ByteBlock.Pos = mAddress.AddressStart; | ||||
|                 ModbusServer02ByteBlock.Read(bytes1); | ||||
|                 return OperResult.CreateSuccessResult(bytes1); | ||||
|             case 3: | ||||
|  | ||||
|                 byte[] bytes3 = new byte[len]; | ||||
|                 ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||
|                 ModbusServer03ByteBlock.Read(bytes3); | ||||
|                 return OperResult.CreateSuccessResult(bytes3); | ||||
|             case 4: | ||||
|                 byte[] bytes4 = new byte[len]; | ||||
|                 ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||
|                 ModbusServer04ByteBlock.Read(bytes4); | ||||
|                 return OperResult.CreateSuccessResult(bytes4); | ||||
|         } | ||||
|         return new OperResult<byte[]>("功能码错误"); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(Read(address, length)); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient) | ||||
|     { | ||||
|         ModbusSerialServerDataHandleAdapter dataHandleAdapter = new(); | ||||
|         dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout); | ||||
|         SerialSession.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         ModbusAddress mAddress; | ||||
|         try | ||||
|         { | ||||
|             mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|         if (MulStation) | ||||
|         { | ||||
|             Init(mAddress); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (Station != mAddress.Station) | ||||
|             { | ||||
|                 return new OperResult("地址错误"); | ||||
|             } | ||||
|             Init(mAddress); | ||||
|         } | ||||
|         var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station]; | ||||
|         var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station]; | ||||
|         switch (mAddress.ReadFunction) | ||||
|         { | ||||
|             case 3: | ||||
|                 ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||
|                 ModbusServer03ByteBlock.Write(value); | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             case 4: | ||||
|                 ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||
|                 ModbusServer04ByteBlock.Write(value); | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|         } | ||||
|         return new OperResult("功能码错误"); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         ModbusAddress mAddress; | ||||
|         try | ||||
|         { | ||||
|             mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return (new OperResult(ex)); | ||||
|         } | ||||
|         if (MulStation) | ||||
|         { | ||||
|             Init(mAddress); | ||||
|  | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (Station != mAddress.Station) | ||||
|             { | ||||
|                 return (new OperResult("地址错误")); | ||||
|             } | ||||
|             Init(mAddress); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station]; | ||||
|         var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station]; | ||||
|         switch (mAddress.ReadFunction) | ||||
|         { | ||||
|             case 1: | ||||
|                 ModbusServer01ByteBlock.Pos = mAddress.AddressStart; | ||||
|                 ModbusServer01ByteBlock.Write(value.BoolArrayToByte()); | ||||
|                 return (OperResult.CreateSuccessResult()); | ||||
|             case 2: | ||||
|                 ModbusServer02ByteBlock.Pos = mAddress.AddressStart; | ||||
|                 ModbusServer02ByteBlock.Write(value.BoolArrayToByte()); | ||||
|                 return (OperResult.CreateSuccessResult()); | ||||
|         } | ||||
|         return new OperResult("功能码错误"); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(Write(address, value)); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(Write(address, value)); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Received(ByteBlock byteBlock, IRequestInfo requestInfo) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|  | ||||
|             //接收外部报文 | ||||
|             if (requestInfo is ModbusSerialServerMessage modbusServerMessage) | ||||
|             { | ||||
|                 if (modbusServerMessage.CurModbusAddress == null) | ||||
|                 { | ||||
|                     return;//无法解析直接返回 | ||||
|                 } | ||||
|                 if (!modbusServerMessage.IsSuccess) | ||||
|                 { | ||||
|                     return;//无法解析直接返回 | ||||
|                 } | ||||
|  | ||||
|                 if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取 | ||||
|                 { | ||||
|                     var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length); | ||||
|                     if (data.IsSuccess) | ||||
|                     { | ||||
|                         var coreData = data.Content; | ||||
|                         if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||
|                         { | ||||
|                             coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0)); | ||||
|                         } | ||||
|                         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2).SpliceArray(new byte[] { (byte)coreData.Length }, coreData); | ||||
|                         SerialSession.Send(sendData); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         WriteError(SerialSession, modbusServerMessage);//返回错误码 | ||||
|                     } | ||||
|                 } | ||||
|                 else//写入 | ||||
|                 { | ||||
|                     var coreData = modbusServerMessage.Content; | ||||
|                     if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||
|                     { | ||||
|                         //写入继电器 | ||||
|                         if (WriteData != null) | ||||
|                         { | ||||
|                             // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||
|                             if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess) | ||||
|                             { | ||||
|                                 var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); | ||||
|                                 if (result.IsSuccess) | ||||
|                                 { | ||||
|                                     WriteSuccess03(SerialSession, modbusServerMessage); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     WriteError(SerialSession, modbusServerMessage); | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(SerialSession, modbusServerMessage); | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             //写入内存区 | ||||
|                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); | ||||
|                             if (result.IsSuccess) | ||||
|                             { | ||||
|                                 WriteSuccess03(SerialSession, modbusServerMessage); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(SerialSession, modbusServerMessage); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         //写入寄存器 | ||||
|                         if (WriteData != null) | ||||
|                         { | ||||
|  | ||||
|                             if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess) | ||||
|                             { | ||||
|                                 var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||
|                                 if (result.IsSuccess) | ||||
|                                 { | ||||
|                                     WriteSuccess03(SerialSession, modbusServerMessage); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     WriteError(SerialSession, modbusServerMessage); | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(SerialSession, modbusServerMessage); | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||
|                             if (result.IsSuccess) | ||||
|                             { | ||||
|                                 WriteSuccess03(SerialSession, modbusServerMessage); | ||||
|  | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(SerialSession, modbusServerMessage); | ||||
|  | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Logger.LogError(ex, ToString()); | ||||
|         } | ||||
|         //返回错误码 | ||||
|         static void WriteError(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage) | ||||
|         { | ||||
|             var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2) | ||||
| .SpliceArray(new byte[] { (byte)1 });//01 lllegal function | ||||
|             sendData[1] = (byte)(sendData[1] + 128); | ||||
|             SerialSession.Send(sendData); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static void WriteSuccess03(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage) | ||||
|     { | ||||
|         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 6); | ||||
|         SerialSession.Send(sendData); | ||||
|     } | ||||
|  | ||||
|     private void Init(ModbusAddress mAddress) | ||||
|     { | ||||
|         if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,141 @@ | ||||
| #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.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class ModbusSerialServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusSerialServerMessage> | ||||
| { | ||||
|     private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="command"></param> | ||||
|     /// <returns></returns> | ||||
|     public override byte[] PackCommand(byte[] command) | ||||
|     { | ||||
|         return ModbusHelper.AddCrc(command); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取modbus写入数据区内容 | ||||
|     /// </summary> | ||||
|     /// <param name="response">返回数据</param> | ||||
|     /// <returns></returns> | ||||
|     internal OperResult<byte[]> GetModbusData(byte[] response) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var func = ThingsGatewayBitConverter.ToByte(response, 1); | ||||
|             if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6) | ||||
|             { | ||||
|                 if (response.Length == 6) | ||||
|                     return OperResult.CreateSuccessResult(response); | ||||
|             } | ||||
|             else if (func == 15 || func == 16) | ||||
|             { | ||||
|                 var length = ThingsGatewayBitConverter.ToByte(response, 6); | ||||
|                 if (response.Length == 7 + length) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(response); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" }; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override ModbusSerialServerMessage GetInstance() | ||||
|     { | ||||
|         return new ModbusSerialServerMessage(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override FilterResult UnpackResponse(ModbusSerialServerMessage request, byte[] send, byte[] body, byte[] response) | ||||
|     { | ||||
|         var result1 = ModbusHelper.GetModbusRtuData(new byte[0], response, true); | ||||
|         if (result1.IsSuccess) | ||||
|         { | ||||
|             var result = GetModbusData(response.RemoveLast(2)); | ||||
|             if (result.IsSuccess) | ||||
|             { | ||||
|                 //解析01 03 00 00 00 0A | ||||
|                 var station = ThingsGatewayBitConverter.ToByte(response, 0); | ||||
|                 var function = ThingsGatewayBitConverter.ToByte(response, 1); | ||||
|                 int addressStart = ThingsGatewayBitConverter.ToInt16(response, 2); | ||||
|                 if (addressStart == -1) | ||||
|                 { | ||||
|                     addressStart = 65535; | ||||
|                 } | ||||
|                 if (function > 4) | ||||
|                 { | ||||
|                     if (function > 6) | ||||
|                     { | ||||
|                         request.CurModbusAddress = new ModbusAddress() | ||||
|                         { | ||||
|                             Station = station, | ||||
|                             Address = addressStart.ToString(), | ||||
|                             WriteFunction = function, | ||||
|                             ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3), | ||||
|                         }; | ||||
|                         request.Length = ThingsGatewayBitConverter.ToByte(response, 5); | ||||
|                         request.Content = result.Content.RemoveBegin(7); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         request.CurModbusAddress = new ModbusAddress() | ||||
|                         { | ||||
|                             Station = station, | ||||
|                             Address = addressStart.ToString(), | ||||
|                             WriteFunction = function, | ||||
|                             ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3), | ||||
|                         }; | ||||
|                         request.Length = 1; | ||||
|                         request.Content = result.Content.RemoveBegin(4); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     request.CurModbusAddress = new ModbusAddress() | ||||
|                     { | ||||
|                         Station = station, | ||||
|                         Address = addressStart.ToString(), | ||||
|                         ReadFunction = function, | ||||
|                     }; | ||||
|                     request.Length = ThingsGatewayBitConverter.ToByte(response, 5); | ||||
|                 } | ||||
|                 request.ErrorCode = result.ErrorCode; | ||||
|                 request.Message = result.Message; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 request.ErrorCode = result.ErrorCode; | ||||
|                 request.Message = result.Message; | ||||
|                 return FilterResult.Cache; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return result1.Content2; | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -10,35 +10,34 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using Furion.DependencyInjection; | ||||
| 
 | ||||
| namespace ThingsGateway.Admin.Core; | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// 对象拓展 | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| [SuppressSniffer] | ||||
| public static class JsonExtensions | ||||
| public class ModbusSerialServerMessage : MessageBase, IMessage | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 转换为Json | ||||
|     /// 当前关联的地址 | ||||
|     /// </summary> | ||||
|     /// <param name="item"></param> | ||||
|     /// <returns></returns> | ||||
|     public static string ToJsonString(this object item) | ||||
|     { | ||||
|         return Newtonsoft.Json.JsonConvert.SerializeObject(item); | ||||
|     } | ||||
| 
 | ||||
|     public ModbusAddress CurModbusAddress { get; set; } | ||||
|     /// <summary> | ||||
|     /// 从字符串到json | ||||
|     /// 当前读写的数据长度 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="T"></typeparam> | ||||
|     /// <param name="json"></param> | ||||
|     /// <returns></returns> | ||||
|     public static T ToJsonWithT<T>(this string json) | ||||
|     public int Length { get; set; } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override int HeadBytesLength => -1; | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override bool CheckHeadBytes(byte[] head) | ||||
|     { | ||||
|         return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(json); | ||||
|         BodyLength = -1; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override void SendBytesThen() | ||||
|     { | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,185 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| /// <inheritdoc/> | ||||
| public class ModbusTcp : ReadWriteDevicesTcpClientBase | ||||
| { | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusTcp(TcpClient tcpClient) : base(tcpClient) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 检测事务标识符 | ||||
|     /// </summary> | ||||
|     [Description("检测事务标识符")] | ||||
|     public bool IsCheckMessageId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 站号 | ||||
|     /// </summary> | ||||
|     [Description("站号")] | ||||
|     public byte Station { get; set; } = 1; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             var data = SendThenReturn(commandResult, cancellationToken); | ||||
|             return data; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|  | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             var data = await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|             return data; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient = null) | ||||
|     { | ||||
|         ModbusTcpDataHandleAdapter dataHandleAdapter = new() | ||||
|         { | ||||
|             IsCheckMessageId = IsCheckMessageId, | ||||
|             CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||
|         }; | ||||
|         TcpClient.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             if (FrameTime != 0) | ||||
|                 Thread.Sleep(FrameTime); | ||||
|             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             await Task.Delay(FrameTime, cancellationToken); | ||||
|             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -10,13 +10,12 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| 
 | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// ModbusTcpDataHandleAdapter | ||||
| /// </summary> | ||||
| public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage> | ||||
| { | ||||
| @@ -56,50 +55,16 @@ public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<M | ||||
|         var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6)); | ||||
|         if (result.IsSuccess) | ||||
|         { | ||||
|             request.ResultCode = result.ResultCode; | ||||
|             request.ErrorCode = result.ErrorCode; | ||||
|             request.Message = result.Message; | ||||
|             request.Content = result.Content; | ||||
|             return FilterResult.Success; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             //如果返回错误,具体分析 | ||||
|             var op = result.Copy<byte[], FilterResult>(); | ||||
|             if (response.Length == 9) | ||||
|             { | ||||
|                 if (response[7] >= 0x80)//错误码 | ||||
|                 { | ||||
|                     request.ResultCode = result.ResultCode; | ||||
|                     request.Message = result.Message; | ||||
|                     request.Content = result.Content; | ||||
|                     return FilterResult.Success; | ||||
|                 } | ||||
|             } | ||||
|             if (response.Length < 10) | ||||
|             { | ||||
|                 request.ResultCode = result.ResultCode; | ||||
|                 request.Message = result.Message; | ||||
|                 request.Content = result.Content; | ||||
|                 return FilterResult.Cache; | ||||
|                 //如果长度不足,返回缓存 | ||||
|             } | ||||
|             if ((response.Length > response[8] + 9)) | ||||
|             { | ||||
|                 request.ResultCode = result.ResultCode; | ||||
|                 request.Message = result.Message; | ||||
|                 request.Content = result.Content; | ||||
|                 return FilterResult.Success; | ||||
|                 //如果长度已经超了,说明这段报文已经不能继续解析了,直接返回放弃 | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 request.ResultCode = result.ResultCode; | ||||
|                 request.Message = result.Message; | ||||
|                 request.Content = result.Content; | ||||
|                 return FilterResult.Cache; | ||||
|                 //否则返回缓存 | ||||
|             } | ||||
|             request.ErrorCode = result.ErrorCode; | ||||
|             request.Message = result.Message; | ||||
|         } | ||||
|         return result.Content2; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,259 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| /// <inheritdoc/> | ||||
| public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusTcpDtu(TcpService tcpService) : base(tcpService) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|         ModbusTcpDtuPlugin modbusTcpSalvePlugin = new ModbusTcpDtuPlugin(); | ||||
|         tcpService.Config.ConfigurePlugins(a => | ||||
|          { | ||||
|              a.Add(modbusTcpSalvePlugin); | ||||
|          }); | ||||
|         tcpService.Setup(tcpService.Config); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 检测事务标识符 | ||||
|     /// </summary> | ||||
|     [Description("检测事务标识符")] | ||||
|     public bool IsCheckMessageId { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 站号 | ||||
|     /// </summary> | ||||
|     [Description("站号")] | ||||
|     public byte Station { get; set; } = 1; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="client"></param> | ||||
|     /// <param name="e"></param> | ||||
|     protected override void Connecting(SocketClient client, ConnectingEventArgs e) | ||||
|     { | ||||
|         Logger?.Debug(client.IP + ":" + client.Port + "正在连接"); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient = null) | ||||
|     { | ||||
|         if (socketClient is SocketClient client) | ||||
|         { | ||||
|             ModbusTcpDataHandleAdapter dataHandleAdapter = new() | ||||
|             { | ||||
|                 IsCheckMessageId = IsCheckMessageId, | ||||
|                 CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||
|             }; | ||||
|             client.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             foreach (var item in TcpService.GetClients()) | ||||
|             { | ||||
|                 ModbusTcpDataHandleAdapter dataHandleAdapter = new() | ||||
|                 { | ||||
|                     IsCheckMessageId = IsCheckMessageId, | ||||
|                     CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||
|                 }; | ||||
|                 item.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private OperResult<byte[]> SendThenReturn(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             if (TcpService.TryGetSocketClient($"ID={id}", out var client)) | ||||
|             { | ||||
|                 SetDataAdapter(client); | ||||
|  | ||||
|                 var item = commandResult.Content; | ||||
|                 if (FrameTime != 0) | ||||
|                     Thread.Sleep(FrameTime); | ||||
|                 var WaitingClientEx = client.GetWaitingClientEx(new() { BreakTrigger = true }); | ||||
|                 var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<byte[]>("客户端未连接"); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return commandResult; | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private async Task<OperResult<byte[]>> SendThenReturnAsync(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             if (TcpService.TryGetSocketClient($"ID={id}", out var client)) | ||||
|             { | ||||
|                 SetDataAdapter(client); | ||||
|  | ||||
|                 var item = commandResult.Content; | ||||
|                 await Task.Delay(FrameTime, cancellationToken); | ||||
|                 var WaitingClientEx = client.GetWaitingClientEx(new() { BreakTrigger = true }); | ||||
|                 var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||
|                 return (MessageBase)result.RequestInfo; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<byte[]>("客户端未连接"); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return commandResult; | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|     internal class ModbusTcpDtuPlugin : PluginBase, ITcpReceivingPlugin | ||||
|     { | ||||
|         public Task OnTcpReceiving(ITcpClientBase client, ByteBlockEventArgs e) | ||||
|         { | ||||
|             if (client is ISocketClient socket) | ||||
|             { | ||||
|                 if (!socket.Id.StartsWith("ID=")) | ||||
|                 { | ||||
|                     ByteBlock byteBlock = e.ByteBlock; | ||||
|                     var id = $"ID={byteBlock.ToArray().ToHexString()}"; | ||||
|                     socket.ResetId(id); | ||||
|                 } | ||||
|             } | ||||
|             return e.InvokeNext();//如果本插件无法处理当前数据,请将数据转至下一个插件。 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -11,43 +11,44 @@ | ||||
| #endregion | ||||
| 
 | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Extension.Bool; | ||||
| using ThingsGateway.Foundation.Extension.Byte; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| 
 | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class ModbusServer : ReadWriteDevicesTcpServerBase | ||||
| public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 继电器 | ||||
|     /// </summary> | ||||
|     public ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new(); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 开关输入 | ||||
|     /// </summary> | ||||
|     public ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new(); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 输入寄存器 | ||||
|     /// </summary> | ||||
|     public ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new(); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 保持寄存器 | ||||
|     /// </summary> | ||||
|     public ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new(); | ||||
|     /// <summary> | ||||
|     /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||
|     /// </summary> | ||||
|     public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, OperResult> WriteData; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 继电器 | ||||
|     /// </summary> | ||||
|     private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new(); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 开关输入 | ||||
|     /// </summary> | ||||
|     private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new(); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 输入寄存器 | ||||
|     /// </summary> | ||||
|     private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new(); | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 保持寄存器 | ||||
|     /// </summary> | ||||
|     private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new(); | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusServer(TcpService tcpService) : base(tcpService) | ||||
|     public ModbusTcpServer(TcpService tcpService) : base(tcpService) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
| @@ -56,46 +57,18 @@ public class ModbusServer : ReadWriteDevicesTcpServerBase | ||||
|     /// <summary> | ||||
|     /// 多站点 | ||||
|     /// </summary> | ||||
|     [Description("多站点")] | ||||
|     public bool MulStation { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 默认站点 | ||||
|     /// </summary> | ||||
|     [Description("默认站点")] | ||||
|     public byte Station { get; set; } = 1; | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default) | ||||
|     { | ||||
|         return Task.FromResult(Read(address, length)); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(SocketClient client) | ||||
|     { | ||||
|         ModbusServerDataHandleAdapter DataHandleAdapter = new(); | ||||
|         client.SetDataHandlingAdapter(DataHandleAdapter); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) | ||||
|     { | ||||
|         return Task.FromResult(Write(address, value)); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) | ||||
|     { | ||||
|         return Task.FromResult(Write(address, value)); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     public override void Dispose() | ||||
|     { | ||||
|         foreach (var item in ModbusServer01ByteBlocks) | ||||
|         { | ||||
| @@ -117,167 +90,28 @@ public class ModbusServer : ReadWriteDevicesTcpServerBase | ||||
|         ModbusServer02ByteBlocks.Clear(); | ||||
|         ModbusServer03ByteBlocks.Clear(); | ||||
|         ModbusServer04ByteBlocks.Clear(); | ||||
|         Disconnect(); | ||||
|         base.Dispose(disposing); | ||||
|         base.Dispose(); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Received(SocketClient client, IRequestInfo requestInfo) | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         //接收外部报文 | ||||
|         if (requestInfo is ModbusServerMessage modbusServerMessage) | ||||
|         { | ||||
|             if (!modbusServerMessage.IsSuccess) | ||||
|             { | ||||
|                 return;//无法解析直接返回 | ||||
|             } | ||||
|             if (modbusServerMessage.CurModbusAddress == null) | ||||
|             { | ||||
|                 WriteError(client, modbusServerMessage);//无法解析变量地址,返回错误码 | ||||
|             } | ||||
|             if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取 | ||||
|             { | ||||
|                 var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.CurModbusAddress.Length); | ||||
|                 if (data.IsSuccess) | ||||
|                 { | ||||
|                     var coreData = data.Content; | ||||
|                     if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||
|                     { | ||||
|                         coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling((double)modbusServerMessage.CurModbusAddress.Length / 8.0)); | ||||
|                     } | ||||
|                     var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8) | ||||
|                         .SpliceArray(new byte[] { (byte)coreData.Length }, coreData); | ||||
|                     sendData[5] = (byte)(sendData.Length - 6); | ||||
|                     client.Send(sendData); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     WriteError(client, modbusServerMessage);//返回错误码 | ||||
|                 } | ||||
|             } | ||||
|             else//写入 | ||||
|             { | ||||
|                 var coreData = modbusServerMessage.Content; | ||||
|                 if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||
|                 { | ||||
|                     //写入继电器 | ||||
|                     if (WriteData != null) | ||||
|                     { | ||||
|                         // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||
|                         if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) | ||||
|                         { | ||||
|                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.CurModbusAddress.Length)); | ||||
|                             if (result.IsSuccess) | ||||
|                             { | ||||
|                                 WriteSuccess03(client, modbusServerMessage); | ||||
| 
 | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
| 
 | ||||
|                             } | ||||
| 
 | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             WriteError(client, modbusServerMessage); | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         //写入内存区 | ||||
|                         var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.CurModbusAddress.Length)); | ||||
|                         if (result.IsSuccess) | ||||
|                         { | ||||
|                             WriteSuccess03(client, modbusServerMessage); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             WriteError(client, modbusServerMessage); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //写入寄存器 | ||||
|                     if (WriteData != null) | ||||
|                     { | ||||
| 
 | ||||
|                         if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) | ||||
|                         { | ||||
|                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||
|                             if (result.IsSuccess) | ||||
|                             { | ||||
|                                 WriteSuccess03(client, modbusServerMessage); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             WriteError(client, modbusServerMessage); | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||
|                         if (result.IsSuccess) | ||||
|                         { | ||||
|                             WriteSuccess03(client, modbusServerMessage); | ||||
| 
 | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             WriteError(client, modbusServerMessage); | ||||
| 
 | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         //返回错误码 | ||||
|         static void WriteError(SocketClient client, ModbusServerMessage modbusServerMessage) | ||||
|         { | ||||
|             var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8) | ||||
| .SpliceArray(new byte[] { (byte)1 });//01 lllegal function | ||||
|             sendData[5] = (byte)(sendData.Length - 6); | ||||
|             sendData[7] = (byte)(sendData[7] + 128); | ||||
|             client.Send(sendData); | ||||
| 
 | ||||
| 
 | ||||
|         } | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
| 
 | ||||
|     private static void WriteSuccess03(SocketClient client, ModbusServerMessage modbusServerMessage) | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12); | ||||
|         sendData[5] = (byte)(sendData.Length - 6); | ||||
|         client.Send(sendData); | ||||
|     } | ||||
| 
 | ||||
|     private void Init(ModbusAddress mAddress) | ||||
|     { | ||||
|         if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private OperResult<byte[]> Read(string address, int length) | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         ModbusAddress mAddress; | ||||
|         try | ||||
|         { | ||||
|             mAddress = new ModbusAddress(address, Station); | ||||
|             mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
| @@ -328,12 +162,42 @@ public class ModbusServer : ReadWriteDevicesTcpServerBase | ||||
|         } | ||||
|         return new OperResult<byte[]>("功能码错误"); | ||||
|     } | ||||
|     private OperResult Write(string address, byte[] value) | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(Read(address, length)); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient) | ||||
|     { | ||||
|         if (socketClient is SocketClient client) | ||||
|         { | ||||
|             ModbusTcpServerDataHandleAdapter dataHandleAdapter = new(); | ||||
|             dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout); | ||||
|             client.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             foreach (var item in TcpService.GetClients()) | ||||
|             { | ||||
|                 ModbusTcpDataHandleAdapter dataHandleAdapter = new() | ||||
|                 { | ||||
|                     CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||
|                 }; | ||||
|                 item.SetDataHandlingAdapter(dataHandleAdapter); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         ModbusAddress mAddress; | ||||
|         try | ||||
|         { | ||||
|             mAddress = new ModbusAddress(address, Station); | ||||
|             mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
| @@ -366,12 +230,14 @@ public class ModbusServer : ReadWriteDevicesTcpServerBase | ||||
|         } | ||||
|         return new OperResult("功能码错误"); | ||||
|     } | ||||
|     private OperResult Write(string address, bool[] value) | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         ModbusAddress mAddress; | ||||
|         try | ||||
|         { | ||||
|             mAddress = new ModbusAddress(address, Station); | ||||
|             mAddress = ModbusAddress.ParseFrom(address, Station); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
| @@ -407,4 +273,169 @@ public class ModbusServer : ReadWriteDevicesTcpServerBase | ||||
|         } | ||||
|         return new OperResult("功能码错误"); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(Write(address, value)); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         return Task.FromResult(Write(address, value)); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Received(SocketClient client, IRequestInfo requestInfo) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
| 
 | ||||
|             //接收外部报文 | ||||
|             if (requestInfo is ModbusTcpServerMessage modbusServerMessage) | ||||
|             { | ||||
|                 if (modbusServerMessage.CurModbusAddress == null) | ||||
|                 { | ||||
|                     return;//无法解析直接返回 | ||||
|                 } | ||||
|                 if (!modbusServerMessage.IsSuccess) | ||||
|                 { | ||||
|                     return;//无法解析直接返回 | ||||
|                 } | ||||
| 
 | ||||
|                 if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取 | ||||
|                 { | ||||
|                     var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length); | ||||
|                     if (data.IsSuccess) | ||||
|                     { | ||||
|                         var coreData = data.Content; | ||||
|                         if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||
|                         { | ||||
|                             coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0)); | ||||
|                         } | ||||
|                         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8).SpliceArray(new byte[] { (byte)coreData.Length }, coreData); | ||||
|                         sendData[5] = (byte)(sendData.Length - 6); | ||||
|                         client.Send(sendData); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         WriteError(client, modbusServerMessage);//返回错误码 | ||||
|                     } | ||||
|                 } | ||||
|                 else//写入 | ||||
|                 { | ||||
|                     var coreData = modbusServerMessage.Content; | ||||
|                     if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||
|                     { | ||||
|                         //写入继电器 | ||||
|                         if (WriteData != null) | ||||
|                         { | ||||
|                             // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||
|                             if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) | ||||
|                             { | ||||
|                                 var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); | ||||
|                                 if (result.IsSuccess) | ||||
|                                 { | ||||
|                                     WriteSuccess03(client, modbusServerMessage); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     WriteError(client, modbusServerMessage); | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             //写入内存区 | ||||
|                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); | ||||
|                             if (result.IsSuccess) | ||||
|                             { | ||||
|                                 WriteSuccess03(client, modbusServerMessage); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         //写入寄存器 | ||||
|                         if (WriteData != null) | ||||
|                         { | ||||
| 
 | ||||
|                             if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) | ||||
|                             { | ||||
|                                 var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||
|                                 if (result.IsSuccess) | ||||
|                                 { | ||||
|                                     WriteSuccess03(client, modbusServerMessage); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     WriteError(client, modbusServerMessage); | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||
|                             if (result.IsSuccess) | ||||
|                             { | ||||
|                                 WriteSuccess03(client, modbusServerMessage); | ||||
| 
 | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
| 
 | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             Logger.LogError(ex, ToString()); | ||||
|         } | ||||
|         //返回错误码 | ||||
|         static void WriteError(SocketClient client, ModbusTcpServerMessage modbusServerMessage) | ||||
|         { | ||||
|             var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8) | ||||
| .SpliceArray(new byte[] { (byte)1 });//01 lllegal function | ||||
|             sendData[5] = (byte)(sendData.Length - 6); | ||||
|             sendData[7] = (byte)(sendData[7] + 128); | ||||
|             client.Send(sendData); | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static void WriteSuccess03(SocketClient client, ModbusTcpServerMessage modbusServerMessage) | ||||
|     { | ||||
|         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12); | ||||
|         sendData[5] = (byte)(sendData.Length - 6); | ||||
|         client.Send(sendData); | ||||
|     } | ||||
| 
 | ||||
|     private void Init(ModbusAddress mAddress) | ||||
|     { | ||||
|         if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|         if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128))) | ||||
|             ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|     } | ||||
| } | ||||
| @@ -15,7 +15,7 @@ using ThingsGateway.Foundation.Extension.Generic; | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| 
 | ||||
| /// <inheritdoc/> | ||||
| public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusServerMessage> | ||||
| public class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpServerMessage> | ||||
| { | ||||
|     private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big); | ||||
| 
 | ||||
| @@ -53,7 +53,7 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return new OperResult<byte[]>(response) { Message = $"数据长度{response.Length}错误" }; | ||||
|             return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" }; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
| @@ -62,13 +62,13 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override ModbusServerMessage GetInstance() | ||||
|     protected override ModbusTcpServerMessage GetInstance() | ||||
|     { | ||||
|         return new ModbusServerMessage(); | ||||
|         return new ModbusTcpServerMessage(); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override FilterResult UnpackResponse(ModbusServerMessage request, byte[] send, byte[] body, byte[] response) | ||||
|     protected override FilterResult UnpackResponse(ModbusTcpServerMessage request, byte[] send, byte[] body, byte[] response) | ||||
|     { | ||||
|         var result = GetModbusData(response.RemoveBegin(6)); | ||||
|         if (result.IsSuccess) | ||||
| @@ -88,11 +88,11 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte | ||||
|                     request.CurModbusAddress = new ModbusAddress() | ||||
|                     { | ||||
|                         Station = station, | ||||
|                         AddressStart = addressStart, | ||||
|                         Address = addressStart.ToString(), | ||||
|                         WriteFunction = function, | ||||
|                         ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3), | ||||
|                         Length = ThingsGatewayBitConverter.ToByte(response, 11), | ||||
|                     }; | ||||
|                     request.Length = ThingsGatewayBitConverter.ToByte(response, 11); | ||||
|                     request.Content = result.Content.RemoveBegin(7); | ||||
|                 } | ||||
|                 else | ||||
| @@ -100,11 +100,11 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte | ||||
|                     request.CurModbusAddress = new ModbusAddress() | ||||
|                     { | ||||
|                         Station = station, | ||||
|                         AddressStart = addressStart, | ||||
|                         Address = addressStart.ToString(), | ||||
|                         WriteFunction = function, | ||||
|                         ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3), | ||||
|                         Length = 1, | ||||
|                     }; | ||||
|                     request.Length = 1; | ||||
|                     request.Content = result.Content.RemoveBegin(4); | ||||
|                 } | ||||
|             } | ||||
| @@ -113,18 +113,18 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte | ||||
|                 request.CurModbusAddress = new ModbusAddress() | ||||
|                 { | ||||
|                     Station = station, | ||||
|                     AddressStart = addressStart, | ||||
|                     Address = addressStart.ToString(), | ||||
|                     ReadFunction = function, | ||||
|                     Length = ThingsGatewayBitConverter.ToByte(response, 11), | ||||
|                 }; | ||||
|                 request.Length = ThingsGatewayBitConverter.ToByte(response, 11); | ||||
|             } | ||||
|             request.ResultCode = result.ResultCode; | ||||
|             request.ErrorCode = result.ErrorCode; | ||||
|             request.Message = result.Message; | ||||
|             return FilterResult.Success; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             request.ResultCode = result.ResultCode; | ||||
|             request.ErrorCode = result.ErrorCode; | ||||
|             request.Message = result.Message; | ||||
|             return FilterResult.Cache; | ||||
|         } | ||||
| @@ -15,12 +15,16 @@ namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public class ModbusServerMessage : MessageBase, IMessage | ||||
|     public class ModbusTcpServerMessage : MessageBase, IMessage | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// 当前关联的地址 | ||||
|         /// </summary> | ||||
|         public ModbusAddress CurModbusAddress { get; set; } | ||||
|         /// <summary> | ||||
|         /// 当前读写的数据长度 | ||||
|         /// </summary> | ||||
|         public int Length { get; set; } | ||||
| 
 | ||||
|         /// <inheritdoc/> | ||||
|         public override int HeadBytesLength => 6; | ||||
| @@ -0,0 +1,188 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class ModbusUdp : ReadWriteDevicesUdpSessionBase | ||||
| { | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public ModbusUdp(UdpSession udpSession) : base(udpSession) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 检测事务标识符 | ||||
|     /// </summary> | ||||
|     [Description("检测事务标识符")] | ||||
|     public bool IsCheckMessageId { get; set; } | ||||
|     /// <summary> | ||||
|     /// 站号 | ||||
|     /// </summary> | ||||
|     [Description("站号")] | ||||
|     public byte Station { get; set; } = 1; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||
|     { | ||||
|         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string GetAddressDescription() | ||||
|     { | ||||
|         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<byte[]>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void SetDataAdapter(object socketClient = null) | ||||
|     { | ||||
|         ModbusUdpDataHandleAdapter dataHandleAdapter = new() | ||||
|         { | ||||
|             IsCheckMessageId = IsCheckMessageId | ||||
|         }; | ||||
|         UdpSession.Config.SetUdpDataHandlingAdapter(() => | ||||
|         { | ||||
|             return dataHandleAdapter; | ||||
|         }); | ||||
|         UdpSession.Setup(UdpSession.Config); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             Connect(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return SendThenReturn(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await ConnectAsync(cancellationToken); | ||||
|             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             if (FrameTime != 0) | ||||
|                 Thread.Sleep(FrameTime); | ||||
|             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (commandResult.IsSuccess) | ||||
|         { | ||||
|             var item = commandResult.Content; | ||||
|             await Task.Delay(FrameTime, cancellationToken); | ||||
|             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||
|             return (MessageBase)result.RequestInfo; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<byte[]>(commandResult.Message); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -50,6 +50,7 @@ public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<M | ||||
|     /// <inheritdoc/> | ||||
|     protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response) | ||||
|     { | ||||
|         return ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6)); | ||||
|         var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6)); | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
| @@ -10,131 +10,140 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using ThingsGateway.Application; | ||||
| using ThingsGateway.Application.Extensions; | ||||
| using ThingsGateway.Foundation; | ||||
| using ThingsGateway.Foundation.Adapter.Modbus; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
| 
 | ||||
| namespace ThingsGateway.Modbus; | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| 
 | ||||
| internal static class ModbusHelper | ||||
| /// <summary> | ||||
| /// PackHelper | ||||
| /// </summary> | ||||
| public class PackHelper | ||||
| { | ||||
|     internal static List<DeviceVariableSourceRead> LoadSourceRead(this List<DeviceVariableRunTime> deviceVariables, IReadWriteDevice device, int MaxPack) | ||||
|     /// <summary> | ||||
|     /// 打包变量,添加到<see href="deviceVariableSourceReads"></see> | ||||
|     /// </summary> | ||||
|     /// <param name="device"></param> | ||||
|     /// <param name="deviceVariables"></param> | ||||
|     /// <param name="maxPack">最大打包长度</param> | ||||
|     /// <returns></returns> | ||||
|     public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new() | ||||
|     { | ||||
|         if (deviceVariables == null) | ||||
|             throw new ArgumentNullException(nameof(deviceVariables)); | ||||
| 
 | ||||
|         var deviceVariableSourceReads = new List<T>(); | ||||
|         var byteConverter = device.ThingsGatewayBitConverter; | ||||
|         var result = new List<DeviceVariableSourceRead>(); | ||||
|         //需要先剔除额外信息,比如dataformat等 | ||||
|         foreach (var item in deviceVariables) | ||||
|         { | ||||
|             var address = item.VariableAddress; | ||||
| 
 | ||||
|             IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter); | ||||
|             item.ThingsGatewayBitConverter = transformParameter; | ||||
|             //item.VariableAddress = address; | ||||
|             item.Index = device.GetBitOffset(item.VariableAddress); | ||||
|         } | ||||
| 
 | ||||
|         //按读取间隔分组 | ||||
|         var tags = deviceVariables.GroupBy(it => it.IntervalTime); | ||||
|         foreach (var item in tags) | ||||
|         var deviceVariableRunTimeGroups = deviceVariables.GroupBy(it => it.IntervalTime); | ||||
|         foreach (var group in deviceVariableRunTimeGroups) | ||||
|         { | ||||
|             Dictionary<ModbusAddress, DeviceVariableRunTime> map = item.ToDictionary(it => | ||||
|             Dictionary<ModbusAddress, T2> map = group.ToDictionary(it => | ||||
|             { | ||||
|                 var lastLen = it.DataTypeEnum.GetByteLength(); | ||||
|                 if (lastLen <= 0) | ||||
|                 { | ||||
|                     if (it.DataTypeEnum.GetSystemType() == typeof(bool)) | ||||
|                     switch (it.DataTypeEnum) | ||||
|                     { | ||||
|                         lastLen = 2; | ||||
|                     } | ||||
|                     else if (it.DataTypeEnum.GetSystemType() == typeof(string)) | ||||
|                     { | ||||
|                         lastLen = it.ThingsGatewayBitConverter.StringLength; | ||||
|                     } | ||||
|                     else if (it.DataTypeEnum.GetSystemType() == typeof(object)) | ||||
|                     { | ||||
|                         lastLen = 2; | ||||
|                         case DataTypeEnum.String: | ||||
|                             lastLen = it.ThingsGatewayBitConverter.Length == null ? throw new("数据类型为字符串时,必须指定字符串长度,才能进行打包") : it.ThingsGatewayBitConverter.Length.Value; | ||||
|                             break; | ||||
|                         default: | ||||
|                             lastLen = 2; | ||||
|                             break; | ||||
|                     } | ||||
|                 } | ||||
|                 if (it.ThingsGatewayBitConverter.Length != null && it.DataTypeEnum != DataTypeEnum.String) | ||||
|                 { | ||||
|                     lastLen *= it.ThingsGatewayBitConverter.Length.Value; | ||||
|                 } | ||||
| 
 | ||||
|                 var address = it.VariableAddress; | ||||
|                 if (address.IndexOf('.') > 0) | ||||
|                 { | ||||
|                     var addressSplits = address.SplitDot(); | ||||
| 
 | ||||
|                     address = addressSplits.RemoveLast(1).ArrayToString("."); | ||||
|                 } | ||||
| 
 | ||||
|                 var result = new ModbusAddress(address, (ushort)lastLen); | ||||
|                 if (result == null) | ||||
|                 { | ||||
|                 } | ||||
| 
 | ||||
|                 var result = ModbusAddress.ParseFrom(address); | ||||
|                 result.ByteLength = lastLen; | ||||
|                 return result; | ||||
|             }); | ||||
| 
 | ||||
|             //获取变量的地址 | ||||
|             var modbusAddressList = map.Keys.ToList(); | ||||
|             var modbusAddressList = map.Keys; | ||||
| 
 | ||||
|             //获取功能码 | ||||
|             var functionCodes = modbusAddressList.Select(t => t.ReadFunction).Distinct(); | ||||
|             foreach (var functionCode in functionCodes) | ||||
|             { | ||||
|                 var modbusAddressSameFunList = modbusAddressList | ||||
|                     .Where(t => t.ReadFunction == functionCode).ToList(); | ||||
|                 var stationNumbers = modbusAddressSameFunList | ||||
|                     .Select(t => t.Station).Distinct().ToList(); | ||||
|                 var modbusAddressSameFunList = modbusAddressList.Where(t => t.ReadFunction == functionCode); | ||||
|                 var stationNumbers = modbusAddressSameFunList.Select(t => t.Station).Distinct(); | ||||
|                 foreach (var stationNumber in stationNumbers) | ||||
|                 { | ||||
|                     var addressList = modbusAddressSameFunList.Where(t => t.Station == stationNumber) | ||||
|                     var addressList = modbusAddressSameFunList | ||||
|                         .Where(t => t.Station == stationNumber) | ||||
|                         .ToDictionary(t => t, t => map[t]); | ||||
|                     var tempResult = LoadSourceRead(addressList, functionCode, item.Key, MaxPack); | ||||
|                     result.AddRange(tempResult); | ||||
| 
 | ||||
|                     var tempResult = LoadSourceRead<T, T2>(addressList, functionCode, group.Key, maxPack); | ||||
|                     deviceVariableSourceReads.AddRange(tempResult); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         return result; | ||||
| 
 | ||||
|         return deviceVariableSourceReads; | ||||
|     } | ||||
| 
 | ||||
|     private static List<DeviceVariableSourceRead> LoadSourceRead(Dictionary<ModbusAddress, DeviceVariableRunTime> addressList, int functionCode, int timeInterval, int MaxPack) | ||||
|     private static List<T> LoadSourceRead<T, T2>(Dictionary<ModbusAddress, T2> addressList, int functionCode, int intervalTime, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new() | ||||
|     { | ||||
|         List<DeviceVariableSourceRead> sourceReads = new(); | ||||
|         List<T> sourceReads = new(); | ||||
|         //按地址和长度排序 | ||||
|         var orderByAddressAndLen = addressList.Keys.OrderBy(it => it.AddressStart + Math.Ceiling(it.Length / 2.0)).ToList(); | ||||
|         var orderByAddressEnd = addressList.Keys.OrderBy(it => it.AddressEnd); | ||||
|         //按地址和长度排序 | ||||
|         var orderByAddress = addressList.Keys.OrderBy(it => it.AddressStart).ToList(); | ||||
|         var orderByAddressStart = addressList.Keys.OrderBy(it => it.AddressStart); | ||||
|         //地址最小,在循环中更改 | ||||
|         var minAddress = orderByAddress.First().AddressStart; | ||||
|         var minAddress = orderByAddressStart.First().AddressStart; | ||||
|         //地址最大 | ||||
|         var maxAddress = orderByAddress.Last().AddressStart; | ||||
|         var maxAddress = orderByAddressStart.Last().AddressStart; | ||||
| 
 | ||||
|         while (maxAddress >= minAddress) | ||||
|         { | ||||
|             //最大的打包长度 | ||||
|             int readLength = MaxPack; | ||||
|             int readLength = maxPack; | ||||
|             if (functionCode == 1 || functionCode == 2) | ||||
|             { | ||||
|                 readLength = MaxPack * 8 * 2; | ||||
|                 readLength = maxPack * 8 * 2; | ||||
|             } | ||||
|             //获取当前的一组打包地址信息, | ||||
|             var tempAddressAndLen = orderByAddressAndLen.Where(t => (t.AddressStart + (t.Length / 2.0)) <= minAddress + readLength).ToList(); | ||||
|             //起始地址 | ||||
|             var startAddress = tempAddressAndLen.OrderBy(it => it.AddressStart).First(); | ||||
|             //读取寄存器长度 | ||||
|             var sourceLen = tempAddressAndLen.Last().AddressStart + (int)Math.Ceiling(tempAddressAndLen.Last().Length / 2.0) - startAddress.AddressStart; | ||||
| 
 | ||||
|             DeviceVariableSourceRead sourceRead = new(timeInterval) | ||||
| 
 | ||||
|             //获取当前的一组打包地址信息, | ||||
|             var tempAddressEnd = orderByAddressEnd.Where(t => t.AddressEnd <= minAddress + readLength).ToList(); | ||||
|             //起始地址 | ||||
|             var startAddress = tempAddressEnd.OrderBy(it => it.AddressStart).First(); | ||||
|             //读取寄存器长度 | ||||
|             var sourceLen = tempAddressEnd.Last().AddressEnd - startAddress.AddressStart; | ||||
| 
 | ||||
|             T sourceRead = new() | ||||
|             { | ||||
|                 TimerTick = new TimerTick(intervalTime), | ||||
|                 //这里只需要根据地址排序的第一个地址,作为实际打包报文中的起始地址 | ||||
|                 Address = startAddress.ToString(), | ||||
|                 VariableAddress = startAddress.ToString(), | ||||
|                 Length = sourceLen | ||||
|             }; | ||||
|             foreach (var item in tempAddressAndLen) | ||||
|             foreach (var item in tempAddressEnd) | ||||
|             { | ||||
|                 var readNode = addressList[item]; | ||||
|                 if ((functionCode == -1 || functionCode == 3 || functionCode == 4) && | ||||
|                     readNode.DataTypeEnum == DataTypeEnum.Boolean) | ||||
|                 if ((functionCode == -1 || functionCode == 3 || functionCode == 4) && readNode.DataTypeEnum == DataTypeEnum.Boolean) | ||||
|                 { | ||||
|                     readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 16) + readNode.Index; | ||||
|                 } | ||||
| @@ -146,15 +155,13 @@ internal static class ModbusHelper | ||||
|                         readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 2) + readNode.Index; | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 sourceRead.DeviceVariables.Add(readNode); | ||||
|                 orderByAddressAndLen.Remove(item); | ||||
|                 orderByAddress.Remove(item); | ||||
|                 sourceRead.DeviceVariableRunTimes.Add(readNode); | ||||
|                 addressList.Remove(item); | ||||
|             } | ||||
| 
 | ||||
|             sourceReads.Add(sourceRead); | ||||
|             if (orderByAddressAndLen.Count > 0) | ||||
|                 minAddress = orderByAddress.First().AddressStart; | ||||
|             if (orderByAddressEnd.Count() > 0) | ||||
|                 minAddress = orderByAddressStart.First().AddressStart; | ||||
|             else | ||||
|                 break; | ||||
|         } | ||||
| @@ -0,0 +1,5 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" /> | ||||
| 	</ItemGroup> | ||||
| </Project> | ||||
| @@ -14,11 +14,10 @@ | ||||
|             <inheritdoc/> | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor(System.String,System.UInt16)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor(System.String,System.Byte)"> | ||||
|             <inheritdoc/> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressStart"> | ||||
|             <summary> | ||||
|             读取功能码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ReadFunction"> | ||||
|             <summary> | ||||
| @@ -35,9 +34,29 @@ | ||||
|             写入功能码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Parse(System.String,System.Int32)"> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ByteLength"> | ||||
|             <summary> | ||||
|             打包临时写入,需要读取的字节长度 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressEnd"> | ||||
|             <summary> | ||||
|             读取功能码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Parse(System.String)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,System.Byte)"> | ||||
|             <summary> | ||||
|             解析地址 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress)"> | ||||
|             <summary> | ||||
|             解析地址 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ToString"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
| @@ -127,24 +146,14 @@ | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.TcpClientEx)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.CacheTimeout"> | ||||
|             <summary> | ||||
|             组包缓存时间/ms | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Crc16CheckEnable"> | ||||
|             <summary> | ||||
|             Crc校验 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.FrameTime"> | ||||
|             <summary> | ||||
|             帧前时间ms | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Station"> | ||||
|             <summary> | ||||
|             站号 | ||||
| @@ -153,10 +162,22 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.GetAddressDescription"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SetDataAdapter"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SetDataAdapter(System.Object)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
| @@ -165,13 +186,10 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(TouchSocket.Sockets.UdpSession)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Crc16CheckEnable"> | ||||
| @@ -179,11 +197,6 @@ | ||||
|             Crc校验 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.FrameTime"> | ||||
|             <summary> | ||||
|             帧前时间ms | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Station"> | ||||
|             <summary> | ||||
|             站号 | ||||
| @@ -192,10 +205,22 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.GetAddressDescription"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SetDataAdapter"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SetDataAdapter(System.Object)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
| @@ -204,9 +229,6 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter"> | ||||
|             <summary> | ||||
|             <inheritdoc/> | ||||
| @@ -231,27 +253,17 @@ | ||||
|             ModbusRtu | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.#ctor(ThingsGateway.Foundation.Serial.SerialClient)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.#ctor(ThingsGateway.Foundation.Serial.SerialSession)"> | ||||
|             <summary> | ||||
|             ModbusRtu | ||||
|             </summary> | ||||
|             <param name="serialClient"></param> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.CacheTimeout"> | ||||
|             <summary> | ||||
|             组包缓存时间/ms | ||||
|             </summary> | ||||
|             <param name="serialSession"></param> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Crc16CheckEnable"> | ||||
|             <summary> | ||||
|             Crc校验 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.FrameTime"> | ||||
|             <summary> | ||||
|             帧前时间ms | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Station"> | ||||
|             <summary> | ||||
|             站号 | ||||
| @@ -260,10 +272,22 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.GetAddressDescription"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.SetDataAdapter"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.SetDataAdapter(System.Object)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
| @@ -339,7 +363,7 @@ | ||||
|             接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(TouchSocket.Sockets.TcpService)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(ThingsGateway.Foundation.Sockets.TcpService)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.MulStation"> | ||||
| @@ -352,13 +376,28 @@ | ||||
|             默认站点 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Dispose"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.GetAddressDescription"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.SetDataAdapter(TouchSocket.Sockets.SocketClient)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.SetDataAdapter(System.Object)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
| @@ -367,10 +406,7 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Dispose(System.Boolean)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Received(TouchSocket.Sockets.SocketClient,TouchSocket.Sockets.IRequestInfo)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Received(ThingsGateway.Foundation.Sockets.SocketClient,ThingsGateway.Foundation.IRequestInfo)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter"> | ||||
| @@ -406,6 +442,11 @@ | ||||
|             当前关联的地址 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.Length"> | ||||
|             <summary> | ||||
|             当前读写的数据长度 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.HeadBytesLength"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
| @@ -415,19 +456,9 @@ | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.TcpClientEx)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.CacheTimeout"> | ||||
|             <summary> | ||||
|             组包缓存时间/ms | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.FrameTime"> | ||||
|             <summary> | ||||
|             帧前时间ms | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.IsCheckMessageId"> | ||||
|             <summary> | ||||
|             检测事务标识符 | ||||
| @@ -441,10 +472,22 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.GetAddressDescription"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SetDataAdapter"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SetDataAdapter(System.Object)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
| @@ -453,12 +496,9 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter"> | ||||
|             <summary> | ||||
|             <inheritdoc/> | ||||
|             ModbusTcpDataHandleAdapter | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.IsCheckMessageId"> | ||||
| @@ -494,14 +534,9 @@ | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(TouchSocket.Sockets.UdpSession)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.FrameTime"> | ||||
|             <summary> | ||||
|             帧前时间ms | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.IsCheckMessageId"> | ||||
|             <summary> | ||||
|             检测事务标识符 | ||||
| @@ -512,13 +547,25 @@ | ||||
|             站号 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.GetAddressDescription"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SetDataAdapter"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SetDataAdapter(System.Object)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||
| @@ -527,9 +574,6 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter"> | ||||
|             <summary> | ||||
|             <inheritdoc/> | ||||
| @@ -549,5 +593,19 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.UnpackResponse(System.Byte[],System.Byte[])"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.PackHelper"> | ||||
|             <summary> | ||||
|             PackHelper | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.PackHelper.ModbusLoadSourceRead``2(ThingsGateway.Foundation.IReadWrite,System.Collections.Generic.List{``1},System.Int32)"> | ||||
|             <summary> | ||||
|             打包变量,添加到<see href="deviceVariableSourceReads"></see> | ||||
|             </summary> | ||||
|             <param name="device"></param> | ||||
|             <param name="deviceVariables"></param> | ||||
|             <param name="MaxPack">最大打包长度</param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|     </members> | ||||
| </doc> | ||||
| @@ -10,7 +10,6 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
| @@ -10,8 +10,6 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||
| 
 | ||||
| /// <summary> | ||||
| @@ -10,10 +10,7 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Text; | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
| 
 | ||||
| @@ -201,7 +198,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|         OnWriteCompleted?.Invoke(itemwrite); | ||||
|     } | ||||
| 
 | ||||
|     internal OperResult AddOpcItem(OpcItem[] items) | ||||
|     internal List<Tuple<OpcItem, int>> AddOpcItem(OpcItem[] items) | ||||
|     { | ||||
|         IntPtr pResults = IntPtr.Zero; | ||||
|         IntPtr pErrors = IntPtr.Zero; | ||||
| @@ -228,7 +225,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|             m_ItemManagement?.AddItems(items.Length, itemDefyArray, out pResults, out pErrors); | ||||
|             IntPtr Pos = pResults; | ||||
|             Marshal.Copy(pErrors, errors, 0, items.Length); | ||||
|             StringBuilder stringBuilder = new(); | ||||
|             List<Tuple<OpcItem, int>> results = new(); | ||||
|             for (int j = 0; j < items.Length; j++) | ||||
|             { | ||||
|                 if (errors[j] == 0) | ||||
| @@ -250,21 +247,10 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     stringBuilder.AppendLine(items[j].ItemID + "添加失败,错误代码:" + errors[j]); | ||||
|                     results.Add(Tuple.Create(items[j], errors[j])); | ||||
|                 } | ||||
|             } | ||||
|             if (stringBuilder.Length > 0) | ||||
|             { | ||||
|                 return new OperResult(stringBuilder.ToString()); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|         } | ||||
|         catch (COMException ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|             return results; | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
| @@ -299,7 +285,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|     /// 组读取 | ||||
|     /// </summary> | ||||
|     /// <exception cref="ExternalException"></exception> | ||||
|     internal OperResult ReadAsync() | ||||
|     internal void ReadAsync() | ||||
|     { | ||||
|         IntPtr pErrors = IntPtr.Zero; | ||||
|         try | ||||
| @@ -316,16 +302,11 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|                 Marshal.Copy(pErrors, PErrors, 0, OpcItems.Count); | ||||
|                 if (PErrors.Any(a => a > 0)) | ||||
|                 { | ||||
|                     return new OperResult("读取错误,错误代码:" + pErrors); | ||||
|                     throw new("读取错误,错误代码:" + pErrors); | ||||
|                 } | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|             else | ||||
|                 return new OperResult("连接无效"); | ||||
|         } | ||||
|         catch (COMException ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|                 throw new("连接无效"); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
| @@ -336,7 +317,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     internal OperResult RemoveItem(OpcItem[] items) | ||||
|     internal List<Tuple<OpcItem, int>> RemoveItem(OpcItem[] items) | ||||
|     { | ||||
|         IntPtr pErrors = IntPtr.Zero; | ||||
|         int[] errors = new int[items.Length]; | ||||
| @@ -356,59 +337,42 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|             { | ||||
|                 Marshal.FreeCoTaskMem(pErrors); | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         StringBuilder stringBuilder = new(); | ||||
|         List<Tuple<OpcItem, int>> results = new(); | ||||
|         for (int i = 0; i < errors.Length; i++) | ||||
|         { | ||||
|             if (errors[i] != 0) | ||||
|             { | ||||
|                 stringBuilder.AppendLine(items[i].ItemID + "移除失败,错误代码:" + errors[i]); | ||||
|                 results.Add(Tuple.Create(items[i], errors[i])); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 OpcItems.Remove(items[i]); | ||||
|             } | ||||
|         } | ||||
|         if (stringBuilder.Length > 0) | ||||
|         { | ||||
|             return new OperResult(stringBuilder.ToString()); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|     internal OperResult Write(object[] values, int[] serverHandle, out int[] errors) | ||||
| 
 | ||||
| 
 | ||||
|     internal List<Tuple<int, int>> Write(object[] values, int[] serverHandle) | ||||
|     { | ||||
|         IntPtr pErrors = IntPtr.Zero; | ||||
|         errors = new int[values.Length]; | ||||
|         var errors = new int[values.Length]; | ||||
|         if (m_Async2IO != null) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 m_SyncIO.Write(values.Length, serverHandle, values, out pErrors); | ||||
|                 Marshal.Copy(pErrors, errors, 0, values.Length); | ||||
|                 StringBuilder stringBuilder = new(); | ||||
|                 List<Tuple<int, int>> results = new(); | ||||
|                 for (int i = 0; i < errors.Length; i++) | ||||
|                 { | ||||
|                     if (errors[i] != 0) | ||||
|                     { | ||||
|                         stringBuilder.AppendLine("写入错误,错误代码:" + errors[i]); | ||||
|                         results.Add(Tuple.Create(serverHandle[i], errors[i])); | ||||
|                     } | ||||
|                 } | ||||
|                 if (stringBuilder.Length > 0) | ||||
|                 { | ||||
|                     return new(stringBuilder.ToString()); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|             } | ||||
|             catch (COMException ex) | ||||
|             { | ||||
|                 return new OperResult(ex); | ||||
|                 return results; | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
| @@ -419,7 +383,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|             return new OperResult("连接无效"); | ||||
|             throw new("连接无效"); | ||||
|     } | ||||
|     protected virtual void Dispose(bool disposing) | ||||
|     { | ||||
| @@ -465,7 +429,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private OperResult ActiveDataChanged(bool active) | ||||
|     private void ActiveDataChanged(bool active) | ||||
|     { | ||||
|         IntPtr pRequestedUpdateRate = IntPtr.Zero; | ||||
|         IntPtr hClientGroup = IntPtr.Zero; | ||||
| @@ -484,11 +448,6 @@ internal class OpcGroup : IOPCDataCallback, IDisposable | ||||
|                                         pDeadband, | ||||
|                                         pLCID, | ||||
|                                         hClientGroup); | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         } | ||||
|         catch (COMException ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
| @@ -10,8 +10,6 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
| @@ -51,58 +49,51 @@ internal class OpcServer : IDisposable | ||||
|         GC.SuppressFinalize(this); | ||||
|     } | ||||
| 
 | ||||
|     internal OperResult<OpcGroup> AddGroup(string groupName) | ||||
|     internal OpcGroup AddGroup(string groupName) | ||||
|     { | ||||
|         return AddGroup(groupName, true, 1000, 0); | ||||
|     } | ||||
| 
 | ||||
|     /// <returns></returns> | ||||
|     internal OperResult<OpcGroup> AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand) | ||||
|     internal OpcGroup AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand) | ||||
|     { | ||||
|         if (null == m_OpcServer || IsConnected == false) | ||||
|             return new OperResult<OpcGroup>("未初始化连接!"); | ||||
|             throw new("未初始化连接!"); | ||||
|         OpcGroup group = new(groupName, active, reqUpdateRate, deadBand); | ||||
|         Guid riid = typeof(IOPCItemMgt).GUID; | ||||
|         try | ||||
|         m_OpcServer?.AddGroup(group.Name, | ||||
|             group.IsActive ? 1 : 0,//IsActive | ||||
|             group.RequestUpdateRate,//RequestedUpdateRate 1000ms | ||||
|             group.ClientGroupHandle, | ||||
|             group.TimeBias.AddrOfPinnedObject(), | ||||
|             group.PercendDeadBand.AddrOfPinnedObject(), | ||||
|             group.LCID, | ||||
|             out group.serverGroupHandle, | ||||
|             out group.revisedUpdateRate, | ||||
|             ref riid, | ||||
|             out group.groupPointer); | ||||
|         if (group.groupPointer != null) | ||||
|         { | ||||
|             m_OpcServer?.AddGroup(group.Name, | ||||
|                 group.IsActive ? 1 : 0,//IsActive | ||||
|                 group.RequestUpdateRate,//RequestedUpdateRate 1000ms | ||||
|                 group.ClientGroupHandle, | ||||
|                 group.TimeBias.AddrOfPinnedObject(), | ||||
|                 group.PercendDeadBand.AddrOfPinnedObject(), | ||||
|                 group.LCID, | ||||
|                 out group.serverGroupHandle, | ||||
|                 out group.revisedUpdateRate, | ||||
|                 ref riid, | ||||
|                 out group.groupPointer); | ||||
|             if (group.groupPointer != null) | ||||
|             { | ||||
|                 group.InitIoInterfaces(group.groupPointer); | ||||
|                 OpcGroups.Add(group); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return new OperResult<OpcGroup>("添加OPC组错误,OPC服务器返回null"); | ||||
|             } | ||||
|             return OperResult.CreateSuccessResult(group); | ||||
|             group.InitIoInterfaces(group.groupPointer); | ||||
|             OpcGroups.Add(group); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         else | ||||
|         { | ||||
|             return new OperResult<OpcGroup>(ex); | ||||
|             throw new("添加OPC组错误,OPC服务器返回null"); | ||||
|         } | ||||
|         return group; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 获取节点 | ||||
|     /// </summary> | ||||
|     internal OperResult<List<BrowseElement>> Browse(string itemId = null) | ||||
|     internal List<BrowseElement> Browse(string itemId = null) | ||||
|     { | ||||
|         lock (this) | ||||
|         { | ||||
|             if (null == m_OpcServer || IsConnected == false) | ||||
|                 return new OperResult<List<BrowseElement>>("未初始化连接!"); | ||||
| 
 | ||||
|                 throw new("未初始化连接!"); | ||||
| 
 | ||||
|             var count = 0; | ||||
|             var moreElements = 0; | ||||
| @@ -118,108 +109,107 @@ internal class OpcServer : IDisposable | ||||
|                            new PropertyID(6), | ||||
|                            new PropertyID(101), | ||||
|                          }; | ||||
|             try | ||||
|             { | ||||
| 
 | ||||
|                 var server = m_OpcServer as IOPCBrowse; | ||||
|                 server.Browse( | ||||
|                          string.IsNullOrEmpty(itemId) ? "" : itemId, | ||||
|                      ref pContinuationPoint, | ||||
|                      int.MaxValue, | ||||
|                         OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL, | ||||
|                           "", | ||||
|                          "", | ||||
|                          0, | ||||
|                          1, | ||||
|                          filterId.Length, | ||||
|                          Interop.GetPropertyIDs(filterId), | ||||
|                      out moreElements, | ||||
|                      out count, | ||||
|                      out pElements); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<List<BrowseElement>>(ex); | ||||
|             } | ||||
| 
 | ||||
|             var server = m_OpcServer as IOPCBrowse; | ||||
|             server.Browse( | ||||
|                      string.IsNullOrEmpty(itemId) ? "" : itemId, | ||||
|                  ref pContinuationPoint, | ||||
|                  int.MaxValue, | ||||
|                     OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL, | ||||
|                       "", | ||||
|                      "", | ||||
|                      0, | ||||
|                      1, | ||||
|                      filterId.Length, | ||||
|                      Interop.GetPropertyIDs(filterId), | ||||
|                  out moreElements, | ||||
|                  out count, | ||||
|                  out pElements); | ||||
|             BrowseElement[] browseElements = Interop.GetBrowseElements(ref pElements, count, true); | ||||
|             string stringUni = Marshal.PtrToStringUni(pContinuationPoint); | ||||
|             Marshal.FreeCoTaskMem(pContinuationPoint); | ||||
|             this.ProcessResults(browseElements, filterId); | ||||
|             return OperResult.CreateSuccessResult(browseElements?.ToList()); | ||||
|             return browseElements?.ToList(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     internal OperResult Connect() | ||||
|     internal void Connect() | ||||
|     { | ||||
|         if (!string.IsNullOrEmpty(Host) && !string.IsNullOrEmpty(Name)) | ||||
|         { | ||||
|             var info = Discovery.OpcDiscovery.GetOpcServer(Name, Host); | ||||
|             if (!info.IsSuccess) | ||||
|             { | ||||
|                 return info; | ||||
|             } | ||||
|             object o = Comn.ComInterop.CreateInstance(info.Content.CLSID, Host); | ||||
|             object o = Comn.ComInterop.CreateInstance(info.CLSID, Host); | ||||
|             if (o == null) | ||||
|             { | ||||
|                 return new(string.Format("{0}{1}无法创建com对象", info.Content.CLSID, Host)); | ||||
|                 throw new(string.Format("{0}{1}无法创建com对象", info.CLSID, Host)); | ||||
|             } | ||||
|             m_OpcServer = (IOPCServer)o; | ||||
|             IsConnected = true; | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         } | ||||
|         return new("应初始化Host与Name"); | ||||
|         else | ||||
|             throw new("应初始化Host与Name"); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 服务器状态 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     internal OperResult<ServerStatus> GetServerStatus() | ||||
|     internal ServerStatus GetServerStatus() | ||||
|     { | ||||
|         if (null == m_OpcServer || IsConnected == false) | ||||
|             return new OperResult<ServerStatus>("未初始化连接!"); | ||||
|         IntPtr statusPtr = IntPtr.Zero; | ||||
|         ServerStatus serverStatus = null; | ||||
|         try | ||||
|         { | ||||
|             if (null == m_OpcServer || IsConnected == false) | ||||
|                 throw new("未初始化连接!"); | ||||
|             IntPtr statusPtr = IntPtr.Zero; | ||||
|             m_OpcServer?.GetStatus(out statusPtr); | ||||
|             OPCSERVERSTATUS status; | ||||
|             ServerStatus = new ServerStatus(); | ||||
|             if (statusPtr != IntPtr.Zero) | ||||
|             { | ||||
|                 try | ||||
| 
 | ||||
|                 object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS)); | ||||
| 
 | ||||
|                 Marshal.FreeCoTaskMem(statusPtr); | ||||
| 
 | ||||
|                 if (o != null) | ||||
|                 { | ||||
|                     object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS)); | ||||
|                     if (null != o) | ||||
|                     { | ||||
|                         status = (OPCSERVERSTATUS)o; | ||||
|                         ServerStatus.Version = status.wMajorVersion.ToString() + "." + status.wMinorVersion.ToString() + "." + status.wBuildNumber.ToString(); | ||||
|                         ServerStatus.ServerState = status.dwServerState; | ||||
|                         ServerStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime); | ||||
|                         ServerStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime); | ||||
|                         ServerStatus.LastUpdateTime = Comn.Convert.FileTimeToDateTime(status.ftLastUpdateTime); | ||||
|                         ServerStatus.VendorInfo = status.szVendorInfo; | ||||
|                     } | ||||
|                     status = (OPCSERVERSTATUS)o; | ||||
|                     serverStatus = new(); | ||||
|                     serverStatus.Version = status.wMajorVersion.ToString() + "." + status.wMinorVersion.ToString() + "." + status.wBuildNumber.ToString(); | ||||
|                     serverStatus.ServerState = status.dwServerState; | ||||
|                     serverStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime); | ||||
|                     serverStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime); | ||||
|                     serverStatus.LastUpdateTime = Comn.Convert.FileTimeToDateTime(status.ftLastUpdateTime); | ||||
|                     serverStatus.VendorInfo = status.szVendorInfo; | ||||
|                     IsConnected = true; | ||||
|                     return OperResult.CreateSuccessResult(ServerStatus); | ||||
| 
 | ||||
|                     return serverStatus; | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 else | ||||
|                 { | ||||
|                     IsConnected = false; | ||||
|                     return new OperResult<ServerStatus>(ex); | ||||
|                     throw new("未知错误"); | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 IsConnected = false; | ||||
|                 return new OperResult<ServerStatus>("获取状态失败"); | ||||
|                 throw new("未知错误"); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         finally | ||||
|         { | ||||
|             IsConnected = false; | ||||
|             return new OperResult<ServerStatus>(ex); | ||||
|             if (serverStatus != null) | ||||
|                 IsConnected = true; | ||||
|             else | ||||
|                 IsConnected = false; | ||||
|             ServerStatus = serverStatus; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     internal void RemoveGroup(OpcGroup group) | ||||
| @@ -10,15 +10,11 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using Newtonsoft.Json.Linq; | ||||
| 
 | ||||
| using System.Collections; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
| 
 | ||||
| using TouchSocket.Core; | ||||
| 
 | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Discovery; | ||||
| /// <summary> | ||||
| /// OpcDiscovery | ||||
| @@ -38,58 +34,58 @@ public class OpcDiscovery | ||||
|     /// <param name="serverName"></param> | ||||
|     /// <param name="host"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static OperResult<ServerInfo> GetOpcServer(string serverName, string host) | ||||
|     internal static ServerInfo GetOpcServer(string serverName, string host) | ||||
|     { | ||||
| 
 | ||||
|         if (string.IsNullOrEmpty(serverName)) | ||||
|         { | ||||
|             throw new("检索失败,需提供OPCName"); | ||||
|         } | ||||
|         ServerInfo result = null; | ||||
|         ServerInfo[] serverInfos = null; | ||||
|         object o_Server = Comn.ComInterop.CreateInstance(OPCEnumCLSID, host); | ||||
|         if (o_Server == null) | ||||
|             throw new("检索失败,请检查是否安装OPC Runtime"); | ||||
|         try | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(serverName)) | ||||
|             { | ||||
|                 return new OperResult<ServerInfo>("检索失败,需提供OPCName"); | ||||
|             } | ||||
|             ServerInfo result = null; | ||||
|             ServerInfo[] serverInfos = null; | ||||
|             object o_Server = Comn.ComInterop.CreateInstance(OPCEnumCLSID, host); | ||||
|             if (o_Server == null) | ||||
|                 return new OperResult<ServerInfo>("检索失败,请检查是否安装OPC Runtime"); | ||||
|             Guid catid = CATID_OPC_DA20; | ||||
| 
 | ||||
|             //两种方式,兼容国产部分OPCServer不支持IOPCServerList2的情况 | ||||
|             try | ||||
|             { | ||||
|                 Guid catid = CATID_OPC_DA20; | ||||
| 
 | ||||
|                 //两种方式,兼容国产部分OPCServer不支持IOPCServerList2的情况 | ||||
|                 try | ||||
|                 { | ||||
|                     IOPCServerList2 m_server2 = (IOPCServerList2)o_Server; | ||||
|                     GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server2, catid); | ||||
|                     if (result == null) | ||||
|                     { | ||||
|                         IOPCServerList m_server = (IOPCServerList)o_Server; | ||||
|                         GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception) | ||||
|                 IOPCServerList2 m_server2 = (IOPCServerList2)o_Server; | ||||
|                 GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server2, catid); | ||||
|                 if (result == null) | ||||
|                 { | ||||
|                     IOPCServerList m_server = (IOPCServerList)o_Server; | ||||
|                     GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid); | ||||
|                 } | ||||
|                 if (result == null) | ||||
|                 { | ||||
|                     return new OperResult<ServerInfo>($"无法创建OPCServer连接,请检查OPC名称是否一致,以下为Host{host}中的OPC列表:" | ||||
|                         + Environment.NewLine + | ||||
|                       JToken.Parse(serverInfos.ToJson()).ToString() | ||||
|                         ); | ||||
|                 } | ||||
|                 return OperResult.CreateSuccessResult(result); | ||||
|             } | ||||
|             finally | ||||
|             catch | ||||
|             { | ||||
|                 Comn.ComInterop.RealseComServer(o_Server); | ||||
|                 o_Server = null; | ||||
|                 IOPCServerList m_server = (IOPCServerList)o_Server; | ||||
|                 GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid); | ||||
|             } | ||||
|             if (result == null) | ||||
|             { | ||||
|                 StringBuilder sb = new StringBuilder(); | ||||
|                 foreach (var item in serverInfos) | ||||
|                 { | ||||
|                     sb.AppendLine(item.ToString()); | ||||
|                 } | ||||
|                 throw new($"无法创建OPCServer连接,请检查OPC名称是否一致,以下为{host}中的OPC列表:" | ||||
|                     + Environment.NewLine + | ||||
|                   sb.ToString() | ||||
|                     ); | ||||
|             } | ||||
|             return result; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         finally | ||||
|         { | ||||
|             return new OperResult<ServerInfo>(ex); | ||||
|             Comn.ComInterop.RealseComServer(o_Server); | ||||
|             o_Server = null; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList m_server, Guid catid) | ||||
| @@ -246,4 +242,15 @@ internal class ServerInfo | ||||
|     internal string Host { get; set; } = string.Empty; | ||||
|     internal string ProgID { get; set; } = string.Empty; | ||||
|     internal string VerIndProgID { get; set; } = string.Empty; | ||||
| 
 | ||||
|     public override string ToString() | ||||
|     { | ||||
|         StringBuilder stringBuilder = new StringBuilder(); | ||||
|         stringBuilder.AppendLine($"{nameof(CLSID)}:{CLSID}"); | ||||
|         stringBuilder.AppendLine($"{nameof(Description)}:{Description}"); | ||||
|         stringBuilder.AppendLine($"{nameof(Host)}:{Host}"); | ||||
|         stringBuilder.AppendLine($"{nameof(ProgID)}:{ProgID}"); | ||||
|         stringBuilder.AppendLine($"{nameof(VerIndProgID)}:{VerIndProgID}"); | ||||
|         return stringBuilder.ToString(); | ||||
|     } | ||||
| } | ||||
| @@ -10,9 +10,6 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA; | ||||
| 
 | ||||
| internal static class CollectionExtensions | ||||
| @@ -23,11 +20,30 @@ internal static class CollectionExtensions | ||||
|     /// <typeparam name="T"></typeparam> | ||||
|     /// <param name="this"></param> | ||||
|     /// <param name="where"></param> | ||||
|     public static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where) | ||||
|     internal static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where) | ||||
|     { | ||||
|         foreach (var obj in @this.Where(where).ToList()) | ||||
|         { | ||||
|             @this.Remove(obj); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 将项目列表分解为特定大小的块 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="T"></typeparam> | ||||
|     /// <param name="source"></param> | ||||
|     /// <param name="chunksize"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static List<List<T>> ChunkTrivialBetter<T>(this IEnumerable<T> source, int chunksize) | ||||
|     { | ||||
|         var pos = 0; | ||||
|         List<List<T>> n = new(); | ||||
|         while (source.Skip(pos).Any()) | ||||
|         { | ||||
|             n.Add(source.Skip(pos).Take(chunksize).ToList()); | ||||
|             pos += chunksize; | ||||
|         } | ||||
|         return n; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @@ -0,0 +1,123 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 | ||||
| //  CSDN博客:https://blog.csdn.net/qq_40374647 | ||||
| //  哔哩哔哩视频:https://space.bilibili.com/94253567 | ||||
| //  Gitee源代码仓库:https://gitee.com/RRQM_Home | ||||
| //  Github源代码仓库:https://github.com/RRQM | ||||
| //  API首页:http://rrqm_home.gitee.io/touchsocket/ | ||||
| //  交流QQ群:234762506 | ||||
| //  感谢您的下载和使用 | ||||
| //------------------------------------------------------------------------------ | ||||
| //------------------------------------------------------------------------------ | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA; | ||||
|  | ||||
| /// <summary> | ||||
| /// DictionaryExtension | ||||
| /// </summary> | ||||
| internal static class DictionaryExtension | ||||
| { | ||||
|     #region 字典扩展 | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 移除满足条件的项目。 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="TKey"></typeparam> | ||||
|     /// <typeparam name="TValue"></typeparam> | ||||
|     /// <param name="pairs"></param> | ||||
|     /// <param name="func"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static int RemoveWhen<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> pairs, Func<KeyValuePair<TKey, TValue>, bool> func) | ||||
|     { | ||||
|         var list = new List<TKey>(); | ||||
|         foreach (var item in pairs) | ||||
|         { | ||||
|             if (func?.Invoke(item) == true) | ||||
|             { | ||||
|                 list.Add(item.Key); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var count = 0; | ||||
|         foreach (var item in list) | ||||
|         { | ||||
|             if (pairs.TryRemove(item, out _)) | ||||
|             { | ||||
|                 count++; | ||||
|             } | ||||
|         } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
| #if NET45_OR_GREATER || NETSTANDARD2_0_OR_GREATER | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 尝试添加 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="Tkey"></typeparam> | ||||
|     /// <typeparam name="TValue"></typeparam> | ||||
|     /// <param name="dictionary"></param> | ||||
|     /// <param name="tkey"></param> | ||||
|     /// <param name="value"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static bool TryAdd<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value) | ||||
|     { | ||||
|         if (dictionary.ContainsKey(tkey)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         dictionary.Add(tkey, value); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 尝试添加 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="Tkey"></typeparam> | ||||
|     /// <typeparam name="TValue"></typeparam> | ||||
|     /// <param name="dictionary"></param> | ||||
|     /// <param name="tkey"></param> | ||||
|     /// <param name="value"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static void AddOrUpdate<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value) | ||||
|     { | ||||
|         if (dictionary.ContainsKey(tkey)) | ||||
|         { | ||||
|             dictionary[tkey] = value; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             dictionary.Add(tkey, value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取值。如果键不存在,则返回默认值。 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="Tkey"></typeparam> | ||||
|     /// <typeparam name="TValue"></typeparam> | ||||
|     /// <param name="dictionary"></param> | ||||
|     /// <param name="tkey"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey) | ||||
|     { | ||||
|         return dictionary.TryGetValue(tkey, out var value) ? value : default; | ||||
|     } | ||||
|     #endregion 字典扩展 | ||||
| } | ||||
| @@ -13,5 +13,5 @@ | ||||
| global using System; | ||||
| global using System.Collections.Generic; | ||||
| global using System.Linq; | ||||
| global using System.Threading.Tasks; | ||||
| global using System.Threading; | ||||
| 
 | ||||
| @@ -10,20 +10,12 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using Newtonsoft.Json.Linq; | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Text; | ||||
| using System.Threading; | ||||
| using System.Timers; | ||||
| 
 | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| 
 | ||||
| using TouchSocket.Core; | ||||
| 
 | ||||
| using Timer = System.Timers.Timer; | ||||
| 
 | ||||
| @@ -38,30 +30,29 @@ public delegate void DataChangedEventHandler(List<ItemReadResult> values); | ||||
| /// <summary> | ||||
| /// OPCDAClient | ||||
| /// </summary> | ||||
| public class OPCDAClient : DisposableObject | ||||
| public class OPCDAClient : IDisposable | ||||
| { | ||||
|     private readonly ILog _logger; | ||||
|     /// <summary> | ||||
|     /// LogAction | ||||
|     /// </summary> | ||||
|     private readonly Action<byte, object, string, Exception> _logAction; | ||||
| 
 | ||||
|     private readonly EasyLock checkLock = new(); | ||||
|     private readonly object checkLock = new(); | ||||
| 
 | ||||
|     private Timer checkTimer; | ||||
| 
 | ||||
|     private bool FirstConnect; | ||||
| 
 | ||||
|     private int IsExit = 1; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 当前保存的需订阅列表 | ||||
|     /// </summary> | ||||
|     private Dictionary<string, List<OpcItem>> ItemDicts = new(); | ||||
| 
 | ||||
|     private OpcServer m_server; | ||||
| 
 | ||||
|     private bool publicConnect; | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="logger"></param> | ||||
|     public OPCDAClient(ILog logger) | ||||
|     public OPCDAClient(Action<byte, object, string, Exception> logAction) | ||||
|     { | ||||
| #if (NET6_0_OR_GREATER) | ||||
|         if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||||
| @@ -69,7 +60,7 @@ public class OPCDAClient : DisposableObject | ||||
|             throw new NotSupportedException("不支持非windows系统"); | ||||
|         } | ||||
| #endif | ||||
|         _logger = logger; | ||||
|         _logAction = logAction; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
| @@ -87,68 +78,56 @@ public class OPCDAClient : DisposableObject | ||||
|     /// </summary> | ||||
|     public OPCNode OPCNode { get; private set; } | ||||
|     private List<OpcGroup> Groups => m_server.OpcGroups; | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 添加节点,需要在连接成功后执行 | ||||
|     /// </summary> | ||||
|     /// <param name="items">组名称/变量节点,注意每次添加的组名称不能相同</param> | ||||
|     public OperResult AddItems(Dictionary<string, List<OpcItem>> items) | ||||
|     public void AddItems(Dictionary<string, List<OpcItem>> items) | ||||
|     { | ||||
|         try | ||||
|         if (IsExit == 1) throw new("对象已释放"); | ||||
|         foreach (var item in items) | ||||
|         { | ||||
|             if (IsExit == 1) return new("对象已释放"); | ||||
|             StringBuilder stringBuilder = new(); | ||||
|             foreach (var item in items) | ||||
|             if (IsExit == 1) throw new("对象已释放"); | ||||
|             try | ||||
|             { | ||||
|                 if (IsExit == 1) return new("对象已释放"); | ||||
|                 var subscription = m_server.AddGroup(item.Key, true, OPCNode.UpdateRate, OPCNode.DeadBand); | ||||
|                 if (subscription.IsSuccess) | ||||
|                 { | ||||
|                     subscription.Content.ActiveSubscribe = OPCNode.ActiveSubscribe; | ||||
|                     subscription.Content.OnDataChanged += Subscription_OnDataChanged; | ||||
|                     subscription.Content.OnReadCompleted += Subscription_OnDataChanged; | ||||
|                 subscription.ActiveSubscribe = OPCNode.ActiveSubscribe; | ||||
|                 subscription.OnDataChanged += Subscription_OnDataChanged; | ||||
|                 subscription.OnReadCompleted += Subscription_OnDataChanged; | ||||
| 
 | ||||
|                     var result = subscription.Content.AddOpcItem(item.Value.ToArray()); | ||||
|                     if (!result.IsSuccess) | ||||
|                 var result = subscription.AddOpcItem(item.Value.ToArray()); | ||||
|                 StringBuilder stringBuilder = new StringBuilder(); | ||||
|                 if (result.Count > 0) | ||||
|                 { | ||||
|                     foreach (var item1 in result) | ||||
|                     { | ||||
|                         stringBuilder.AppendLine("添加变量失败" + result.Message); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         ItemDicts.AddOrUpdate(item.Key, item.Value); | ||||
|                         _logger?.Debug($"添加成功"); | ||||
|                         stringBuilder.Append($"{item1.Item1.ItemID}:{item1.Item2}"); | ||||
|                     } | ||||
|                     _logAction?.Invoke(3, this, $"添加变量失败:{stringBuilder}", null); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     stringBuilder.AppendLine("添加组失败" + subscription.Message); | ||||
|                     ItemDicts.AddOrUpdate(item.Key, item.Value.Where(a => !result.Select(b => b.Item1).Contains(a)).ToList()); | ||||
|                 } | ||||
|             } | ||||
|             for (int i = 0; i < Groups?.Count; i++) | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 var group = Groups[i]; | ||||
|                 if (group != null) | ||||
|                 { | ||||
|                     if (group.OpcItems.Count == 0) | ||||
|                     { | ||||
|                         ItemDicts.Remove(group.Name); | ||||
|                         m_server.RemoveGroup(group); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (stringBuilder.Length > 0) | ||||
|             { | ||||
|                 return new(stringBuilder.ToString()); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|                 _logAction?.Invoke(3, this, $"添加组失败:{ex.Message}", ex); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         for (int i = 0; i < Groups?.Count; i++) | ||||
|         { | ||||
|             return new(ex); | ||||
|             var group = Groups[i]; | ||||
|             if (group != null) | ||||
|             { | ||||
|                 if (group.OpcItems.Count == 0) | ||||
|                 { | ||||
|                     ItemDicts.Remove(group.Name); | ||||
|                     m_server.RemoveGroup(group); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
| @@ -159,8 +138,7 @@ public class OPCDAClient : DisposableObject | ||||
|     public Dictionary<string, List<OpcItem>> AddItemsWithSave(List<string> items) | ||||
|     { | ||||
|         int i = 0; | ||||
|         ItemDicts = items.ToList().ConvertAll(o => new OpcItem(o) | ||||
|           ).ChunkTrivialBetter(OPCNode.GroupSize).ToDictionary(a => "default" + (i++)); | ||||
|         ItemDicts = items.ToList().ConvertAll(o => new OpcItem(o)).ChunkTrivialBetter(OPCNode.GroupSize).ToDictionary(a => "default" + (i++)); | ||||
|         return ItemDicts; | ||||
|     } | ||||
| 
 | ||||
| @@ -169,9 +147,9 @@ public class OPCDAClient : DisposableObject | ||||
|     /// </summary> | ||||
|     public void Connect() | ||||
|     { | ||||
|         publicConnect = true; | ||||
|         Interlocked.CompareExchange(ref IsExit, 0, 1); | ||||
|         PrivateConnect(); | ||||
|         FirstConnect = true; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
| @@ -183,12 +161,26 @@ public class OPCDAClient : DisposableObject | ||||
|         PrivateDisconnect(); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             PrivateDisconnect(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex); | ||||
|         } | ||||
|         Interlocked.CompareExchange(ref IsExit, 1, 0); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 浏览节点 | ||||
|     /// </summary> | ||||
|     /// <param name="itemId"></param> | ||||
|     /// <returns></returns> | ||||
|     public OperResult<List<BrowseElement>> GetBrowseElements(string itemId = null) | ||||
|     public List<BrowseElement> GetBrowseElements(string itemId = null) | ||||
|     { | ||||
|         return this.m_server?.Browse(itemId); | ||||
|     } | ||||
| @@ -197,7 +189,7 @@ public class OPCDAClient : DisposableObject | ||||
|     /// 获取服务状态 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public OperResult<ServerStatus> GetServerStatus() | ||||
|     public ServerStatus GetServerStatus() | ||||
|     { | ||||
|         return this.m_server?.GetServerStatus(); | ||||
|     } | ||||
| @@ -211,11 +203,18 @@ public class OPCDAClient : DisposableObject | ||||
|         if (node != null) | ||||
|             OPCNode = node; | ||||
|         checkTimer?.Stop(); | ||||
|         checkTimer?.SafeDispose(); | ||||
|         checkTimer?.Dispose(); | ||||
|         checkTimer = new Timer(OPCNode.CheckRate * 60 * 1000); | ||||
|         checkTimer.Elapsed += CheckTimer_Elapsed; | ||||
|         checkTimer.Start(); | ||||
|         m_server?.SafeDispose(); | ||||
|         try | ||||
|         { | ||||
|             m_server?.Dispose(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex); | ||||
|         } | ||||
|         m_server = new OpcServer(OPCNode.OPCName, OPCNode.OPCIP); | ||||
|     } | ||||
| 
 | ||||
| @@ -224,32 +223,18 @@ public class OPCDAClient : DisposableObject | ||||
|     /// </summary> | ||||
|     /// <param name="groupName">组名称,值为null时读取全部组</param> | ||||
|     /// <returns></returns> | ||||
|     public OperResult ReadItemsWithGroup(string groupName = null) | ||||
|     public void ReadItemsWithGroup(string groupName = null) | ||||
|     { | ||||
|         try | ||||
|         PrivateConnect(); | ||||
|         { | ||||
|             if (PrivateConnect()) | ||||
|             var groups = groupName != null ? Groups.Where(a => a.Name == groupName) : Groups; | ||||
|             foreach (var group in groups) | ||||
|             { | ||||
|                 var groups = groupName != null ? Groups.Where(a => a.Name == groupName) : Groups; | ||||
|                 foreach (var group in groups) | ||||
|                 if (group.OpcItems.Count > 0) | ||||
|                 { | ||||
|                     if (group.OpcItems.Count > 0) | ||||
|                     { | ||||
|                         return group.ReadAsync(); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         return new OperResult("不存在任何变量"); | ||||
|                     } | ||||
|                     group.ReadAsync(); | ||||
|                 } | ||||
|                 return new OperResult("不存在任何变量"); | ||||
|             } | ||||
|             return new OperResult("未初始化连接"); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -263,23 +248,11 @@ public class OPCDAClient : DisposableObject | ||||
|         { | ||||
|             if (IsExit == 1) return; | ||||
|             var opcGroups = Groups.Where(it => it.OpcItems.Any(a => a.ItemID == item)); | ||||
|             if (!opcGroups.Any()) | ||||
|             { | ||||
|                 _logger.Warning("找不到变量" + item); | ||||
|                 continue; | ||||
|             } | ||||
|             foreach (var opcGroup in opcGroups) | ||||
|             { | ||||
|                 var tag = opcGroup.OpcItems.Where(a => item == a.ItemID); | ||||
|                 var result = opcGroup.RemoveItem(tag.ToArray()); | ||||
|                 if (!result.IsSuccess) | ||||
|                 { | ||||
|                     _logger.Warning($"移除变量{item}-" + result.Message); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     _logger?.Debug($"移除变量{item}成功"); | ||||
|                 } | ||||
| 
 | ||||
|                 if (opcGroup.OpcItems.Count == 0) | ||||
|                 { | ||||
|                     opcGroup.OnDataChanged -= Subscription_OnDataChanged; | ||||
| @@ -288,7 +261,7 @@ public class OPCDAClient : DisposableObject | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ItemDicts[opcGroup.Name].RemoveWhere(a => tag.Contains(a)); | ||||
|                     ItemDicts[opcGroup.Name].RemoveWhere(a => tag.Contains(a) && !result.Select(b => b.Item1).Contains(a)); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
| @@ -302,83 +275,94 @@ public class OPCDAClient : DisposableObject | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 写入值 | ||||
|     /// 批量写入值 | ||||
|     /// </summary> | ||||
|     /// <param name="valueName">写入</param> | ||||
|     /// <param name="value"></param> | ||||
|     /// <returns></returns> | ||||
|     public OperResult WriteItem(string valueName, JToken value) | ||||
|     public Dictionary<string, Tuple<bool, string>> WriteItem(Dictionary<string, object> writeInfos) | ||||
|     { | ||||
|         if (PrivateConnect()) | ||||
|         PrivateConnect(); | ||||
|         Dictionary<string, Tuple<bool, string>> results = new(); | ||||
| 
 | ||||
|         var valueGroup = writeInfos.GroupBy(itemId => | ||||
|           { | ||||
|               var group = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == itemId.Key)); | ||||
|               return group; | ||||
|           }).ToList(); | ||||
| 
 | ||||
|         foreach (var item1 in valueGroup) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var group = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == valueName)); | ||||
|                 if (group == null) | ||||
|                     return new OperResult("不存在该变量" + valueName); | ||||
|                 var item = group.OpcItems.Where(it => it.ItemID == valueName).FirstOrDefault(); | ||||
|                 int[] serverHandle = new int[1] { item.ServerHandle }; | ||||
|                 int[] PErrors = new int[1]; | ||||
|                 var jtoken = value; | ||||
|                 var rank = jtoken.CalculateActualValueRank(); | ||||
|                 object rawWriteValue; | ||||
|                 switch (rank) | ||||
|                 if (item1.Key == null) | ||||
|                 { | ||||
|                     case -1: | ||||
|                         rawWriteValue = ((JValue)jtoken).Value; | ||||
|                         break; | ||||
|                     default: | ||||
|                         var jarray = ((JArray)jtoken); | ||||
|                         rawWriteValue = jarray.Select(j => (object)j).ToArray(); | ||||
|                         break; | ||||
|                 } | ||||
| 
 | ||||
|                 object[] Value = new object[1] { rawWriteValue }; | ||||
|                 var result = group.Write(Value, serverHandle, out PErrors); | ||||
|                 return result; | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult(ex.Message); | ||||
|             } | ||||
|         } | ||||
|         return new OperResult(); | ||||
|     } | ||||
| 
 | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         PrivateDisconnect(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
| 
 | ||||
|     private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e) | ||||
|     { | ||||
|         if (checkLock.IsWaitting) return; | ||||
|         checkLock.Wait(); | ||||
|         try | ||||
|         { | ||||
|             if (IsExit == 0) | ||||
|             { | ||||
|                 var status = m_server.GetServerStatus(); | ||||
|                 if (status.IsSuccess) | ||||
|                 { | ||||
|                     _logger?.Trace(OPCNode.ToString() + "OPC状态检查正常!"); | ||||
|                     foreach (var item2 in item1) | ||||
|                     { | ||||
|                         results.AddOrUpdate(item2.Key, Tuple.Create(true, $"不存在该变量{item2.Key}")); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if (IsExit == 0 && FirstConnect) | ||||
|                     List<int> serverHandles = new(); | ||||
|                     Dictionary<int, OpcItem> handleItems = new(); | ||||
|                     List<object> values = new(); | ||||
|                     foreach (var item2 in item1) | ||||
|                     { | ||||
|                         if (PrivateConnect()) | ||||
|                         { | ||||
|                             _logger?.Warning(OPCNode.ToString() + "OPC重新链接成功!"); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
| 
 | ||||
|                         } | ||||
|                         var opcItem = item1.Key.OpcItems.Where(it => it.ItemID == item2.Key).FirstOrDefault(); | ||||
|                         serverHandles.Add(opcItem.ServerHandle); | ||||
|                         handleItems.AddOrUpdate(opcItem.ServerHandle, opcItem); | ||||
|                         var rawWriteValue = item2.Value; | ||||
|                         values.Add(rawWriteValue); | ||||
|                     } | ||||
| 
 | ||||
|                     var result = item1.Key.Write(values.ToArray(), serverHandles.ToArray()); | ||||
|                     var data = item1.ToList(); | ||||
|                     foreach (var item2 in result) | ||||
|                     { | ||||
|                         results.AddOrUpdate(handleItems[item2.Item1].ItemID, Tuple.Create(true, $"错误代码{item2.Item2}")); | ||||
|                     } | ||||
|                 } | ||||
|                 foreach (var item2 in item1) | ||||
|                 { | ||||
|                     results.AddOrUpdate(item2.Key, Tuple.Create(false, $"成功")); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 var keys = writeInfos.Keys.ToList(); | ||||
|                 foreach (var item in keys) | ||||
|                 { | ||||
|                     results.AddOrUpdate(item, Tuple.Create(true, ex.Message)); | ||||
|                 } | ||||
|                 return results; | ||||
|             } | ||||
|         } | ||||
|         return results; | ||||
|     } | ||||
|     private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e) | ||||
|     { | ||||
|         lock (checkLock) | ||||
|         { | ||||
| 
 | ||||
|             if (IsExit == 0) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var status = m_server.GetServerStatus(); | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     if (IsExit == 0 && publicConnect) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             PrivateConnect(); | ||||
|                             _logAction?.Invoke(1, this, $"重新链接成功", null); | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             _logAction?.Invoke(3, this, $"重新链接失败:{ex.Message}", ex); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
| @@ -387,103 +371,81 @@ public class OPCDAClient : DisposableObject | ||||
|                 timeer.Enabled = false; | ||||
|                 timeer.Stop(); | ||||
|             } | ||||
|         } | ||||
|         finally { checkLock.Release(); } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void PrivateAddItems() | ||||
|     { | ||||
|         var result = AddItems(ItemDicts); | ||||
|         if (!result.IsSuccess) | ||||
|         try | ||||
|         { | ||||
|             _logger.Warning(result.Message); | ||||
|             AddItems(ItemDicts); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logAction?.Invoke(3, this, $"添加点位失败:{ex.Message}", ex); | ||||
|         } | ||||
|     } | ||||
|     private bool PrivateConnect() | ||||
|     private void PrivateConnect() | ||||
|     { | ||||
|         lock (this) | ||||
|         { | ||||
|             try | ||||
| 
 | ||||
|             if (m_server?.IsConnected == true) | ||||
|             { | ||||
|                 if (m_server?.IsConnected == true) | ||||
|                 try | ||||
|                 { | ||||
|                     var status = m_server.GetServerStatus(); | ||||
|                     if (!status.IsSuccess) | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var status1 = m_server.GetServerStatus(); | ||||
|                         if (!status1.IsSuccess) | ||||
|                         { | ||||
|                             _logger?.Error(status1.Message); | ||||
|                             //失败重新连接 | ||||
|                             try | ||||
|                             { | ||||
|                                 //disconnect(); | ||||
|                                 Init(OPCNode); | ||||
|                                 _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 正在连接"); | ||||
|                                 var result = m_server?.Connect(); | ||||
|                                 if (result.IsSuccess) | ||||
|                                 { | ||||
|                                     _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 连接成功"); | ||||
|                                     PrivateAddItems(); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     _logger?.Error(result.Message); | ||||
|                                     return IsConnected; | ||||
|                                 } | ||||
|                             } | ||||
|                             catch (Exception ex2) | ||||
|                             { | ||||
|                                 _logger?.Exception(ex2); | ||||
|                                 return IsConnected; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //disconnect(); | ||||
|                     Init(OPCNode); | ||||
|                     _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 正在连接"); | ||||
|                     var result = m_server?.Connect(); | ||||
|                     if (result.IsSuccess) | ||||
|                     catch | ||||
|                     { | ||||
|                         _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 连接成功"); | ||||
| 
 | ||||
|                         Init(OPCNode); | ||||
|                         m_server?.Connect(); | ||||
|                         _logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 连接成功", null); | ||||
|                         PrivateAddItems(); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _logger?.Error(result.Message); | ||||
|                         return IsConnected; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             else | ||||
|             { | ||||
|                 _logger?.Exception(OPCNode.ToString(), ex); | ||||
|                 return false; | ||||
|                 Init(OPCNode); | ||||
|                 m_server?.Connect(); | ||||
|                 _logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 连接成功", null); | ||||
|                 PrivateAddItems(); | ||||
|             } | ||||
|             return IsConnected; | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void PrivateDisconnect() | ||||
|     { | ||||
|         lock (this) | ||||
|         { | ||||
|             if (IsConnected) | ||||
|                 _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 断开连接"); | ||||
|             checkTimer.Enabled = false; | ||||
|             checkTimer.Stop(); | ||||
|                 _logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 断开连接", null); | ||||
|             if (checkTimer != null) | ||||
|             { | ||||
|                 checkTimer.Enabled = false; | ||||
|                 checkTimer.Stop(); | ||||
|             } | ||||
| 
 | ||||
|             try | ||||
|             { | ||||
|                 m_server?.SafeDispose(); | ||||
|                 m_server?.Dispose(); | ||||
|                 m_server = null; | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 _logger?.Exception(ToString(), ex); | ||||
|                 _logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -21,7 +21,7 @@ public class OPCNode | ||||
|     /// <summary> | ||||
|     /// 是否订阅 | ||||
|     /// </summary> | ||||
|     [Description("ActiveSubscribe")] | ||||
|     [Description("订阅")] | ||||
|     public bool ActiveSubscribe { get; set; } = true; | ||||
|     /// <summary> | ||||
|     /// 内部检测重连间隔/min | ||||
| @@ -0,0 +1,3 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
| </Project> | ||||
| @@ -441,7 +441,7 @@ | ||||
|             当前保存的需订阅列表 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.#ctor(TouchSocket.Core.ILog)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.#ctor(ThingsGateway.Foundation.ILog)"> | ||||
|             <summary> | ||||
|             <inheritdoc/> | ||||
|             </summary> | ||||
| @@ -520,12 +520,10 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.ToString"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.WriteItem(System.String,Newtonsoft.Json.Linq.JToken)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.WriteItem(System.Collections.Generic.Dictionary{System.String,Newtonsoft.Json.Linq.JToken})"> | ||||
|             <summary> | ||||
|             写入值 | ||||
|             批量写入值 | ||||
|             </summary> | ||||
|             <param name="valueName">写入</param> | ||||
|             <param name="value"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.Dispose(System.Boolean)"> | ||||
| @@ -34,6 +34,11 @@ public class OPCNode | ||||
|     /// </summary> | ||||
|     [Description("登录密码")] | ||||
|     public string Password { get; set; } | ||||
|     /// <summary> | ||||
|     /// 检查域 | ||||
|     /// </summary> | ||||
|     [Description("检查域")] | ||||
|     public bool CheckDomain { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// 更新间隔 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks> | ||||
|  | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.4.372.56" /> | ||||
| 		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.4.372.56" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| </Project> | ||||
| @@ -38,6 +38,14 @@ | ||||
|             The references found. Null if an error occurred. | ||||
|             </returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.PrepareBrowseNext(Opc.Ua.BrowseResultCollection)"> | ||||
|             <summary> | ||||
|             Create the continuation point collection from the browse result | ||||
|             collection for the BrowseNext service. | ||||
|             </summary> | ||||
|             <param name="browseResultCollection">The browse result collection to use.</param> | ||||
|             <returns>The collection of continuation points for the BrowseNext service.</returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.BrowseAsync(Opc.Ua.Client.ISession,Opc.Ua.BrowseDescriptionCollection,System.Boolean,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             浏览地址空间 | ||||
| @@ -45,7 +53,7 @@ | ||||
|             <param name="session"></param> | ||||
|             <param name="nodesToBrowse"></param> | ||||
|             <param name="throwOnError"></param> | ||||
|             <param name="token"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|             <exception cref="T:Opc.Ua.ServiceResultException"></exception> | ||||
|         </member> | ||||
| @@ -56,7 +64,7 @@ | ||||
|             <param name="session"></param> | ||||
|             <param name="nodeToBrowse"></param> | ||||
|             <param name="throwOnError"></param> | ||||
|             <param name="token"></param> | ||||
|             <param name="cancellationToken"></param> | ||||
|             <returns></returns> | ||||
|             <exception cref="T:Opc.Ua.ServiceResultException"></exception> | ||||
|         </member> | ||||
| @@ -161,22 +169,12 @@ | ||||
|             <summary> | ||||
|             解析获取DataValue | ||||
|             </summary> | ||||
|             <param name="Context"></param> | ||||
|             <param name="dataTypeId"></param> | ||||
|             <param name="builtInType"></param> | ||||
|             <param name="valueRank"></param> | ||||
|             <param name="json"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.DecoderObject(Opc.Ua.IServiceMessageContext,Opc.Ua.NodeId,Opc.Ua.BuiltInType,System.Int32,Newtonsoft.Json.Linq.JToken)"> | ||||
|             <summary> | ||||
|             解析获取object | ||||
|             </summary> | ||||
|             <param name="Context"></param> | ||||
|             <param name="dataTypeId"></param> | ||||
|             <param name="builtInType"></param> | ||||
|             <param name="valueRank"></param> | ||||
|             <param name="json"></param> | ||||
|             <returns></returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.DecodeRawData(Opc.Ua.JsonDecoder,Opc.Ua.BuiltInType,System.Int32,System.String)"> | ||||
| @@ -227,6 +225,11 @@ | ||||
|             登录密码 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.CheckDomain"> | ||||
|             <summary> | ||||
|             检查域 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.UpdateRate"> | ||||
|             <summary> | ||||
|             更新间隔 | ||||
| @@ -296,7 +299,12 @@ | ||||
|             当前的订阅组,组名称/组 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.#ctor(TouchSocket.Core.ILog)"> | ||||
|         <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReConnectHandler"> | ||||
|             <summary> | ||||
|             SessionReconnectHandler | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.#ctor(ThingsGateway.Foundation.ILog)"> | ||||
|             <summary> | ||||
|             默认的构造函数,实例化一个新的OPC UA类 | ||||
|             </summary> | ||||
| @@ -326,9 +334,9 @@ | ||||
|             当前活动会话。 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.AddSubscription(System.String,System.String[])"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.AddSubscriptionAsync(System.String,System.String[])"> | ||||
|             <summary> | ||||
|             新增订阅,需要指定订阅的关键字,订阅的tag名数组,以及回调方法 | ||||
|             新增订阅,需要指定订阅组名称,订阅的tag名数组 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.RemoveAllSubscription"> | ||||
| @@ -367,7 +375,7 @@ | ||||
|             <param name="end">结束时间</param> | ||||
|             <param name="count">读取的个数</param> | ||||
|             <param name="containBound">是否包含边界</param> | ||||
|             <param name="token">token</param> | ||||
|             <param name="cancellationToken">cancellationToken</param> | ||||
|             <returns>读取的数据列表</returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ConnectAsync"> | ||||
| @@ -380,12 +388,18 @@ | ||||
|             断开连接。 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ConnectAsync(System.String)"> | ||||
|             <summary> | ||||
|             Creates a new session. | ||||
|             </summary> | ||||
|             <returns>The new session object.</returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadJTokenValueAsync(System.String[],System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             从服务器读取值 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.WriteNodeAsync(System.String,Newtonsoft.Json.Linq.JToken,System.Threading.CancellationToken)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.WriteNodeAsync(System.Collections.Generic.Dictionary{System.String,Newtonsoft.Json.Linq.JToken},System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             异步写opc标签 | ||||
|             </summary> | ||||
| @@ -395,7 +409,7 @@ | ||||
|             从服务器读取值 | ||||
|             </summary> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadNode(System.String,System.Boolean)"> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadNodeAsync(System.String,System.Boolean,System.Threading.CancellationToken)"> | ||||
|             <summary> | ||||
|             从服务器或缓存读取节点 | ||||
|             </summary> | ||||
| @@ -420,12 +434,6 @@ | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.Dispose(System.Boolean)"> | ||||
|             <inheritdoc/> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ConnectAsync(System.String)"> | ||||
|             <summary> | ||||
|             Creates a new session. | ||||
|             </summary> | ||||
|             <returns>The new session object.</returns> | ||||
|         </member> | ||||
|         <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.Server_ReconnectComplete(System.Object,System.EventArgs)"> | ||||
|             <summary> | ||||
|             连接处理器连接事件处理完成。 | ||||
| @@ -10,10 +10,6 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCUA; | ||||
| 
 | ||||
| internal static class CollectionExtensions | ||||
| @@ -24,7 +20,7 @@ internal static class CollectionExtensions | ||||
|     /// <typeparam name="T"></typeparam> | ||||
|     /// <param name="this"></param> | ||||
|     /// <param name="where"></param> | ||||
|     public static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where) | ||||
|     internal static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where) | ||||
|     { | ||||
|         foreach (var obj in @this.Where(where).ToList()) | ||||
|         { | ||||
| @@ -39,7 +35,7 @@ internal static class CollectionExtensions | ||||
|     /// <param name="source"></param> | ||||
|     /// <param name="selector"></param> | ||||
|     /// <returns></returns> | ||||
|     public static Task<TResult[]> SelectAsync<T, TResult>(this IEnumerable<T> source, Func<T, Task<TResult>> selector) | ||||
|     internal static Task<TResult[]> SelectAsync<T, TResult>(this IEnumerable<T> source, Func<T, Task<TResult>> selector) | ||||
|     { | ||||
|         return Task.WhenAll(source.Select(selector)); | ||||
|     } | ||||
| @@ -0,0 +1,123 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 | ||||
| //  CSDN博客:https://blog.csdn.net/qq_40374647 | ||||
| //  哔哩哔哩视频:https://space.bilibili.com/94253567 | ||||
| //  Gitee源代码仓库:https://gitee.com/RRQM_Home | ||||
| //  Github源代码仓库:https://github.com/RRQM | ||||
| //  API首页:http://rrqm_home.gitee.io/touchsocket/ | ||||
| //  交流QQ群:234762506 | ||||
| //  感谢您的下载和使用 | ||||
| //------------------------------------------------------------------------------ | ||||
| //------------------------------------------------------------------------------ | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCUA; | ||||
|  | ||||
| /// <summary> | ||||
| /// DictionaryExtension | ||||
| /// </summary> | ||||
| internal static class DictionaryExtension | ||||
| { | ||||
|     #region 字典扩展 | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 移除满足条件的项目。 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="TKey"></typeparam> | ||||
|     /// <typeparam name="TValue"></typeparam> | ||||
|     /// <param name="pairs"></param> | ||||
|     /// <param name="func"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static int RemoveWhen<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> pairs, Func<KeyValuePair<TKey, TValue>, bool> func) | ||||
|     { | ||||
|         var list = new List<TKey>(); | ||||
|         foreach (var item in pairs) | ||||
|         { | ||||
|             if (func?.Invoke(item) == true) | ||||
|             { | ||||
|                 list.Add(item.Key); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         var count = 0; | ||||
|         foreach (var item in list) | ||||
|         { | ||||
|             if (pairs.TryRemove(item, out _)) | ||||
|             { | ||||
|                 count++; | ||||
|             } | ||||
|         } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
| #if NET45_OR_GREATER || NETSTANDARD2_0_OR_GREATER | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 尝试添加 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="Tkey"></typeparam> | ||||
|     /// <typeparam name="TValue"></typeparam> | ||||
|     /// <param name="dictionary"></param> | ||||
|     /// <param name="tkey"></param> | ||||
|     /// <param name="value"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static bool TryAdd<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value) | ||||
|     { | ||||
|         if (dictionary.ContainsKey(tkey)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         dictionary.Add(tkey, value); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| #endif | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 尝试添加 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="Tkey"></typeparam> | ||||
|     /// <typeparam name="TValue"></typeparam> | ||||
|     /// <param name="dictionary"></param> | ||||
|     /// <param name="tkey"></param> | ||||
|     /// <param name="value"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static void AddOrUpdate<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value) | ||||
|     { | ||||
|         if (dictionary.ContainsKey(tkey)) | ||||
|         { | ||||
|             dictionary[tkey] = value; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             dictionary.Add(tkey, value); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取值。如果键不存在,则返回默认值。 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="Tkey"></typeparam> | ||||
|     /// <typeparam name="TValue"></typeparam> | ||||
|     /// <param name="dictionary"></param> | ||||
|     /// <param name="tkey"></param> | ||||
|     /// <returns></returns> | ||||
|     internal static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey) | ||||
|     { | ||||
|         return dictionary.TryGetValue(tkey, out var value) ? value : default; | ||||
|     } | ||||
|     #endregion 字典扩展 | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user