mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-25 12:43:09 +08:00 
			
		
		
		
	Compare commits
	
		
			38 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a533286658 | ||
|   | e59f91cd82 | ||
|   | 5f8b85d8a4 | ||
|   | 47c7b88436 | ||
|   | 90006782f2 | ||
|   | c3d49cbe70 | ||
|   | 112323a360 | ||
|   | 9d08c90fda | ||
|   | 602d24deec | ||
|   | a2b9f66785 | ||
|   | 7cbf289b50 | ||
|   | 4097da79a5 | ||
|   | 91b7ae554f | ||
|   | 3121aa2542 | ||
|   | 4bf895e6e1 | ||
|   | 0c5489e920 | ||
|   | d63c3aaa80 | ||
|   | 4f188ea6cc | ||
|   | acb17018ae | ||
|   | 2affe2988d | ||
|   | 4174dd2206 | ||
|   | e1c492f238 | ||
|   | fb08e34fa3 | ||
|   | a1793a0afe | ||
|   | 4da9763b49 | ||
|   | 81e0918bd0 | ||
|   | c1e064f06d | ||
|   | 1c52be8b47 | ||
|   | bcd82055ca | ||
|   | c47d95d170 | ||
|   | 3e62f1ad51 | ||
|   | 8dcae973ef | ||
|   | 4cf35f7294 | ||
|   | 94c77d151b | ||
|   | 7f600e2b4b | ||
|   | c809d0ba87 | ||
|   | 50f038ec89 | ||
|   | 9199a255a2 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -363,4 +363,6 @@ MigrationBackup/ | ||||
| FodyWeavers.xsd | ||||
|  | ||||
|  | ||||
| /framework/*pro* | ||||
| /framework/*Pro* | ||||
|  | ||||
|   | ||||
| @@ -1,103 +0,0 @@ | ||||
| [*.cs] | ||||
|  | ||||
| # CA1848: 使用 LoggerMessage 委托 | ||||
| dotnet_diagnostic.CA1848.severity = none | ||||
|  | ||||
| # CA2254: 模板应为静态表达式 | ||||
| dotnet_diagnostic.CA2254.severity = suggestion | ||||
|  | ||||
| [*.cs] | ||||
| #### 命名样式 #### | ||||
|  | ||||
| # 命名规则 | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i | ||||
|  | ||||
| dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.types_should_be_pascal_case.symbols = types | ||||
| dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members | ||||
| dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case | ||||
|  | ||||
| # 符号规范 | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.types.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||
| dotnet_naming_symbols.non_field_members.required_modifiers =  | ||||
|  | ||||
| # 命名样式 | ||||
|  | ||||
| dotnet_naming_style.begins_with_i.required_prefix = I | ||||
| dotnet_naming_style.begins_with_i.required_suffix =  | ||||
| dotnet_naming_style.begins_with_i.word_separator =  | ||||
| dotnet_naming_style.begins_with_i.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix =  | ||||
| dotnet_naming_style.pascal_case.required_suffix =  | ||||
| dotnet_naming_style.pascal_case.word_separator =  | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.pascal_case.required_prefix =  | ||||
| dotnet_naming_style.pascal_case.required_suffix =  | ||||
| dotnet_naming_style.pascal_case.word_separator =  | ||||
| dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||
|  | ||||
| [*.vb] | ||||
| #### 命名样式 #### | ||||
|  | ||||
| # 命名规则 | ||||
|  | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface | ||||
| dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始 | ||||
|  | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 | ||||
| dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 | ||||
|  | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 | ||||
| dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 | ||||
|  | ||||
| # 符号规范 | ||||
|  | ||||
| dotnet_naming_symbols.interface.applicable_kinds = interface | ||||
| dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.interface.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum | ||||
| dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.类型.required_modifiers =  | ||||
|  | ||||
| dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method | ||||
| dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||
| dotnet_naming_symbols.非字段成员.required_modifiers =  | ||||
|  | ||||
| # 命名样式 | ||||
|  | ||||
| dotnet_naming_style.以_i_开始.required_prefix = I | ||||
| dotnet_naming_style.以_i_开始.required_suffix =  | ||||
| dotnet_naming_style.以_i_开始.word_separator =  | ||||
| dotnet_naming_style.以_i_开始.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_prefix =  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_suffix =  | ||||
| dotnet_naming_style.帕斯卡拼写法.word_separator =  | ||||
| dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case | ||||
|  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_prefix =  | ||||
| dotnet_naming_style.帕斯卡拼写法.required_suffix =  | ||||
| dotnet_naming_style.帕斯卡拼写法.word_separator =  | ||||
| dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case | ||||
							
								
								
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -1,63 +0,0 @@ | ||||
| ############################################################################### | ||||
| # 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
									
									
								
							
							
						
						
									
										364
									
								
								framework/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,364 +0,0 @@ | ||||
| ## 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 | ||||
|  | ||||
| @@ -1,159 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFrameworks>net6.0;net7.0</TargetFrameworks> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'"> | ||||
| 		<DefineConstants>Pro</DefineConstants> | ||||
| 	</PropertyGroup> | ||||
| 	<ItemGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro' AND '$(Configuration)' == 'Debug'"> | ||||
|  | ||||
| 		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Melsec\Page\QnA3E_BinaryDebugPage.razor.cs" Link="Pages\Melsec\QnA3E_BinaryDebugPage.razor.cs" /> | ||||
| 		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Melsec\Page\QnA3E_BinaryDebugPage.razor" Link="Pages\Melsec\QnA3E_BinaryDebugPage.razor" /> | ||||
| 		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Melsec\ThingsGateway.Foundation.Adapter.Melsec.csproj" /> | ||||
|  | ||||
|  | ||||
| 		<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 Condition="'$(SolutionName)'=='ThingsGateway - Pro' "> | ||||
| 		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.LQTCP\Page\LQTCPDebugPage.razor.cs" Link="Pages\LQTCP\LQTCPDebugPage.razor.cs" /> | ||||
| 	<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.LQTCP\Page\LQTCPDebugPage.razor" Link="Pages\LQTCP\LQTCPDebugPage.razor" /> | ||||
| 	<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.LQTCP\ThingsGateway.Foundation.Adapter.LQTCP.csproj" /> | ||||
|  | ||||
|  | ||||
| 	<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008DebugPage.razor.cs" Link="Pages\KELID2008\KELID2008DebugPage.razor.cs" /> | ||||
| 	<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008OverTcpDebugPage.razor.cs" Link="Pages\KELID2008\KELID2008OverTcpDebugPage.razor.cs" /> | ||||
| 	<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008DebugPage.razor" Link="Pages\KELID2008\KELID2008DebugPage.razor" /> | ||||
| 	<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008OverTcpDebugPage.razor" Link="Pages\KELID2008\KELID2008OverTcpDebugPage.razor" /> | ||||
| 	<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.KELID2008\ThingsGateway.Foundation.Adapter.KELID2008.csproj" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
|  | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\MqttRpcNameVaueWithId.cs" Link="Pages\Mqtt\MqttRpcNameVaueWithId.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor.cs" Link="Pages\Mqtt\MqttClientDebugPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor.cs" Link="Pages\Mqtt\MqttClientPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClient.cs" Link="Pages\Mqtt\MqttRpcClient.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClientExtensions.cs" Link="Pages\Mqtt\MqttRpcClientExtensions.cs" /> | ||||
| 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcTopicPair.cs" Link="Pages\Mqtt\MqttRpcTopicPair.cs" /> | ||||
| 		<Compile Include="..\..\Web\ThingsGateway.Gateway.Application\Workers\ManageGateway\MqttLoggerExtensions.cs" Link="Pages\Mqtt\MqttLoggerExtensions.cs" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor" Link="Pages\Mqtt\MqttClientDebugPage.razor" /> | ||||
| 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor" Link="Pages\Mqtt\MqttClientPage.razor" /> | ||||
| 		<PackageReference Include="MQTTnet" Version="4.3.1.873" /> | ||||
| 		 | ||||
| 		 | ||||
| 		<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> | ||||
| 		<!--<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.Modbus" Version="*" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCDA" Version="*" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCUA" Version="*" /> | ||||
| 		<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />--> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" /> | ||||
|  | ||||
| 	</ItemGroup> | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
|   | ||||
|  | ||||
|  | ||||
| </Project> | ||||
| @@ -1,41 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD> | ||||
| //  <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD> | ||||
| //  Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD> | ||||
| //  GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||
| //  GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||
| //  ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQȺ<51><C8BA>605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Microsoft.AspNetCore.Components.WebView.WindowsForms; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
|  | ||||
| using ThingsGateway.Components; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo.Winform | ||||
| { | ||||
|     public partial class MainFrom : Form | ||||
|     { | ||||
|         public MainFrom() | ||||
|         { | ||||
|             InitializeComponent(); | ||||
|  | ||||
|             var services = new ServiceCollection(); | ||||
|             services.AddWindowsFormsBlazorWebView(); | ||||
|             services.ThingsGatewayComponentsConfigureServices(); | ||||
|  | ||||
|             blazorWebView1.HostPage = "wwwroot/index.html"; | ||||
|             blazorWebView1.Services = services.BuildServiceProvider(); | ||||
|             this.Text = "ThingsGateway.Foundation.Demo"; | ||||
|             blazorWebView1.RootComponents.Add<ThingsGateway.Foundation.Demo.App>("#app"); | ||||
|         } | ||||
|  | ||||
|         private void MainFrom_FormClosed(object sender, FormClosedEventArgs e) | ||||
|         { | ||||
|             Application.Exit(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,19 +0,0 @@ | ||||
| using System.Windows.Forms; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo.Winform | ||||
| { | ||||
|     internal static class Program | ||||
|     { | ||||
|         /// <summary> | ||||
|         ///  The main entry point for the application. | ||||
|         /// </summary> | ||||
|         [STAThread] | ||||
|         static void Main() | ||||
|         { | ||||
|             // To customize application configuration such as set high DPI settings or default font, | ||||
|             // see https://aka.ms/applicationconfiguration. | ||||
|             ApplicationConfiguration.Initialize(); | ||||
|             Application.Run(new MainFrom()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,47 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>WinExe</OutputType> | ||||
| 		<ApplicationIcon>favicon.ico</ApplicationIcon> | ||||
| 		<TargetFrameworks>net7.0-windows</TargetFrameworks> | ||||
| 		<UseWindowsForms>true</UseWindowsForms> | ||||
| 	</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="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="7.0.96" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <Content Update="wwwroot\favicon.ico"> | ||||
| 	    <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 	  </Content> | ||||
| 	  <Content Update="wwwroot\favicon.png"> | ||||
| 	    <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 	  </Content> | ||||
| 	  <Content Update="wwwroot\index.html"> | ||||
| 	    <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 	  </Content> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
|  | ||||
| </Project> | ||||
| @@ -1,480 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.ComponentModel; | ||||
|  | ||||
| using 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 | ||||
|  | ||||
| } | ||||
| @@ -1,480 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.ComponentModel; | ||||
|  | ||||
| using 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 | ||||
|  | ||||
| } | ||||
| @@ -1,452 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| 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, Task<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); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     EasyLock easyLock = new(); | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|  | ||||
|             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[]>("功能码错误"); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <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) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|             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("功能码错误"); | ||||
|  | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|             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("功能码错误"); | ||||
|  | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <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 async Task Received(SerialSession client, ReceivedDataEventArgs e) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var requestInfo = e.RequestInfo; | ||||
|             //接收外部报文 | ||||
|             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 ((await 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 ((await 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) | ||||
|     { | ||||
|         ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|     } | ||||
| } | ||||
| @@ -1,470 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.ComponentModel; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.Bool; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||
|     /// </summary> | ||||
|     public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<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 ModbusTcpServer(TcpService tcpService) : base(tcpService) | ||||
|     { | ||||
|         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         RegisterByteLength = 2; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 多站点 | ||||
|     /// </summary> | ||||
|     [Description("多站点")] | ||||
|     public bool MulStation { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 默认站点 | ||||
|     /// </summary> | ||||
|     [Description("默认站点")] | ||||
|     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); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     EasyLock easyLock = new(); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|  | ||||
|             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[]>("功能码错误"); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <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) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|             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("功能码错误"); | ||||
|  | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             easyLock.Wait(); | ||||
|             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("功能码错误"); | ||||
|  | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|  | ||||
|             easyLock.Release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <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 async Task Received(SocketClient client, ReceivedDataEventArgs e) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var requestInfo = e.RequestInfo; | ||||
|             //接收外部报文 | ||||
|             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 ((await 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 ((await 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) | ||||
|     { | ||||
|         ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|         ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||
|     } | ||||
| } | ||||
| @@ -1,4 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
| 	 | ||||
| </Project> | ||||
| @@ -1,129 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Siemens; | ||||
|  | ||||
| public partial class SiemensS7PLC : ReadWriteDevicesTcpClientBase | ||||
| { | ||||
|     private static OperResult<byte[]> GetWriteBitCommand(string address, bool data) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var result = SiemensAddress.ParseFrom(address); | ||||
|             return SiemensHelper.GetWriteBitCommand(result, data); | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private OperResult<List<byte[]>> GetReadByteCommand(string address, int length) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var from = SiemensAddress.ParseFrom(address, length); | ||||
|             ushort num1 = 0; | ||||
|             var listBytes = new List<byte[]>(); | ||||
|             while (num1 < length) | ||||
|             { | ||||
|                 //pdu长度,重复生成报文,直至全部生成 | ||||
|                 ushort num2 = (ushort)Math.Min(length - num1, pdu_length); | ||||
|                 from.Length = num2; | ||||
|                 var result = GetReadByteCommand(new SiemensAddress[1] { from }); | ||||
|                 if (!result.IsSuccess) return new(result); | ||||
|                 listBytes.AddRange(result.Content); | ||||
|                 num1 += num2; | ||||
|                 if (from.DataCode == (byte)S7WordLength.Timer || from.DataCode == (byte)S7WordLength.Counter) | ||||
|                 { | ||||
|                     from.Address += num2 / 2; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     from.Address += num2 * 8; | ||||
|                 } | ||||
|             } | ||||
|             return OperResult.CreateSuccessResult(listBytes); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<List<byte[]>>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private OperResult<List<byte[]>> GetReadByteCommand(SiemensAddress[] siemensAddress) | ||||
|     { | ||||
|         if (siemensAddress.Length <= 19) | ||||
|         { | ||||
|             return ByteTransformUtil.GetResultFromBytes(SiemensHelper.GetReadCommand(siemensAddress), m => new List<byte[]>() { m }); | ||||
|         } | ||||
|  | ||||
|         List<byte[]> byteList = new(); | ||||
|         List<SiemensAddress[]> s7AddressDataArrayList = siemensAddress.ArraySplitByLength(19); | ||||
|         for (int index = 0; index < s7AddressDataArrayList.Count; ++index) | ||||
|         { | ||||
|             var result = GetReadByteCommand(s7AddressDataArrayList[index]); | ||||
|             if (!result.IsSuccess) | ||||
|             { | ||||
|                 return result; | ||||
|             } | ||||
|             byteList.AddRange(result.Content); | ||||
|         } | ||||
|         return OperResult.CreateSuccessResult(byteList); | ||||
|     } | ||||
|  | ||||
|     private OperResult<List<byte[]>> GetWriteByteCommand(string address, byte[] value) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var s_Address = SiemensAddress.ParseFrom(address); | ||||
|  | ||||
|             return GetWriteByteCommand(s_Address, value); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new(ex); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// DefalutConverter | ||||
|     /// </summary> | ||||
|     public static ThingsGatewayBitConverter DefalutConverter = new(BitConverter.IsLittleEndian ? EndianType.Little : EndianType.Big); | ||||
|     private OperResult<List<byte[]>> GetWriteByteCommand(SiemensAddress address, byte[] value) | ||||
|     { | ||||
|         int length1 = value.Length; | ||||
|         ushort index = 0; | ||||
|         List<byte[]> bytes = new(); | ||||
|         while (index < length1) | ||||
|         { | ||||
|             //pdu长度,重复生成报文,直至全部生成 | ||||
|             ushort length2 = (ushort)Math.Min(length1 - index, pdu_length); | ||||
|             byte[] data = DefalutConverter.ToByte(value, index, length2); | ||||
|             OperResult<byte[]> result1 = SiemensHelper.GetWriteByteCommand(address, data); | ||||
|             if (!result1.IsSuccess) | ||||
|             { | ||||
|                 return new(result1); | ||||
|             } | ||||
|             bytes.Add(result1.Content); | ||||
|             index += length2; | ||||
|             address.Address += length2 * 8; | ||||
|         } | ||||
|         return OperResult.CreateSuccessResult(bytes); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,121 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Core; | ||||
|  | ||||
| /// <summary> | ||||
| /// 读写扩展方法 | ||||
| /// </summary> | ||||
| public static class ReadWriteDevicesExtensions | ||||
| { | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 在返回的字节数组中解析每个变量的值 | ||||
|     /// 根据每个变量的<see cref="IDeviceVariableRunTime.Index"/> | ||||
|     /// 不支持变长字符串类型变量,一定不能存在于变量List中 | ||||
|     /// </summary> | ||||
|     /// <param name="values">设备变量List</param> | ||||
|     /// <param name="plc">设备</param> | ||||
|     /// <param name="buffer">返回的字节数组</param> | ||||
|     /// <param name="startIndex">开始序号</param> | ||||
|     public static void PraseStructContent<T>(this IList<T> values, IReadWrite plc, byte[] buffer, int startIndex = 0) where T : IDeviceVariableRunTime | ||||
|     { | ||||
|         foreach (IDeviceVariableRunTime organizedVariable in values) | ||||
|         { | ||||
|             var deviceValue = organizedVariable; | ||||
|             IThingsGatewayBitConverter byteConverter = deviceValue.ThingsGatewayBitConverter; | ||||
|             var dataType = organizedVariable.DataTypeEnum; | ||||
|             int index = organizedVariable.Index; | ||||
|             switch (dataType) | ||||
|             { | ||||
|                 case DataTypeEnum.String: | ||||
|                     Set(organizedVariable, byteConverter.ToString(buffer, index + startIndex, byteConverter.Length ?? 1)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.Boolean: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToBoolean(buffer, index + (startIndex * 8), byteConverter.Length.Value, plc.IsBitReverse(organizedVariable.VariableAddress)) : | ||||
|                         byteConverter.ToBoolean(buffer, index + (startIndex * 8), plc.IsBitReverse(organizedVariable.VariableAddress)) | ||||
|                         ); | ||||
|                     break; | ||||
|                 case DataTypeEnum.Byte: | ||||
|                     Set(organizedVariable, | ||||
|                         byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToByte(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToByte(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.Int16: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToInt16(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToInt16(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.UInt16: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToUInt16(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToUInt16(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.Int32: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToInt32(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToInt32(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.UInt32: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToUInt32(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToUInt32(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.Int64: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToInt64(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToInt64(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.UInt64: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToUInt64(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToUInt64(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.Single: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToSingle(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToSingle(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 case DataTypeEnum.Double: | ||||
|                     Set(organizedVariable, | ||||
|                          byteConverter.Length > 1 ? | ||||
|                         byteConverter.ToDouble(buffer, index + (startIndex), byteConverter.Length.Value) : | ||||
|                         byteConverter.ToDouble(buffer, index + startIndex)); | ||||
|                     break; | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         static void Set(IDeviceVariableRunTime organizedVariable, object num) | ||||
|         { | ||||
|             var operResult = organizedVariable.SetValue(num); ; | ||||
|             if (!operResult.IsSuccess) | ||||
|             { | ||||
|                 throw new Exception(operResult.Message); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,118 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Extension; | ||||
| /// <summary> | ||||
| /// 对象拓展类 | ||||
| /// </summary> | ||||
| public static class ObjectExtensions | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 转换布尔值 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public static bool ToBoolean(this object value, bool defaultValue = false) => value?.ToString().ToUpper() switch | ||||
|     { | ||||
|         "0" or "FALSE" => false, | ||||
|         "1" or "TRUE" => true, | ||||
|         _ => defaultValue, | ||||
|     }; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ToLong | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public static long ToLong(this object value, long defaultValue = 0) | ||||
|     { | ||||
|         if (value == null || value.ToString().IsNullOrEmpty()) | ||||
|         { | ||||
|             return defaultValue; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (value is bool boolValue) | ||||
|             { | ||||
|                 return boolValue ? 1 : 0; | ||||
|             } | ||||
|             return Int64.TryParse(value.ToString(), out var n) ? n : defaultValue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ToInt | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public static int ToInt(this object value, int defaultValue = 0) | ||||
|     { | ||||
|         if (value == null || value.ToString().IsNullOrEmpty()) | ||||
|         { | ||||
|             return defaultValue; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (value is bool boolValue) | ||||
|             { | ||||
|                 return boolValue ? 1 : 0; | ||||
|             } | ||||
|             return int.TryParse(value.ToString(), out int n) ? n : defaultValue; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// ToDecimal | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public static decimal ToDecimal(this object value, int defaultValue = 0) | ||||
|     { | ||||
|         if (value is Double d) | ||||
|         { | ||||
|             return Double.IsNaN(d) ? defaultValue : (Decimal)d; | ||||
|         } | ||||
|         var str = value?.ToString(); | ||||
|         if (str.IsNullOrEmpty()) | ||||
|         { | ||||
|             return defaultValue; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (value is bool boolValue) | ||||
|             { | ||||
|                 return boolValue ? 1 : 0; | ||||
|             } | ||||
|             return Decimal.TryParse(str, out var n) ? n : defaultValue; | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// ToDecimal | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public static double ToDouble(this object value, double defaultValue = 0) | ||||
|     { | ||||
|         if (value is Double d) | ||||
|         { | ||||
|             return Double.IsNaN(d) ? defaultValue : (Double)d; | ||||
|         } | ||||
|         var str = value?.ToString(); | ||||
|         if (str.IsNullOrEmpty()) | ||||
|         { | ||||
|             return (double)defaultValue; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (value is bool boolValue) | ||||
|             { | ||||
|                 return boolValue ? 1 : 0; | ||||
|             } | ||||
|             return (double)(double.TryParse(str, out var n) ? n : defaultValue); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,120 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Core | ||||
| { | ||||
|     /// <summary> | ||||
|     /// DateHandleAdapterExtension | ||||
|     /// </summary> | ||||
|     public static class DataHandlingAdapterExtension | ||||
|     { | ||||
|         #region SingleStreamDataHandlingAdapter | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 将<see cref="TouchSocketConfig"/>中的配置,装载在<see cref="SingleStreamDataHandlingAdapter"/>上。 | ||||
|         /// </summary> | ||||
|         /// <param name="adapter"></param> | ||||
|         /// <param name="config"></param> | ||||
|         public static void Config(this SingleStreamDataHandlingAdapter adapter, TouchSocketConfig config) | ||||
|         { | ||||
|             if (config.GetValue(DataHandlingAdapterExtension.MaxPackageSizeProperty) is int v1) | ||||
|             { | ||||
|                 adapter.MaxPackageSize = v1; | ||||
|             } | ||||
|             if (config.GetValue(DataHandlingAdapterExtension.CacheTimeoutProperty) != TimeSpan.Zero) | ||||
|             { | ||||
|                 adapter.CacheTimeout = config.GetValue(DataHandlingAdapterExtension.CacheTimeoutProperty); | ||||
|             } | ||||
|             if (config.GetValue(DataHandlingAdapterExtension.CacheTimeoutEnableProperty) is bool v2) | ||||
|             { | ||||
|                 adapter.CacheTimeoutEnable = v2; | ||||
|             } | ||||
|             if (config.GetValue(DataHandlingAdapterExtension.UpdateCacheTimeWhenRevProperty) is bool v3) | ||||
|             { | ||||
|                 adapter.UpdateCacheTimeWhenRev = v3; | ||||
|             } | ||||
|         } | ||||
|         #endregion | ||||
|  | ||||
|         #region 适配器配置 | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 适配器数据包缓存启用。默认为缺省(null),如果有正常值会在设置适配器时,直接作用于<see cref="SingleStreamDataHandlingAdapter.CacheTimeout"/> | ||||
|         /// </summary> | ||||
|         public static readonly DependencyProperty<bool?> CacheTimeoutEnableProperty = DependencyProperty<bool?>.Register("CacheTimeoutEnable", null); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 适配器数据包缓存时长。默认为缺省(<see cref="TimeSpan.Zero"/>)。当该值有效时会在设置适配器时,直接作用于<see cref="SingleStreamDataHandlingAdapter.CacheTimeout"/> | ||||
|         /// </summary> | ||||
|         public static readonly DependencyProperty<TimeSpan> CacheTimeoutProperty = DependencyProperty<TimeSpan>.Register("CacheTimeout", TimeSpan.Zero); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 适配器数据包最大值。默认缺省(null),当该值有效时会在设置适配器时,直接作用于<see cref="DataHandlingAdapter.MaxPackageSize"/> | ||||
|         /// </summary> | ||||
|         public static readonly DependencyProperty<int?> MaxPackageSizeProperty = DependencyProperty<int?>.Register("MaxPackageSize", null); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 适配器数据包缓存策略。默认缺省(null),当该值有效时会在设置适配器时,直接作用于<see cref="SingleStreamDataHandlingAdapter.UpdateCacheTimeWhenRev"/> | ||||
|         /// </summary> | ||||
|         public static readonly DependencyProperty<bool?> UpdateCacheTimeWhenRevProperty = DependencyProperty<bool?>.Register("UpdateCacheTimeWhenRev", null); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 适配器数据包缓存时长。默认为缺省(<see cref="TimeSpan.Zero"/>)。当该值有效时会在设置适配器时,直接作用于<see cref="SingleStreamDataHandlingAdapter.CacheTimeout"/> | ||||
|         /// </summary> | ||||
|         /// <param name="config"></param> | ||||
|         /// <param name="value"></param> | ||||
|         /// <returns></returns> | ||||
|         public static TouchSocketConfig SetCacheTimeout(this TouchSocketConfig config, TimeSpan value) | ||||
|         { | ||||
|             config.SetValue(CacheTimeoutProperty, value); | ||||
|             return config; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 适配器数据包缓存启用。默认为缺省(null),如果有正常值会在设置适配器时,直接作用于<see cref="SingleStreamDataHandlingAdapter.CacheTimeoutEnable"/> | ||||
|         /// </summary> | ||||
|         /// <param name="config"></param> | ||||
|         /// <param name="value"></param> | ||||
|         /// <returns></returns> | ||||
|         public static TouchSocketConfig SetCacheTimeoutEnable(this TouchSocketConfig config, bool value) | ||||
|         { | ||||
|             config.SetValue(CacheTimeoutEnableProperty, value); | ||||
|             return config; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 适配器数据包最大值。默认缺省(null),当该值有效时会在设置适配器时,直接作用于<see cref="DataHandlingAdapter.MaxPackageSize"/> | ||||
|         /// </summary> | ||||
|         /// <param name="config"></param> | ||||
|         /// <param name="value"></param> | ||||
|         /// <returns></returns> | ||||
|         public static TouchSocketConfig SetMaxPackageSize(this TouchSocketConfig config, int value) | ||||
|         { | ||||
|             config.SetValue(MaxPackageSizeProperty, value); | ||||
|             return config; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 适配器数据包缓存策略。默认缺省(null),当该值有效时会在设置适配器时,直接作用于<see cref="SingleStreamDataHandlingAdapter.UpdateCacheTimeWhenRev"/> | ||||
|         /// </summary> | ||||
|         /// <param name="config"></param> | ||||
|         /// <param name="value"></param> | ||||
|         /// <returns></returns> | ||||
|         public static TouchSocketConfig SetUpdateCacheTimeWhenRev(this TouchSocketConfig config, bool value) | ||||
|         { | ||||
|             config.SetValue(UpdateCacheTimeWhenRevProperty, value); | ||||
|             return config; | ||||
|         } | ||||
|  | ||||
|         #endregion 适配器配置 | ||||
|     } | ||||
| } | ||||
| @@ -1,243 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权(除特别声明或在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 ThingsGateway.Foundation.Resources; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Http | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Http客户端 | ||||
|     /// </summary> | ||||
|     public class HttpClient : HttpClientBase | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Http客户端基类 | ||||
|     /// </summary> | ||||
|     public class HttpClientBase : TcpClientBase, IHttpClient | ||||
|     { | ||||
|         private readonly object m_requestLocker = new object(); | ||||
|         private readonly WaitData<HttpResponse> m_waitData; | ||||
|         private bool m_getContent; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 构造函数 | ||||
|         /// </summary> | ||||
|         public HttpClientBase() | ||||
|         { | ||||
|             this.m_waitData = new WaitData<HttpResponse>(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// <inheritdoc/> | ||||
|         /// </summary> | ||||
|         /// <param name="timeout"></param> | ||||
|         /// <returns></returns> | ||||
|         public override ITcpClient Connect(int timeout = 5000) | ||||
|         { | ||||
|             if (this.Config.GetValue(HttpConfigExtensions.HttpProxyProperty) is HttpProxy httpProxy) | ||||
|             { | ||||
|                 var proxyHost = httpProxy.Host; | ||||
|                 var credential = httpProxy.Credential; | ||||
|                 var remoteHost = this.Config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty); | ||||
|                 try | ||||
|                 { | ||||
|                     this.Config.SetRemoteIPHost(proxyHost); | ||||
|                     base.Connect(timeout); | ||||
|                     var request = new HttpRequest(); | ||||
|                     request.InitHeaders() | ||||
|                         .SetHost(remoteHost.Host) | ||||
|                         .SetUrl(remoteHost.Host, true) | ||||
|                         .AsMethod("CONNECT"); | ||||
|                     var response = this.Request(request, timeout: timeout); | ||||
|                     if (response.IsProxyAuthenticationRequired) | ||||
|                     { | ||||
|                         if (credential is null) | ||||
|                         { | ||||
|                             throw new Exception("未指定代理的凭据。"); | ||||
|                         } | ||||
|                         var authHeader = response.Headers.Get(HttpHeaders.ProxyAuthenticate); | ||||
|                         if (authHeader.IsNullOrEmpty()) | ||||
|                         { | ||||
|                             throw new Exception("未指定代理身份验证质询。"); | ||||
|                         } | ||||
|  | ||||
|                         var ares = new AuthenticationChallenge(authHeader, credential); | ||||
|  | ||||
|                         request.Headers.Add(HttpHeaders.ProxyAuthorization, ares.ToString()); | ||||
|                         if (!response.KeepAlive) | ||||
|                         { | ||||
|                             base.Close("代理要求关闭连接,随后重写连接。"); | ||||
|                             base.Connect(timeout); | ||||
|                         } | ||||
|  | ||||
|                         response = this.Request(request, timeout: timeout); | ||||
|                     } | ||||
|  | ||||
|                     if (response.StatusCode != 200) | ||||
|                     { | ||||
|                         throw new Exception(response.StatusMessage); | ||||
|                     } | ||||
|                 } | ||||
|                 finally | ||||
|                 { | ||||
|                     this.Config.SetRemoteIPHost(remoteHost); | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 base.Connect(timeout); | ||||
|             } | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// <inheritdoc/> | ||||
|         /// </summary> | ||||
|         /// <param name="request"><inheritdoc/></param> | ||||
|         /// <param name="onlyRequest"><inheritdoc/></param> | ||||
|         /// <param name="timeout"><inheritdoc/></param> | ||||
|         /// <param name="token"><inheritdoc/></param> | ||||
|         /// <returns></returns> | ||||
|         public HttpResponse Request(HttpRequest request, bool onlyRequest = false, int timeout = 10 * 1000, CancellationToken token = default) | ||||
|         { | ||||
|             lock (this.m_requestLocker) | ||||
|             { | ||||
|                 this.m_getContent = false; | ||||
|                 using (var byteBlock = new ByteBlock()) | ||||
|                 { | ||||
|                     request.Build(byteBlock); | ||||
|  | ||||
|                     this.m_waitData.Reset(); | ||||
|                     this.m_waitData.SetCancellationToken(token); | ||||
|  | ||||
|                     this.DefaultSend(byteBlock); | ||||
|                     if (onlyRequest) | ||||
|                     { | ||||
|                         return default; | ||||
|                     } | ||||
|  | ||||
|                     switch (this.m_waitData.Wait(timeout)) | ||||
|                     { | ||||
|                         case WaitDataStatus.SetRunning: | ||||
|                             return this.m_waitData.WaitResult; | ||||
|  | ||||
|                         case WaitDataStatus.Overtime: | ||||
|                             throw new TimeoutException(TouchSocketHttpResource.Overtime.GetDescription()); | ||||
|                         case WaitDataStatus.Canceled: | ||||
|                             return default; | ||||
|  | ||||
|                         case WaitDataStatus.Default: | ||||
|                         case WaitDataStatus.Disposed: | ||||
|                         default: | ||||
|                             throw new Exception(TouchSocketHttpResource.UnknownError.GetDescription()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// <inheritdoc/> | ||||
|         /// </summary> | ||||
|         /// <param name="request"></param> | ||||
|         /// <param name="onlyRequest"></param> | ||||
|         /// <param name="timeout"></param> | ||||
|         /// <param name="token"></param> | ||||
|         /// <returns></returns> | ||||
|         public HttpResponse RequestContent(HttpRequest request, bool onlyRequest = false, int timeout = 10 * 1000, CancellationToken token = default) | ||||
|         { | ||||
|             lock (this.m_requestLocker) | ||||
|             { | ||||
|                 this.m_getContent = true; | ||||
|                 using (var byteBlock = new ByteBlock()) | ||||
|                 { | ||||
|                     request.Build(byteBlock); | ||||
|  | ||||
|                     this.m_waitData.Reset(); | ||||
|                     this.m_waitData.SetCancellationToken(token); | ||||
|  | ||||
|                     this.DefaultSend(byteBlock); | ||||
|                     if (onlyRequest) | ||||
|                     { | ||||
|                         return default; | ||||
|                     } | ||||
|  | ||||
|                     switch (this.m_waitData.Wait(timeout)) | ||||
|                     { | ||||
|                         case WaitDataStatus.SetRunning: | ||||
|                             return this.m_waitData.WaitResult; | ||||
|  | ||||
|                         case WaitDataStatus.Overtime: | ||||
|                             throw new TimeoutException(TouchSocketHttpResource.Overtime.GetDescription()); | ||||
|                         case WaitDataStatus.Canceled: | ||||
|                             return default; | ||||
|  | ||||
|                         case WaitDataStatus.Default: | ||||
|                         case WaitDataStatus.Disposed: | ||||
|                         default: | ||||
|                             throw new Exception(TouchSocketHttpResource.UnknownError.GetDescription()); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// <inheritdoc/> | ||||
|         /// </summary> | ||||
|         /// <param name="disposing"></param> | ||||
|         protected override void Dispose(bool disposing) | ||||
|         { | ||||
|             this.m_waitData?.Dispose(); | ||||
|             base.Dispose(disposing); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override Task ReceivedData(ReceivedDataEventArgs e) | ||||
|         { | ||||
|             if (e.RequestInfo is HttpResponse response) | ||||
|             { | ||||
|                 if (this.m_getContent) | ||||
|                 { | ||||
|                     response.TryGetContent(out _); | ||||
|                 } | ||||
|                 this.m_waitData.Set(response); | ||||
|             } | ||||
|  | ||||
|             return base.ReceivedData(e); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// <inheritdoc/> | ||||
|         /// </summary> | ||||
|         /// <param name="e"></param> | ||||
|         protected override async Task OnConnecting(ConnectingEventArgs e) | ||||
|         { | ||||
|             this.Protocol = Protocol.Http; | ||||
|             this.SetDataHandlingAdapter(new HttpClientDataHandlingAdapter()); | ||||
|             await base.OnConnecting(e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,93 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权(除特别声明或在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 | ||||
| //  感谢您的下载和使用 | ||||
| //------------------------------------------------------------------------------ | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Rpc | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Rpc异常 | ||||
|     /// </summary> | ||||
|     [Serializable] | ||||
|     public class RpcException : Exception | ||||
|     { | ||||
|         /// <summary> | ||||
|         ///构造函数 | ||||
|         /// </summary> | ||||
|         public RpcException() : base() { } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///构造函数 | ||||
|         /// </summary> | ||||
|         /// <param name="message"></param> | ||||
|         public RpcException(string message) : base(message) { } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///构造函数 | ||||
|         /// </summary> | ||||
|         /// <param name="message"></param> | ||||
|         /// <param name="inner"></param> | ||||
|         public RpcException(string message, System.Exception inner) : base(message, inner) { } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///构造函数 | ||||
|         /// </summary> | ||||
|         /// <param name="info"></param> | ||||
|         /// <param name="context"></param> | ||||
|         protected RpcException(System.Runtime.Serialization.SerializationInfo info, | ||||
|             System.Runtime.Serialization.StreamingContext context) : base(info, context) { } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Rpc调用异常 | ||||
|     /// </summary> | ||||
|     [Serializable] | ||||
|     public class RpcInvokeException : Exception | ||||
|     { | ||||
|         /// <summary> | ||||
|         ///构造函数 | ||||
|         /// </summary> | ||||
|         public RpcInvokeException() : base() { } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///构造函数 | ||||
|         /// </summary> | ||||
|         /// <param name="message"></param> | ||||
|         public RpcInvokeException(string message) : base(message) { } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///构造函数 | ||||
|         /// </summary> | ||||
|         /// <param name="message"></param> | ||||
|         /// <param name="inner"></param> | ||||
|         public RpcInvokeException(string message, System.Exception inner) : base(message, inner) { } | ||||
|  | ||||
|         /// <summary> | ||||
|         ///构造函数 | ||||
|         /// </summary> | ||||
|         /// <param name="info"></param> | ||||
|         /// <param name="context"></param> | ||||
|         protected RpcInvokeException(System.Runtime.Serialization.SerializationInfo info, | ||||
|             System.Runtime.Serialization.StreamingContext context) : base(info, context) { } | ||||
|     } | ||||
| } | ||||
| @@ -1,111 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.DLT645; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class DLT645_2007 : CollectBase | ||||
| { | ||||
|     private readonly DLT645_2007Property driverPropertys = new(); | ||||
|     private ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007 _plc; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(DLT645_2007DebugPage); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public override bool IsSupportRequest => true; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override IReadWrite PLC => _plc; | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         await _plc?.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void InitDataAdapter() | ||||
|     { | ||||
|         _plc.SetDataAdapter(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.SerialSession?.CanSend == true; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) | ||||
|     { | ||||
|         return _plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(deviceVariables, 0); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|     { | ||||
|         if (client == null) | ||||
|         { | ||||
|             FoundataionConfig.SetSerialProperty(new() | ||||
|             { | ||||
|                 PortName = driverPropertys.PortName, | ||||
|                 BaudRate = driverPropertys.BaudRate, | ||||
|                 DataBits = driverPropertys.DataBits, | ||||
|                 Parity = driverPropertys.Parity, | ||||
|                 StopBits = driverPropertys.StopBits, | ||||
|             }) | ||||
|                 ; | ||||
|             client = new SerialSession(); | ||||
|             ((SerialSession)client).Setup(FoundataionConfig); | ||||
|         } | ||||
|         //载入配置 | ||||
|         _plc = new((SerialSession)client) | ||||
|         { | ||||
|             FrameTime = driverPropertys.FrameTime, | ||||
|             CacheTimeout = driverPropertys.CacheTimeout, | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             EnableFEHead = driverPropertys.EnableFEHead, | ||||
|             OperCode = driverPropertys.OperCode, | ||||
|             Password = driverPropertys.Password, | ||||
|             Station = driverPropertys.Station, | ||||
|             TimeOut = driverPropertys.TimeOut | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return await _plc.ReadAsync(address, length, cancellationToken); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,103 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.DLT645; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class DLT645_2007OverTcp : CollectBase | ||||
| { | ||||
|  | ||||
|     private readonly DLT645_2007OverTcpProperty driverPropertys = new(); | ||||
|     private ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp _plc; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(DLT645_2007OverTcpDebugPage); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsSupportRequest => true; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override IReadWrite PLC => _plc; | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         await _plc?.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void InitDataAdapter() | ||||
|     { | ||||
|         _plc.SetDataAdapter(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.TcpClient?.CanSend == true; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) | ||||
|     { | ||||
|         return _plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(deviceVariables, 0); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|     { | ||||
|         if (client == null) | ||||
|         { | ||||
|             FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                 ; | ||||
|             client = new TcpClient(); | ||||
|             ((TcpClient)client).Setup(FoundataionConfig); | ||||
|         } | ||||
|         //载入配置 | ||||
|         _plc = new((TcpClient)client) | ||||
|         { | ||||
|             FrameTime = driverPropertys.FrameTime, | ||||
|             CacheTimeout = driverPropertys.CacheTimeout, | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             EnableFEHead = driverPropertys.EnableFEHead, | ||||
|             OperCode = driverPropertys.OperCode, | ||||
|             Password = driverPropertys.Password, | ||||
|             Station = driverPropertys.Station, | ||||
|             TimeOut = driverPropertys.TimeOut | ||||
|         }; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return await _plc.ReadAsync(address, length, cancellationToken); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,23 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| global using System; | ||||
| global using System.Collections.Generic; | ||||
| global using System.Threading; | ||||
| global using System.Threading.Tasks; | ||||
|  | ||||
| global using ThingsGateway.Foundation.Core; | ||||
| global using ThingsGateway.Foundation.Demo; | ||||
| global using ThingsGateway.Foundation.Serial; | ||||
| global using ThingsGateway.Foundation.Sockets; | ||||
| global using ThingsGateway.Gateway.Application; | ||||
| global using ThingsGateway.Gateway.Core; | ||||
| @@ -1,413 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Confluent.Kafka; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Kafka; | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using System; | ||||
| using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| /// <summary> | ||||
| /// Kafka消息生产 | ||||
| /// </summary> | ||||
| public class KafkaProducer : UpLoadBase | ||||
| { | ||||
|     private readonly ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new(); | ||||
|     private readonly ConcurrentQueue<VariableData> _collectVariableRunTimes = new(); | ||||
|     private readonly KafkaProducerProperty driverPropertys = new(); | ||||
|     private readonly KafkaProducerVariableProperty variablePropertys = new(); | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|     private bool isSuccess = true; | ||||
|     private IProducer<Null, string> producer; | ||||
|     private ProducerBuilder<Null, string> producerBuilder; | ||||
|     private ProducerConfig producerconfig; | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => null; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override DriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|     private TimerTick exVariableTimerTick; | ||||
|     private TimerTick exDeviceTimerTick; | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 ////变化推送 | ||||
|                 var varList = _collectVariableRunTimes.ToListWithDequeue(); | ||||
|                 if (varList?.Count != 0) | ||||
|                 { | ||||
|                     //分解List,避免超出mqtt字节大小限制 | ||||
|                     var varData = varList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                     foreach (var item in varData) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             if (!cancellationToken.IsCancellationRequested) | ||||
|                             { | ||||
|                                 await KafKaUp(driverPropertys.VariableTopic, item.ToJsonString(), cancellationToken); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage.LogWarning(ex); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                     if (isSuccess) | ||||
|                         producer.Flush(cancellationToken); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exVariableTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.Adapt<List<VariableData>>(); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             //分解List,避免超出mqtt字节大小限制 | ||||
|                             var varData = varList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                             foreach (var item in varData) | ||||
|                             { | ||||
|                                 try | ||||
|                                 { | ||||
|                                     if (!cancellationToken.IsCancellationRequested) | ||||
|                                     { | ||||
|                                         await KafKaUp(driverPropertys.VariableTopic, item.ToJsonString(), cancellationToken); | ||||
|                                     } | ||||
|                                     else | ||||
|                                     { | ||||
|                                         break; | ||||
|                                     } | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage.LogWarning(ex); | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
|                             if (isSuccess) | ||||
|                                 producer.Flush(cancellationToken); | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 ////变化推送 | ||||
|                 var devList = _collectDeviceRunTimes.ToListWithDequeue(); | ||||
|                 if (devList?.Count != 0) | ||||
|                 { | ||||
|                     //分解List,避免超出mqtt字节大小限制 | ||||
|                     var devData = devList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                     foreach (var item in devData) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             if (!cancellationToken.IsCancellationRequested) | ||||
|                             { | ||||
|                                 await KafKaUp(driverPropertys.DeviceTopic, item.ToJsonString(), cancellationToken); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|                     } | ||||
|                     if (isSuccess) | ||||
|                         producer.Flush(cancellationToken); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exDeviceTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     var devList = _collectDevice.Adapt<List<DeviceData>>(); | ||||
|                     if (devList?.Count != 0) | ||||
|                     { | ||||
|                         //分解List,避免超出mqtt字节大小限制 | ||||
|                         var devData = devList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                         foreach (var item in devData) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 if (!cancellationToken.IsCancellationRequested) | ||||
|                                 { | ||||
|                                     await KafKaUp(driverPropertys.DeviceTopic, item.ToJsonString(), cancellationToken); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|                         if (isSuccess) | ||||
|                             producer.Flush(cancellationToken); | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public override bool IsConnected() => isSuccess; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _collectDevice.Where(a => _uploadVariables.Select(b => b.DeviceId).Contains(a.Id)).ForEach(a => | ||||
|         { | ||||
|             a.DeviceStatusChange -= DeviceStatusChange; | ||||
|         }); | ||||
|         _uploadVariables?.ForEach(a => | ||||
|         { | ||||
|             a.VariableValueChange -= VariableValueChange; | ||||
|         }); | ||||
|         producer?.Dispose(); | ||||
|         _uploadVariables = null; | ||||
|         _collectDeviceRunTimes.Clear(); | ||||
|         _collectVariableRunTimes.Clear(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     private List<CollectDeviceRunTime> _collectDevice; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 初始化 | ||||
|     /// </summary> | ||||
|     /// <param name="device"></param> | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|         #region Kafka 生产者 | ||||
|         //1、生产者配置 | ||||
|         producerconfig = new ProducerConfig | ||||
|         { | ||||
|             BootstrapServers = driverPropertys.BootStrapServers, | ||||
|             ClientId = driverPropertys.ClientId, | ||||
|         }; | ||||
|         //2、创建生产者 | ||||
|         producerBuilder = new ProducerBuilder<Null, string>(producerconfig); | ||||
|         //3、错误日志监视 | ||||
|         producerBuilder.SetErrorHandler((p, msg) => | ||||
|         { | ||||
|             isSuccess = false; | ||||
|             LogMessage?.LogWarning(msg.Reason); | ||||
|         }); | ||||
|         //kafka | ||||
|         try | ||||
|         { | ||||
|             producer = producerBuilder.Build(); | ||||
|         } | ||||
|         catch (DllNotFoundException) | ||||
|         { | ||||
|             if (!Library.IsLoaded) | ||||
|             { | ||||
|                 string fileEx = ".dll"; | ||||
|                 string osStr = "win-"; | ||||
|                 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||||
|                 { | ||||
|                     osStr = "win-"; | ||||
|                     fileEx = ".dll"; | ||||
|                 } | ||||
|                 else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||||
|                 { | ||||
|                     osStr = "linux-"; | ||||
|                     fileEx = ".so"; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|  | ||||
|                     osStr = "osx-"; | ||||
|                     fileEx = ".dylib"; | ||||
|                 } | ||||
|                 osStr += RuntimeInformation.ProcessArchitecture.ToString().ToLower(); | ||||
|  | ||||
|                 var pathToLibrd = System.IO.Path.Combine(AppContext.BaseDirectory, "Plugins", "ThingsGateway.Plugin.Kafka", "runtimes", osStr, "native", $"librdkafka{fileEx}"); | ||||
|                 Library.Load(pathToLibrd); | ||||
|             } | ||||
|             producer = producerBuilder.Build(); | ||||
|         } | ||||
|         #endregion | ||||
|  | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|            .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).GetBoolValue()).ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         _collectDevice = _globalDeviceData.CollectDevices.Where(a => _uploadVariables.Select(b => b.DeviceId).Contains(a.Id)).ToList(); | ||||
|  | ||||
|         _collectDevice.ForEach(a => | ||||
|         { | ||||
|             a.DeviceStatusChange += DeviceStatusChange; | ||||
|         }); | ||||
|         _uploadVariables.ForEach(a => | ||||
|         { | ||||
|             a.VariableValueChange += VariableValueChange; | ||||
|         }); | ||||
|         if (driverPropertys.UploadInterval <= 1000) driverPropertys.UploadInterval = 1000; | ||||
|         exVariableTimerTick = new(driverPropertys.UploadInterval); | ||||
|         exDeviceTimerTick = new(driverPropertys.UploadInterval); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void DeviceStatusChange(CollectDeviceRunTime collectDeviceRunTime) | ||||
|     { | ||||
|         if (driverPropertys?.IsInterval != true) | ||||
|             _collectDeviceRunTimes.Enqueue(collectDeviceRunTime.Adapt<DeviceData>()); | ||||
|     } | ||||
|  | ||||
|     private async Task KafKaUp(string topic, string payLoad, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             using CancellationTokenSource cancellationTokenSource = new(); | ||||
|             using CancellationTokenSource stoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource.Token, cancellationToken); | ||||
|             Task<DeliveryResult<Null, string>> resultTask = producer.ProduceAsync(topic, new Message<Null, string> { Value = payLoad }, stoppingToken.Token); | ||||
|             var timeOutResult = await Task.WhenAny(resultTask, Task.Delay(driverPropertys.TimeOut, stoppingToken.Token)); | ||||
|             if (timeOutResult == resultTask) | ||||
|             { | ||||
|                 var result = (timeOutResult as Task<DeliveryResult<Null, string>>).Result; | ||||
|                 if (result.Status != PersistenceStatus.Persisted) | ||||
|                 { | ||||
|                     isSuccess = false; | ||||
|                     await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     isSuccess = true; | ||||
|                     //连接成功时补发缓存数据 | ||||
|                     var cacheData = await CacheDb.GetCacheData(); | ||||
|                     foreach (var item in cacheData) | ||||
|                     { | ||||
|                         var cacheResult = await producer.ProduceAsync(item.Topic, new Message<Null, string> { Value = item.CacheStr }, stoppingToken.Token); | ||||
|  | ||||
|                         if (cacheResult.Status == PersistenceStatus.Persisted) | ||||
|                         { | ||||
|                             LogMessage.Trace($"{FoundationConst.LogMessageHeader}主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|  | ||||
|                             await CacheDb.DeleteCacheData(item.Id); | ||||
|                         } | ||||
|                     } | ||||
|                     LogMessage.Trace($"{FoundationConst.LogMessageHeader}主题:{topic}{Environment.NewLine}负载:{payLoad}"); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 isSuccess = false; | ||||
|                 stoppingToken.Cancel(); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|     } | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (driverPropertys?.IsInterval != true) | ||||
|             _collectVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<VariableData>()); | ||||
|     } | ||||
| } | ||||
| @@ -1,108 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Modbus; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class ModbusRtu : CollectBase | ||||
| { | ||||
|     private readonly ModbusRtuProperty driverPropertys = new(); | ||||
|     /// <inheritdoc/> | ||||
|     protected override IReadWrite PLC => _plc; | ||||
|     private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu _plc; | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(ModbusRtuDebugPage); | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public override bool IsSupportRequest => true; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         await _plc?.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override void InitDataAdapter() | ||||
|     { | ||||
|         _plc.SetDataAdapter(); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.SerialSession?.CanSend == true; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) | ||||
|     { | ||||
|         return _plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(deviceVariables, driverPropertys.MaxPack); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|     { | ||||
|         if (client == null) | ||||
|         { | ||||
|             FoundataionConfig.SetSerialProperty(new() | ||||
|             { | ||||
|                 PortName = driverPropertys.PortName, | ||||
|                 BaudRate = driverPropertys.BaudRate, | ||||
|                 DataBits = driverPropertys.DataBits, | ||||
|                 Parity = driverPropertys.Parity, | ||||
|                 StopBits = driverPropertys.StopBits, | ||||
|             }) | ||||
|                 ; | ||||
|             client = new SerialSession(); | ||||
|             ((SerialSession)client).Setup(FoundataionConfig); | ||||
|         } | ||||
|         //载入配置 | ||||
|         _plc = new((SerialSession)client) | ||||
|         { | ||||
|             Crc16CheckEnable = driverPropertys.Crc16CheckEnable, | ||||
|             FrameTime = driverPropertys.FrameTime, | ||||
|             CacheTimeout = driverPropertys.CacheTimeout, | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             Station = driverPropertys.Station, | ||||
|             TimeOut = driverPropertys.TimeOut | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return await _plc.ReadAsync(address, length, cancellationToken); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,98 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Modbus; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class ModbusRtuOverTcp : CollectBase | ||||
| { | ||||
|  | ||||
|     private readonly ModbusRtuOverTcpProperty driverPropertys = new(); | ||||
|     /// <inheritdoc/> | ||||
|     protected override IReadWrite PLC => _plc; | ||||
|     private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp _plc; | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(ModbusRtuOverTcpDebugPage); | ||||
|     /// <inheritdoc/> | ||||
|     public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsSupportRequest => true; | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; } | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         await _plc?.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void InitDataAdapter() | ||||
|     { | ||||
|         _plc.SetDataAdapter(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.TcpClient?.CanSend == true; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) | ||||
|     { | ||||
|         return _plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(deviceVariables, driverPropertys.MaxPack); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|     { | ||||
|         if (client == null) | ||||
|         { | ||||
|             FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                 ; | ||||
|             client = new TcpClient(); | ||||
|             ((TcpClient)client).Setup(FoundataionConfig); | ||||
|         } | ||||
|         //载入配置 | ||||
|         _plc = new((TcpClient)client) | ||||
|         { | ||||
|             Crc16CheckEnable = driverPropertys.Crc16CheckEnable, | ||||
|             FrameTime = driverPropertys.FrameTime, | ||||
|             CacheTimeout = driverPropertys.CacheTimeout, | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|             Station = driverPropertys.Station, | ||||
|             TimeOut = driverPropertys.TimeOut | ||||
|         }; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return await _plc.ReadAsync(address, length, cancellationToken); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,102 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Modbus; | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class ModbusRtuOverUdp : CollectBase | ||||
| { | ||||
|     private readonly ModbusRtuOverUdpProperty driverPropertys = new(); | ||||
|     /// <inheritdoc/> | ||||
|     protected override IReadWrite PLC => _plc; | ||||
|     private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp _plc; | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(ModbusRtuOverUdpDebugPage); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsSupportRequest => true; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         _plc.Disconnect(); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         return _plc.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void InitDataAdapter() | ||||
|     { | ||||
|         _plc.SetDataAdapter(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.UdpSession?.CanSend == true; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) | ||||
|     { | ||||
|         return _plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(deviceVariables, driverPropertys.MaxPack); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|     { | ||||
|         if (client == null) | ||||
|         { | ||||
|             FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                 .SetBindIPHost(new IPHost(0)) | ||||
|                 ; | ||||
|  | ||||
|             client = new UdpSession(); | ||||
|             ((UdpSession)client).Setup(FoundataionConfig); | ||||
|         } | ||||
|         //载入配置 | ||||
|         _plc = new((UdpSession)client) | ||||
|         { | ||||
|             Crc16CheckEnable = driverPropertys.Crc16CheckEnable, | ||||
|             FrameTime = driverPropertys.FrameTime, | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             Station = driverPropertys.Station, | ||||
|             TimeOut = driverPropertys.TimeOut | ||||
|         }; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return await _plc.ReadAsync(address, length, cancellationToken); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,241 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.Modbus; | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Modbus; | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class ModbusSerialServer : UpLoadBase | ||||
| { | ||||
|  | ||||
|     private readonly ModbusSerialServerProperty driverPropertys = new(); | ||||
|     private readonly ModbusSerialServerVariableProperty variablePropertys = new(); | ||||
|     private Dictionary<ModbusAddress, DeviceVariableRunTime> _ModbusTags; | ||||
|     private ThingsGateway.Foundation.Adapter.Modbus.ModbusSerialServer _plc; | ||||
|     private ConcurrentQueue<(string, DeviceVariableRunTime)> Values = new(); | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(ModbusSerialServerDebugPage); | ||||
|     /// <inheritdoc/> | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _ModbusTags?.Values.ToList(); | ||||
|     /// <inheritdoc/> | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|     RpcSingletonService RpcCore { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         return _plc?.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var list = Values.ToListWithDequeue(); | ||||
|         foreach (var item in list) | ||||
|         { | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 break; | ||||
|             var type = GetPropertyValue(item.Item2, nameof(ModbusSerialServerVariableProperty.ModbusType)); | ||||
|             if (Enum.TryParse<DataTypeEnum>(type, out DataTypeEnum result)) | ||||
|             { | ||||
|                 await _plc.WriteAsync(item.Item1, item.Item2.Value?.ToString(), 1, result, cancellationToken); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 await _plc.WriteAsync(item.Item1, item.Item2.Value?.ToString(), 1, item.Item2.DataTypeEnum, cancellationToken); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.SerialSession?.CanSend == true; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _ModbusTags?.Values?.ToList()?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|         if (_plc != null) | ||||
|             _plc.WriteData -= WriteData; | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         _ModbusTags?.Clear(); | ||||
|         _ModbusTags = null; | ||||
|         Values?.Clear(); | ||||
|         Values = null; | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|         var service = new SerialSession(); | ||||
|         FoundataionConfig.SetSerialProperty(new() | ||||
|         { | ||||
|             PortName = driverPropertys.PortName, | ||||
|             BaudRate = driverPropertys.BaudRate, | ||||
|             DataBits = driverPropertys.DataBits, | ||||
|             Parity = driverPropertys.Parity, | ||||
|             StopBits = driverPropertys.StopBits, | ||||
|         }) | ||||
|             ; | ||||
|         service = new SerialSession(); | ||||
|         ((SerialSession)service).Setup(FoundataionConfig); | ||||
|         //载入配置 | ||||
|         _plc = new(service) | ||||
|         { | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             Station = driverPropertys.Station, | ||||
|             CacheTimeout = driverPropertys.CacheTimeout, | ||||
|             MulStation = driverPropertys.MulStation | ||||
|         }; | ||||
|  | ||||
|         var _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|             .Where(b => !string.IsNullOrEmpty(GetPropertyValue(b, nameof(variablePropertys.ServiceAddress)))) | ||||
|             .ToList(); | ||||
|  | ||||
|         tags.ForEach(a => | ||||
|         { | ||||
|             a.VariableValueChange += VariableValueChange; | ||||
|             VariableValueChange(a); | ||||
|         }); | ||||
|         _plc.WriteData += WriteData; | ||||
|         try | ||||
|         { | ||||
|             _ModbusTags = tags.ToDictionary(a => | ||||
|             { | ||||
|                 ModbusAddress address = ModbusAddress.ParseFrom( | ||||
|                     GetPropertyValue(a, nameof(variablePropertys.ServiceAddress)) | ||||
|                     , driverPropertys.Station); | ||||
|                 return address; | ||||
|             }); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|             tags.ForEach(a => | ||||
|             { | ||||
|                 a.VariableValueChange -= VariableValueChange; | ||||
|             }); | ||||
|             _plc.WriteData -= WriteData; | ||||
|         } | ||||
|         RpcCore = App.GetService<RpcSingletonService>(); | ||||
|  | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         var address = GetPropertyValue(collectVariableRunTime, nameof(variablePropertys.ServiceAddress)); | ||||
|         if (address != null && collectVariableRunTime.Value != null) | ||||
|         { | ||||
|             Values?.Enqueue((address, collectVariableRunTime)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// RPC写入 | ||||
|     /// </summary> | ||||
|     /// <param name="address"></param> | ||||
|     /// <param name="bytes"></param> | ||||
|     /// <param name="thingsGatewayBitConverter"></param> | ||||
|     /// <param name="client"></param> | ||||
|     /// <returns></returns> | ||||
|     private async Task<OperResult> WriteData(ModbusAddress address, byte[] bytes, IThingsGatewayBitConverter thingsGatewayBitConverter, SerialSession client) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var tag = _ModbusTags.FirstOrDefault(a => a.Key?.AddressStart == address.AddressStart && a.Key?.Station == address.Station && a.Key?.ReadFunction == address.ReadFunction); | ||||
|  | ||||
|             if (tag.Value == null) return OperResult.CreateSuccessResult(); | ||||
|             var enable = | ||||
|                 GetPropertyValue(tag.Value, nameof(variablePropertys.VariableRpcEnable)).ToBoolean() | ||||
|                 && driverPropertys.DeviceRpcEnable; | ||||
|             if (!enable) return new OperResult("不允许写入"); | ||||
|             var type = GetPropertyValue(tag.Value, nameof(ModbusSerialServerVariableProperty.ModbusType)); | ||||
|             var addressStr = GetPropertyValue(tag.Value, nameof(ModbusSerialServerVariableProperty.ServiceAddress)); | ||||
|             if (Enum.TryParse<DataTypeEnum>(type, out DataTypeEnum result)) | ||||
|             { | ||||
|                 var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusSerialServer)}-{CurrentDevice.Name}", | ||||
|                new Dictionary<string, string> | ||||
| { | ||||
|     { | ||||
|                        tag.Value.Name, | ||||
|                        thingsGatewayBitConverter.GetDataFormBytes(addressStr ,  bytes,result).ToString() | ||||
|  | ||||
|                    }, | ||||
| } | ||||
|  | ||||
|                 ); | ||||
|                 return result1.FirstOrDefault().Value; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusSerialServer)}-{CurrentDevice.Name}", | ||||
|                new Dictionary<string, string> | ||||
| { | ||||
|     { | ||||
|                        tag.Value.Name, | ||||
|                        thingsGatewayBitConverter.GetDataFormBytes(addressStr ,   bytes,result).ToString() | ||||
|                    }, | ||||
| } | ||||
|                 ); | ||||
|                 return result1.FirstOrDefault().Value; | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,102 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Modbus; | ||||
|  | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class ModbusTcp : CollectBase | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     protected override IReadWrite PLC => _plc; | ||||
|     private ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp _plc; | ||||
|  | ||||
|     private readonly ModbusTcpProperty driverPropertys = new(); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(ModbusTcpDebugPage); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsSupportRequest => true; | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         await _plc?.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void InitDataAdapter() | ||||
|     { | ||||
|         _plc.SetDataAdapter(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.TcpClient?.CanSend == true; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) | ||||
|     { | ||||
|         return _plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(deviceVariables, driverPropertys.MaxPack); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|     { | ||||
|         if (client == null) | ||||
|         { | ||||
|             FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                 ; | ||||
|             client = new TcpClient(); | ||||
|             ((TcpClient)client).Setup(FoundataionConfig); | ||||
|         } | ||||
|         //载入配置 | ||||
|         _plc = new((TcpClient)client) | ||||
|         { | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             FrameTime = driverPropertys.FrameTime, | ||||
|             CacheTimeout = driverPropertys.CacheTimeout, | ||||
|             ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|             Station = driverPropertys.Station, | ||||
|             TimeOut = driverPropertys.TimeOut, | ||||
|             IsCheckMessageId = driverPropertys.MessageIdCheckEnable | ||||
|         }; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return await _plc.ReadAsync(address, length, cancellationToken); | ||||
|     } | ||||
| } | ||||
| @@ -1,105 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Modbus; | ||||
|  | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class ModbusTcpDtu : CollectBase | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     protected override IReadWrite PLC => _plc; | ||||
|     private ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDtu _plc; | ||||
|  | ||||
|     private readonly ModbusTcpProperty driverPropertys = new(); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(ModbusTcpDebugPage); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsSupportRequest => true; | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         await _plc?.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void InitDataAdapter() | ||||
|     { | ||||
|         _plc.SetDataAdapter(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.TcpService?.ServerState == ServerState.Running; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) | ||||
|     { | ||||
|         return _plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(deviceVariables, driverPropertys.MaxPack); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|     { | ||||
|         if (client == null) | ||||
|         { | ||||
|             IPHost iPHost = new(driverPropertys.Port); | ||||
|             if (!string.IsNullOrEmpty(driverPropertys.IP)) | ||||
|             { | ||||
|                 iPHost = new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}"); | ||||
|             } | ||||
|             FoundataionConfig.SetListenIPHosts(new IPHost[] { iPHost }); | ||||
|             client = new TcpService(); | ||||
|             ((TcpService)client).Setup(FoundataionConfig); | ||||
|         } | ||||
|         //载入配置 | ||||
|         _plc = new((TcpService)client) | ||||
|         { | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             FrameTime = driverPropertys.FrameTime, | ||||
|             CacheTimeout = driverPropertys.CacheTimeout, | ||||
|             ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|             Station = driverPropertys.Station, | ||||
|             TimeOut = driverPropertys.TimeOut, | ||||
|             IsCheckMessageId = driverPropertys.MessageIdCheckEnable | ||||
|         }; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return await _plc.ReadAsync(address, length, cancellationToken); | ||||
|     } | ||||
| } | ||||
| @@ -1,237 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.Modbus; | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Modbus; | ||||
| /// <summary> | ||||
| /// <inheritdoc/> | ||||
| /// </summary> | ||||
| public class ModbusTcpServer : UpLoadBase | ||||
| { | ||||
|  | ||||
|     private readonly ModbusTcpServerProperty driverPropertys = new(); | ||||
|     private readonly ModbusTcpServerVariableProperty variablePropertys = new(); | ||||
|     private Dictionary<ModbusAddress, DeviceVariableRunTime> _ModbusTags; | ||||
|     private ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpServer _plc; | ||||
|     private ConcurrentQueue<(string, DeviceVariableRunTime)> Values = new(); | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(ModbusTcpServerDebugPage); | ||||
|     /// <inheritdoc/> | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _ModbusTags?.Values.ToList(); | ||||
|     /// <inheritdoc/> | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|     RpcSingletonService RpcCore { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         return _plc?.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var list = Values.ToListWithDequeue(); | ||||
|         foreach (var item in list) | ||||
|         { | ||||
|             if (cancellationToken.IsCancellationRequested) | ||||
|                 break; | ||||
|             var type = GetPropertyValue(item.Item2, nameof(ModbusTcpServerVariableProperty.ModbusType)); | ||||
|             if (Enum.TryParse<DataTypeEnum>(type, out DataTypeEnum result)) | ||||
|             { | ||||
|                 await _plc.WriteAsync(item.Item1, item.Item2.Value?.ToString(), 1, result, cancellationToken); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 await _plc.WriteAsync(item.Item1, item.Item2.Value?.ToString(), 1, item.Item2.DataTypeEnum, cancellationToken); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.TcpService?.ServerState == ServerState.Running; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _ModbusTags?.Values?.ToList()?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|         if (_plc != null) | ||||
|             _plc.WriteData -= WriteData; | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         _ModbusTags?.Clear(); | ||||
|         _ModbusTags = null; | ||||
|         Values?.Clear(); | ||||
|         Values = null; | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|         IPHost iPHost = new(driverPropertys.Port); | ||||
|         if (!string.IsNullOrEmpty(driverPropertys.IP)) | ||||
|         { | ||||
|             iPHost = new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}"); | ||||
|         } | ||||
|         FoundataionConfig.SetListenIPHosts(new IPHost[] { iPHost }); | ||||
|         var service = new TcpService(); | ||||
|         service.Setup(FoundataionConfig); | ||||
|         //载入配置 | ||||
|         _plc = new(service) | ||||
|         { | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             Station = driverPropertys.Station, | ||||
|             CacheTimeout = driverPropertys.CacheTimeout, | ||||
|             MulStation = driverPropertys.MulStation | ||||
|         }; | ||||
|  | ||||
|         var _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|             .Where(b => !string.IsNullOrEmpty(GetPropertyValue(b, nameof(variablePropertys.ServiceAddress)))) | ||||
|             .ToList(); | ||||
|  | ||||
|         tags.ForEach(a => | ||||
|         { | ||||
|             a.VariableValueChange += VariableValueChange; | ||||
|             VariableValueChange(a); | ||||
|         }); | ||||
|         _plc.WriteData += WriteData; | ||||
|         try | ||||
|         { | ||||
|             _ModbusTags = tags.ToDictionary(a => | ||||
|             { | ||||
|                 ModbusAddress address = ModbusAddress.ParseFrom( | ||||
|                     GetPropertyValue(a, nameof(variablePropertys.ServiceAddress)) | ||||
|                     , driverPropertys.Station); | ||||
|                 return address; | ||||
|             }); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|             tags.ForEach(a => | ||||
|             { | ||||
|                 a.VariableValueChange -= VariableValueChange; | ||||
|             }); | ||||
|             _plc.WriteData -= WriteData; | ||||
|         } | ||||
|         RpcCore = App.GetService<RpcSingletonService>(); | ||||
|  | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         var address = GetPropertyValue(collectVariableRunTime, nameof(variablePropertys.ServiceAddress)); | ||||
|         if (address != null && collectVariableRunTime.Value != null) | ||||
|         { | ||||
|             Values?.Enqueue((address, collectVariableRunTime)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// RPC写入 | ||||
|     /// </summary> | ||||
|     /// <param name="address"></param> | ||||
|     /// <param name="bytes"></param> | ||||
|     /// <param name="thingsGatewayBitConverter"></param> | ||||
|     /// <param name="client"></param> | ||||
|     /// <returns></returns> | ||||
|     private async Task<OperResult> WriteData(ModbusAddress address, byte[] bytes, IThingsGatewayBitConverter thingsGatewayBitConverter, SocketClient client) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var tag = _ModbusTags.FirstOrDefault(a => a.Key?.AddressStart == address.AddressStart && a.Key?.Station == address.Station && a.Key?.ReadFunction == address.ReadFunction); | ||||
|  | ||||
|             if (tag.Value == null) return OperResult.CreateSuccessResult(); | ||||
|             var enable = | ||||
|                 GetPropertyValue(tag.Value, nameof(variablePropertys.VariableRpcEnable)).ToBoolean() | ||||
|                 && driverPropertys.DeviceRpcEnable; | ||||
|             if (!enable) return new OperResult("不允许写入"); | ||||
|             var type = GetPropertyValue(tag.Value, nameof(ModbusTcpServerVariableProperty.ModbusType)); | ||||
|             var addressStr = GetPropertyValue(tag.Value, nameof(ModbusTcpServerVariableProperty.ServiceAddress)); | ||||
|             if (Enum.TryParse<DataTypeEnum>(type, out DataTypeEnum result)) | ||||
|             { | ||||
|                 var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{$"{client.IP}:{client.Port}"}", | ||||
|                new Dictionary<string, string> | ||||
| { | ||||
|     { | ||||
|                        tag.Value.Name, | ||||
|                        thingsGatewayBitConverter.GetDataFormBytes(addressStr ,  bytes,result).ToString() | ||||
|  | ||||
|                    }, | ||||
| } | ||||
|  | ||||
|                 ); | ||||
|                 return result1.FirstOrDefault().Value; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var result1 = await RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{$"{client.IP}:{client.Port}"}", | ||||
|                new Dictionary<string, string> | ||||
| { | ||||
|     { | ||||
|                        tag.Value.Name, | ||||
|                        thingsGatewayBitConverter.GetDataFormBytes(addressStr ,   bytes,result).ToString() | ||||
|                    }, | ||||
| } | ||||
|                 ); | ||||
|                 return result1.FirstOrDefault().Value; | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,101 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Modbus; | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public class ModbusUdp : CollectBase | ||||
| { | ||||
|     private readonly ModbusUdpProperty driverPropertys = new(); | ||||
|  | ||||
|     private ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp _plc; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(ModbusUdpDebugPage); | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsSupportRequest => true; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override IReadWrite PLC => _plc; | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         _plc.Disconnect(); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         return _plc.ConnectAsync(cancellationToken); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override void InitDataAdapter() | ||||
|     { | ||||
|         _plc.SetDataAdapter(); | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() | ||||
|     { | ||||
|         return _plc?.UdpSession?.CanSend == true; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) | ||||
|     { | ||||
|         return _plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(deviceVariables, driverPropertys.MaxPack); | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _plc?.Disconnect(); | ||||
|         _plc?.SafeDispose(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|     { | ||||
|         if (client == null) | ||||
|         { | ||||
|             FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                 .SetBindIPHost(new IPHost(0)) | ||||
|                 ; | ||||
|             client = new UdpSession(); | ||||
|             ((UdpSession)client).Setup(FoundataionConfig); | ||||
|         } | ||||
|         //载入配置 | ||||
|         _plc = new((UdpSession)client) | ||||
|         { | ||||
|             DataFormat = driverPropertys.DataFormat, | ||||
|             FrameTime = driverPropertys.FrameTime, | ||||
|             Station = driverPropertys.Station, | ||||
|             TimeOut = driverPropertys.TimeOut, | ||||
|             IsCheckMessageId = driverPropertys.MessageIdCheckEnable | ||||
|         }; | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken) | ||||
|     { | ||||
|         return await _plc.ReadAsync(address, length, cancellationToken); | ||||
|     } | ||||
| } | ||||
| @@ -1,59 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/ModbusRtu" | ||||
|  | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Microsoft.JSInterop; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using Masa.Blazor; | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
|  | ||||
| <SerialSessionPage @ref=SerialSessionPage></SerialSessionPage> | ||||
|  | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">驱动配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
|         @if (_plc != null) | ||||
|         { | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Station)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> | ||||
|  | ||||
|             <MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.DescriptionWithOutSugar(x => x.DataFormat))" | ||||
|                      Items=@(typeof(DataFormat).GetEnumListWithOutSugar()) | ||||
|                      MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                      ItemText=@((u) =>u.Description) | ||||
|                      ItemValue=@(u =>(DataFormat)u.Value) | ||||
|                      HideDetails=@("auto") Height="30" | ||||
|                   Dense> | ||||
|         </MSelect> | ||||
|  | ||||
|             <MCheckbox Class="ma-1" Style="max-width:200px" Label=@(_plc.DescriptionWithOutSugar(x => x.Crc16CheckEnable)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox> | ||||
|  | ||||
|         } | ||||
|     </MRow> | ||||
| </MCard> | ||||
|  | ||||
|  | ||||
| <DriverDebugUIPage @ref=driverDebugUIPage Sections="_sections"> | ||||
|  | ||||
| </DriverDebugUIPage> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
| } | ||||
| @@ -1,40 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|  | ||||
|  | ||||
| 	<Target Name="PostBuild" AfterTargets="PostBuildEvent"> | ||||
|  | ||||
| 		<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25  md %25dir%25  
copy "$(TargetDir)*Modbus*.dll"  %25dir%25


" /> | ||||
|  | ||||
| 	</Target> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\DriverDebugUIBase.cs" Link="DebugPage\DriverDebugUIBase.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\DriverDebugUIPage.razor.cs" Link="DebugPage\DriverDebugUIPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\SerialSessionPage.razor.cs" Link="DebugPage\SerialSessionPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\TcpClientPage.razor.cs" Link="DebugPage\TcpClientPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\TcpServerPage.razor.cs" Link="DebugPage\TcpServerPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\UdpSessionPage.razor.cs" Link="DebugPage\UdpSessionPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\DriverDebugUIPage.razor" Link="DebugPage\DriverDebugUIPage.razor" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\SerialSessionPage.razor" Link="DebugPage\SerialSessionPage.razor" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\TcpClientPage.razor" Link="DebugPage\TcpClientPage.razor" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\TcpServerPage.razor" Link="DebugPage\TcpServerPage.razor" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\UdpSessionPage.razor" Link="DebugPage\UdpSessionPage.razor" /> | ||||
|  | ||||
|  | ||||
|  | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj"> | ||||
| 			<Private>false</Private> | ||||
| 			<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies> | ||||
| 			<ExcludeAssets>runtime</ExcludeAssets> | ||||
| 		</ProjectReference> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| </Project> | ||||
| @@ -1,609 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using IoTSharp.Data; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using MQTTnet; | ||||
| using MQTTnet.Client; | ||||
| using MQTTnet.Diagnostics; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
|  | ||||
| using ThingsGateway.Admin.Core; | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Mqtt; | ||||
|  | ||||
| /// <summary> | ||||
| /// 参考IotSharpClient.SDK.MQTT | ||||
| /// </summary> | ||||
| public class IotSharpClient : UpLoadBase | ||||
| { | ||||
|     /// <summary> | ||||
|     /// rpcmethodname存疑,定为自定义方法,在ThingsGateway上写入变量的方法固定为"Write" | ||||
|     /// </summary> | ||||
|     private const string WriteMethod = "WRITE"; | ||||
|  | ||||
|     private readonly IotSharpClientProperty driverPropertys = new(); | ||||
|     private readonly EasyLock easyLock = new(); | ||||
|     private readonly IotSharpClientVariableProperty variablePropertys = new(); | ||||
|     private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new(); | ||||
|     private ConcurrentQueue<VariableData> _collectVariableRunTimes = new(); | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|  | ||||
|     private IMqttClient _mqttClient; | ||||
|  | ||||
|     private MqttClientOptions _mqttClientOptions; | ||||
|  | ||||
|     private MqttClientSubscribeOptions _mqttSubscribeOptions; | ||||
|  | ||||
|     private RpcSingletonService _rpcCore; | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|     private CollectDeviceWorker collectDeviceHostService; | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => null; | ||||
|     private TimerTick exVariableTimerTick; | ||||
|     private TimerTick exDeviceTimerTick; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|     /// <inheritdoc/> | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_mqttClient != null) | ||||
|         { | ||||
|             var result = await TryMqttClientAsync(cancellationToken); | ||||
|             if (!result.IsSuccess) | ||||
|             { | ||||
|                 LogMessage?.LogWarning($"{ToString()}-连接MqttServer失败:{result.Message}"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 ////变化推送 | ||||
|                 var varList = _collectVariableRunTimes.ToListWithDequeue(); | ||||
|                 if (varList?.Count != 0) | ||||
|                 { | ||||
|                     //分解List,避免超出mqtt字节大小限制 | ||||
|                     var varData = varList.GroupBy(a => a.DeviceName).ToList(); | ||||
|                     foreach (var item in varData) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             Dictionary<string, object> nameValueDict = new(); | ||||
|                             foreach (var pair in item) | ||||
|                             { | ||||
|                                 //只用最新的变量值 | ||||
|                                 nameValueDict.AddOrUpdate(pair.Name, pair.Value); | ||||
|                             } | ||||
|                             if (!cancellationToken.IsCancellationRequested) | ||||
|                             { | ||||
|                                 await MqttUp($"devices/{item.Key}/telemetry", nameValueDict.ToJsonString(), cancellationToken); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 break; | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exVariableTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.Adapt<List<VariableData>>(); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             //分解List,避免超出mqtt字节大小限制 | ||||
|                             var varData = varList.GroupBy(a => a.DeviceName).ToList(); | ||||
|                             foreach (var item in varData) | ||||
|                             { | ||||
|                                 try | ||||
|                                 { | ||||
|                                     Dictionary<string, object> nameValueDict = new(); | ||||
|                                     foreach (var pair in item) | ||||
|                                     { | ||||
|                                         //只用最新的变量值 | ||||
|                                         nameValueDict.AddOrUpdate(pair.Name, pair.Value); | ||||
|                                     } | ||||
|                                     if (!cancellationToken.IsCancellationRequested) | ||||
|                                     { | ||||
|                                         await MqttUp($"devices/{item.Key}/telemetry", nameValueDict.ToJsonString(), cancellationToken); | ||||
|                                     } | ||||
|                                     else | ||||
|                                     { | ||||
|                                         break; | ||||
|                                     } | ||||
|  | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage?.LogWarning(ex); | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 ////变化推送 | ||||
|                 var devList = _collectDeviceRunTimes.ToListWithDequeue(); | ||||
|                 if (devList?.Count != 0) | ||||
|                 { | ||||
|                     //分解List,避免超出mqtt字节大小限制 | ||||
|                     var devData = devList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                     foreach (var item in devData) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             if (!cancellationToken.IsCancellationRequested) | ||||
|                             { | ||||
|  | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exDeviceTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     var devList = _collectDevice.Adapt<List<DeviceData>>(); | ||||
|                     if (devList?.Count != 0) | ||||
|                     { | ||||
|                         //分解List,避免超出mqtt字节大小限制 | ||||
|                         var devData = devList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                         foreach (var item in devData) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 if (!cancellationToken.IsCancellationRequested) | ||||
|                                 { | ||||
|  | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() => _mqttClient?.IsConnected == true; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return $" {nameof(IotSharpClient)}-IP:{driverPropertys.IP}-Port:{driverPropertys.Port}-Accesstoken:{driverPropertys.Accesstoken}"; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|  | ||||
|             _collectDevice?.ForEach(a => | ||||
|             { | ||||
|                 a.DeviceStatusChange -= DeviceStatusChange; | ||||
|             }); | ||||
|             _mqttClient?.SafeDispose(); | ||||
|             _mqttClient = null; | ||||
|             _uploadVariables = null; | ||||
|             _collectDeviceRunTimes.Clear(); | ||||
|             _collectVariableRunTimes.Clear(); | ||||
|             _collectDeviceRunTimes = null; | ||||
|             _collectVariableRunTimes = null; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private List<CollectDeviceRunTime> _collectDevice; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|         var log = new MqttNetEventLogger(); | ||||
|         log.LogMessagePublished += Log_LogMessagePublished; | ||||
|         var mqttFactory = new MqttFactory(log); | ||||
|         _mqttClientOptions = mqttFactory.CreateClientOptionsBuilder() | ||||
|            .WithClientId(Guid.NewGuid().ToString()) | ||||
|            .WithCredentials(driverPropertys.Accesstoken)//账密 | ||||
|            .WithTcpServer(driverPropertys.IP, driverPropertys.Port)//服务器 | ||||
|            .WithCleanSession(true) | ||||
|            .WithKeepAlivePeriod(TimeSpan.FromSeconds(120.0)) | ||||
|            .Build(); | ||||
|         _mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder() | ||||
|             .WithTopicFilter( | ||||
|                 f => | ||||
|                 { | ||||
|                     f.WithTopic($"devices/+/rpc/request/+/+");//RPC控制请求,需要订阅 | ||||
|                 }) | ||||
|  | ||||
|             .Build(); | ||||
|         _mqttClient = mqttFactory.CreateMqttClient(); | ||||
|         _mqttClient.ConnectedAsync += MqttClient_ConnectedAsync; | ||||
|         _mqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync; | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|         _rpcCore = App.GetService<RpcSingletonService>(); | ||||
|         collectDeviceHostService = BackgroundServiceUtil.GetBackgroundService<CollectDeviceWorker>(); | ||||
|  | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|            .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).GetBoolValue()) | ||||
|            .ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         _collectDevice = _globalDeviceData.CollectDevices.Where(a => _uploadVariables.Select(b => b.DeviceId).Contains(a.Id)).ToList(); | ||||
|  | ||||
|         _collectDevice?.ForEach(a => | ||||
|         { | ||||
|             a.DeviceStatusChange += DeviceStatusChange; | ||||
|         }); | ||||
|         _uploadVariables.ForEach(a => | ||||
|         { | ||||
|             a.VariableValueChange += VariableValueChange; | ||||
|         }); | ||||
|         if (driverPropertys.UploadInterval <= 1000) driverPropertys.UploadInterval = 1000; | ||||
|         exVariableTimerTick = new(driverPropertys.UploadInterval); | ||||
|         exDeviceTimerTick = new(driverPropertys.UploadInterval); | ||||
|     } | ||||
|  | ||||
|     private void Log_LogMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs e) | ||||
|     { | ||||
|         LogMessage.LogOut(e.LogMessage.Level, e.LogMessage.Source, e.LogMessage.Message, e.LogMessage.Exception); | ||||
|     } | ||||
|  | ||||
|     private void DeviceStatusChange(CollectDeviceRunTime collectDeviceRunTime) | ||||
|     { | ||||
|         if (driverPropertys?.IsInterval != true) | ||||
|             _collectDeviceRunTimes.Enqueue(collectDeviceRunTime.Adapt<DeviceData>()); | ||||
|     } | ||||
|  | ||||
|     private async Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e) | ||||
|     { | ||||
|  | ||||
|         if (e.ApplicationMessage.Topic.StartsWith($"devices/") && e.ApplicationMessage.Topic.Contains("/rpc/request/")) | ||||
|         { | ||||
|             var tps = e.ApplicationMessage.Topic.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); | ||||
|             var rpcmethodname = tps[4]; | ||||
|             var rpcdevicename = tps[1]; | ||||
|             var rpcrequestid = tps[5]; | ||||
|             if (!string.IsNullOrEmpty(rpcmethodname) && !string.IsNullOrEmpty(rpcdevicename) && !string.IsNullOrEmpty(rpcrequestid)) | ||||
|             { | ||||
|                 var rpcResponse = new RpcResponse() | ||||
|                 { | ||||
|                     DeviceId = rpcdevicename, | ||||
|                     ResponseId = rpcrequestid, | ||||
|                     Method = rpcmethodname, | ||||
|                     Success = false, | ||||
|                     Data = "参数为空" | ||||
|                 }; | ||||
|                 await SendResponseAsync(rpcResponse); | ||||
|                 return; | ||||
|             } | ||||
|             if (!driverPropertys.DeviceRpcEnable) | ||||
|             { | ||||
|                 var rpcResponse = new RpcResponse() | ||||
|                 { | ||||
|                     DeviceId = rpcdevicename, | ||||
|                     ResponseId = rpcrequestid, | ||||
|                     Method = rpcmethodname, | ||||
|                     Success = false, | ||||
|                     Data = "不允许写入" | ||||
|                 }; | ||||
|                 await SendResponseAsync(rpcResponse); | ||||
|                 return; | ||||
|             } | ||||
|             //rpcmethodname定为自定义方法,在ThingsGateway上写入变量的方法固定为"Write" | ||||
|             if (rpcmethodname.ToUpper() != WriteMethod) | ||||
|             { | ||||
|                 var rpcResponse = new RpcResponse() | ||||
|                 { | ||||
|                     DeviceId = rpcdevicename, | ||||
|                     ResponseId = rpcrequestid, | ||||
|                     Method = rpcmethodname, | ||||
|                     Success = false, | ||||
|                     Data = "不支持的方法" | ||||
|                 }; | ||||
|                 await SendResponseAsync(rpcResponse); | ||||
|                 return; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 RpcResponse rpcResponse = new(); | ||||
|                 var nameValue = e.ApplicationMessage.ConvertPayloadToString().FromJsonString<List<KeyValuePair<string, string>>>(); | ||||
|                 Dictionary<string, OperResult> results = new(); | ||||
|                 if (nameValue?.Count > 0) | ||||
|                 { | ||||
|                     foreach (var item in nameValue) | ||||
|                     { | ||||
|                         var tag = _uploadVariables.FirstOrDefault(a => a.Name == item.Key); | ||||
|                         if (tag != null) | ||||
|                         { | ||||
|                             var rpcEnable = | ||||
| GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean() | ||||
| && driverPropertys.DeviceRpcEnable; | ||||
|                             if (rpcEnable == true) | ||||
|                             { | ||||
|  | ||||
|  | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 results.Add(item.Key, new OperResult("权限不足,变量不支持写入")); | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             results.Add(item.Key, new OperResult("不存在该变量")); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + rpcrequestid, nameValue | ||||
|                         .Where(a => !results.Any(b => b.Key == a.Key)) | ||||
|                         .ToDictionary(a => a.Key, a => a.Value)); | ||||
|  | ||||
|                     results.AddRange(result); | ||||
|                     rpcResponse = new() | ||||
|                     { | ||||
|                         DeviceId = rpcdevicename, | ||||
|                         ResponseId = rpcrequestid, | ||||
|                         Method = rpcmethodname, | ||||
|                         Success = !results.Any(a => !a.Value.IsSuccess), | ||||
|                         Data = results.ToJsonString() | ||||
|                     }; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     rpcResponse = new() | ||||
|                     { | ||||
|                         DeviceId = rpcdevicename, | ||||
|                         ResponseId = rpcrequestid, | ||||
|                         Method = rpcmethodname, | ||||
|                         Success = false, | ||||
|                         Data = "消息体参数无法解析" | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|                 await SendResponseAsync(rpcResponse); | ||||
|  | ||||
|             } | ||||
|  | ||||
|  | ||||
|         } | ||||
|  | ||||
|         async Task SendResponseAsync(RpcResponse rpcResponse) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var topic = $"devices/{rpcResponse.DeviceId}/rpc/response/{rpcResponse.Method}/{rpcResponse.ResponseId}"; | ||||
|  | ||||
|                 var variableMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic($"{topic}") | ||||
| .WithPayload(rpcResponse.ToJsonString()).Build(); | ||||
|                 var isConnect = await TryMqttClientAsync(CancellationToken.None); | ||||
|                 if (isConnect.IsSuccess) | ||||
|                     await _mqttClient.PublishAsync(variableMessage); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private async Task MqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg) | ||||
|     { | ||||
|         var subResult = await _mqttClient.SubscribeAsync(_mqttSubscribeOptions); | ||||
|  | ||||
|         if (subResult.Items.Any(a => a.ResultCode > (MqttClientSubscribeResultCode)10)) | ||||
|         { | ||||
|             LogMessage?.LogWarning(subResult.Items | ||||
|                 .Where(a => a.ResultCode > (MqttClientSubscribeResultCode)10) | ||||
|                 .Select(a => a.ToString()).ToJsonString()); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 上传mqtt内容,并进行离线缓存 | ||||
|     /// </summary> | ||||
|     /// <param name="topic"></param> | ||||
|     /// <param name="payLoad"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     private async Task MqttUp(string topic, string payLoad, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var variableMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic(topic) | ||||
| .WithPayload(payLoad).Build(); | ||||
|         var isConnect = await TryMqttClientAsync(cancellationToken); | ||||
|         if (isConnect.IsSuccess) | ||||
|         { | ||||
|             //连接成功时补发缓存数据 | ||||
|             var cacheData = await CacheDb.GetCacheData(); | ||||
|             foreach (var item in cacheData) | ||||
|             { | ||||
|                 var cacheMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic(item.Topic) | ||||
| .WithPayload(item.CacheStr).Build(); | ||||
|                 var cacheResult = await _mqttClient.PublishAsync(cacheMessage, cancellationToken); | ||||
|                 if (cacheResult.IsSuccess) | ||||
|                 { | ||||
|                     await CacheDb.DeleteCacheData(item.Id); | ||||
|                     LogMessage.Trace($"{FoundationConst.LogMessageHeader}主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var result = await _mqttClient.PublishAsync(variableMessage, cancellationToken); | ||||
|             if (!result.IsSuccess) | ||||
|             { | ||||
|                 await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 LogMessage.Trace($"{FoundationConst.LogMessageHeader}主题:{topic}{Environment.NewLine}负载:{payLoad}"); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<OperResult> TryMqttClientAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_mqttClient?.IsConnected == true) | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         return await Cilent(); | ||||
|  | ||||
|         async Task<OperResult> Cilent() | ||||
|         { | ||||
|             if (_mqttClient?.IsConnected == true) | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             try | ||||
|             { | ||||
|                 await easyLock.WaitAsync(); | ||||
|                 if (_mqttClient?.IsConnected == true) | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(driverPropertys.ConnectTimeOut)); | ||||
|                 using CancellationTokenSource StoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token); | ||||
|                 if (_mqttClient?.IsConnected == true) | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 if (_mqttClient == null) | ||||
|                     return new OperResult("未初始化"); | ||||
|                 var result = await _mqttClient?.ConnectAsync(_mqttClientOptions, StoppingToken.Token); | ||||
|                 if (result.ResultCode == MqttClientConnectResultCode.Success) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result.ReasonString); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult(ex); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 easyLock.Release(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (driverPropertys?.IsInterval != true) | ||||
|             _collectVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<VariableData>()); | ||||
|     } | ||||
| } | ||||
| @@ -1,577 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using MQTTnet; | ||||
| using MQTTnet.Client; | ||||
| using MQTTnet.Diagnostics; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Mqtt; | ||||
|  | ||||
| /// <summary> | ||||
| /// MqttClient | ||||
| /// </summary> | ||||
| public class MqttClient : UpLoadBase | ||||
| { | ||||
|     private readonly MqttClientProperty driverPropertys = new(); | ||||
|     private readonly EasyLock easyLock = new(); | ||||
|     private readonly MqttClientVariableProperty variablePropertys = new(); | ||||
|     private List<CollectDeviceRunTime> _collectDevice; | ||||
|     private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new(); | ||||
|     private ConcurrentQueue<VariableData> _collectVariableRunTimes = new(); | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|     private IMqttClient _mqttClient; | ||||
|  | ||||
|     private MqttClientOptions _mqttClientOptions; | ||||
|  | ||||
|     private MqttClientSubscribeOptions _mqttSubscribeOptions; | ||||
|  | ||||
|     private RpcSingletonService _rpcCore; | ||||
|  | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|  | ||||
|     private CollectDeviceWorker collectDeviceHostService; | ||||
|     private TimerTick exDeviceTimerTick; | ||||
|  | ||||
|     private TimerTick exVariableTimerTick; | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => typeof(MqttClientDebugPage); | ||||
|     /// <inheritdoc/> | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_mqttClient != null) | ||||
|         { | ||||
|             var result = await TryMqttClientAsync(cancellationToken); | ||||
|             if (!result.IsSuccess) | ||||
|             { | ||||
|                 LogMessage?.LogWarning($"{ToString()}-连接MqttServer失败:{result.Message}"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 ////变化推送 | ||||
|                 var varList = _collectVariableRunTimes.ToListWithDequeue(); | ||||
|                 if (varList?.Count != 0) | ||||
|                 { | ||||
|                     //分解List,避免超出mqtt字节大小限制 | ||||
|                     var varData = varList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                     foreach (var item in varData) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             if (!cancellationToken.IsCancellationRequested) | ||||
|                             { | ||||
|                                 await MqttUp($"{driverPropertys.VariableTopic}", item.GetSciptListValue(driverPropertys.BigTextScriptVariableModel), cancellationToken); | ||||
|  | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 break; | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exVariableTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.Adapt<List<VariableData>>(); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             //分解List,避免超出mqtt字节大小限制 | ||||
|                             var varData = varList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                             foreach (var item in varData) | ||||
|                             { | ||||
|                                 try | ||||
|                                 { | ||||
|                                     if (!cancellationToken.IsCancellationRequested) | ||||
|                                     { | ||||
|  | ||||
|                                         await MqttUp($"{driverPropertys.VariableTopic}", item.GetSciptListValue(driverPropertys.BigTextScriptVariableModel), cancellationToken); | ||||
|  | ||||
|                                     } | ||||
|                                     else | ||||
|                                     { | ||||
|                                         break; | ||||
|                                     } | ||||
|  | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage?.LogWarning(ex); | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 ////变化推送 | ||||
|                 var devList = _collectDeviceRunTimes.ToListWithDequeue(); | ||||
|                 if (devList?.Count != 0) | ||||
|                 { | ||||
|                     //分解List,避免超出mqtt字节大小限制 | ||||
|                     var devData = devList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                     foreach (var item in devData) | ||||
|                     { | ||||
|                         try | ||||
|                         { | ||||
|                             if (!cancellationToken.IsCancellationRequested) | ||||
|                             { | ||||
|                                 await MqttUp($"{driverPropertys.DeviceTopic}", item.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel), cancellationToken); | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                         catch (Exception ex) | ||||
|                         { | ||||
|                             LogMessage?.LogWarning(ex); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|             else | ||||
|             { | ||||
|                 if (exDeviceTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     var devList = _collectDevice.Adapt<List<DeviceData>>(); | ||||
|                     if (devList?.Count != 0) | ||||
|                     { | ||||
|                         //分解List,避免超出mqtt字节大小限制 | ||||
|                         var devData = devList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                         foreach (var item in devData) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 if (!cancellationToken.IsCancellationRequested) | ||||
|                                 { | ||||
|                                     await MqttUp($"{driverPropertys.DeviceTopic}", item.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel), cancellationToken); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public override bool IsConnected() => _mqttClient?.IsConnected == true; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return $" {nameof(MqttClient)} IP:{driverPropertys.IP} Port:{driverPropertys.Port}"; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|  | ||||
|             _globalDeviceData?.CollectDevices?.ForEach(a => | ||||
|             { | ||||
|                 a.DeviceStatusChange -= DeviceStatusChange; | ||||
|             }); | ||||
|             _mqttClient?.SafeDispose(); | ||||
|             _mqttClient = null; | ||||
|             _uploadVariables = null; | ||||
|             _collectDeviceRunTimes.Clear(); | ||||
|             _collectVariableRunTimes.Clear(); | ||||
|             _collectDeviceRunTimes = null; | ||||
|             _collectVariableRunTimes = null; | ||||
|             base.Dispose(disposing); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|         var log = new MqttNetEventLogger(); | ||||
|         log.LogMessagePublished += Log_LogMessagePublished; | ||||
|         var mqttFactory = new MqttFactory(log); | ||||
|         _mqttClientOptions = mqttFactory.CreateClientOptionsBuilder() | ||||
|            .WithClientId(driverPropertys.ConnectId) | ||||
|            .WithCredentials(driverPropertys.UserName, driverPropertys.Password)//账密 | ||||
|            .WithTcpServer(driverPropertys.IP, driverPropertys.Port)//服务器 | ||||
|            .WithCleanSession(true) | ||||
|            .WithKeepAlivePeriod(TimeSpan.FromSeconds(120.0)) | ||||
|            .WithoutThrowOnNonSuccessfulConnectResponse() | ||||
|            .Build(); | ||||
|         _mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder() | ||||
|             .WithTopicFilter( | ||||
|                 f => | ||||
|                 { | ||||
|                     f.WithTopic(driverPropertys.RpcWriteTopic); | ||||
|                 }) | ||||
|                      .WithTopicFilter( | ||||
|                 f => | ||||
|                 { | ||||
|                     f.WithTopic(driverPropertys.QuestRpcTopic); | ||||
|                 }) | ||||
|             .Build(); | ||||
|         _mqttClient = mqttFactory.CreateMqttClient(); | ||||
|         _mqttClient.ConnectedAsync += MqttClient_ConnectedAsync; | ||||
|         _mqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync; | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|         _rpcCore = App.GetService<RpcSingletonService>(); | ||||
|         collectDeviceHostService = BackgroundServiceUtil.GetBackgroundService<CollectDeviceWorker>(); | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|            .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).GetBoolValue()) | ||||
|            .ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         _collectDevice = _globalDeviceData.CollectDevices.Where(a => _uploadVariables.Select(b => b.DeviceId).Contains(a.Id)).ToList(); | ||||
|         if (!driverPropertys.IsInterval) | ||||
|         { | ||||
|             _collectDevice.ForEach(a => | ||||
|             { | ||||
|                 a.DeviceStatusChange += DeviceStatusChange; | ||||
|             }); | ||||
|             _uploadVariables.ForEach(a => | ||||
|             { | ||||
|                 a.VariableValueChange += VariableValueChange; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.UploadInterval <= 1000) driverPropertys.UploadInterval = 1000; | ||||
|         exVariableTimerTick = new(driverPropertys.UploadInterval); | ||||
|         exDeviceTimerTick = new(driverPropertys.UploadInterval); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void Log_LogMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs e) | ||||
|     { | ||||
|         LogMessage.LogOut(e.LogMessage.Level, e.LogMessage.Source, e.LogMessage.Message, e.LogMessage.Exception); | ||||
|     } | ||||
|  | ||||
|     private async Task AllPublishAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         //保留消息 | ||||
|         //分解List,避免超出mqtt字节大小限制 | ||||
|         var varData = _globalDeviceData.AllVariables.Adapt<List<VariableData>>().ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|         var devData = _globalDeviceData.CollectDevices.Adapt<List<DeviceData>>().ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|         var isConnect = await TryMqttClientAsync(cancellationToken); | ||||
|         foreach (var item in devData) | ||||
|         { | ||||
|             var devMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic($"{driverPropertys.DeviceTopic}") | ||||
| .WithPayload(item.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel)).Build(); | ||||
|             if (isConnect.IsSuccess) | ||||
|                 await _mqttClient.PublishAsync(devMessage, cancellationToken); | ||||
|         } | ||||
|  | ||||
|         foreach (var item in varData) | ||||
|         { | ||||
|             var varMessage = new MqttApplicationMessageBuilder() | ||||
|             .WithTopic($"{driverPropertys.VariableTopic}") | ||||
|             .WithPayload(item.GetSciptListValue(driverPropertys.BigTextScriptVariableModel)).Build(); | ||||
|             if (isConnect.IsSuccess) | ||||
|                 await _mqttClient.PublishAsync(varMessage, cancellationToken); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void DeviceStatusChange(CollectDeviceRunTime collectDeviceRunTime) | ||||
|     { | ||||
|         if (driverPropertys?.IsInterval != true) | ||||
|             _collectDeviceRunTimes.Enqueue(collectDeviceRunTime.Adapt<DeviceData>()); | ||||
|     } | ||||
|  | ||||
|     private async Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs args) | ||||
|     { | ||||
|         if (args.ApplicationMessage.Topic == driverPropertys.QuestRpcTopic && args.ApplicationMessage.PayloadSegment.Count > 0) | ||||
|         { | ||||
|             await AllPublishAsync(CancellationToken.None); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!driverPropertys.DeviceRpcEnable || string.IsNullOrEmpty(args.ClientId)) | ||||
|             return; | ||||
|         if (args.ApplicationMessage.Topic != driverPropertys.RpcWriteTopic) | ||||
|             return; | ||||
|         var rpcDatas = Encoding.UTF8.GetString(args.ApplicationMessage.PayloadSegment).FromJsonString<MqttRpcNameVaueWithId>(); | ||||
|         if (rpcDatas == null) | ||||
|             return; | ||||
|  | ||||
|         MqttRpcResult mqttRpcResult = new() { RpcId = rpcDatas.RpcId, Success = true }; | ||||
|         try | ||||
|         { | ||||
|             foreach (var rpcData in rpcDatas.WriteInfos) | ||||
|             { | ||||
|  | ||||
|                 var tag = _uploadVariables.FirstOrDefault(a => a.Name == rpcData.Key); | ||||
|                 if (tag != null) | ||||
|                 { | ||||
|                     var rpcEnable = GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean(); | ||||
|                     if (rpcEnable == true) | ||||
|                     { | ||||
|  | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         mqttRpcResult.Success = false; | ||||
|                         mqttRpcResult.Message.Add(rpcData.Key, new("权限不足,变量不支持写入")); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     mqttRpcResult.Success = false; | ||||
|                     mqttRpcResult.Message.Add(rpcData.Key, new("不存在该变量")); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + args.ClientId, | ||||
|                 rpcDatas.WriteInfos.Where( | ||||
|                 a => !mqttRpcResult.Message.Any(b => b.Key == a.Key)).ToDictionary(a => a.Key, a => a.Value)); | ||||
|  | ||||
|             mqttRpcResult.Message.AddRange(result); | ||||
|             mqttRpcResult.Success = !mqttRpcResult.Message.Any(a => !a.Value.IsSuccess); | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
|             var variableMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic($"{driverPropertys.RpcSubTopic}") | ||||
| .WithPayload(mqttRpcResult.ToJsonString()).Build(); | ||||
|             var isConnect = await TryMqttClientAsync(CancellationToken.None); | ||||
|             if (isConnect.IsSuccess) | ||||
|                 await _mqttClient.PublishAsync(variableMessage); | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task MqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg) | ||||
|     { | ||||
|         var subResult = await _mqttClient.SubscribeAsync(_mqttSubscribeOptions); | ||||
|         if (subResult.Items.Any(a => a.ResultCode > (MqttClientSubscribeResultCode)10)) | ||||
|         { | ||||
|             LogMessage?.Warning($"订阅失败-{subResult.Items | ||||
|                 .Where(a => a.ResultCode > (MqttClientSubscribeResultCode)10) | ||||
|                 .Select(a => | ||||
|                 new | ||||
|                 { | ||||
|                     Topic = a.TopicFilter.Topic, | ||||
|                     ResultCode = a.ResultCode.ToString() | ||||
|                 } | ||||
|                 ) | ||||
|                 .ToJsonString()}"); | ||||
|         } | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 上传mqtt内容,并进行离线缓存 | ||||
|     /// </summary> | ||||
|     /// <param name="topic"></param> | ||||
|     /// <param name="payLoad"></param> | ||||
|     /// <param name="cancellationToken"></param> | ||||
|     /// <returns></returns> | ||||
|     private async Task MqttUp(string topic, string payLoad, CancellationToken cancellationToken) | ||||
|     { | ||||
|         var variableMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic(topic) | ||||
| .WithPayload(payLoad).Build(); | ||||
|         var isConnect = await TryMqttClientAsync(cancellationToken); | ||||
|         if (isConnect.IsSuccess) | ||||
|         { | ||||
|             //连接成功时补发缓存数据 | ||||
|             var cacheData = await CacheDb.GetCacheData(); | ||||
|             foreach (var item in cacheData) | ||||
|             { | ||||
|                 var cacheMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic(item.Topic) | ||||
| .WithPayload(item.CacheStr).Build(); | ||||
|                 var cacheResult = await _mqttClient.PublishAsync(cacheMessage, cancellationToken); | ||||
|                 if (cacheResult.IsSuccess) | ||||
|                 { | ||||
|                     await CacheDb.DeleteCacheData(item.Id); | ||||
|                     LogMessage.Trace($"{FoundationConst.LogMessageHeader}主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             var result = await _mqttClient.PublishAsync(variableMessage, cancellationToken); | ||||
|             if (!result.IsSuccess) | ||||
|             { | ||||
|                 await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 LogMessage.Trace($"{FoundationConst.LogMessageHeader}主题:{topic}{Environment.NewLine}负载:{payLoad}"); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             await CacheDb.AddCacheData(topic, payLoad, driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|     } | ||||
|     private async Task<OperResult> TryMqttClientAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         if (_mqttClient?.IsConnected == true) | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         return await Cilent(); | ||||
|  | ||||
|         async Task<OperResult> Cilent() | ||||
|         { | ||||
|             if (_mqttClient?.IsConnected == true) | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             try | ||||
|             { | ||||
|                 await easyLock.WaitAsync(); | ||||
|                 if (_mqttClient?.IsConnected == true) | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(driverPropertys.ConnectTimeOut)); | ||||
|                 using CancellationTokenSource StoppingToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutToken.Token); | ||||
|                 if (_mqttClient?.IsConnected == true) | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 if (_mqttClient == null) | ||||
|                 { | ||||
|                     return new OperResult("未初始化"); | ||||
|  | ||||
|                 } | ||||
|                 var result = await _mqttClient?.ConnectAsync(_mqttClientOptions, StoppingToken.Token); | ||||
|                 if (result.ResultCode == MqttClientConnectResultCode.Success) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(result.ReasonString); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult(ex); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 easyLock.Release(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (driverPropertys?.IsInterval != true) | ||||
|             _collectVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<VariableData>()); | ||||
|     } | ||||
| } | ||||
| @@ -1,85 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/MqttClient" | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Masa.Blazor.Presets; | ||||
| @using Microsoft.JSInterop; | ||||
|  | ||||
|  | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension; | ||||
| @using Masa.Blazor | ||||
|  | ||||
|  | ||||
|  | ||||
| <MqttClientPage @ref=mqttClientPage></MqttClientPage> | ||||
|  | ||||
|  | ||||
| <DriverDebugUIPage @ref=driverDebugUIPage Sections="_sections" ShowDefaultOtherContent=false> | ||||
|     <ReadWriteContent> | ||||
|         <div class="my-1 py-1"> | ||||
|  | ||||
|             <MTextarea Class="mx-1 my-1" Label="订阅主题" Dense Outlined HideDetails="@("auto")" @bind-Value=@driverDebugUIPage.Address /> | ||||
|  | ||||
|              <MButton Class="mx-1 my-1" Color="primary" OnClick="SubscribeAsync"> | ||||
|                 添加 | ||||
|             </MButton> | ||||
|              <MButton Class="mx-1 my-1" Color="primary" OnClick="UnsubscribeAsync"> | ||||
|                 移除 | ||||
|             </MButton> | ||||
|  | ||||
|              <MTextarea Class="mx-1 my-1" Label="发布主题" Dense Outlined HideDetails="@("auto")" @bind-Value=@PublishTopic /> | ||||
|              <MTextarea Class="mx-1 my-1" Label="发布内容" Dense Outlined HideDetails="@("auto")" @bind-Value=@PublishValue /> | ||||
|  | ||||
|              <MButton Class="mx-1 my-1" Color="primary" OnClick="PublishAsync"> | ||||
|                  发布 | ||||
|              </MButton> | ||||
|  | ||||
|  | ||||
|              <MSubheader Class="mt-4 font-weight-black"> Rpc  </MSubheader> | ||||
|  | ||||
|              <MTextarea Class="mx-1 mt-3 my-1" Label="Rpc主题" Dense Outlined HideDetails="@("auto")" @bind-Value=@MqttRpcTopicPair.RequestTopic /> | ||||
|              <MTextarea Class="mx-1 mt-3 my-1" Label="Rpc返回主题" Dense Outlined HideDetails="@("auto")" @bind-Value=@MqttRpcTopicPair.ResponseTopic /> | ||||
|              <MTextarea Class="mx-1 mt-3 my-1" Label="Rpc内容" Dense Outlined HideDetails="@("auto")" @bind-Value=@driverDebugUIPage.WriteValue /> | ||||
|              <MButton Class="mx-1 my-1" Color="primary" OnClick="RpcExecuteAsync"> | ||||
|                 执行 | ||||
|             </MButton> | ||||
|  | ||||
|  | ||||
|         </div> | ||||
|  | ||||
|  | ||||
|  | ||||
|         <MCol Class="my-1 py-1"> | ||||
|             <MRow NoGutters> | ||||
|  | ||||
|  | ||||
|  | ||||
|             </MRow> | ||||
|  | ||||
|         </MCol> | ||||
|  | ||||
|     </ReadWriteContent> | ||||
| </DriverDebugUIPage> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
|  | ||||
| } | ||||
| @@ -1,152 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Masa.Blazor; | ||||
|  | ||||
| using Microsoft.AspNetCore.Components; | ||||
|  | ||||
| using MQTTnet; | ||||
| using MQTTnet.Extensions.Rpc; | ||||
|  | ||||
| using System.Collections.Generic; | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Plugin.Mqtt; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
| /// <summary> | ||||
| /// MqttClientDebugPage | ||||
| /// </summary> | ||||
| public partial class MqttClientDebugPage : IDisposable | ||||
| { | ||||
|     private DriverDebugUIPage driverDebugUIPage; | ||||
|     private MqttClientPage mqttClientPage; | ||||
|  | ||||
|     [Inject] | ||||
|     IPopupService PopupService { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         mqttClientPage.SafeDispose(); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             if (mqttClientPage != null) | ||||
|             { | ||||
|                 mqttClientPage.LogAction = driverDebugUIPage.LogOut; | ||||
|             } | ||||
|             //初始化 | ||||
|             driverDebugUIPage.Address = "ThingsGateway/Variable"; | ||||
|  | ||||
|             driverDebugUIPage.WriteValue = new MqttRpcNameVaueWithId() | ||||
|             { | ||||
|                 RpcId = Guid.NewGuid().ToString(), | ||||
|                 WriteInfos = new Dictionary<string, string>() | ||||
| { | ||||
|     { "tag1", "123" } | ||||
| } | ||||
|             }.ToJsonString(); | ||||
|             ; | ||||
|             mqttClientPage.IP = "127.0.0.1"; | ||||
|             mqttClientPage.Port = 1883; | ||||
|             mqttClientPage.UserName = "admin"; | ||||
|             mqttClientPage.Password = "111111"; | ||||
|             mqttClientPage.StateHasChangedAsync(); | ||||
|  | ||||
|             //载入配置 | ||||
|             StateHasChanged(); | ||||
|             driverDebugUIPage.Sections.Clear(); | ||||
|         } | ||||
|  | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|  | ||||
|     private async Task SubscribeAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var mqttSubscribeOptions = mqttClientPage.MqttFactory.CreateSubscribeOptionsBuilder() | ||||
| .WithTopicFilter( | ||||
| f => | ||||
| { | ||||
|     f.WithTopic(driverDebugUIPage.Address); | ||||
| }) | ||||
| .Build(); | ||||
|  | ||||
|             await mqttClientPage.MqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 订阅{driverDebugUIPage.Address}成功")); | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private async Task UnsubscribeAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var mqttSubscribeOptions = mqttClientPage.MqttFactory.CreateUnsubscribeOptionsBuilder() | ||||
| .WithTopicFilter(driverDebugUIPage.Address) | ||||
| .Build(); | ||||
|  | ||||
|             await mqttClientPage.MqttClient.UnsubscribeAsync(mqttSubscribeOptions, CancellationToken.None); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 取消订阅{driverDebugUIPage.Address}成功")); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|  | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     string PublishTopic; | ||||
|     string PublishValue; | ||||
|     private async Task PublishAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var devMessage = new MqttApplicationMessageBuilder() | ||||
| .WithTopic($"{PublishTopic}") | ||||
| .WithPayload(PublishValue).Build(); | ||||
|             await mqttClientPage.MqttClient.PublishAsync(devMessage, CancellationToken.None); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - 发布{PublishTopic}成功")); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     MqttRpcTopicPair MqttRpcTopicPair = new() { RequestTopic = "ThingsGateway/RpcWrite", ResponseTopic = "ThingsGateway/RpcSub" }; | ||||
|     private async Task RpcExecuteAsync() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             using MqttRpcClient mqttRpcClient = new(mqttClientPage.MqttClient); | ||||
|             var data = await mqttRpcClient.ExecuteAsync(MqttRpcTopicPair, driverDebugUIPage.WriteValue, MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce, TimeSpan.FromSeconds(10)); | ||||
|             var str = Encoding.UTF8.GetString(data); | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {str}")); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             driverDebugUIPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Error, $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(driverDebugUIPage.InitTimezone.TimezoneOffset)} - {ex}")); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,125 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using MQTTnet; | ||||
| using MQTTnet.Client; | ||||
| using MQTTnet.Diagnostics; | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Gateway.Application; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
| /// <summary> | ||||
| /// MqttClientPage | ||||
| /// </summary> | ||||
| public partial class MqttClientPage | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 日志输出 | ||||
|     /// </summary> | ||||
|     public Action<LogLevel, object, string, Exception> LogAction; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// OPC | ||||
|     /// </summary> | ||||
|     public IMqttClient MqttClient; | ||||
|     public MqttClientOptions MqttClientOptions; | ||||
|  | ||||
|     public MqttFactory MqttFactory; | ||||
|  | ||||
|     public string ConnectId; | ||||
|  | ||||
|     public string IP; | ||||
|  | ||||
|     public string Password; | ||||
|  | ||||
|     public int Port; | ||||
|  | ||||
|     public string UserName; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     public void Dispose() | ||||
|     { | ||||
|         MqttClient.SafeDispose(); | ||||
|     } | ||||
|     /// <inheritdoc/> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         var log = new MqttNetEventLogger(); | ||||
|         log.LogMessagePublished += Log_LogMessagePublished; | ||||
|         MqttFactory = new MqttFactory(log); | ||||
|  | ||||
|  | ||||
|         MqttClient = MqttFactory.CreateMqttClient(); | ||||
|         MqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync; | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|     private void Log_LogMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs e) | ||||
|     { | ||||
|         new EasyLogger(LogAction) { LogLevel = LogLevel.Trace }.LogOut(e.LogMessage.Level, e.LogMessage.Source, e.LogMessage.Message, e.LogMessage.Exception); | ||||
|     } | ||||
|     private async Task Connect() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await MqttClient.DisconnectAsync(); | ||||
|             await GetMqttClient(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task DisConnect() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             await MqttClient.DisconnectAsync(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private async Task<IMqttClient> GetMqttClient() | ||||
|     { | ||||
|         //载入配置 | ||||
|         MqttClientOptions = MqttFactory.CreateClientOptionsBuilder() | ||||
|    .WithClientId(ConnectId) | ||||
|    .WithCredentials(UserName, Password)//账密 | ||||
|    .WithTcpServer(IP, Port)//服务器 | ||||
|    .WithCleanSession(true) | ||||
|    .WithKeepAlivePeriod(TimeSpan.FromSeconds(120.0)) | ||||
|    .WithoutThrowOnNonSuccessfulConnectResponse() | ||||
|    .Build(); | ||||
|         await MqttClient.ConnectAsync(MqttClientOptions); | ||||
|         return MqttClient; | ||||
|     } | ||||
|  | ||||
|     private void LogOut(byte logLevel, object source, string message, Exception exception) => LogAction?.Invoke((LogLevel)logLevel, source, message, exception); | ||||
|  | ||||
|     private Task MqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs args) | ||||
|     { | ||||
|         LogAction?.Invoke(LogLevel.Info, this, $"[{args.ApplicationMessage.Topic}]:{Encoding.UTF8.GetString(args.ApplicationMessage.PayloadSegment)}", null); | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public Task StateHasChangedAsync() | ||||
|     { | ||||
|         return InvokeAsync(StateHasChanged); | ||||
|     } | ||||
| } | ||||
| @@ -1,37 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|  | ||||
|  | ||||
| 	<Target Name="PostBuild" AfterTargets="PostBuildEvent"> | ||||
|  | ||||
| 		<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25  md %25dir%25  
copy "$(TargetDir)*Mqtt*.dll"  %25dir%25


" /> | ||||
| 	 | ||||
| 	</Target> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj"> | ||||
| 			<Private>false</Private> | ||||
| 			<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies> | ||||
| 			<ExcludeAssets>runtime</ExcludeAssets> | ||||
| 		</ProjectReference> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\DriverDebugUIBase.cs" Link="DebugPage\DriverDebugUIBase.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\DriverDebugUIPage.razor.cs" Link="DebugPage\DriverDebugUIPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\SerialSessionPage.razor.cs" Link="DebugPage\SerialSessionPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\TcpClientPage.razor.cs" Link="DebugPage\TcpClientPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\TcpServerPage.razor.cs" Link="DebugPage\TcpServerPage.razor.cs" /> | ||||
| 		<Compile Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\UdpSessionPage.razor.cs" Link="DebugPage\UdpSessionPage.razor.cs" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\DriverDebugUIPage.razor" Link="DebugPage\DriverDebugUIPage.razor" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\SerialSessionPage.razor" Link="DebugPage\SerialSessionPage.razor" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\TcpClientPage.razor" Link="DebugPage\TcpClientPage.razor" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\TcpServerPage.razor" Link="DebugPage\TcpServerPage.razor" /> | ||||
| 		<Content Include="..\..\Demo\ThingsGateway.Foundation.Demo.Rcl\Components\DebugPage\UdpSessionPage.razor" Link="DebugPage\UdpSessionPage.razor" /> | ||||
|  | ||||
|  | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
|  | ||||
| </Project> | ||||
| @@ -1,46 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人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 System.Collections.Concurrent; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Adapter.OPCDA; | ||||
| @using Masa.Blazor | ||||
| @implements IDisposable | ||||
| <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" Label=@node.DescriptionWithOutSugar(a=>a.GroupSize) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.GroupSize /> | ||||
|         <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.UpdateRate) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.UpdateRate /> | ||||
|  | ||||
|         <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.DeadBand) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.DeadBand /> | ||||
|         <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.CheckRate) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.CheckRate /> | ||||
|         <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.OPCIP) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.OPCIP /> | ||||
|         <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.OPCName) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.OPCName /> | ||||
|  | ||||
|         <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.ActiveSubscribe) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.ActiveSubscribe /> | ||||
|  | ||||
|         <MButton Class="ma-1" OnClick=@Connect Color="primary"> | ||||
|             连接 | ||||
|         </MButton> | ||||
|  | ||||
|         <MButton Class="ma-1" OnClick=@DisConnect Color="red"> | ||||
|             断开 | ||||
|         </MButton> | ||||
|     </MRow> | ||||
|  | ||||
|  | ||||
|  | ||||
| </MCard> | ||||
| @@ -1,59 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.OPCUA; | ||||
|  | ||||
|  | ||||
| public partial class OPCUAServer | ||||
| { | ||||
|     private class OPCUALogger : ILogger | ||||
|     { | ||||
|         private ILog _log; | ||||
|         public OPCUALogger(ILog log) | ||||
|         { | ||||
|             _log = log; | ||||
|  | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Set the log level | ||||
|         /// </summary> | ||||
|         public Microsoft.Extensions.Logging.LogLevel LogLevel { get; set; } = Microsoft.Extensions.Logging.LogLevel.Trace; | ||||
|         /// <inheritdoc/> | ||||
|         public IDisposable BeginScope<TState>(TState state) => default; | ||||
|         /// <inheritdoc/> | ||||
|         public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) => logLevel >= LogLevel; | ||||
|         /// <inheritdoc/> | ||||
|         public void Log<TState>( | ||||
|             Microsoft.Extensions.Logging.LogLevel logLevel, | ||||
|             EventId eventId, | ||||
|             TState state, | ||||
|             Exception exception, | ||||
|             Func<TState, Exception, string> formatter) | ||||
|         { | ||||
|             if (!IsEnabled(logLevel)) | ||||
|             { | ||||
|                 return; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var message = formatter(state, exception); | ||||
|                 _log.Log((Foundation.Core.LogLevel)(byte)logLevel, state, message, exception); | ||||
|                 if (logLevel > Microsoft.Extensions.Logging.LogLevel.Information) | ||||
|                 { | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人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 System.Collections.Concurrent; | ||||
| @using Opc.Ua.Client; | ||||
| @using Opc.Ua; | ||||
| @using System.Linq; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Adapter.OPCUA; | ||||
| @using Masa.Blazor | ||||
| @implements IDisposable | ||||
|  | ||||
| <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" Label=@node.DescriptionWithOutSugar(a=>a.GroupSize) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.GroupSize /> | ||||
|          <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.UpdateRate) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.UpdateRate /> | ||||
|  | ||||
|          <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.DeadBand) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.DeadBand /> | ||||
|          <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.KeepAliveInterval) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.KeepAliveInterval /> | ||||
|  | ||||
|          <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.OPCUrl) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.OPCUrl /> | ||||
|          <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.UserName) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.UserName /> | ||||
|          <MTextField Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.Password) Dense Outlined HideDetails="@("auto")" @bind-Value=@node.Password /> | ||||
|  | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.ActiveSubscribe) Dense  HideDetails="@("auto")" @bind-Value=@node.ActiveSubscribe /> | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.IsUseSecurity) Dense HideDetails="@("auto")" @bind-Value=@node.IsUseSecurity /> | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.CheckDomain) Dense HideDetails="@("auto")" @bind-Value=@node.CheckDomain /> | ||||
|          <MCheckbox Class="ma-1" Label=@node.DescriptionWithOutSugar(a=>a.LoadType) Dense HideDetails="@("auto")" @bind-Value=@node.LoadType /> | ||||
|  | ||||
|          <MButton Class="ma-1" OnClick=@ConnectAsync Color="primary"> | ||||
|              连接 | ||||
|          </MButton> | ||||
|  | ||||
|          <MButton Class="ma-1" OnClick=@DisConnect Color="red"> | ||||
|              断开 | ||||
|          </MButton> | ||||
|      </MRow> | ||||
|  | ||||
|  | ||||
|  | ||||
|  </MCard> | ||||
|  | ||||
|  | ||||
| @@ -1,35 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人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 System.Net.Http.Json | ||||
| @using System.IO; | ||||
| @using System.Text.Json; | ||||
| @using System.Reflection; | ||||
|  | ||||
| @using ThingsGateway.Components; | ||||
| @using ThingsGateway.Admin.Core; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension.String; | ||||
| @using ThingsGateway.Admin.Application; | ||||
|  | ||||
| @using ThingsGateway.Core; | ||||
| @using ThingsGateway.Gateway.Application; | ||||
| @using ThingsGateway.Gateway.Core; | ||||
| @@ -1,325 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Hosting; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.QuestDB; | ||||
| public class QuestDB : UpLoadBase | ||||
| { | ||||
|     private readonly ConcurrentQueue<QuestDBHistoryValue> DeviceVariableRunTimes = new(); | ||||
|     private readonly QuestDBProperty driverPropertys = new(); | ||||
|     private readonly QuestDBVariableProperty variablePropertys = new(); | ||||
|     private TypeAdapterConfig _config; | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|  | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|     private TimerTick exTimerTick; | ||||
|     public QuestDB() | ||||
|     { | ||||
|         _config = new TypeAdapterConfig(); | ||||
|         _config.ForType<DeviceVariableRunTime, HistoryValue>() | ||||
|             .Map(dest => dest.Value, (src) => ValueReturn(src)) | ||||
|             .Map(dest => dest.CollectTime, (src) => src.CollectTime.ToUniversalTime())//注意sqlsugar插入时无时区,直接utc时间 | ||||
|             .Map(dest => dest.CreateTime, (src) => DateTime.UtcNow);//注意sqlsugar插入时无时区,直接utc时间 | ||||
|     } | ||||
|     private static object ValueReturn(DeviceVariableRunTime src) | ||||
|     { | ||||
|         if (src.Value?.ToString()?.IsBoolValue() == true) | ||||
|         { | ||||
|             if (src.Value.ToBoolean()) | ||||
|             { | ||||
|                 return 1; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return 0; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return src.Value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public override Type DriverDebugUIType => null; | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|  | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|  | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         SqlSugarClient db = GetHisDbAsync(); | ||||
|         db.DbMaintenance.CreateDatabase(); | ||||
|         db.CodeFirst.InitTables(typeof(QuestDBHistoryValue)); | ||||
|         await Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var db = GetHisDbAsync(); | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     ////变化推送 | ||||
|                     var varList = DeviceVariableRunTimes.ToListWithDequeue(); | ||||
|                     if (varList?.Count != 0) | ||||
|                     { | ||||
|                         await InserableAsync(db, varList, cancellationToken); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.ToList().Adapt<List<QuestDBHistoryValue>>(_config); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             await InserableAsync(db, varList, cancellationToken); | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public override bool IsConnected() => _uploadVariables?.Count > 0; | ||||
|  | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|             _uploadVariables = null; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|  | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|  | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|            .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean()) | ||||
|            .ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         if (!driverPropertys.IsInterval) | ||||
|         { | ||||
|             _uploadVariables.ForEach(a => | ||||
|             { | ||||
|                 a.VariableValueChange += VariableValueChange; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         if (_uploadVariables.Count == 0) | ||||
|         { | ||||
|             LogMessage.LogWarning("插件变量数量为0"); | ||||
|         } | ||||
|         if (driverPropertys.IntervalTime < 1) | ||||
|             driverPropertys.IntervalTime = 10; | ||||
|         exTimerTick = new(driverPropertys.IntervalTime * 1000); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Aop设置 | ||||
|     /// </summary> | ||||
|     /// <param name="db"></param> | ||||
|     private static void AopSetting(SqlSugarClient db) | ||||
|     { | ||||
|         var config = db.CurrentConnectionConfig; | ||||
|  | ||||
|         // 设置超时时间 | ||||
|         db.Ado.CommandTimeOut = 30; | ||||
|  | ||||
|         // 打印SQL语句 | ||||
|         db.Aop.OnLogExecuting = (sql, pars) => | ||||
|         { | ||||
|             //如果不是开发环境就打印sql | ||||
|             if (App.HostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (sql.StartsWith("SELECT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Green; | ||||
|                 } | ||||
|                 if (sql.StartsWith("UPDATE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Yellow; | ||||
|                 } | ||||
|                 if (sql.StartsWith("INSERT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Blue; | ||||
|                 } | ||||
|                 if (sql.StartsWith("DELETE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 } | ||||
|                 WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|                 Console.WriteLine(); | ||||
|             } | ||||
|         }; | ||||
|         //异常 | ||||
|         db.Aop.OnError = (ex) => | ||||
|         { | ||||
|             //如果不是开发环境就打印日志 | ||||
|             if (App.WebHostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (ex.Parametres == null) return; | ||||
|                 Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value)); | ||||
|                 WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private static void WriteSqlLog(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|  | ||||
|     private static void WriteSqlLogError(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行错误时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取数据库链接 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     private SqlSugarClient GetHisDbAsync() | ||||
|     { | ||||
|         var configureExternalServices = new ConfigureExternalServices | ||||
|         { | ||||
|             EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required | ||||
|             { | ||||
|                 if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) | ||||
|                     || (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null)) | ||||
|                     column.IsNullable = true; | ||||
|             }, | ||||
|         }; | ||||
|         var sqlSugarClient = new SqlSugarClient(new ConnectionConfig() | ||||
|         { | ||||
|             ConnectionString = driverPropertys.ConnectStr,//连接字符串 | ||||
|             DbType = DbType.QuestDB,//数据库类型 | ||||
|             IsAutoCloseConnection = true, //不设成true要手动close | ||||
|             ConfigureExternalServices = configureExternalServices, | ||||
|         } | ||||
|         ); | ||||
|         AopSetting(sqlSugarClient);//aop配置 | ||||
|         return sqlSugarClient; | ||||
|     } | ||||
|  | ||||
|     private async Task InserableAsync(SqlSugarClient db, List<QuestDBHistoryValue> dbInserts, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var result = await db.Insertable(dbInserts).ExecuteCommandAsync(cancellationToken); | ||||
|             if (result > 0) | ||||
|                 LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString()); | ||||
|             //连接成功时补发缓存数据 | ||||
|             var cacheData = await CacheDb.GetCacheData(); | ||||
|             foreach (var item in cacheData) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var data = item.CacheStr.FromJsonString<List<QuestDBHistoryValue>>(); | ||||
|                     var cacheresult = await db.Insertable(data).ExecuteCommandAsync(cancellationToken); | ||||
|                     if (cacheresult > 0) | ||||
|                     { | ||||
|                         await CacheDb.DeleteCacheData(item.Id); | ||||
|                         LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (!driverPropertys.IsInterval) | ||||
|             DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<QuestDBHistoryValue>(_config)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,31 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.QuestDB; | ||||
|  | ||||
| public class QuestDBProperty : UpDriverPropertyBase | ||||
| { | ||||
|     [DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "host=localhost;port=8812;username=admin;password=quest;database=qdb;ServerCompatibilityMode=NoTypeLoading;"; | ||||
|     [DeviceProperty("是否间隔插入", "False时将每次变化写入")] public bool IsInterval { get; set; } = true; | ||||
|     [DeviceProperty("间隔时间", "秒,实时表时代表更新间隔,历史表时代表插入间隔")] public int IntervalTime { get; set; } = 10; | ||||
|     [DeviceProperty("缓存最大条数", "默认2千条")] public int CacheMaxCount { get; set; } = 2000; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 线程循环间隔 | ||||
|     /// </summary> | ||||
|     [DeviceProperty("线程循环间隔", "最小10ms")] | ||||
|     public int CycleInterval { get; set; } = 1000; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,495 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using RabbitMQ.Client; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.RabbitMQ; | ||||
| /// <summary> | ||||
| /// RabbitMQ | ||||
| /// </summary> | ||||
| public class RabbitMQClient : UpLoadBase | ||||
| { | ||||
|     private readonly RabbitMQClientProperty driverPropertys = new(); | ||||
|     private readonly RabbitMQClientVariableProperty variablePropertys = new(); | ||||
|     private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new(); | ||||
|     private ConcurrentQueue<VariableData> _collectVariableRunTimes = new(); | ||||
|     private IConnection _connection; | ||||
|     private ConnectionFactory _connectionFactory; | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|     private IModel _model; | ||||
|     private RpcSingletonService _rpcCore; | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|     /// <inheritdoc/> | ||||
|     public override Type DriverDebugUIType => null; | ||||
|     /// <inheritdoc/> | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|  | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     private TimerTick exDeviceTimerTick; | ||||
|  | ||||
|     private TimerTick exVariableTimerTick; | ||||
|     /// <inheritdoc/> | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (_model == null) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|  | ||||
|                     // 创建连接 | ||||
|                     _connection ??= _connectionFactory.CreateConnection(); | ||||
|                     // 创建通道 | ||||
|                     _model ??= _connection.CreateModel(); | ||||
|                     // 声明路由队列 | ||||
|                     if (driverPropertys.IsQueueDeclare) | ||||
|                     { | ||||
|                         _model?.QueueDeclare(driverPropertys.VariableQueueName, true, false, false); | ||||
|                         _model?.QueueDeclare(driverPropertys.DeviceQueueName, true, false, false); | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 ////变化推送 | ||||
|                 var varList = _collectVariableRunTimes.ToListWithDequeue(); | ||||
|                 if (varList?.Count != 0) | ||||
|                 { | ||||
|                     if (driverPropertys.IsList) | ||||
|                     { | ||||
|                         var listChunk = varList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                         foreach (var variables in listChunk) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 if (!cancellationToken.IsCancellationRequested) | ||||
|                                 { | ||||
|                                     var data = variables.GetSciptListValue(driverPropertys.BigTextScriptVariableModel); | ||||
|                                     // 设置消息持久化 | ||||
|                                     IBasicProperties properties = _model?.CreateBasicProperties(); | ||||
|                                     await Publish(driverPropertys.VariableQueueName, data, properties); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         foreach (var variable in varList) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 if (!cancellationToken.IsCancellationRequested) | ||||
|                                 { | ||||
|                                     var data = variable.GetSciptListValue(driverPropertys.BigTextScriptVariableModel); | ||||
|                                     // 设置消息持久化 | ||||
|                                     IBasicProperties properties = _model?.CreateBasicProperties(); | ||||
|                                     await Publish(driverPropertys.VariableQueueName, data, properties); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     break; | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exVariableTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.Adapt<List<VariableData>>(); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             if (driverPropertys.IsList) | ||||
|                             { | ||||
|                                 var listChunk = varList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                                 foreach (var variables in listChunk) | ||||
|                                 { | ||||
|                                     try | ||||
|                                     { | ||||
|                                         if (!cancellationToken.IsCancellationRequested) | ||||
|                                         { | ||||
|                                             var data = variables.GetSciptListValue(driverPropertys.BigTextScriptVariableModel); | ||||
|                                             // 设置消息持久化 | ||||
|                                             IBasicProperties properties = _model?.CreateBasicProperties(); | ||||
|                                             await Publish(driverPropertys.VariableQueueName, data, properties); | ||||
|                                         } | ||||
|                                         else | ||||
|                                         { | ||||
|                                             break; | ||||
|                                         } | ||||
|                                     } | ||||
|                                     catch (Exception ex) | ||||
|                                     { | ||||
|                                         LogMessage?.LogWarning(ex); | ||||
|                                     } | ||||
|  | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 foreach (var variable in varList) | ||||
|                                 { | ||||
|                                     try | ||||
|                                     { | ||||
|                                         if (!cancellationToken.IsCancellationRequested) | ||||
|                                         { | ||||
|                                             var data = variable.GetSciptListValue(driverPropertys.BigTextScriptVariableModel); | ||||
|                                             // 设置消息持久化 | ||||
|                                             IBasicProperties properties = _model?.CreateBasicProperties(); | ||||
|                                             await Publish(driverPropertys.VariableQueueName, data, properties); | ||||
|                                         } | ||||
|                                         else | ||||
|                                         { | ||||
|                                             break; | ||||
|                                         } | ||||
|  | ||||
|                                     } | ||||
|                                     catch (Exception ex) | ||||
|                                     { | ||||
|                                         LogMessage?.LogWarning(ex); | ||||
|                                     } | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|         try | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 ////变化推送 | ||||
|                 var devList = _collectDeviceRunTimes.ToListWithDequeue(); | ||||
|                 if (devList?.Count != 0) | ||||
|                 { | ||||
|                     if (driverPropertys.IsList) | ||||
|                     { | ||||
|                         var listChunk = devList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                         foreach (var devices in listChunk) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 var data = devices.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel); | ||||
|                                 // 设置消息持久化 | ||||
|                                 IBasicProperties properties = _model?.CreateBasicProperties(); | ||||
|                                 await Publish(driverPropertys.DeviceQueueName, data, properties); | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         foreach (var devices in devList) | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 var data = devices.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel); | ||||
|                                 // 设置消息持久化 | ||||
|                                 IBasicProperties properties = _model?.CreateBasicProperties(); | ||||
|                                 await Publish(driverPropertys.DeviceQueueName, data, properties); | ||||
|                             } | ||||
|                             catch (Exception ex) | ||||
|                             { | ||||
|                                 LogMessage?.LogWarning(ex); | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exDeviceTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     var devList = _collectDevice.Adapt<List<DeviceData>>(); | ||||
|                     if (devList?.Count != 0) | ||||
|                     { | ||||
|                         if (driverPropertys.IsList) | ||||
|                         { | ||||
|                             var listChunk = devList.ChunkTrivialBetter(driverPropertys.SplitSize); | ||||
|                             foreach (var devices in listChunk) | ||||
|                             { | ||||
|                                 try | ||||
|                                 { | ||||
|                                     var data = devices.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel); | ||||
|                                     // 设置消息持久化 | ||||
|                                     IBasicProperties properties = _model?.CreateBasicProperties(); | ||||
|                                     await Publish(driverPropertys.DeviceQueueName, data, properties); | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage?.LogWarning(ex); | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             foreach (var devices in devList) | ||||
|                             { | ||||
|                                 try | ||||
|                                 { | ||||
|                                     var data = devices.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel); | ||||
|                                     // 设置消息持久化 | ||||
|                                     IBasicProperties properties = _model?.CreateBasicProperties(); | ||||
|                                     await Publish(driverPropertys.DeviceQueueName, data, properties); | ||||
|                                 } | ||||
|                                 catch (Exception ex) | ||||
|                                 { | ||||
|                                     LogMessage?.LogWarning(ex); | ||||
|                                 } | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     public override bool IsConnected() => _connection?.IsOpen == true; | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return $" {nameof(RabbitMQClient)} IP:{driverPropertys.IP} Port:{driverPropertys.Port}"; | ||||
|     } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         _globalDeviceData?.AllVariables.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|  | ||||
|         _collectDevice?.ForEach(a => | ||||
|         { | ||||
|             a.DeviceStatusChange -= DeviceStatusChange; | ||||
|         }); | ||||
|         _model?.SafeDispose(); | ||||
|         _connection?.SafeDispose(); | ||||
|         _uploadVariables = null; | ||||
|         _collectDeviceRunTimes.Clear(); | ||||
|         _collectVariableRunTimes.Clear(); | ||||
|         _collectDeviceRunTimes = null; | ||||
|         _collectVariableRunTimes = null; | ||||
|     } | ||||
|     private List<CollectDeviceRunTime> _collectDevice; | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|         _connectionFactory = new ConnectionFactory | ||||
|         { | ||||
|             HostName = driverPropertys.IP, | ||||
|             Port = driverPropertys.Port, | ||||
|             UserName = driverPropertys.UserName, | ||||
|             Password = driverPropertys.Password, | ||||
|             VirtualHost = driverPropertys.VirtualHost, | ||||
|         }; | ||||
|  | ||||
|  | ||||
|  | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|         _rpcCore = App.GetService<RpcSingletonService>(); | ||||
|  | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|        .Where(b => b.VariablePropertys[device.Id].Any(c => | ||||
|        { | ||||
|            if (c.PropertyName == nameof(variablePropertys.Enable)) | ||||
|            { | ||||
|                return c.Value?.GetBoolValue() == true; | ||||
|            } | ||||
|            else | ||||
|                return false; | ||||
|        })) | ||||
|        .ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         _collectDevice = _globalDeviceData.CollectDevices.Where(a => _uploadVariables.Select(b => b.DeviceId).Contains(a.Id)).ToList(); | ||||
|  | ||||
|         _collectDevice.ForEach(a => | ||||
|         { | ||||
|             a.DeviceStatusChange += DeviceStatusChange; | ||||
|             DeviceStatusChange(a); | ||||
|         }); | ||||
|         _uploadVariables.ForEach(a => | ||||
|         { | ||||
|             a.VariableValueChange += VariableValueChange; | ||||
|             VariableValueChange(a); | ||||
|         }); | ||||
|  | ||||
|         if (driverPropertys.UploadInterval <= 1000) driverPropertys.UploadInterval = 1000; | ||||
|         exVariableTimerTick = new(driverPropertys.UploadInterval); | ||||
|         exDeviceTimerTick = new(driverPropertys.UploadInterval); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void DeviceStatusChange(CollectDeviceRunTime collectDeviceRunTime) | ||||
|     { | ||||
|         if (driverPropertys?.IsInterval != true) | ||||
|             _collectDeviceRunTimes.Enqueue(collectDeviceRunTime.Adapt<DeviceData>()); | ||||
|     } | ||||
|  | ||||
|     private async Task Publish(string queueName, string data, IBasicProperties properties) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (properties != null) | ||||
|                 properties.Persistent = true; | ||||
|             if (_model != null) | ||||
|                 _model.BasicPublish(driverPropertys.ExchangeName, queueName, properties, Encoding.UTF8.GetBytes(data)); | ||||
|             else | ||||
|             { | ||||
|                 await CacheDb.AddCacheData(queueName, data, driverPropertys.CacheMaxCount); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             //连接成功时补发缓存数据 | ||||
|             var cacheData = await CacheDb.GetCacheData(10); | ||||
|             foreach (var item in cacheData) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     _model?.BasicPublish(driverPropertys.ExchangeName, item.Topic, properties, Encoding.UTF8.GetBytes(item.CacheStr)); | ||||
|                     LogMessage.Trace($"{FoundationConst.LogMessageHeader}主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|  | ||||
|                     await CacheDb.DeleteCacheData(item.Id); | ||||
|                 } | ||||
|                 catch | ||||
|                 { | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             LogMessage.Trace($"{FoundationConst.LogMessageHeader}主题:{queueName}{Environment.NewLine}负载:{data}"); | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData(queueName, data, driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (driverPropertys?.IsInterval != true) | ||||
|             _collectVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<VariableData>()); | ||||
|     } | ||||
| } | ||||
| @@ -1,356 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Hosting; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| using Yitter.IdGenerator; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SQLDB; | ||||
| public class SQLDB : UpLoadBase | ||||
| { | ||||
|     private readonly ConcurrentQueue<SQLHistoryValue> DeviceVariableRunTimes = new(); | ||||
|     private readonly SQLDBProperty driverPropertys = new(); | ||||
|     private readonly SQLDBVariableProperty variablePropertys = new(); | ||||
|     private TypeAdapterConfig _config; | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|  | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|     private TimerTick exTimerTick; | ||||
|     private TimerTick exRealTimerTick; | ||||
|     public SQLDB() | ||||
|     { | ||||
|         _config = new TypeAdapterConfig(); | ||||
|         _config.ForType<DeviceVariableRunTime, SQLHistoryValue>() | ||||
| .Map(dest => dest.Id, (src) => YitIdHelper.NextId()) | ||||
| .Map(dest => dest.CreateTime, (src) => DateTime.Now); | ||||
|     } | ||||
|  | ||||
|     public override Type DriverDebugUIType => null; | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|  | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|  | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var db = GetHisDbAsync(); | ||||
|         db.CodeFirst.InitTables(typeof(SQLHistoryValue)); | ||||
|         db.MappingTables.Add("SQLRealValue", driverPropertys.ReadDBTableName); // typeof(类).Name 可以拿到类名 | ||||
|         db.CodeFirst.InitTables(typeof(SQLRealValue)); //生成的表名是 newTableName | ||||
|         await Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     private bool readDBInitSuccess; | ||||
|  | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var db = GetHisDbAsync(); | ||||
|  | ||||
|         if (driverPropertys.IsReadDB) | ||||
|         { | ||||
|             if (exRealTimerTick.IsTickHappen()) | ||||
|             { | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     var varList = _uploadVariables.ToList().Adapt<List<SQLRealValue>>(); | ||||
|  | ||||
|                     if (!readDBInitSuccess) | ||||
|                     { | ||||
|                         //事务 | ||||
|                         var result = db.UseTran(() => | ||||
|                         { | ||||
|                             db.Storageable(varList).As(driverPropertys.ReadDBTableName).PageSize(10000).ExecuteSqlBulkCopy(); | ||||
|                             //db.Fastest<SQLRealValue>().AS(driverPropertys.ReadDBTableName).PageSize(100000).BulkMerge(varList); | ||||
|                         }); | ||||
|                         if (result.IsSuccess)//如果成功了 | ||||
|                         { | ||||
|                             readDBInitSuccess = true; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             throw new(result.ErrorMessage); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     if (varList?.Count != 0) | ||||
|                     { | ||||
|                         //var result = await db.Storageable(varList).As(driverPropertys.ReadDBTableName).ExecuteCommandAsync(cancellationToken); | ||||
|                         db.Fastest<SQLRealValue>().AS(driverPropertys.ReadDBTableName).PageSize(100000).BulkUpdate(varList); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|  | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     ////变化推送 | ||||
|                     var varList = DeviceVariableRunTimes.ToListWithDequeue(); | ||||
|                     if (varList?.Count != 0) | ||||
|                     { | ||||
|                         await InserableAsync(db, varList, cancellationToken); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.ToList().Adapt<List<SQLHistoryValue>>(_config); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             await InserableAsync(db, varList, cancellationToken); | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取数据库链接 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public SqlSugarClient GetHisDbAsync() | ||||
|     { | ||||
|         var configureExternalServices = new ConfigureExternalServices | ||||
|         { | ||||
|             EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required | ||||
|             { | ||||
|                 if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) | ||||
|                     || (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null)) | ||||
|                     column.IsNullable = true; | ||||
|             }, | ||||
|         }; | ||||
|         var sqlSugarClient = new SqlSugarClient(new ConnectionConfig() | ||||
|         { | ||||
|             ConnectionString = driverPropertys.ConnectStr,//连接字符串 | ||||
|             DbType = driverPropertys.DbType,//数据库类型 | ||||
|             IsAutoCloseConnection = true, //不设成true要手动close | ||||
|             ConfigureExternalServices = configureExternalServices, | ||||
|         } | ||||
|         ); | ||||
|         AopSetting(sqlSugarClient);//aop配置 | ||||
|         return sqlSugarClient; | ||||
|     } | ||||
|  | ||||
|     public override bool IsConnected() => _uploadVariables?.Count > 0; | ||||
|  | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             readDBInitSuccess = false; | ||||
|             _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|             _uploadVariables = null; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|  | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|  | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|            .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean()) | ||||
|            .ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         if (!driverPropertys.IsReadDB) | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 _uploadVariables.ForEach(a => | ||||
|                 { | ||||
|                     a.VariableValueChange += VariableValueChange; | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|         if (_uploadVariables.Count == 0) | ||||
|         { | ||||
|             LogMessage.LogWarning("插件变量数量为0"); | ||||
|         } | ||||
|         if (driverPropertys.IntervalTime < 1) | ||||
|             driverPropertys.IntervalTime = 10; | ||||
|         exTimerTick = new(driverPropertys.IntervalTime * 1000); | ||||
|         exRealTimerTick = new(driverPropertys.IntervalTime * 1000); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Aop设置 | ||||
|     /// </summary> | ||||
|     /// <param name="db"></param> | ||||
|     private static void AopSetting(SqlSugarClient db) | ||||
|     { | ||||
|         var config = db.CurrentConnectionConfig; | ||||
|  | ||||
|         // 设置超时时间 | ||||
|         db.Ado.CommandTimeOut = 30; | ||||
|  | ||||
|         // 打印SQL语句 | ||||
|         db.Aop.OnLogExecuting = (sql, pars) => | ||||
|         { | ||||
|             //如果不是开发环境就打印sql | ||||
|             if (App.HostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (sql.StartsWith("SELECT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Green; | ||||
|                 } | ||||
|                 if (sql.StartsWith("UPDATE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Yellow; | ||||
|                 } | ||||
|                 if (sql.StartsWith("INSERT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Blue; | ||||
|                 } | ||||
|                 if (sql.StartsWith("DELETE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 } | ||||
|                 WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|                 Console.WriteLine(); | ||||
|             } | ||||
|         }; | ||||
|         //异常 | ||||
|         db.Aop.OnError = (ex) => | ||||
|         { | ||||
|             //如果不是开发环境就打印日志 | ||||
|             if (App.WebHostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (ex.Parametres == null) return; | ||||
|                 Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value)); | ||||
|                 WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|     } | ||||
|     private static void WriteSqlLog(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|     private static void WriteSqlLogError(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行错误时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|     private async Task InserableAsync(SqlSugarClient db, List<SQLHistoryValue> dbInserts, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var result = await db.Insertable(dbInserts).SplitTable().ExecuteCommandAsync(); | ||||
|             if (result > 0) | ||||
|                 LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString()); | ||||
|             //连接成功时补发缓存数据 | ||||
|             var cacheData = await CacheDb.GetCacheData(); | ||||
|             foreach (var item in cacheData) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var data = item.CacheStr.FromJsonString<List<SQLHistoryValue>>(); | ||||
|                     var cacheresult = await db.Insertable(data).SplitTable().ExecuteCommandAsync(); | ||||
|                     if (cacheresult > 0) | ||||
|                     { | ||||
|                         await CacheDb.DeleteCacheData(item.Id); | ||||
|                         LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (!driverPropertys.IsReadDB) | ||||
|             if (!driverPropertys.IsInterval) | ||||
|                 DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<SQLHistoryValue>(_config)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,68 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/S7_1200" | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Microsoft.JSInterop; | ||||
|  | ||||
| @using ThingsGateway.Foundation.Adapter.Siemens; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
|  | ||||
| @using Masa.Blazor | ||||
|  | ||||
| <TcpClientPage @ref=TcpClientPage></TcpClientPage> | ||||
|  | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">驱动配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
|         @if (_plc != null) | ||||
|         { | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Slot)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Rack)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> | ||||
|  | ||||
|             <MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.DescriptionWithOutSugar(x => x.DataFormat))" | ||||
|                      Items=@(typeof(DataFormat).GetEnumListWithOutSugar()) | ||||
|                      MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                      ItemText=@((u) =>u.Description) | ||||
|                      ItemValue=@(u =>(DataFormat)u.Value) | ||||
|                      HideDetails=@("auto") Height="30" | ||||
|                   Dense> | ||||
|         </MSelect> | ||||
|  | ||||
|  | ||||
|         } | ||||
|     </MRow> | ||||
| </MCard> | ||||
|  | ||||
|  | ||||
| <DriverDebugUIPage @ref=driverDebugUIPage Sections="_sections"> | ||||
|  | ||||
| </DriverDebugUIPage> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,91 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.Siemens; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class S7_1200DebugPage | ||||
| { | ||||
|     /// <summary> | ||||
|     /// SerialSessionPage | ||||
|     /// </summary> | ||||
|     private TcpClientPage TcpClientPage; | ||||
|     private DriverDebugUIPage driverDebugUIPage; | ||||
|  | ||||
|     private ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC _plc; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             _sections.Add(( | ||||
|                 """ | ||||
|                 private static async Task ModbusClientAsync() | ||||
|                 { | ||||
|                     //链路基础配置项 | ||||
|                     var config = new TouchSocketConfig(); | ||||
|                     config | ||||
|                     .SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要 | ||||
|                  | ||||
|                 var tcpClient1 = new TcpClient();//链路对象 | ||||
|                 tcpClient1.Setup(config); | ||||
|  | ||||
|                     //创建协议对象,构造函数需要传入对应链路对象 | ||||
|                     SiemensS7PLC plc = new(tcpClient1,SiemensEnum.S1500) | ||||
|                 { | ||||
|                     //协议配置 | ||||
|                     DataFormat = DataFormat.ABCD, | ||||
|                     FrameTime = 0, | ||||
|                     CacheTimeout = 1000, | ||||
|                     ConnectTimeOut = 3000, | ||||
|                     Station = 1, | ||||
|                     TimeOut = 3000, | ||||
|                     IsCheckMessageId = true | ||||
|                 }; | ||||
|  | ||||
|                     #region 读写测试 | ||||
|                         var bytesResult = await plc.ReadAsync("400001", 20); | ||||
|                         var int32sResult = await plc.ReadInt32Async("400001", 20); | ||||
|  | ||||
|                     #endregion | ||||
|  | ||||
|                 } | ||||
|                  | ||||
|                 """, "csharp")); | ||||
|  | ||||
|             if (TcpClientPage != null) | ||||
|                 TcpClientPage.LogAction = driverDebugUIPage.LogOut; | ||||
|             _plc = new ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC(TcpClientPage.GetTcpClient(), SiemensEnum.S1200); | ||||
|             driverDebugUIPage.Plc = _plc; | ||||
|  | ||||
|             //初始化 | ||||
|             driverDebugUIPage.Address = "M100"; | ||||
|             int index = 0; | ||||
|             driverDebugUIPage.DeviceVariableRunTimes.ForEach(a => a.VariableAddress = "M" + (100 + index++)); | ||||
|             TcpClientPage.Port = 102; | ||||
|             TcpClientPage.StateHasChangedAsync(); | ||||
|  | ||||
|             //载入配置 | ||||
|             StateHasChanged(); | ||||
|         } | ||||
|  | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/S7_1500" | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Microsoft.JSInterop; | ||||
|  | ||||
| @using ThingsGateway.Foundation.Adapter.Siemens; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
|  | ||||
| @using Masa.Blazor | ||||
|  | ||||
| <TcpClientPage @ref=TcpClientPage></TcpClientPage> | ||||
|  | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">驱动配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
|         @if (_plc != null) | ||||
|         { | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Slot)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Rack)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> | ||||
|  | ||||
|             <MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.DescriptionWithOutSugar(x => x.DataFormat))" | ||||
|                      Items=@(typeof(DataFormat).GetEnumListWithOutSugar()) | ||||
|                      MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                      ItemText=@((u) =>u.Description) | ||||
|                      ItemValue=@(u =>(DataFormat)u.Value) | ||||
|                      HideDetails=@("auto") Height="30" | ||||
|                   Dense> | ||||
|         </MSelect> | ||||
|  | ||||
|  | ||||
|         } | ||||
|     </MRow> | ||||
| </MCard> | ||||
|  | ||||
|  | ||||
| <DriverDebugUIPage @ref=driverDebugUIPage Sections="_sections"> | ||||
|  | ||||
| </DriverDebugUIPage> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,92 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.Siemens; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class S7_1500DebugPage | ||||
| { | ||||
|     /// <summary> | ||||
|     /// SerialSessionPage | ||||
|     /// </summary> | ||||
|     private TcpClientPage TcpClientPage; | ||||
|     private DriverDebugUIPage driverDebugUIPage; | ||||
|  | ||||
|     private ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC _plc; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             _sections.Add(( | ||||
|                 """ | ||||
|                 private static async Task ModbusClientAsync() | ||||
|                 { | ||||
|                     //链路基础配置项 | ||||
|                     var config = new TouchSocketConfig(); | ||||
|                     config | ||||
|                     .SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要 | ||||
|                  | ||||
|                 var tcpClient1 = new TcpClient();//链路对象 | ||||
|                 tcpClient1.Setup(config); | ||||
|  | ||||
|                     //创建协议对象,构造函数需要传入对应链路对象 | ||||
|                     SiemensS7PLC plc = new(tcpClient1,SiemensEnum.S1500) | ||||
|                 { | ||||
|                     //协议配置 | ||||
|                     DataFormat = DataFormat.ABCD, | ||||
|                     FrameTime = 0, | ||||
|                     CacheTimeout = 1000, | ||||
|                     ConnectTimeOut = 3000, | ||||
|                     Station = 1, | ||||
|                     TimeOut = 3000, | ||||
|                     IsCheckMessageId = true | ||||
|                 }; | ||||
|  | ||||
|                     #region 读写测试 | ||||
|                         var bytesResult = await plc.ReadAsync("400001", 20); | ||||
|                         var int32sResult = await plc.ReadInt32Async("400001", 20); | ||||
|  | ||||
|                     #endregion | ||||
|  | ||||
|                 } | ||||
|                  | ||||
|                 """, "csharp")); | ||||
|  | ||||
|             if (TcpClientPage != null) | ||||
|                 TcpClientPage.LogAction = driverDebugUIPage.LogOut; | ||||
|             _plc = new ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC(TcpClientPage.GetTcpClient(), SiemensEnum.S1500); | ||||
|             driverDebugUIPage.Plc = _plc; | ||||
|  | ||||
|             //初始化 | ||||
|             driverDebugUIPage.Address = "M100"; | ||||
|             int index = 0; | ||||
|             driverDebugUIPage.DeviceVariableRunTimes.ForEach(a => a.VariableAddress = "M" + (100 + index++)); | ||||
|             TcpClientPage.Port = 102; | ||||
|             TcpClientPage.StateHasChangedAsync(); | ||||
|  | ||||
|  | ||||
|             //载入配置 | ||||
|             StateHasChanged(); | ||||
|         } | ||||
|  | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/S7_200" | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Microsoft.JSInterop; | ||||
|  | ||||
| @using ThingsGateway.Foundation.Adapter.Siemens; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
|  | ||||
| @using Masa.Blazor | ||||
|  | ||||
| <TcpClientPage @ref=TcpClientPage></TcpClientPage> | ||||
|  | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">驱动配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
|         @if (_plc != null) | ||||
|         { | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Slot)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Rack)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> | ||||
|  | ||||
|             <MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.DescriptionWithOutSugar(x => x.DataFormat))" | ||||
|                      Items=@(typeof(DataFormat).GetEnumListWithOutSugar()) | ||||
|                      MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                      ItemText=@((u) =>u.Description) | ||||
|                      ItemValue=@(u =>(DataFormat)u.Value) | ||||
|                      HideDetails=@("auto") Height="30" | ||||
|                   Dense> | ||||
|         </MSelect> | ||||
|  | ||||
|  | ||||
|         } | ||||
|     </MRow> | ||||
| </MCard> | ||||
|  | ||||
|  | ||||
| <DriverDebugUIPage @ref=driverDebugUIPage Sections="_sections"> | ||||
|  | ||||
| </DriverDebugUIPage> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,92 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.Siemens; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class S7_200DebugPage | ||||
| { | ||||
|     /// <summary> | ||||
|     /// SerialSessionPage | ||||
|     /// </summary> | ||||
|     private TcpClientPage TcpClientPage; | ||||
|     private DriverDebugUIPage driverDebugUIPage; | ||||
|  | ||||
|     private ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC _plc; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             _sections.Add(( | ||||
|                 """ | ||||
|                 private static async Task ModbusClientAsync() | ||||
|                 { | ||||
|                     //链路基础配置项 | ||||
|                     var config = new TouchSocketConfig(); | ||||
|                     config | ||||
|                     .SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要 | ||||
|                  | ||||
|                 var tcpClient1 = new TcpClient();//链路对象 | ||||
|                 tcpClient1.Setup(config); | ||||
|  | ||||
|                     //创建协议对象,构造函数需要传入对应链路对象 | ||||
|                     SiemensS7PLC plc = new(tcpClient1,SiemensEnum.S1500) | ||||
|                 { | ||||
|                     //协议配置 | ||||
|                     DataFormat = DataFormat.ABCD, | ||||
|                     FrameTime = 0, | ||||
|                     CacheTimeout = 1000, | ||||
|                     ConnectTimeOut = 3000, | ||||
|                     Station = 1, | ||||
|                     TimeOut = 3000, | ||||
|                     IsCheckMessageId = true | ||||
|                 }; | ||||
|  | ||||
|                     #region 读写测试 | ||||
|                         var bytesResult = await plc.ReadAsync("400001", 20); | ||||
|                         var int32sResult = await plc.ReadInt32Async("400001", 20); | ||||
|  | ||||
|                     #endregion | ||||
|  | ||||
|                 } | ||||
|                  | ||||
|                 """, "csharp")); | ||||
|  | ||||
|             if (TcpClientPage != null) | ||||
|                 TcpClientPage.LogAction = driverDebugUIPage.LogOut; | ||||
|             _plc = new ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC(TcpClientPage.GetTcpClient(), SiemensEnum.S200); | ||||
|             driverDebugUIPage.Plc = _plc; | ||||
|  | ||||
|             //初始化 | ||||
|             driverDebugUIPage.Address = "M100"; | ||||
|             int index = 0; | ||||
|             driverDebugUIPage.DeviceVariableRunTimes.ForEach(a => a.VariableAddress = "M" + (100 + index++)); | ||||
|             TcpClientPage.Port = 102; | ||||
|             TcpClientPage.StateHasChangedAsync(); | ||||
|  | ||||
|  | ||||
|             //载入配置 | ||||
|             StateHasChanged(); | ||||
|         } | ||||
|  | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/S7_200SMART" | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Microsoft.JSInterop; | ||||
|  | ||||
| @using ThingsGateway.Foundation.Adapter.Siemens; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
|  | ||||
| @using Masa.Blazor | ||||
|  | ||||
| <TcpClientPage @ref=TcpClientPage></TcpClientPage> | ||||
|  | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">驱动配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
|         @if (_plc != null) | ||||
|         { | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Slot)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Rack)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> | ||||
|  | ||||
|             <MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.DescriptionWithOutSugar(x => x.DataFormat))" | ||||
|                      Items=@(typeof(DataFormat).GetEnumListWithOutSugar()) | ||||
|                      MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                      ItemText=@((u) =>u.Description) | ||||
|                      ItemValue=@(u =>(DataFormat)u.Value) | ||||
|                      HideDetails=@("auto") Height="30" | ||||
|                   Dense> | ||||
|         </MSelect> | ||||
|  | ||||
|  | ||||
|         } | ||||
|     </MRow> | ||||
| </MCard> | ||||
|  | ||||
|  | ||||
| <DriverDebugUIPage @ref=driverDebugUIPage Sections="_sections"> | ||||
|  | ||||
| </DriverDebugUIPage> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,92 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.Siemens; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class S7_200SMARTDebugPage | ||||
| { | ||||
|     /// <summary> | ||||
|     /// SerialSessionPage | ||||
|     /// </summary> | ||||
|     private TcpClientPage TcpClientPage; | ||||
|     private DriverDebugUIPage driverDebugUIPage; | ||||
|  | ||||
|     private ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC _plc; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             _sections.Add(( | ||||
|                 """ | ||||
|                 private static async Task ModbusClientAsync() | ||||
|                 { | ||||
|                     //链路基础配置项 | ||||
|                     var config = new TouchSocketConfig(); | ||||
|                     config | ||||
|                     .SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要 | ||||
|                  | ||||
|                 var tcpClient1 = new TcpClient();//链路对象 | ||||
|                 tcpClient1.Setup(config); | ||||
|  | ||||
|                     //创建协议对象,构造函数需要传入对应链路对象 | ||||
|                     SiemensS7PLC plc = new(tcpClient1,SiemensEnum.S1500) | ||||
|                 { | ||||
|                     //协议配置 | ||||
|                     DataFormat = DataFormat.ABCD, | ||||
|                     FrameTime = 0, | ||||
|                     CacheTimeout = 1000, | ||||
|                     ConnectTimeOut = 3000, | ||||
|                     Station = 1, | ||||
|                     TimeOut = 3000, | ||||
|                     IsCheckMessageId = true | ||||
|                 }; | ||||
|  | ||||
|                     #region 读写测试 | ||||
|                         var bytesResult = await plc.ReadAsync("400001", 20); | ||||
|                         var int32sResult = await plc.ReadInt32Async("400001", 20); | ||||
|  | ||||
|                     #endregion | ||||
|  | ||||
|                 } | ||||
|                  | ||||
|                 """, "csharp")); | ||||
|  | ||||
|             if (TcpClientPage != null) | ||||
|                 TcpClientPage.LogAction = driverDebugUIPage.LogOut; | ||||
|             _plc = new ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC(TcpClientPage.GetTcpClient(), SiemensEnum.S200Smart); | ||||
|             driverDebugUIPage.Plc = _plc; | ||||
|  | ||||
|             //初始化 | ||||
|             driverDebugUIPage.Address = "M100"; | ||||
|             int index = 0; | ||||
|             driverDebugUIPage.DeviceVariableRunTimes.ForEach(a => a.VariableAddress = "M" + (100 + index++)); | ||||
|             TcpClientPage.Port = 102; | ||||
|             TcpClientPage.StateHasChangedAsync(); | ||||
|  | ||||
|  | ||||
|             //载入配置 | ||||
|             StateHasChanged(); | ||||
|         } | ||||
|  | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/S7_300" | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Microsoft.JSInterop; | ||||
|  | ||||
| @using ThingsGateway.Foundation.Adapter.Siemens; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
|  | ||||
| @using Masa.Blazor | ||||
|  | ||||
| <TcpClientPage @ref=TcpClientPage></TcpClientPage> | ||||
|  | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">驱动配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
|         @if (_plc != null) | ||||
|         { | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Slot)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Rack)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> | ||||
|  | ||||
|             <MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.DescriptionWithOutSugar(x => x.DataFormat))" | ||||
|                      Items=@(typeof(DataFormat).GetEnumListWithOutSugar()) | ||||
|                      MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                      ItemText=@((u) =>u.Description) | ||||
|                      ItemValue=@(u =>(DataFormat)u.Value) | ||||
|                      HideDetails=@("auto") Height="30" | ||||
|                   Dense> | ||||
|         </MSelect> | ||||
|  | ||||
|  | ||||
|         } | ||||
|     </MRow> | ||||
| </MCard> | ||||
|  | ||||
|  | ||||
| <DriverDebugUIPage @ref=driverDebugUIPage Sections="_sections"> | ||||
|  | ||||
| </DriverDebugUIPage> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,68 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/S7_400" | ||||
|  | ||||
| @namespace ThingsGateway.Foundation.Demo | ||||
| @using BlazorComponent; | ||||
| @using Microsoft.AspNetCore.Components.Web; | ||||
| @using Microsoft.JSInterop; | ||||
|  | ||||
| @using ThingsGateway.Foundation.Adapter.Siemens; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension; | ||||
| @using ThingsGateway.Foundation.Serial; | ||||
|  | ||||
| @using Masa.Blazor | ||||
|  | ||||
| <TcpClientPage @ref=TcpClientPage></TcpClientPage> | ||||
|  | ||||
| <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|     <div class="mb-4">驱动配置</div> | ||||
|     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||
|         @if (_plc != null) | ||||
|         { | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.FrameTime)) Dense HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.CacheTimeout)) Dense HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> | ||||
|  | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.TimeOut)) Dense HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Slot)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> | ||||
|             <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(_plc.DescriptionWithOutSugar(x => x.Rack)) Dense HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> | ||||
|  | ||||
|             <MSelect Class="ma-1" Outlined Style="max-width:200px" @bind-Value="_plc.DataFormat" Label="@(_plc.DescriptionWithOutSugar(x => x.DataFormat))" | ||||
|                      Items=@(typeof(DataFormat).GetEnumListWithOutSugar()) | ||||
|                      MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||
|                      ItemText=@((u) =>u.Description) | ||||
|                      ItemValue=@(u =>(DataFormat)u.Value) | ||||
|                      HideDetails=@("auto") Height="30" | ||||
|                   Dense> | ||||
|         </MSelect> | ||||
|  | ||||
|  | ||||
|         } | ||||
|     </MRow> | ||||
| </MCard> | ||||
|  | ||||
|  | ||||
| <DriverDebugUIPage @ref=driverDebugUIPage Sections="_sections"> | ||||
|  | ||||
| </DriverDebugUIPage> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private readonly List<(string Code, string Language)> _sections = new(); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,92 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.Siemens; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Demo; | ||||
|  | ||||
|  | ||||
| /// <inheritdoc/> | ||||
| public partial class S7_400DebugPage | ||||
| { | ||||
|     /// <summary> | ||||
|     /// SerialSessionPage | ||||
|     /// </summary> | ||||
|     private TcpClientPage TcpClientPage; | ||||
|     private DriverDebugUIPage driverDebugUIPage; | ||||
|  | ||||
|     private ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC _plc; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <param name="firstRender"></param> | ||||
|     protected override void OnAfterRender(bool firstRender) | ||||
|     { | ||||
|         if (firstRender) | ||||
|         { | ||||
|             _sections.Add(( | ||||
|                 """ | ||||
|                 private static async Task ModbusClientAsync() | ||||
|                 { | ||||
|                     //链路基础配置项 | ||||
|                     var config = new TouchSocketConfig(); | ||||
|                     config | ||||
|                     .SetRemoteIPHost(new IPHost("127.0.0.1:502"))//TCP/UDP链路才需要 | ||||
|                  | ||||
|                 var tcpClient1 = new TcpClient();//链路对象 | ||||
|                 tcpClient1.Setup(config); | ||||
|  | ||||
|                     //创建协议对象,构造函数需要传入对应链路对象 | ||||
|                     SiemensS7PLC plc = new(tcpClient1,SiemensEnum.S1500) | ||||
|                 { | ||||
|                     //协议配置 | ||||
|                     DataFormat = DataFormat.ABCD, | ||||
|                     FrameTime = 0, | ||||
|                     CacheTimeout = 1000, | ||||
|                     ConnectTimeOut = 3000, | ||||
|                     Station = 1, | ||||
|                     TimeOut = 3000, | ||||
|                     IsCheckMessageId = true | ||||
|                 }; | ||||
|  | ||||
|                     #region 读写测试 | ||||
|                         var bytesResult = await plc.ReadAsync("400001", 20); | ||||
|                         var int32sResult = await plc.ReadInt32Async("400001", 20); | ||||
|  | ||||
|                     #endregion | ||||
|  | ||||
|                 } | ||||
|                  | ||||
|                 """, "csharp")); | ||||
|  | ||||
|             if (TcpClientPage != null) | ||||
|                 TcpClientPage.LogAction = driverDebugUIPage.LogOut; | ||||
|             _plc = new ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC(TcpClientPage.GetTcpClient(), SiemensEnum.S400); | ||||
|             driverDebugUIPage.Plc = _plc; | ||||
|  | ||||
|             //初始化 | ||||
|             driverDebugUIPage.Address = "M100"; | ||||
|             int index = 0; | ||||
|             driverDebugUIPage.DeviceVariableRunTimes.ForEach(a => a.VariableAddress = "M" + (100 + index++)); | ||||
|             TcpClientPage.Port = 102; | ||||
|             TcpClientPage.StateHasChangedAsync(); | ||||
|  | ||||
|  | ||||
|             //载入配置 | ||||
|             StateHasChanged(); | ||||
|         } | ||||
|  | ||||
|         base.OnAfterRender(firstRender); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Siemens | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public class S7_1200 : Siemens | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// <inheritdoc/> | ||||
|         /// </summary> | ||||
|         public override Type DriverDebugUIType => typeof(S7_1200DebugPage); | ||||
|         /// <inheritdoc/> | ||||
|         public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|         { | ||||
|             if (client == null) | ||||
|             { | ||||
|                 FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                     ; | ||||
|                 client = new TcpClient(); | ||||
|                 ((TcpClient)client).Setup(FoundataionConfig); | ||||
|             } | ||||
|             //载入配置 | ||||
|             _plc = new((TcpClient)client, SiemensEnum.S1200) | ||||
|             { | ||||
|                 DataFormat = driverPropertys.DataFormat, | ||||
|                 ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|                 TimeOut = driverPropertys.TimeOut | ||||
|             }; | ||||
|             if (driverPropertys.LocalTSAP != 0) | ||||
|             { | ||||
|                 _plc.LocalTSAP = driverPropertys.LocalTSAP; | ||||
|             } | ||||
|             if (driverPropertys.Rack != 0) | ||||
|             { | ||||
|                 _plc.Rack = driverPropertys.Rack; | ||||
|             } | ||||
|             if (driverPropertys.Slot != 0) | ||||
|             { | ||||
|                 _plc.Slot = driverPropertys.Slot; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Siemens | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public class S7_1500 : Siemens | ||||
|     { | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override Type DriverDebugUIType => typeof(S7_1500DebugPage); | ||||
|         /// <inheritdoc/> | ||||
|         public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|         { | ||||
|             if (client == null) | ||||
|             { | ||||
|                 FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                     ; | ||||
|                 client = new TcpClient(); | ||||
|                 ((TcpClient)client).Setup(FoundataionConfig); | ||||
|             } | ||||
|             //载入配置 | ||||
|             _plc = new((TcpClient)client, SiemensEnum.S1500) | ||||
|             { | ||||
|                 DataFormat = driverPropertys.DataFormat, | ||||
|                 ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|                 TimeOut = driverPropertys.TimeOut | ||||
|             }; | ||||
|             if (driverPropertys.LocalTSAP != 0) | ||||
|             { | ||||
|                 _plc.LocalTSAP = driverPropertys.LocalTSAP; | ||||
|             } | ||||
|             if (driverPropertys.Rack != 0) | ||||
|             { | ||||
|                 _plc.Rack = driverPropertys.Rack; | ||||
|             } | ||||
|             if (driverPropertys.Slot != 0) | ||||
|             { | ||||
|                 _plc.Slot = driverPropertys.Slot; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Siemens | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public class S7_200 : Siemens | ||||
|     { | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override Type DriverDebugUIType => typeof(S7_200DebugPage); | ||||
|         /// <inheritdoc/> | ||||
|         public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|         { | ||||
|             if (client == null) | ||||
|             { | ||||
|                 FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                     ; | ||||
|                 client = new TcpClient(); | ||||
|                 ((TcpClient)client).Setup(FoundataionConfig); | ||||
|             } | ||||
|             //载入配置 | ||||
|             _plc = new((TcpClient)client, SiemensEnum.S200) | ||||
|             { | ||||
|                 DataFormat = driverPropertys.DataFormat, | ||||
|                 ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|                 TimeOut = driverPropertys.TimeOut | ||||
|             }; | ||||
|             if (driverPropertys.LocalTSAP != 0) | ||||
|             { | ||||
|                 _plc.LocalTSAP = driverPropertys.LocalTSAP; | ||||
|             } | ||||
|             if (driverPropertys.Rack != 0) | ||||
|             { | ||||
|                 _plc.Rack = driverPropertys.Rack; | ||||
|             } | ||||
|             if (driverPropertys.Slot != 0) | ||||
|             { | ||||
|                 _plc.Slot = driverPropertys.Slot; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,56 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Siemens | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public class S7_200SMART : Siemens | ||||
|     { | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override Type DriverDebugUIType => typeof(S7_200SMARTDebugPage); | ||||
|         /// <inheritdoc/> | ||||
|         public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|         { | ||||
|             if (client == null) | ||||
|             { | ||||
|                 FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                     ; | ||||
|                 client = new TcpClient(); | ||||
|                 ((TcpClient)client).Setup(FoundataionConfig); | ||||
|             } | ||||
|             //载入配置 | ||||
|             _plc = new((TcpClient)client, SiemensEnum.S200Smart) | ||||
|             { | ||||
|                 DataFormat = driverPropertys.DataFormat, | ||||
|                 ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|                 TimeOut = driverPropertys.TimeOut | ||||
|             }; | ||||
|             if (driverPropertys.LocalTSAP != 0) | ||||
|             { | ||||
|                 _plc.LocalTSAP = driverPropertys.LocalTSAP; | ||||
|             } | ||||
|             if (driverPropertys.Rack != 0) | ||||
|             { | ||||
|                 _plc.Rack = driverPropertys.Rack; | ||||
|             } | ||||
|             if (driverPropertys.Slot != 0) | ||||
|             { | ||||
|                 _plc.Slot = driverPropertys.Slot; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Siemens | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public class S7_300 : Siemens | ||||
|     { | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override Type DriverDebugUIType => typeof(S7_300DebugPage); | ||||
|         /// <inheritdoc/> | ||||
|         public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|         { | ||||
|             if (client == null) | ||||
|             { | ||||
|                 FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                     ; | ||||
|                 client = new TcpClient(); | ||||
|                 ((TcpClient)client).Setup(FoundataionConfig); | ||||
|             } | ||||
|             //载入配置 | ||||
|             _plc = new((TcpClient)client, SiemensEnum.S300) | ||||
|             { | ||||
|                 DataFormat = driverPropertys.DataFormat, | ||||
|                 ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|                 TimeOut = driverPropertys.TimeOut | ||||
|             }; | ||||
|             if (driverPropertys.LocalTSAP != 0) | ||||
|             { | ||||
|                 _plc.LocalTSAP = driverPropertys.LocalTSAP; | ||||
|             } | ||||
|             if (driverPropertys.Rack != 0) | ||||
|             { | ||||
|                 _plc.Rack = driverPropertys.Rack; | ||||
|             } | ||||
|             if (driverPropertys.Slot != 0) | ||||
|             { | ||||
|                 _plc.Slot = driverPropertys.Slot; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.Siemens | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public class S7_400 : Siemens | ||||
|     { | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         public override Type DriverDebugUIType => typeof(S7_400DebugPage); | ||||
|         /// <inheritdoc/> | ||||
|         public override CollectDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|  | ||||
|         /// <inheritdoc/> | ||||
|         protected override void Init(CollectDeviceRunTime device, object client = null) | ||||
|         { | ||||
|             if (client == null) | ||||
|             { | ||||
|                 FoundataionConfig.SetRemoteIPHost(new IPHost($"{driverPropertys.IP}:{driverPropertys.Port}")) | ||||
|                     ; | ||||
|                 client = new TcpClient(); | ||||
|                 ((TcpClient)client).Setup(FoundataionConfig); | ||||
|             } | ||||
|             //载入配置 | ||||
|             _plc = new((TcpClient)client, SiemensEnum.S400) | ||||
|             { | ||||
|                 DataFormat = driverPropertys.DataFormat, | ||||
|                 ConnectTimeOut = driverPropertys.ConnectTimeOut, | ||||
|                 TimeOut = driverPropertys.TimeOut | ||||
|             }; | ||||
|             if (driverPropertys.LocalTSAP != 0) | ||||
|             { | ||||
|                 _plc.LocalTSAP = driverPropertys.LocalTSAP; | ||||
|             } | ||||
|             if (driverPropertys.Rack != 0) | ||||
|             { | ||||
|                 _plc.Rack = driverPropertys.Rack; | ||||
|             } | ||||
|             if (driverPropertys.Slot != 0) | ||||
|             { | ||||
|                 _plc.Slot = driverPropertys.Slot; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人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 System.Net.Http.Json | ||||
| @using System.IO; | ||||
| @using System.Text.Json; | ||||
| @using System.Reflection; | ||||
|  | ||||
| @using ThingsGateway.Components; | ||||
| @using ThingsGateway.Admin.Core; | ||||
| @using ThingsGateway.Foundation.Core; | ||||
| @using ThingsGateway.Foundation.Extension.String; | ||||
| @using ThingsGateway.Admin.Application; | ||||
|  | ||||
| @using ThingsGateway.Core; | ||||
| @using ThingsGateway.Gateway.Application; | ||||
| @using ThingsGateway.Gateway.Core; | ||||
| @@ -1,324 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
|  | ||||
| using Mapster; | ||||
|  | ||||
| using Microsoft.Extensions.Hosting; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections.Concurrent; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.ConcurrentQueue; | ||||
| using ThingsGateway.Foundation.Extension.String; | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SQLDB; | ||||
| public class TDengineDB : UpLoadBase | ||||
| { | ||||
|     private readonly ConcurrentQueue<TDHistoryValue> DeviceVariableRunTimes = new(); | ||||
|     private readonly TDengineDBProperty driverPropertys = new(); | ||||
|     private readonly TDengineDBVariableProperty variablePropertys = new(); | ||||
|     private GlobalDeviceData _globalDeviceData; | ||||
|     private TypeAdapterConfig _config; | ||||
|  | ||||
|     private List<DeviceVariableRunTime> _uploadVariables = new(); | ||||
|     private TimerTick exTimerTick; | ||||
|     public TDengineDB() | ||||
|     { | ||||
|         _config = new TypeAdapterConfig(); | ||||
|         _config.ForType<DeviceVariableRunTime, HistoryValue>() | ||||
|             .Map(dest => dest.Value, (src) => ValueReturn(src)); | ||||
|     } | ||||
|     private static object ValueReturn(DeviceVariableRunTime src) | ||||
|     { | ||||
|         if (src.Value?.ToString()?.IsBoolValue() == true) | ||||
|         { | ||||
|             if (src.Value.ToBoolean()) | ||||
|             { | ||||
|                 return 1; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return 0; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return src.Value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public override Type DriverDebugUIType => null; | ||||
|     public override UpDriverPropertyBase DriverPropertys => driverPropertys; | ||||
|     public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables; | ||||
|  | ||||
|     public override VariablePropertyBase VariablePropertys => variablePropertys; | ||||
|  | ||||
|     public override Task AfterStopAsync() | ||||
|     { | ||||
|         return Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task BeforStartAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         SqlSugarClient db = GetHisDbAsync(); | ||||
|         db.DbMaintenance.CreateDatabase(); | ||||
|         db.CodeFirst.InitTables(typeof(TDHistoryValue)); | ||||
|         await Task.CompletedTask; | ||||
|     } | ||||
|  | ||||
|     public override async Task ExecuteAsync(CancellationToken cancellationToken) | ||||
|     { | ||||
|         var db = GetHisDbAsync(); | ||||
|         { | ||||
|             if (!driverPropertys.IsInterval) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     ////变化推送 | ||||
|                     var varList = DeviceVariableRunTimes.ToListWithDequeue(); | ||||
|                     if (varList?.Count != 0) | ||||
|                     { | ||||
|                         await InserableAsync(db, varList, cancellationToken); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage?.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (exTimerTick.IsTickHappen()) | ||||
|                 { | ||||
|                     try | ||||
|                     { | ||||
|                         var varList = _uploadVariables.ToList().Adapt<List<TDHistoryValue>>(_config); | ||||
|                         if (varList?.Count != 0) | ||||
|                         { | ||||
|                             await InserableAsync(db, varList, cancellationToken); | ||||
|                         } | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         LogMessage?.LogWarning(ex); | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public override bool IsConnected() => _uploadVariables?.Count > 0; | ||||
|  | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); | ||||
|             _uploadVariables = null; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage?.LogError(ex); | ||||
|         } | ||||
|  | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     protected override void Init(UploadDeviceRunTime device) | ||||
|     { | ||||
|  | ||||
|         _globalDeviceData = App.GetService<GlobalDeviceData>(); | ||||
|  | ||||
|         var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id)) | ||||
|            .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean()) | ||||
|            .ToList(); | ||||
|  | ||||
|         _uploadVariables = tags; | ||||
|  | ||||
|         if (!driverPropertys.IsInterval) | ||||
|         { | ||||
|             _uploadVariables.ForEach(a => | ||||
|             { | ||||
|                 a.VariableValueChange += VariableValueChange; | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         if (_uploadVariables.Count == 0) | ||||
|         { | ||||
|             LogMessage.LogWarning("插件变量数量为0"); | ||||
|         } | ||||
|         if (driverPropertys.IntervalTime < 1) | ||||
|             driverPropertys.IntervalTime = 10; | ||||
|         exTimerTick = new(driverPropertys.IntervalTime * 1000); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Aop设置 | ||||
|     /// </summary> | ||||
|     /// <param name="db"></param> | ||||
|     private static void AopSetting(SqlSugarClient db) | ||||
|     { | ||||
|         var config = db.CurrentConnectionConfig; | ||||
|  | ||||
|         // 设置超时时间 | ||||
|         db.Ado.CommandTimeOut = 30; | ||||
|  | ||||
|         // 打印SQL语句 | ||||
|         db.Aop.OnLogExecuting = (sql, pars) => | ||||
|         { | ||||
|             //如果不是开发环境就打印sql | ||||
|             if (App.HostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (sql.StartsWith("SELECT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Green; | ||||
|                 } | ||||
|                 if (sql.StartsWith("UPDATE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Yellow; | ||||
|                 } | ||||
|                 if (sql.StartsWith("INSERT")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Blue; | ||||
|                 } | ||||
|                 if (sql.StartsWith("DELETE")) | ||||
|                 { | ||||
|                     Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 } | ||||
|                 WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|                 Console.WriteLine(); | ||||
|             } | ||||
|         }; | ||||
|         //异常 | ||||
|         db.Aop.OnError = (ex) => | ||||
|         { | ||||
|             //如果不是开发环境就打印日志 | ||||
|             if (App.WebHostEnvironment.IsDevelopment()) | ||||
|             { | ||||
|                 if (ex.Parametres == null) return; | ||||
|                 Console.ForegroundColor = ConsoleColor.Red; | ||||
|                 var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value)); | ||||
|                 WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres)); | ||||
|                 Console.ForegroundColor = ConsoleColor.White; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private static void WriteSqlLog(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|  | ||||
|     private static void WriteSqlLogError(string msg) | ||||
|     { | ||||
|         Console.WriteLine("【Sql执行错误时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|         Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取数据库链接 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     private SqlSugarClient GetHisDbAsync() | ||||
|     { | ||||
|         var configureExternalServices = new ConfigureExternalServices | ||||
|         { | ||||
|             EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required | ||||
|             { | ||||
|                 if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) | ||||
|                     || (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null)) | ||||
|                     column.IsNullable = true; | ||||
|             }, | ||||
|         }; | ||||
|         var sqlSugarClient = new SqlSugarClient(new ConnectionConfig() | ||||
|         { | ||||
|             ConnectionString = driverPropertys.ConnectStr,//连接字符串 | ||||
|             DbType = DbType.TDengine,//数据库类型 | ||||
|             IsAutoCloseConnection = true, //不设成true要手动close | ||||
|             ConfigureExternalServices = configureExternalServices, | ||||
|         } | ||||
|         ); | ||||
|         AopSetting(sqlSugarClient);//aop配置 | ||||
|         return sqlSugarClient; | ||||
|     } | ||||
|  | ||||
|     private async Task InserableAsync(SqlSugarClient db, List<TDHistoryValue> dbInserts, CancellationToken cancellationToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             var result = await db.Insertable(dbInserts).ExecuteCommandAsync(cancellationToken); | ||||
|             if (result > 0) | ||||
|                 LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString()); | ||||
|             //连接成功时补发缓存数据 | ||||
|             var cacheData = await CacheDb.GetCacheData(); | ||||
|             foreach (var item in cacheData) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     var data = item.CacheStr.FromJsonString<List<TDHistoryValue>>(); | ||||
|                     var cacheresult = await db.Insertable(data).ExecuteCommandAsync(cancellationToken); | ||||
|                     if (cacheresult > 0) | ||||
|                     { | ||||
|                         await CacheDb.DeleteCacheData(item.Id); | ||||
|                         LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}"); | ||||
|                     } | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     LogMessage.LogWarning(ex); | ||||
|                 } | ||||
|  | ||||
|  | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage.LogWarning(ex); | ||||
|             await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime) | ||||
|     { | ||||
|         if (!driverPropertys.IsInterval) | ||||
|             DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<TDHistoryValue>(_config)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -1,31 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace ThingsGateway.Plugin.SQLDB; | ||||
|  | ||||
| public class TDengineDBProperty : UpDriverPropertyBase | ||||
| { | ||||
|     [DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "Host=localhost;Port=6030;Username=root;Password=taosdata;Database=test"; | ||||
|     [DeviceProperty("是否间隔插入", "False时将每次变化写入")] public bool IsInterval { get; set; } = true; | ||||
|     [DeviceProperty("间隔时间", "秒,实时表时代表更新间隔,历史表时代表插入间隔")] public int IntervalTime { get; set; } = 10; | ||||
|     [DeviceProperty("缓存最大条数", "默认2千条")] public int CacheMaxCount { get; set; } = 2000; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 线程循环间隔 | ||||
|     /// </summary> | ||||
|     [DeviceProperty("线程循环间隔", "最小10ms")] | ||||
|     public int CycleInterval { get; set; } = 1000; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -3,48 +3,40 @@ Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # Visual Studio Version 17 | ||||
| VisualStudioVersion = 17.6.33927.249 | ||||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "Foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj", "{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{9EB46BB6-D4EA-4B06-95EE-6C971E653030}" | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{4E66C22C-0636-4949-BF6A-9E3BBE1550BA}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		Web\Directory.Build.props = Web\Directory.Build.props | ||||
| 		admin\Directory.Build.props = admin\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "script", "script", "{4A64518E-C072-4607-BBF7-7D392CEC9D58}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "admin\ThingsGateway.Components\ThingsGateway.Components.csproj", "{0A891D8E-23B3-46AD-8D30-565EE5004F93}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "admin\ThingsGateway.Core\ThingsGateway.Core.csproj", "{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "admin\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{5DA3D2BD-6768-4479-B52F-49E022EFF310}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "admin\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "admin\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{D6685A42-2712-417A-92C5-5EFF90B9FA94}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "admin\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web", "web", "{F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		web\Directory.Build.props = web\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "web\ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{D37EC028-EA46-4510-8261-6E780A906314}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "web\ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{C5F662EB-991F-438D-BF61-EF87E7371C04}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{97B23D8B-C6C0-4746-A21F-C7B49354B284}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		..\.gitignore = ..\.gitignore | ||||
| 		..\README.md = ..\README.md | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "Web\ThingsGateway.Components\ThingsGateway.Components.csproj", "{799C49A4-8E23-475A-A82D-080854718BEE}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj", "{6961511A-8787-42AF-827D-B630B2AF4791}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "Web\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{616CA361-B667-42C8-B4DC-097C7CD39830}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "Web\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{16C62A28-BACE-4391-91F8-C2D78D063A1E}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "Web\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{8FA03089-322F-44CB-8E4B-F2637388E944}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "Web\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{14FF7150-6DB7-455B-AD00-6AB4DE37855B}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "Web\ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "Web\ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "Web\ThingsGateway.Core\ThingsGateway.Core.csproj", "{51313113-7BB8-494E-9C24-6787BECE39BB}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{79E7042F-F9E3-4D87-BFA9-4B7DD9736735}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E8D2CBBA-9BCC-44E0-B192-BA0214010203}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		.editorconfig = .editorconfig | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Foundation", "Foundation", "{B6288ADE-A570-4962-8907-991B0FF2D89C}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		Foundation\Directory.Build.props = Foundation\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "foundation", "foundation", "{268A1A81-2685-47E1-9986-5934A58A31A4}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| @@ -52,56 +44,56 @@ Global | ||||
| 		Release|Any CPU = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{6961511A-8787-42AF-827D-B630B2AF4791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{6961511A-8787-42AF-827D-B630B2AF4791}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{6961511A-8787-42AF-827D-B630B2AF4791}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{6961511A-8787-42AF-827D-B630B2AF4791}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(NestedProjects) = preSolution | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891} = {B6288ADE-A570-4962-8907-991B0FF2D89C} | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE} = {BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15} | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735} | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735} | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735} | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735} | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB} = {BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15} | ||||
| 		{79E7042F-F9E3-4D87-BFA9-4B7DD9736735} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030} | ||||
| 		{BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030} | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197} | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197} | ||||
| 		{6961511A-8787-42AF-827D-B630B2AF4791} = {268A1A81-2685-47E1-9986-5934A58A31A4} | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| 		SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB} | ||||
|   | ||||
| @@ -1,49 +0,0 @@ | ||||
|  | ||||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # Visual Studio Version 17 | ||||
| VisualStudioVersion = 17.6.33927.249 | ||||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "Foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj", "{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "Web\ThingsGateway.Components\ThingsGateway.Components.csproj", "{799C49A4-8E23-475A-A82D-080854718BEE}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "Web\ThingsGateway.Core\ThingsGateway.Core.csproj", "{51313113-7BB8-494E-9C24-6787BECE39BB}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.UpgradeManger", "UpgradeManger\ThingsGateway.UpgradeManger\ThingsGateway.UpgradeManger.csproj", "{84362F1D-E788-4646-B555-5A3629B55EFC}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Upgrade", "UpgradeManger\ThingsGateway.Upgrade\ThingsGateway.Upgrade.csproj", "{681F774F-7B0B-450A-917C-1385E1847CA6}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
| 		Release|Any CPU = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| 		SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB} | ||||
| 	EndGlobalSection | ||||
| EndGlobal | ||||
| @@ -3,115 +3,107 @@ Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # Visual Studio Version 17 | ||||
| VisualStudioVersion = 17.6.33927.249 | ||||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "Foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj", "{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "foundation\ThingsGateway.foundation\ThingsGateway.Foundation.csproj", "{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Foundation", "Foundation", "{0874CBC5-C583-4FAD-BA93-94571D446898}" | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "foundation", "foundation", "{0874CBC5-C583-4FAD-BA93-94571D446898}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		Foundation\Directory.Build.props = Foundation\Directory.Build.props | ||||
| 		foundation\Directory.Build.props = foundation\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.DLT645", "Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj", "{92963877-0185-45A5-A0EB-CEC0D55FF9B0}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.DLT645", "foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj", "{92963877-0185-45A5-A0EB-CEC0D55FF9B0}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Modbus", "Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj", "{24510CCE-0B60-4A03-9719-CF367C3528E9}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Modbus", "foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj", "{24510CCE-0B60-4A03-9719-CF367C3528E9}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.OPCDA", "Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj", "{566783A4-222B-46F5-AA12-0753997B3254}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.OPCDA", "foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj", "{566783A4-222B-46F5-AA12-0753997B3254}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.OPCUA", "Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj", "{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.OPCUA", "foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj", "{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Siemens", "Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj", "{9695B353-D773-40DD-B65E-7B10EB0C16EC}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Siemens", "foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj", "{9695B353-D773-40DD-B65E-7B10EB0C16EC}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demo", "Demo", "{95008B83-0324-4A7C-80DE-2BBDDD1A9099}" | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		Demo\Directory.Build.props = Demo\Directory.Build.props | ||||
| 		demo\Directory.Build.props = demo\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{9EB46BB6-D4EA-4B06-95EE-6C971E653030}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Photino", "demo\ThingsGateway.Foundation.Demo.Photino\ThingsGateway.Foundation.Demo.Photino.csproj", "{2192217C-CF77-422E-9E63-DF4003ABF01A}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Rcl", "demo\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj", "{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Winform", "demo\ThingsGateway.Foundation.Demo.Winform\ThingsGateway.Foundation.Demo.Winform.csproj", "{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gateway", "gateway", "{DFB97103-FC3A-4DC3-A327-DA8C65BDA607}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		Web\Directory.Build.props = Web\Directory.Build.props | ||||
| 		gateway\Directory.Build.props = gateway\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "script", "script", "{4A64518E-C072-4607-BBF7-7D392CEC9D58}" | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{4E66C22C-0636-4949-BF6A-9E3BBE1550BA}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		admin\Directory.Build.props = admin\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "admin\ThingsGateway.Components\ThingsGateway.Components.csproj", "{0A891D8E-23B3-46AD-8D30-565EE5004F93}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "admin\ThingsGateway.Core\ThingsGateway.Core.csproj", "{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "admin\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{5DA3D2BD-6768-4479-B52F-49E022EFF310}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "admin\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "admin\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{D6685A42-2712-417A-92C5-5EFF90B9FA94}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "admin\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Core", "gateway\ThingsGateway.Gateway.Core\ThingsGateway.Gateway.Core.csproj", "{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Blazor", "gateway\ThingsGateway.Gateway.Blazor\ThingsGateway.Gateway.Blazor.csproj", "{69EA3D89-CB40-425A-8D70-5E4A33337BE5}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{90AB5C24-1AA3-4F58-9987-B307B92B5193}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.ApiController", "gateway\ThingsGateway.Gateway.ApiController\ThingsGateway.Gateway.ApiController.csproj", "{4C7A5A90-8292-413B-8848-419D9DCDE66F}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web", "web", "{F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		web\Directory.Build.props = web\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "web\ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{D37EC028-EA46-4510-8261-6E780A906314}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "web\ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{C5F662EB-991F-438D-BF61-EF87E7371C04}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugin", "plugin", "{E65490B8-D2E2-4693-B39C-15703B1EBFBB}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		plugin\Directory.Build.props = plugin\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Modbus", "plugin\ThingsGateway.Plugin.Modbus\ThingsGateway.Plugin.Modbus.csproj", "{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Mqtt", "plugin\ThingsGateway.Plugin.Mqtt\ThingsGateway.Plugin.Mqtt.csproj", "{53C870AE-0249-4485-AE63-F8A16EA4BCB4}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{97B23D8B-C6C0-4746-A21F-C7B49354B284}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		..\.gitignore = ..\.gitignore | ||||
| 		..\README.md = ..\README.md | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "Web\ThingsGateway.Components\ThingsGateway.Components.csproj", "{799C49A4-8E23-475A-A82D-080854718BEE}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Kafka", "plugin\ThingsGateway.Plugin.Kafka\ThingsGateway.Plugin.Kafka.csproj", "{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "Web\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{616CA361-B667-42C8-B4DC-097C7CD39830}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLDB", "plugin\ThingsGateway.Plugin.SQLDB\ThingsGateway.Plugin.SQLDB.csproj", "{C102AB3A-377E-4753-AFA7-C13250D7DF00}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "Web\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{16C62A28-BACE-4391-91F8-C2D78D063A1E}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.QuestDB", "plugin\ThingsGateway.Plugin.QuestDB\ThingsGateway.Plugin.QuestDB.csproj", "{0DE1D254-0BD2-4D98-B939-5440BE5EB552}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "Web\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{8FA03089-322F-44CB-8E4B-F2637388E944}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.TDengineDB", "plugin\ThingsGateway.Plugin.TDengineDB\ThingsGateway.Plugin.TDengineDB.csproj", "{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "Web\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{14FF7150-6DB7-455B-AD00-6AB4DE37855B}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.RabbitMQ", "plugin\ThingsGateway.Plugin.RabbitMQ\ThingsGateway.Plugin.RabbitMQ.csproj", "{A8E68E17-4EBF-4E4C-8272-B489329A68BF}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "Web\ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Siemens", "plugin\ThingsGateway.Plugin.Siemens\ThingsGateway.Plugin.Siemens.csproj", "{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "Web\ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.DLT645", "plugin\ThingsGateway.Plugin.DLT645\ThingsGateway.Plugin.DLT645.csproj", "{AC4295F2-AB2F-4137-99EF-80FA5C83896B}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "Web\ThingsGateway.Core\ThingsGateway.Core.csproj", "{51313113-7BB8-494E-9C24-6787BECE39BB}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCDA", "plugin\ThingsGateway.Plugin.OPCDA\ThingsGateway.Plugin.OPCDA.csproj", "{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.ApiController", "Web\ThingsGateway.Gateway.ApiController\ThingsGateway.Gateway.ApiController.csproj", "{5D7BE567-2345-46C8-9F54-DDC1DA96D198}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCUA", "plugin\ThingsGateway.Plugin.OPCUA\ThingsGateway.Plugin.OPCUA.csproj", "{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "Web\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Blazor", "Web\ThingsGateway.Gateway.Blazor\ThingsGateway.Gateway.Blazor.csproj", "{CD0F211A-F65B-4026-9750-68AC3C70D012}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Core", "Web\ThingsGateway.Gateway.Core\ThingsGateway.Gateway.Core.csproj", "{5CD79F91-7182-4A9D-9BEF-4DF410C782D2}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugin", "Plugin", "{CC8D0880-B73E-4DFC-9052-86504728708E}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		Plugin\Directory.Build.props = Plugin\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Modbus", "Plugin\ThingsGateway.Plugin.Modbus\ThingsGateway.Plugin.Modbus.csproj", "{2057E5BE-FACA-4D44-A2BA-E1F864A8DAFF}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.DLT645", "Plugin\ThingsGateway.Plugin.DLT645\ThingsGateway.Plugin.DLT645.csproj", "{A723D4D7-B796-4D97-BA68-95E5696C9559}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Kafka", "Plugin\ThingsGateway.Plugin.Kafka\ThingsGateway.Plugin.Kafka.csproj", "{D9944D52-81B4-4DBC-8C3B-2A334CCBD4F6}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Mqtt", "Plugin\ThingsGateway.Plugin.Mqtt\ThingsGateway.Plugin.Mqtt.csproj", "{C5C89B2F-D9C9-4A67-A9BE-4E5F0B42162F}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCDA", "Plugin\ThingsGateway.Plugin.OPCDA\ThingsGateway.Plugin.OPCDA.csproj", "{7562C85D-267D-4718-8857-422B625C5BD0}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCUA", "Plugin\ThingsGateway.Plugin.OPCUA\ThingsGateway.Plugin.OPCUA.csproj", "{A0E1EAC7-10DD-4520-973F-CC385BBFBD84}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.RabbitMQ", "Plugin\ThingsGateway.Plugin.RabbitMQ\ThingsGateway.Plugin.RabbitMQ.csproj", "{503529C4-842A-47F0-928B-C8DC5B0A367D}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Siemens", "Plugin\ThingsGateway.Plugin.Siemens\ThingsGateway.Plugin.Siemens.csproj", "{8A9F6586-3320-4F03-B3DA-09BF39AA90A0}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E8D2CBBA-9BCC-44E0-B192-BA0214010203}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		.editorconfig = .editorconfig | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UpgradeManger", "UpgradeManger", "{237C7BC5-7B07-40B5-AF42-CE2F8E0893C3}" | ||||
| 	ProjectSection(SolutionItems) = preProject | ||||
| 		UpgradeManger\Directory.Build.props = UpgradeManger\Directory.Build.props | ||||
| 	EndProjectSection | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.UpgradeManger", "UpgradeManger\ThingsGateway.UpgradeManger\ThingsGateway.UpgradeManger.csproj", "{84362F1D-E788-4646-B555-5A3629B55EFC}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Upgrade", "UpgradeManger\ThingsGateway.Upgrade\ThingsGateway.Upgrade.csproj", "{681F774F-7B0B-450A-917C-1385E1847CA6}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Rcl", "Demo\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj", "{637A662B-7B70-4CE8-8F5F-0A095B9D77EC}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Photino", "Demo\ThingsGateway.Foundation.Demo.Photino\ThingsGateway.Foundation.Demo.Photino.csproj", "{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLDB", "Plugin\ThingsGateway.Plugin.SQLDB\ThingsGateway.Plugin.SQLDB.csproj", "{7EBD5500-0DA0-415A-831D-5DC350917501}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.QuestDB", "Plugin\ThingsGateway.Plugin.QuestDB\ThingsGateway.Plugin.QuestDB.csproj", "{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.TDengineDB", "Plugin\ThingsGateway.Plugin.TDengineDB\ThingsGateway.Plugin.TDengineDB.csproj", "{2C827B2C-75DF-413B-9AB2-2D1B438AC082}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{5854938A-F669-40F4-A54C-EC2651730B73}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{C39748E6-34AC-4C4A-AEC7-7D807FFB4813}" | ||||
| EndProject | ||||
| Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateway", "Gateway", "{05A88744-64D9-4959-8A97-5189F327BBBE}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Winform", "Demo\ThingsGateway.Foundation.Demo.Winform\ThingsGateway.Foundation.Demo.Winform.csproj", "{231AEEA6-F914-4F32-AFF6-D554E6C855CF}" | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLHisAlarm", "plugin\ThingsGateway.Plugin.SQLHisAlarm\ThingsGateway.Plugin.SQLHisAlarm.csproj", "{0947E6C0-6371-4483-B0ED-191FB5073151}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| @@ -143,118 +135,114 @@ Global | ||||
| 		{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{5D7BE567-2345-46C8-9F54-DDC1DA96D198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{5D7BE567-2345-46C8-9F54-DDC1DA96D198}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{5D7BE567-2345-46C8-9F54-DDC1DA96D198}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{5D7BE567-2345-46C8-9F54-DDC1DA96D198}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{CD0F211A-F65B-4026-9750-68AC3C70D012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{CD0F211A-F65B-4026-9750-68AC3C70D012}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{CD0F211A-F65B-4026-9750-68AC3C70D012}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{CD0F211A-F65B-4026-9750-68AC3C70D012}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{5CD79F91-7182-4A9D-9BEF-4DF410C782D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{5CD79F91-7182-4A9D-9BEF-4DF410C782D2}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{5CD79F91-7182-4A9D-9BEF-4DF410C782D2}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{5CD79F91-7182-4A9D-9BEF-4DF410C782D2}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{2057E5BE-FACA-4D44-A2BA-E1F864A8DAFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{2057E5BE-FACA-4D44-A2BA-E1F864A8DAFF}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{2057E5BE-FACA-4D44-A2BA-E1F864A8DAFF}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{2057E5BE-FACA-4D44-A2BA-E1F864A8DAFF}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{A723D4D7-B796-4D97-BA68-95E5696C9559}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{A723D4D7-B796-4D97-BA68-95E5696C9559}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{A723D4D7-B796-4D97-BA68-95E5696C9559}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{A723D4D7-B796-4D97-BA68-95E5696C9559}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{D9944D52-81B4-4DBC-8C3B-2A334CCBD4F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{D9944D52-81B4-4DBC-8C3B-2A334CCBD4F6}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{D9944D52-81B4-4DBC-8C3B-2A334CCBD4F6}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{D9944D52-81B4-4DBC-8C3B-2A334CCBD4F6}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{C5C89B2F-D9C9-4A67-A9BE-4E5F0B42162F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{C5C89B2F-D9C9-4A67-A9BE-4E5F0B42162F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{C5C89B2F-D9C9-4A67-A9BE-4E5F0B42162F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{C5C89B2F-D9C9-4A67-A9BE-4E5F0B42162F}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{7562C85D-267D-4718-8857-422B625C5BD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{7562C85D-267D-4718-8857-422B625C5BD0}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{7562C85D-267D-4718-8857-422B625C5BD0}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{7562C85D-267D-4718-8857-422B625C5BD0}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{A0E1EAC7-10DD-4520-973F-CC385BBFBD84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{A0E1EAC7-10DD-4520-973F-CC385BBFBD84}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{A0E1EAC7-10DD-4520-973F-CC385BBFBD84}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{A0E1EAC7-10DD-4520-973F-CC385BBFBD84}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{503529C4-842A-47F0-928B-C8DC5B0A367D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{503529C4-842A-47F0-928B-C8DC5B0A367D}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{503529C4-842A-47F0-928B-C8DC5B0A367D}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{503529C4-842A-47F0-928B-C8DC5B0A367D}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{8A9F6586-3320-4F03-B3DA-09BF39AA90A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{8A9F6586-3320-4F03-B3DA-09BF39AA90A0}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{8A9F6586-3320-4F03-B3DA-09BF39AA90A0}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{8A9F6586-3320-4F03-B3DA-09BF39AA90A0}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{637A662B-7B70-4CE8-8F5F-0A095B9D77EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{637A662B-7B70-4CE8-8F5F-0A095B9D77EC}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{637A662B-7B70-4CE8-8F5F-0A095B9D77EC}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{637A662B-7B70-4CE8-8F5F-0A095B9D77EC}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{7EBD5500-0DA0-415A-831D-5DC350917501}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{7EBD5500-0DA0-415A-831D-5DC350917501}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{7EBD5500-0DA0-415A-831D-5DC350917501}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{7EBD5500-0DA0-415A-831D-5DC350917501}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{231AEEA6-F914-4F32-AFF6-D554E6C855CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{231AEEA6-F914-4F32-AFF6-D554E6C855CF}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{231AEEA6-F914-4F32-AFF6-D554E6C855CF}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{231AEEA6-F914-4F32-AFF6-D554E6C855CF}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{2192217C-CF77-422E-9E63-DF4003ABF01A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{2192217C-CF77-422E-9E63-DF4003ABF01A}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{2192217C-CF77-422E-9E63-DF4003ABF01A}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{2192217C-CF77-422E-9E63-DF4003ABF01A}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{0947E6C0-6371-4483-B0ED-191FB5073151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{0947E6C0-6371-4483-B0ED-191FB5073151}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{0947E6C0-6371-4483-B0ED-191FB5073151}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{0947E6C0-6371-4483-B0ED-191FB5073151}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| @@ -266,35 +254,33 @@ Global | ||||
| 		{566783A4-222B-46F5-AA12-0753997B3254} = {0874CBC5-C583-4FAD-BA93-94571D446898} | ||||
| 		{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF} = {0874CBC5-C583-4FAD-BA93-94571D446898} | ||||
| 		{9695B353-D773-40DD-B65E-7B10EB0C16EC} = {0874CBC5-C583-4FAD-BA93-94571D446898} | ||||
| 		{799C49A4-8E23-475A-A82D-080854718BEE} = {C39748E6-34AC-4C4A-AEC7-7D807FFB4813} | ||||
| 		{616CA361-B667-42C8-B4DC-097C7CD39830} = {5854938A-F669-40F4-A54C-EC2651730B73} | ||||
| 		{16C62A28-BACE-4391-91F8-C2D78D063A1E} = {5854938A-F669-40F4-A54C-EC2651730B73} | ||||
| 		{8FA03089-322F-44CB-8E4B-F2637388E944} = {5854938A-F669-40F4-A54C-EC2651730B73} | ||||
| 		{14FF7150-6DB7-455B-AD00-6AB4DE37855B} = {5854938A-F669-40F4-A54C-EC2651730B73} | ||||
| 		{51313113-7BB8-494E-9C24-6787BECE39BB} = {C39748E6-34AC-4C4A-AEC7-7D807FFB4813} | ||||
| 		{5D7BE567-2345-46C8-9F54-DDC1DA96D198} = {05A88744-64D9-4959-8A97-5189F327BBBE} | ||||
| 		{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F} = {05A88744-64D9-4959-8A97-5189F327BBBE} | ||||
| 		{CD0F211A-F65B-4026-9750-68AC3C70D012} = {05A88744-64D9-4959-8A97-5189F327BBBE} | ||||
| 		{5CD79F91-7182-4A9D-9BEF-4DF410C782D2} = {05A88744-64D9-4959-8A97-5189F327BBBE} | ||||
| 		{2057E5BE-FACA-4D44-A2BA-E1F864A8DAFF} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{A723D4D7-B796-4D97-BA68-95E5696C9559} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{D9944D52-81B4-4DBC-8C3B-2A334CCBD4F6} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{C5C89B2F-D9C9-4A67-A9BE-4E5F0B42162F} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{7562C85D-267D-4718-8857-422B625C5BD0} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{A0E1EAC7-10DD-4520-973F-CC385BBFBD84} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{503529C4-842A-47F0-928B-C8DC5B0A367D} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{8A9F6586-3320-4F03-B3DA-09BF39AA90A0} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{84362F1D-E788-4646-B555-5A3629B55EFC} = {237C7BC5-7B07-40B5-AF42-CE2F8E0893C3} | ||||
| 		{681F774F-7B0B-450A-917C-1385E1847CA6} = {237C7BC5-7B07-40B5-AF42-CE2F8E0893C3} | ||||
| 		{637A662B-7B70-4CE8-8F5F-0A095B9D77EC} = {95008B83-0324-4A7C-80DE-2BBDDD1A9099} | ||||
| 		{C5519C51-0A0C-4317-A43D-FFBB6B344ACB} = {95008B83-0324-4A7C-80DE-2BBDDD1A9099} | ||||
| 		{7EBD5500-0DA0-415A-831D-5DC350917501} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{A99787D7-A93B-4357-A8B5-B5F1FD2930AB} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{2C827B2C-75DF-413B-9AB2-2D1B438AC082} = {CC8D0880-B73E-4DFC-9052-86504728708E} | ||||
| 		{5854938A-F669-40F4-A54C-EC2651730B73} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030} | ||||
| 		{C39748E6-34AC-4C4A-AEC7-7D807FFB4813} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030} | ||||
| 		{05A88744-64D9-4959-8A97-5189F327BBBE} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030} | ||||
| 		{231AEEA6-F914-4F32-AFF6-D554E6C855CF} = {95008B83-0324-4A7C-80DE-2BBDDD1A9099} | ||||
| 		{2192217C-CF77-422E-9E63-DF4003ABF01A} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01} | ||||
| 		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01} | ||||
| 		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01} | ||||
| 		{0A891D8E-23B3-46AD-8D30-565EE5004F93} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{5DA3D2BD-6768-4479-B52F-49E022EFF310} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{D6685A42-2712-417A-92C5-5EFF90B9FA94} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA} | ||||
| 		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607} | ||||
| 		{69EA3D89-CB40-425A-8D70-5E4A33337BE5} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607} | ||||
| 		{90AB5C24-1AA3-4F58-9987-B307B92B5193} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607} | ||||
| 		{4C7A5A90-8292-413B-8848-419D9DCDE66F} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607} | ||||
| 		{D37EC028-EA46-4510-8261-6E780A906314} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197} | ||||
| 		{C5F662EB-991F-438D-BF61-EF87E7371C04} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197} | ||||
| 		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{53C870AE-0249-4485-AE63-F8A16EA4BCB4} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{C102AB3A-377E-4753-AFA7-C13250D7DF00} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{0DE1D254-0BD2-4D98-B939-5440BE5EB552} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{A8E68E17-4EBF-4E4C-8272-B489329A68BF} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{AC4295F2-AB2F-4137-99EF-80FA5C83896B} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 		{0947E6C0-6371-4483-B0ED-191FB5073151} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB} | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| 		SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB} | ||||
|   | ||||
| @@ -1,270 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.Diagnostics; | ||||
|  | ||||
| using ThingsGateway.Foundation.Core; | ||||
|  | ||||
| namespace ThingsGateway.Upgrade | ||||
| { | ||||
|     /// <summary> | ||||
|     /// StartCommand.txt 中配置启动主程序的指令,比如 | ||||
|     /// 如果是直接启动:ThingsGateway.Web.Entry.exe | ||||
|     /// 如果是WindowsService:Net Start ThingsGateway | ||||
|     /// 如果是其他部署(linux,pm2等等),填写对应的启动命令 | ||||
|     /// </summary> | ||||
|     internal class Program | ||||
|     { | ||||
|         static void Main(string[] args) | ||||
|         { | ||||
|             //Thread.Sleep(15000); | ||||
|             Thread.Sleep(5000); | ||||
|             var path = args[0]; | ||||
|             if (Directory.Exists(path)) | ||||
|             { | ||||
|                 var data = DirectoryUtility.GetDirectories(path); | ||||
|                 if (data.Length == 0) | ||||
|                 { | ||||
|                     ConsleError($"目录{path}下不存在文件"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var oldFolder = $"{AppContext.BaseDirectory}FileTemp/ThingsGatewayOld/{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}"; | ||||
|                     try | ||||
|                     { | ||||
|  | ||||
|                         //复制原文件,错误时可恢复 | ||||
|                         ConsleInfo($"正在备份原文件"); | ||||
|                         CopyDirectory(AppContext.BaseDirectory, oldFolder, new[] { "logs", "FileTemp" }, new[] { "ThingsGateway.Upgrade" }); | ||||
|                         ConsleInfo($"备份原文件成功"); | ||||
|  | ||||
|                         //停止主程序进程 | ||||
|  | ||||
|                         string filePath = $"{AppContext.BaseDirectory}ThingsGateway.Web.Entry"; | ||||
|                         // 查找正在运行的进程 | ||||
|                         var process = Process.GetProcesses().Where(p => | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 if (p.ProcessName == "ThingsGateway.Web.Entry") | ||||
|                                 { | ||||
|                                     return (Path.GetDirectoryName(p.MainModule.FileName) + Path.DirectorySeparatorChar).Equals(AppContext.BaseDirectory, StringComparison.OrdinalIgnoreCase); | ||||
|                                     //return true; | ||||
|                                 } | ||||
|                                 return false; | ||||
|                             } | ||||
|                             catch (Exception) | ||||
|                             { | ||||
|                                 return false; // 进程访问被拒绝或没有主模块 | ||||
|                             } | ||||
|                         })?.FirstOrDefault(); | ||||
|                         if (process != null) | ||||
|                         { | ||||
|                             var exit = process.WaitForExit(300000); | ||||
|                             if (!exit) | ||||
|                             { | ||||
|                                 ConsleError("无法终止主程序,更新过程停止"); | ||||
|  | ||||
|                                 Recovery(oldFolder); | ||||
|  | ||||
|                                 Directory.Delete(oldFolder, true); | ||||
|                                 Directory.Delete(path, true); | ||||
|                                 return; | ||||
|                             } | ||||
|                         } | ||||
|                         //程序已退出 | ||||
|  | ||||
|                         //复制文件到主程序文件夹 | ||||
|                         ConsleInfo($"正在复制文件到主程序文件夹"); | ||||
|                         CopyDirectory(path, AppContext.BaseDirectory, new[] { "logs", "FileTemp" }, new[] { "ThingsGateway.Upgrade" }); | ||||
|                         ConsleInfo($"复制文件到主程序文件夹成功"); | ||||
|  | ||||
|                         //尝试启动 | ||||
|                         var dataString = FileUtil.ReadFile($"{AppContext.BaseDirectory}StartCommand.txt");//读取文件 | ||||
|                         var startCommand = dataString; | ||||
|                         StartCommand(startCommand); | ||||
|  | ||||
|  | ||||
|                         Thread.Sleep(5000); | ||||
|                         // 查找正在运行的进程 | ||||
|                         var process1 = Process.GetProcesses().Where(p => | ||||
|                         { | ||||
|                             try | ||||
|                             { | ||||
|                                 if (p.ProcessName == "ThingsGateway.Web.Entry") | ||||
|                                 { | ||||
|                                     return (Path.GetDirectoryName(p.MainModule.FileName) + Path.DirectorySeparatorChar).Equals(AppContext.BaseDirectory, StringComparison.OrdinalIgnoreCase); | ||||
|                                     //return true; | ||||
|                                 } | ||||
|                                 return false; | ||||
|                             } | ||||
|                             catch (Exception) | ||||
|                             { | ||||
|                                 return false; // 进程访问被拒绝或没有主模块 | ||||
|                             } | ||||
|                         })?.FirstOrDefault(); | ||||
|                         if (process1 != null) | ||||
|                         { | ||||
|                             //代表启动正常 | ||||
|                             ConsleInfo("更新成功"); | ||||
|                             Directory.Delete(oldFolder, true); | ||||
|                             Directory.Delete(path, true); | ||||
|                             return; | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             //恢复原文件 | ||||
|                             Recovery(oldFolder); | ||||
|                             Directory.Delete(oldFolder, true); | ||||
|  | ||||
|                             Directory.Delete(path, true); | ||||
|  | ||||
|                         } | ||||
|  | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         ConsleError(ex.ToString()); | ||||
|  | ||||
|                         //恢复原文件 | ||||
|                         Recovery(oldFolder); | ||||
|                         Directory.Delete(oldFolder, true); | ||||
|                         Directory.Delete(path, true); | ||||
|  | ||||
|                     } | ||||
|  | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ConsleError($"不存在目录{path}"); | ||||
|                 //尝试启动 | ||||
|                 var dataString1 = FileUtil.ReadFile($"{AppContext.BaseDirectory}StartCommand.txt");//读取文件 | ||||
|                 var startCommand1 = dataString1; | ||||
|                 StartCommand(startCommand1); | ||||
|  | ||||
|             } | ||||
|  | ||||
|             Thread.Sleep(10000); | ||||
|             Console.ReadLine(); | ||||
|         } | ||||
|  | ||||
|         private static void Recovery(string oldFolder) | ||||
|         { | ||||
|             ConsleInfo("更新失败,恢复原文件"); | ||||
|             CopyDirectory(oldFolder, AppContext.BaseDirectory, new[] { "logs", "FileTemp" }, new[] { "ThingsGateway.Upgrade" }); | ||||
|             //尝试启动 | ||||
|             var dataString1 = FileUtil.ReadFile($"{AppContext.BaseDirectory}StartCommand.txt");//读取文件 | ||||
|             var startCommand1 = dataString1; | ||||
|             StartCommand(startCommand1); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 复制文件夹及文件 | ||||
|         /// </summary> | ||||
|         /// <param name="sourceFolder">原文件路径</param> | ||||
|         /// <param name="destFolder">目标文件路径</param> | ||||
|         /// <param name="ignoreFolder">忽略文件夹</param> | ||||
|         /// <param name="ignoreFiles">忽略文件</param> | ||||
|         /// <returns></returns> | ||||
|         public static void CopyDirectory(string sourceFolder, string destFolder, string[] ignoreFolder, string[] ignoreFiles) | ||||
|         { | ||||
|             //如果目标路径不存在,则创建目标路径 | ||||
|             if (!Directory.Exists(destFolder)) | ||||
|             { | ||||
|                 Directory.CreateDirectory(destFolder); | ||||
|             } | ||||
|             //得到原文件根目录下的所有文件 | ||||
|             var files = Directory.GetFiles(sourceFolder); | ||||
|             foreach (var file in files) | ||||
|             { | ||||
|                 if (ignoreFiles.Contains(Path.GetFileNameWithoutExtension(file))) | ||||
|                 { continue; } | ||||
|                 var name = Path.GetFileName(file); | ||||
|                 var dest = Path.Combine(destFolder, name); | ||||
|                 File.Copy(file, dest, true);//复制文件 | ||||
|             } | ||||
|             //得到原文件根目录下的所有文件夹 | ||||
|             var folders = Directory.GetDirectories(sourceFolder); | ||||
|             foreach (var folder in folders) | ||||
|             { | ||||
|                 if (ignoreFolder.Contains(Path.GetFileName(folder))) | ||||
|                 { continue; } | ||||
|                 var name = Path.GetFileName(folder); | ||||
|                 var dest = Path.Combine(destFolder, name); | ||||
|                 CopyDirectory(folder, dest, ignoreFolder, ignoreFiles);//构建目标路径,递归复制文件 | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public static void ConsleInfo(string str) | ||||
|         { | ||||
|             Console.ForegroundColor = ConsoleColor.Green; | ||||
|             Console.WriteLine(str); | ||||
|         } | ||||
|  | ||||
|         public static void ConsleError(string str) | ||||
|         { | ||||
|             Console.ForegroundColor = ConsoleColor.Red; | ||||
|             Console.WriteLine(str); | ||||
|         } | ||||
|  | ||||
|         public static void StartCommand(string command) | ||||
|         { | ||||
|             // 创建进程对象 | ||||
|             Process process = new Process(); | ||||
|             // 设置进程启动信息 | ||||
|             process.StartInfo.FileName = GetCommandInterpreter();  // 获取当前平台的命令解释器 | ||||
|             process.StartInfo.Arguments = GetCommandArguments(command);  // 获取命令行参数 | ||||
|             process.StartInfo.UseShellExecute = true; | ||||
|             process.StartInfo.WorkingDirectory = AppContext.BaseDirectory; | ||||
|             // 启动进程 | ||||
|             process.Start(); | ||||
|         } | ||||
|  | ||||
|         // 获取当前平台的命令解释器 | ||||
|         static string GetCommandInterpreter() | ||||
|         { | ||||
|             if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) | ||||
|             { | ||||
|                 return "cmd.exe"; | ||||
|             } | ||||
|             else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) | ||||
|             { | ||||
|                 return "/bin/bash"; | ||||
|             } | ||||
|             else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) | ||||
|             { | ||||
|                 return "/bin/bash"; | ||||
|             } | ||||
|  | ||||
|             throw new PlatformNotSupportedException("该平台不支持执行命令行。"); | ||||
|         } | ||||
|  | ||||
|         // 获取命令行参数 | ||||
|         static string GetCommandArguments(string command) | ||||
|         { | ||||
|             if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) | ||||
|             { | ||||
|                 return $"/c {command}"; | ||||
|             } | ||||
|             else  // Linux 或 macOS | ||||
|             { | ||||
|                 return $"-c \"{command}\""; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,8 +0,0 @@ | ||||
| { | ||||
|   "profiles": { | ||||
|     "ThingsGateway.Upgrade": { | ||||
|       "commandName": "Project", | ||||
|       "commandLineArgs": "E:\\Tg\\ThingsGateway\\ThingsGateway-DEV\\framework\\Web\\ThingsGateway.Web.Entry\\bin\\Debug\\net6.0\\FileTemp\\tcp---127.0.0.1-7400-" | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1 +0,0 @@ | ||||
| Net Start ThingsGateway | ||||
| @@ -1,32 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <OutputType>Exe</OutputType> | ||||
|     <ImplicitUsings>enable</ImplicitUsings> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|  | ||||
|   <Target Name="PostBuild" AfterTargets="PostBuildEvent"> | ||||
|     <Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\"
 if not exist %25dir%25  md %25dir%25  
copy "$(TargetDir)*"  %25dir%25


" /> | ||||
|   </Target> | ||||
|  | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <None Remove="StartCommand.txt" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Compile Include="..\..\Foundation\ThingsGateway.Foundation\Foundation\Utils\FileUtil.cs" Link="FileUtil.cs" /> | ||||
|     <Compile Include="..\..\Foundation\ThingsGateway.Foundation\TouchSocket\Core\Extensions\DateTimeExtensions.cs" Link="DateTimeExtensions.cs" /> | ||||
|     <Compile Include="..\..\Foundation\ThingsGateway.Foundation\TouchSocket\Core\IO\DirectoryUtility.cs" Link="DirectoryUtility.cs" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Content Include="StartCommand.txt"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </Content> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
| @@ -1,26 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @namespace ThingsGateway.UpgradeManger | ||||
|  | ||||
| <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> | ||||
|  | ||||
| @@ -1,23 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| global using System; | ||||
| global using System.Collections.Generic; | ||||
| global using System.IO; | ||||
| global using System.Linq; | ||||
| global using System.Threading; | ||||
| global using System.Threading.Tasks; | ||||
|  | ||||
| global using ThingsGateway.Components; | ||||
| global using ThingsGateway.Foundation.Core; | ||||
| global using ThingsGateway.Foundation.Rpc; | ||||
| global using ThingsGateway.Foundation.Sockets; | ||||
| @@ -1,11 +0,0 @@ | ||||
| [ | ||||
|   { | ||||
|     "Href": "/index", | ||||
|     "Title": "首页" | ||||
|   }, | ||||
|   { | ||||
|     "Title": "更新管理", | ||||
|     "Href": "/UpgradeManger" | ||||
|   } | ||||
|  | ||||
| ] | ||||
| @@ -1,36 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人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.UpgradeManger | ||||
| @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); | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人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> | ||||
|  | ||||
| @@ -1,262 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/UpgradeManger" | ||||
| @namespace ThingsGateway.UpgradeManger | ||||
| @using System.Linq.Expressions; | ||||
| @using BlazorComponent; | ||||
|  | ||||
| @using Mapster; | ||||
| @using Masa.Blazor.Presets; | ||||
| @using System.IO; | ||||
| @using Masa.Blazor; | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
|  | ||||
| @inherits BaseComponentBase | ||||
|  | ||||
| @inject NavigationManager NavigationManager | ||||
| @layout MainLayout | ||||
| <MRow NoGutters> | ||||
|     <MCol Md=@(IsShowTreeView?2:0)> | ||||
|         <MSheet Show=IsShowTreeView Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight}px);border-radius:0px;")> | ||||
|             <MCardTitle Class="text-body-2"> | ||||
|                 当前已连接的客户端 | ||||
|             </MCardTitle> | ||||
|  | ||||
|             <MVirtualScroll Context="item" Class="my-2" Height=@($"calc(100vh - {BlazorResourceConst.DefaultHeight+100}px)") OverscanCount=2 ItemSize="60" Items="TcpDmtpSocketClients"> | ||||
|  | ||||
|                 <ItemContent> | ||||
|  | ||||
|                     <MListItem OnClick=@(()=>ChoiceTcpDmtpSocketClient(item))> | ||||
|                         <MListItemContent> | ||||
|                             <MListItemTitle> | ||||
|                                 <div class="mt-1  d-flex align-center justify-space-between" title=@($"{item.GetIPPort()} - {item.Id}")> | ||||
|                                     <span>@($"{item.GetIPPort()} - {item.Id}")</span> | ||||
|                                 </div> | ||||
|                             </MListItemTitle> | ||||
|                         </MListItemContent> | ||||
|  | ||||
|                     </MListItem> | ||||
|  | ||||
|                     <MDivider></MDivider> | ||||
|  | ||||
|  | ||||
|                 </ItemContent> | ||||
|             </MVirtualScroll> | ||||
|  | ||||
|  | ||||
|         </MSheet> | ||||
|     </MCol> | ||||
|     <MCol Md=@(IsShowTreeView?10:12)> | ||||
|         <MButton Class="mx-1" OnClick=@(() => | ||||
|                  { | ||||
|                  IsShowTreeView=!IsShowTreeView; | ||||
|                  } | ||||
|                  ) Color="primary" Icon> | ||||
|             <MIcon>mdi-menu</MIcon> | ||||
|         </MButton> | ||||
|         <MSheet Rounded="false" Class="pa-2" Style="width:100%"> | ||||
|             <MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters> | ||||
|  | ||||
|                 <MCol Md="5"> | ||||
|                     @if (TcpDmtpSocketClient != null && GatewayInfo != null) | ||||
|                     { | ||||
|                         <MSheet Class="ml-4"> | ||||
|                             <MDescriptions Title="当前选择的网关" Bordered="true"> | ||||
|                                 <ActionsContent> | ||||
|                                     <MButton Class="text-capitalize mr-1" Color="primary" OnClick="()=>ChoiceTcpDmtpSocketClient(TcpDmtpSocketClient)"> | ||||
|                                         刷新 | ||||
|                                     </MButton> | ||||
|                                 </ActionsContent> | ||||
|                                 <ChildContent> | ||||
|                                     <MDescriptionsItem Label=连接信息 Span=3>@($"{TcpDmtpSocketClient.GetIPPort()} - {TcpDmtpSocketClient.Id}")</MDescriptionsItem> | ||||
|                                     <MDescriptionsItem Label=连接状态 Span=3 Class=@(!TcpDmtpSocketClient.CanSend?"red--text":"green--text")>@TcpDmtpSocketClient.CanSend</MDescriptionsItem> | ||||
|                                     <MDescriptionsItem Label=更新时间 Span=3>@GatewayInfo.UpdateTime</MDescriptionsItem> | ||||
|                                     <MDescriptionsItem Label=软件版本 Span=3>@GatewayInfo.Version</MDescriptionsItem> | ||||
|                                     <MDescriptionsItem Label=采集设备数量 Span=3>@GatewayInfo.CollectDeviceCount</MDescriptionsItem> | ||||
|                                     <MDescriptionsItem Label=上传设备数量 Span=3>@GatewayInfo.UploadDeviceCount</MDescriptionsItem> | ||||
|                                     <MDescriptionsItem Label=变量数量 Span=3>@GatewayInfo.VariableCount</MDescriptionsItem> | ||||
|                                 </ChildContent> | ||||
|                             </MDescriptions> | ||||
|                         </MSheet> | ||||
|  | ||||
|                         <MTabs @bind-Value="tab" Class="ma-2"> | ||||
|                             <MTab Value=1>    配置更新   </MTab> | ||||
|                             <MTab Value=2>     软件更新  </MTab> | ||||
|                         </MTabs> | ||||
|  | ||||
|                         <MTabsItems Value="tab"> | ||||
|                             <MTabItem Value="1"> | ||||
|                                 @if (tab == 1) | ||||
|                                 { | ||||
|                                     <MContainer> | ||||
|  | ||||
|                                         <MRow Dense> | ||||
|  | ||||
|                                          <MCol Cols="12" Md="12"> | ||||
|                                              <div class="m-descriptions-header__title my-2"> | ||||
|                                                  导出配置信息 | ||||
|                                              </div> | ||||
|                                          </MCol> | ||||
|  | ||||
|  | ||||
|                                          <MCol Cols="12" Md="12"> | ||||
|  | ||||
|                                              <MButton Color="primary" Loading=isUploadLoading Disabled=disabled Class="ma-2" OnClick=@(()=>ExcelUpload())> | ||||
|                                                  导出网关配置信息 | ||||
|                                              </MButton> | ||||
|                                          </MCol> | ||||
|                                          <MCol Cols="12" Md="12"> | ||||
|  | ||||
|                                              <MButton Color="primary" Loading=isUploadLoading Disabled=disabled Class="ma-2" OnClick=@(()=>DBUpload())> | ||||
|                                                  导出网关配置数据库(SQLITE) | ||||
|                                              </MButton> | ||||
|                                          </MCol> | ||||
|  | ||||
|                                      </MRow> | ||||
|  | ||||
|                                      <MDivider></MDivider> | ||||
|  | ||||
|                                      <MRow Dense> | ||||
|  | ||||
|                                          <MCol Cols="12" Md="12"> | ||||
|                                              <div class="m-descriptions-header__title my-2"> | ||||
|                                                  更新网关配置信息 | ||||
|                                              </div> | ||||
|                                          </MCol> | ||||
|  | ||||
|                                          <MCol Cols="12" Md="12"> | ||||
|                                              <MFileInput Label="采集设备Excel" HideDetails=@("true") @bind-Value="_importCollectDevicesFile" Style="width:90%;" ShowSize></MFileInput> | ||||
|                                              </MCol> | ||||
|                                              <MCol Cols="12" Md="12"> | ||||
|                                                  <MSwitch Label=删除后更新 HideDetails=@("true") @bind-Value=@IsCollectDevicesFullUp /> | ||||
|                                              </MCol> | ||||
|  | ||||
|                                              <MCol Cols="12" Md="12"> | ||||
|                                                  <MFileInput Label="上传设备Excel" HideDetails=@("true") @bind-Value="_importUploadDevicesFile" Style="width:90%;" ShowSize></MFileInput> | ||||
|                                              </MCol> | ||||
|  | ||||
|                                              <MCol Cols="12" Md="12"> | ||||
|                                                  <MSwitch Label=删除后更新 HideDetails=@("true") @bind-Value=@IsUploadDevicesFullUp /> | ||||
|                                              </MCol> | ||||
|  | ||||
|  | ||||
|                                              <MCol Cols="12" Md="12"> | ||||
|                                                  <MFileInput Label="采集变量Excel" HideDetails=@("true") @bind-Value="_importDeviceVariablesFile" Style="width:90%;" ShowSize></MFileInput> | ||||
|                                              </MCol> | ||||
|  | ||||
|                                              <MCol Cols="12" Md="12"> | ||||
|                                                  <MSwitch Label=删除后更新 HideDetails=@("true") @bind-Value=@IsDeviceVariablesFullUp /> | ||||
|                                              </MCol> | ||||
|  | ||||
|                                              <MCol Cols="12" Md="12"> | ||||
|                                                  <MFileInput Label="内存变量Excel" HideDetails=@("true") @bind-Value="_importMemoryVariablesFile" Style="width:90%;" ShowSize></MFileInput> | ||||
|                                              </MCol> | ||||
|  | ||||
|                                              <MCol Cols="12" Md="12"> | ||||
|                                                  <MSwitch Label=删除后更新 HideDetails=@("true") @bind-Value=@IsMemoryVariablesFullUp /> | ||||
|                                              </MCol> | ||||
|  | ||||
|  | ||||
|                                              <MCol Cols="12" Md="12"> | ||||
|  | ||||
|                                                  <MButton Loading=isUploadLoading Disabled=disabled Class="ma-2" Color="primary" | ||||
|                                                           OnClick=@(()=>ExcelDown())> | ||||
|                                                      推送Excel文件 | ||||
|                                                  </MButton> | ||||
|                                              </MCol> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|                                          </MRow> | ||||
|  | ||||
|                                          <MDivider></MDivider> | ||||
|  | ||||
|                                          <MRow Dense> | ||||
|  | ||||
|                                          <MCol Cols="12" Md="12"> | ||||
|                                              <div class="m-descriptions-header__title my-2"> | ||||
|                                                  重启 | ||||
|                                              </div> | ||||
|                                          </MCol> | ||||
|  | ||||
|  | ||||
|                                          <MCol Cols="12" Md="12"> | ||||
|  | ||||
|                                              <MButton Color="primary" Loading=isUploadLoading Disabled=disabled Class="ma-2" OnClick=@(()=>DBRestart())> | ||||
|                                                  重启网关运行态 | ||||
|                                              </MButton> | ||||
|                                          </MCol> | ||||
|                                          <MCol Cols="12" Md="12"> | ||||
|  | ||||
|                                          </MCol> | ||||
|  | ||||
|                                      </MRow> | ||||
|  | ||||
|                                  </MContainer> | ||||
|  | ||||
|                                 } | ||||
|                             </MTabItem> | ||||
|  | ||||
|                             <MTabItem Value="2"> | ||||
|                                 @if (tab == 2) | ||||
|                                 { | ||||
|  | ||||
|                                     <MCol Cols="12" Md="12"> | ||||
|  | ||||
|                                         <MButton Loading=isUploadLoading Disabled=disabled Class="ma-2" Color="primary" | ||||
|                                                  OnClick=@(()=>FileDown())> | ||||
|                                             推送文件 | ||||
|                                         </MButton> | ||||
|                                     </MCol> | ||||
|  | ||||
|                                                                         <MCol Cols="12" Md="12"> | ||||
|  | ||||
|                                         <MButton Loading=isUploadLoading Disabled=disabled Class="ma-2" Color="primary" | ||||
|                                                  OnClick=@(()=>FileRestart())> | ||||
|                                             重启更新 | ||||
|                                         </MButton> | ||||
|                                     </MCol> | ||||
|  | ||||
|  | ||||
|                                 } | ||||
|                             </MTabItem> | ||||
|  | ||||
|  | ||||
|                         </MTabsItems> | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                 </MCol> | ||||
|  | ||||
|                 <MCol Md="7"> | ||||
|                     <MCard Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4"> | ||||
|                          <ConsoleTxt Messages="UpgradeManger.Messages" Height=600></ConsoleTxt> | ||||
|                      </MCard> | ||||
|                  </MCol> | ||||
|  | ||||
|              </MRow> | ||||
|  | ||||
|          </MSheet> | ||||
|  | ||||
|      </MCol> | ||||
|  | ||||
|  </MRow> | ||||
|  @code { | ||||
|     StringNumber tab; | ||||
|     bool IsShowTreeView = true; | ||||
|  | ||||
| } | ||||
| @@ -1,324 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using BlazorComponent; | ||||
|  | ||||
| using Masa.Blazor; | ||||
|  | ||||
| using Microsoft.AspNetCore.Components; | ||||
| using Microsoft.AspNetCore.Components.Forms; | ||||
| using Microsoft.JSInterop; | ||||
|  | ||||
| using ThingsGateway.Core; | ||||
| using ThingsGateway.Foundation.Dmtp; | ||||
| using ThingsGateway.Foundation.Dmtp.Rpc; | ||||
|  | ||||
| namespace ThingsGateway.UpgradeManger; | ||||
|  | ||||
| /// <summary> | ||||
| /// UpgradeManger | ||||
| /// </summary> | ||||
| public partial class UpgradeMangerPage | ||||
| { | ||||
|     readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(5)); | ||||
|     IBrowserFile _importCollectDevicesFile; | ||||
|  | ||||
|     IBrowserFile _importDeviceVariablesFile; | ||||
|     IBrowserFile _importMemoryVariablesFile; | ||||
|  | ||||
|     IBrowserFile _importUploadDevicesFile; | ||||
|  | ||||
|     private bool IsCollectDevicesFullUp; | ||||
|  | ||||
|     private bool IsDeviceVariablesFullUp; | ||||
|     private bool IsMemoryVariablesFullUp; | ||||
|  | ||||
|     private bool isUploadLoading; | ||||
|  | ||||
|  | ||||
|     private bool IsUploadDevicesFullUp; | ||||
|  | ||||
|     private IJSObjectReference JSObjectReference; | ||||
|  | ||||
|     [Inject] | ||||
|     IJSRuntime JSRuntime { get; set; } | ||||
|  | ||||
|     List<TcpDmtpSocketClient> TcpDmtpSocketClients { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     UpgradeManger UpgradeManger { get; set; } | ||||
|     /// <inheritdoc/> | ||||
|     public override void Dispose() | ||||
|     { | ||||
|         _periodicTimer?.Dispose(); | ||||
|         base.Dispose(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         _ = RunTimerAsync(); | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|     bool disabled => TcpDmtpSocketClient?.CanSend != true; | ||||
|     GatewayInfo GatewayInfo; | ||||
|     TcpDmtpSocketClient TcpDmtpSocketClient; | ||||
|     GatewayExcel GatewayExcel; | ||||
|  | ||||
|  | ||||
|     async Task ExcelUpload() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             isUploadLoading = true; | ||||
|  | ||||
|             GatewayExcel = await TcpDmtpSocketClient.GetDmtpRpcActor().InvokeTAsync<GatewayExcel>("GetGatewayExcelAsync", InvokeOption.WaitInvoke); | ||||
|  | ||||
|             if (GatewayExcel.CollectDevice?.Length > 0) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     GatewayExcel.CollectDevice.Seek(0, SeekOrigin.Begin); | ||||
|                     using var streamRef = new DotNetStreamReference(stream: GatewayExcel.CollectDevice); | ||||
|                     JSObjectReference ??= await JSRuntime.LoadModuleAsync("js/downloadFileFromStream"); | ||||
|                     await JSObjectReference.InvokeVoidAsync("downloadFileFromStream", $"{TcpDmtpSocketClient.GetIPPort()}采集设备表导出{DateTimeExtensions.CurrentDateTime.ToString("yyyy-MM-dd HH-mm-ss-fff zz")}.xlsx", streamRef); | ||||
|  | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     UpgradeManger.LogMessage.LogWarning(ex, "采集设备表导出失败"); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             if (GatewayExcel.UploadDevice?.Length > 0) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     GatewayExcel.UploadDevice.Seek(0, SeekOrigin.Begin); | ||||
|                     using var streamRef = new DotNetStreamReference(stream: GatewayExcel.UploadDevice); | ||||
|                     JSObjectReference ??= await JSRuntime.LoadModuleAsync("js/downloadFileFromStream"); | ||||
|                     await JSObjectReference.InvokeVoidAsync("downloadFileFromStream", $"{TcpDmtpSocketClient.GetIPPort()}上传设备表导出{DateTimeExtensions.CurrentDateTime.ToString("yyyy-MM-dd HH-mm-ss-fff zz")}.xlsx", streamRef); | ||||
|  | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     UpgradeManger.LogMessage.LogWarning(ex, "上传设备表导出失败"); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             if (GatewayExcel.MemoryVariable?.Length > 0) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     GatewayExcel.MemoryVariable.Seek(0, SeekOrigin.Begin); | ||||
|                     using var streamRef = new DotNetStreamReference(stream: GatewayExcel.MemoryVariable); | ||||
|                     JSObjectReference ??= await JSRuntime.LoadModuleAsync("js/downloadFileFromStream"); | ||||
|                     await JSObjectReference.InvokeVoidAsync("downloadFileFromStream", $"{TcpDmtpSocketClient.GetIPPort()}内存变量表导出{DateTimeExtensions.CurrentDateTime.ToString("yyyy-MM-dd HH-mm-ss-fff zz")}.xlsx", streamRef); | ||||
|  | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     UpgradeManger.LogMessage.LogWarning(ex, "内存变量表导出失败"); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             if (GatewayExcel.DeviceVariable?.Length > 0) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     GatewayExcel.DeviceVariable.Seek(0, SeekOrigin.Begin); | ||||
|                     using var streamRef = new DotNetStreamReference(stream: GatewayExcel.DeviceVariable); | ||||
|                     JSObjectReference ??= await JSRuntime.LoadModuleAsync("js/downloadFileFromStream"); | ||||
|                     await JSObjectReference.InvokeVoidAsync("downloadFileFromStream", $"{TcpDmtpSocketClient.GetIPPort()}采集变量表导出{DateTimeExtensions.CurrentDateTime.ToString("yyyy-MM-dd HH-mm-ss-fff zz")}.xlsx", streamRef); | ||||
|  | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     UpgradeManger.LogMessage.LogWarning(ex, "采集变量表导出失败"); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|  | ||||
|  | ||||
|             await PopupService.EnqueueSnackbarAsync("上传成功", AlertTypes.Success); | ||||
|         } | ||||
|         finally { isUploadLoading = false; } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     async Task DBUpload() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             isUploadLoading = true; | ||||
|             await UpgradeManger.DBUpload(TcpDmtpSocketClient); | ||||
|  | ||||
|         } | ||||
|         finally { isUploadLoading = false; } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 下发子网关配置 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     private async Task ExcelDown() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             isUploadLoading = true; | ||||
|             GatewayExcel gatewayExcel = new(); | ||||
|             if (_importCollectDevicesFile != null) | ||||
|             { | ||||
|                 using var fs1 = new MemoryStream(); | ||||
|                 using var stream1 = _importCollectDevicesFile.OpenReadStream(512000000); | ||||
|                 await stream1.CopyToAsync(fs1); | ||||
|                 fs1.Seek(0, SeekOrigin.Begin); | ||||
|                 gatewayExcel.CollectDevice = fs1; | ||||
|             } | ||||
|             if (_importUploadDevicesFile != null) | ||||
|             { | ||||
|  | ||||
|                 using var fs2 = new MemoryStream(); | ||||
|                 using var stream2 = _importUploadDevicesFile.OpenReadStream(512000000); | ||||
|                 await stream2.CopyToAsync(fs2); | ||||
|                 fs2.Seek(0, SeekOrigin.Begin); | ||||
|                 gatewayExcel.UploadDevice = fs2; | ||||
|             } | ||||
|             if (_importDeviceVariablesFile != null) | ||||
|             { | ||||
|                 using var fs3 = new MemoryStream(); | ||||
|                 using var stream3 = _importDeviceVariablesFile.OpenReadStream(512000000); | ||||
|                 await stream3.CopyToAsync(fs3); | ||||
|                 fs3.Seek(0, SeekOrigin.Begin); | ||||
|                 gatewayExcel.DeviceVariable = fs3; | ||||
|             } | ||||
|             if (_importMemoryVariablesFile != null) | ||||
|             { | ||||
|                 using var fs4 = new MemoryStream(); | ||||
|                 using var stream4 = _importMemoryVariablesFile.OpenReadStream(512000000); | ||||
|                 await stream4.CopyToAsync(fs4); | ||||
|                 fs4.Seek(0, SeekOrigin.Begin); | ||||
|                 gatewayExcel.MemoryVariable = fs4; | ||||
|             } | ||||
|  | ||||
|             var result = await TcpDmtpSocketClient.GetDmtpRpcActor().InvokeTAsync<OperResult>("SetGatewayExcelAsync", new InvokeOption(30000), gatewayExcel); | ||||
|             if (result.IsSuccess) | ||||
|             { | ||||
|                 await PopupService.EnqueueSnackbarAsync("更新成功", AlertTypes.Success); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 await PopupService.EnqueueSnackbarAsync(result.Message, AlertTypes.Error); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             await PopupService.EnqueueSnackbarAsync(ex); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             isUploadLoading = false; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|     private async Task FileDown() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             isUploadLoading = true; | ||||
|             var data = Program.app.MainWindow.ShowOpenFolder("选择更新文件夹", AppContext.BaseDirectory); | ||||
|             if (data.Length > 0) | ||||
|             { | ||||
|                 await Task.Run(async () => | ||||
|                 { | ||||
|                     await UpgradeManger.FileDown(TcpDmtpSocketClient, data.FirstOrDefault()); | ||||
|                 } | ||||
|                 ); | ||||
|                 await PopupService.EnqueueSnackbarAsync("推送成功", AlertTypes.Success); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         finally { isUploadLoading = false; } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private async Task FileRestart() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             isUploadLoading = true; | ||||
|             var confirm = await PopupService.ConfirmAsync("重启", "网关重启,会暂时断开连接,会在约1分钟后重新连接 ?", AlertTypes.Warning); | ||||
|             if (confirm) | ||||
|             { | ||||
|                 await TcpDmtpSocketClient.GetDmtpRpcActor().InvokeTAsync<OperResult>("FileRestart", InvokeOption.WaitSend); | ||||
|             } | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             isUploadLoading = false; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DBRestart | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     private async Task DBRestart() | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             isUploadLoading = true; | ||||
|             await TcpDmtpSocketClient.GetDmtpRpcActor().InvokeTAsync<OperResult>("DBRestartAsync", new InvokeOption(30000)); | ||||
|             await PopupService.EnqueueSnackbarAsync("重启成功", AlertTypes.Success); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             isUploadLoading = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     async Task ChoiceTcpDmtpSocketClient(TcpDmtpSocketClient item) | ||||
|     { | ||||
|         TcpDmtpSocketClient = item; | ||||
|         GatewayInfo = await item.GetDmtpRpcActor().InvokeTAsync<GatewayInfo>("GetGatewayInfo", InvokeOption.WaitInvoke); | ||||
|     } | ||||
|  | ||||
|     private async Task RefreshAsync() | ||||
|     { | ||||
|         await Task.CompletedTask; | ||||
|         TcpDmtpSocketClients = UpgradeManger.TcpDmtpService.GetClients().ToList(); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private async Task RunTimerAsync() | ||||
|     { | ||||
|         await RefreshAsync(); | ||||
|         while (await _periodicTimer.WaitForNextTickAsync()) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await RefreshAsync(); | ||||
|                 await InvokeAsync(StateHasChanged); | ||||
|             } | ||||
|             catch | ||||
|             { | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,49 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
|  | ||||
| using Photino.Blazor; | ||||
|  | ||||
| namespace ThingsGateway.UpgradeManger; | ||||
|  | ||||
| internal class Program | ||||
| { | ||||
|     internal static PhotinoBlazorApp app; | ||||
|     [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(); | ||||
|         appBuilder.Services.AddSingleton<UpgradeManger>(); | ||||
|  | ||||
|         app = appBuilder.Build(); | ||||
|         app.MainWindow.SetTitle("ThingsGateway.UpgradeManger"); | ||||
|         app.MainWindow.SetIconFile("wwwroot/favicon.ico"); | ||||
|         AppDomain.CurrentDomain.UnhandledException += (sender, error) => | ||||
|         { | ||||
|         }; | ||||
|         CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); | ||||
|         var upgradeManger = app.Services.GetService<UpgradeManger>(); | ||||
|         _ = upgradeManger.ExecuteAsync(cancellationTokenSource.Token); | ||||
|         app.Run(); | ||||
|  | ||||
|         cancellationTokenSource.Cancel(); | ||||
|         cancellationTokenSource.Dispose(); | ||||
|  | ||||
|         Thread.Sleep(2000); | ||||
|     } | ||||
| } | ||||
| @@ -1,42 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @namespace ThingsGateway.UpgradeManger | ||||
| @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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,91 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @namespace ThingsGateway.UpgradeManger | ||||
| @using System.Text; | ||||
| @inherits LayoutComponentBase | ||||
| @layout BaseLayout | ||||
|  | ||||
| <PPageTabsProvider> | ||||
|  | ||||
|     <CascadingValue Value="@this" IsFixed> | ||||
|         <CascadingValue Value="@Changed" Name="Changed"> | ||||
|  | ||||
|             <MNavigationDrawer Color="barcolor" @bind-Value="_drawerOpen" App Width="200"> | ||||
|                 <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" /> | ||||
|             </MNavigationDrawer> | ||||
|  | ||||
|             <MAppBar Color="barcolor" Style=@($"{(!(IsMobile||_drawerOpen!=true)? "left:200px;":"")}") Elevation="1" App Flat ClippedRight Dense ElevateOnScroll | ||||
|                      MaxHeight="@(BlazorResourceConst.AppBarHeight)" Height="@(BlazorResourceConst.AppBarHeight)"> | ||||
|                 <MButton Class="mr-0" Icon Small=IsMobile OnClick=@(() => _drawerOpen = !_drawerOpen)> | ||||
|                     <MIcon>mdi-menu</MIcon> | ||||
|                 </MButton> | ||||
|  | ||||
|             </MAppBar> | ||||
|  | ||||
|             <MMain Style=@($"{(!(IsMobile||_drawerOpen!=true)? "padding-left:200px;":"")}")> | ||||
|                 <div class="full-width"> | ||||
|                     <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"> | ||||
|                         @Body | ||||
|                     </PPageContainer> | ||||
|                 </MCard> | ||||
|                 <MSheet Class="d-flex justify-center align-center rounded-0" Style=@($"height: {BlazorResourceConst.FooterHeight}px;")> | ||||
|                     <Foter CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE></Foter> | ||||
|                 </MSheet> | ||||
|             </MMain> | ||||
|         </CascadingValue> | ||||
|     </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"; | ||||
| } | ||||
|  | ||||
|  | ||||
| @code { | ||||
|  | ||||
|     private List<NavItem> Navs { get; set; } = new(); | ||||
|     private List<PageTabItem> pageTabItems { get; set; } = new(); | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         var dataString =FileUtil. ReadFile($"{AppContext.BaseDirectory}Navs.json");//读取文件 | ||||
|         Navs=dataString.FromJsonString<List<NavItem>>(); | ||||
|         pageTabItems = Navs.PasePageTabItem(); | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<OutputType>WinExe</OutputType> | ||||
| 		<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<PackageReference Include="Photino.Blazor" Version="2.6.0" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" /> | ||||
| 		<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<Content Update="wwwroot\**"> | ||||
| 			<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 		</Content> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<Content Update="Navs.json"> | ||||
| 			<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 		</Content> | ||||
| 		<Content Update="UpgradeMangerConfig.json"> | ||||
| 			<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
| 		</Content> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| </Project> | ||||
| @@ -1,266 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.Diagnostics; | ||||
|  | ||||
| using ThingsGateway.Core; | ||||
| using ThingsGateway.Foundation.Dmtp; | ||||
| using ThingsGateway.Foundation.Dmtp.FileTransfer; | ||||
| using ThingsGateway.Foundation.Dmtp.Rpc; | ||||
|  | ||||
| namespace ThingsGateway.UpgradeManger; | ||||
|  | ||||
| /// <summary> | ||||
| /// UpgradeManger | ||||
| /// </summary> | ||||
| public partial class UpgradeManger | ||||
| { | ||||
|     /// <summary> | ||||
|     /// UpgradeMangerConfig | ||||
|     /// </summary> | ||||
|     public UpgradeMangerConfig Config; | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Messages | ||||
|     /// </summary> | ||||
|     public ConcurrentLinkedList<(Microsoft.Extensions.Logging.LogLevel level, string message)> Messages = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// TcpDmtpService | ||||
|     /// </summary> | ||||
|     public TcpDmtpService TcpDmtpService; | ||||
|     /// <summary> | ||||
|     /// LogMessage | ||||
|     /// </summary> | ||||
|     public LoggerGroup LogMessage; | ||||
|     /// <inheritdoc/> | ||||
|     public async Task ExecuteAsync(CancellationToken stoppingToken) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||
|             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||
|  | ||||
|             var dataString = FileUtil.ReadFile($"{AppContext.BaseDirectory}UpgradeMangerConfig.json");//读取文件 | ||||
|             Config = dataString.FromJsonString<UpgradeMangerConfig>(); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             LogMessage.LogError(ex, "程序初始化配置失败"); | ||||
|         } | ||||
|         TcpDmtpService = CreateTcpDmtpService(Config); | ||||
|         while (!stoppingToken.IsCancellationRequested) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 TcpDmtpService.Start(); | ||||
|                 await Task.Delay(10000, stoppingToken); | ||||
|             } | ||||
|             catch (TaskCanceledException) | ||||
|             { | ||||
|  | ||||
|             } | ||||
|             catch (ObjectDisposedException) | ||||
|             { | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 LogMessage?.LogError(ex); | ||||
|             } | ||||
|         } | ||||
|         TcpDmtpService.Dispose(); | ||||
|     } | ||||
|  | ||||
|     private TcpDmtpService CreateTcpDmtpService(UpgradeMangerConfig autoUpdateConfig) | ||||
|     { | ||||
|         var config = new TouchSocketConfig() | ||||
|        .SetListenIPHosts(autoUpdateConfig.UpdateServerUri) | ||||
|        .SetVerifyToken(autoUpdateConfig.VerifyToken) | ||||
|        .ConfigureContainer(a => | ||||
|        { | ||||
|            a.AddLogger(LogMessage); | ||||
|            a.AddDmtpRouteService();//添加路由策略 | ||||
|        }) | ||||
|        .ConfigurePlugins(a => | ||||
|        { | ||||
|            a.UseDmtpFileTransfer();//必须添加文件传输插件 | ||||
|            a.UseDmtpHeartbeat()//使用Dmtp心跳 | ||||
|            .SetTick(TimeSpan.FromSeconds(3)) | ||||
|            .SetMaxFailCount(3); | ||||
|            a.UseDmtpRpc(); | ||||
|        }); | ||||
|         TcpDmtpService service = new TcpDmtpService(); | ||||
|         service.Connecting = (client, e) => | ||||
|         { | ||||
|             service.Logger.Info($"{client.GetIPPort()}:Connecting"); | ||||
|             return EasyTask.CompletedTask; | ||||
|         };//有客户端正在连接 | ||||
|         service.Connected = (client, e) => { service.Logger.Info($"{client.GetIPPort()}:Connected"); return EasyTask.CompletedTask; };//有客户端连接 | ||||
|         service.Disconnected = (client, e) => { service.Logger.Info($"{client.GetIPPort()}:Disconnected"); return EasyTask.CompletedTask; };//有客户端断开连接 | ||||
|         service.Setup(config); | ||||
|         return service; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 底层日志输出 | ||||
|     /// </summary> | ||||
|     private void LogOut(ThingsGateway.Foundation.Core.LogLevel logLevel, object source, string message, Exception exception) | ||||
|     { | ||||
|         Messages.Add(((Microsoft.Extensions.Logging.LogLevel)logLevel, | ||||
|             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()} - {message} {exception}")); | ||||
|         if (Messages.Count > 2500) | ||||
|         { | ||||
|             Messages.Clear(); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DBUpload | ||||
|     /// </summary> | ||||
|     /// <param name="tcpDmtpSocketClient"></param> | ||||
|     /// <returns></returns> | ||||
|     public async Task DBUpload(TcpDmtpSocketClient tcpDmtpSocketClient) | ||||
|     { | ||||
|         var folderPath = $"{AppContext.BaseDirectory}ThingsGatewayDB/"; | ||||
|         // 检查文件夹是否存在 | ||||
|         if (!Directory.Exists(folderPath)) | ||||
|         { | ||||
|             Directory.CreateDirectory(folderPath); | ||||
|         } | ||||
|  | ||||
|         var metadata = new Metadata();//传递到客户端的元数据 | ||||
|         metadata.Add(FilePluginUtil.DmtpType, DmtpTypeEnum.GatewayDB.ToString()); | ||||
|  | ||||
|         var fileOperator = new FileOperator//实例化本次传输的控制器,用于获取传输进度、速度、状态等。 | ||||
|         { | ||||
|             SavePath = $"{folderPath}/{tcpDmtpSocketClient.IP}-{tcpDmtpSocketClient.Port}ThingsGateway.db",//服务器本地保存路径 | ||||
|             ResourcePath = string.Empty,//请求客户端文件的资源路径 | ||||
|             Metadata = metadata,//传递到客户端的元数据 | ||||
|             Timeout = TimeSpan.FromSeconds(60),//传输超时时长 | ||||
|             TryCount = 10,//当遇到失败时,尝试次数 | ||||
|             FileSectionSize = 1024 * 512//分包大小,当网络较差时,应该适当减小该值 | ||||
|         }; | ||||
|  | ||||
|         //此处的作用相当于Timer,定时每秒输出当前的传输进度和速度。 | ||||
|         var loopAction = LoopAction.CreateLoopAction(-1, 1000, (loop) => | ||||
|         { | ||||
|             if (fileOperator.Result.ResultCode != ResultCode.Default) | ||||
|             { | ||||
|                 loop.Dispose(); | ||||
|             } | ||||
|             LogMessage.Info($"进度:{fileOperator.Progress},速度:{fileOperator.Speed() / 1024}kb/s"); | ||||
|         }); | ||||
|  | ||||
|         //此方法会阻塞,直到传输结束,也可以使用PullFileAsync | ||||
|         var result = await tcpDmtpSocketClient.GetDmtpFileTransferActor().PullFileAsync(fileOperator); | ||||
|  | ||||
|         loopAction.Run(); | ||||
|  | ||||
|         LogMessage.Info($" {result.ResultCode},具体消息:{result.Message}"); | ||||
|         if (result.ResultCode != ResultCode.Success) | ||||
|             throw new Exception(result.Message); | ||||
|         OpenFile(folderPath); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// DBUpload | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task FileDown(TcpDmtpSocketClient tcpDmtpSocketClient, string folderPath) | ||||
|     { | ||||
|  | ||||
|         string[] files = Directory.GetFiles(folderPath, "", SearchOption.AllDirectories); | ||||
|         await files.ParallelForEachAsync(async (file, cancellationToken) => | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (!Path.GetRelativePath(folderPath, file).Contains("FileTemp")) | ||||
|                 { | ||||
|  | ||||
|  | ||||
|                     var metadata = new Metadata();//传递到客户端的元数据 | ||||
|                     metadata.Add(FilePluginUtil.DmtpType, DmtpTypeEnum.File.ToString()); | ||||
|                     metadata.Add(FilePluginUtil.FileName, Path.GetRelativePath(folderPath, file)); | ||||
|  | ||||
|                     var fileOperator = new FileOperator//实例化本次传输的控制器,用于获取传输进度、速度、状态等。 | ||||
|                     { | ||||
|                         SavePath = string.Empty,//客户端本地保存路径 | ||||
|                         ResourcePath = file,//服务器文件的资源路径 | ||||
|                         Metadata = metadata,//传递到客户端的元数据 | ||||
|                         Timeout = TimeSpan.FromSeconds(60),//传输超时时长 | ||||
|                         TryCount = 10,//当遇到失败时,尝试次数 | ||||
|                         FileSectionSize = 1024 * 512//分包大小,当网络较差时,应该适当减小该值 | ||||
|                     }; | ||||
|  | ||||
|                     //此处的作用相当于Timer,定时每秒输出当前的传输进度和速度。 | ||||
|                     var loopAction = LoopAction.CreateLoopAction(-1, 1000, (loop) => | ||||
|                     { | ||||
|                         if (fileOperator.Result.ResultCode != ResultCode.Default) | ||||
|                         { | ||||
|                             loop.Dispose(); | ||||
|                         } | ||||
|                         LogMessage.Info($"进度:{fileOperator.Progress},速度:{fileOperator.Speed() / 1024}kb/s"); | ||||
|                     }); | ||||
|  | ||||
|                     //此方法会阻塞,直到传输结束,也可以使用PullFileAsync | ||||
|                     var result = await tcpDmtpSocketClient.GetDmtpFileTransferActor().PushFileAsync(fileOperator); | ||||
|  | ||||
|                     loopAction.Run(); | ||||
|                     LogMessage.Info($" {result.ResultCode},具体消息:{result.Message}"); | ||||
|                     if (result.ResultCode != ResultCode.Success) | ||||
|                         throw new Exception(result.Message); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 LogMessage.LogWarning(ex, "传输出错"); | ||||
|             } | ||||
|         }, 10); | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 打开指定目录下的文件 | ||||
|     /// </summary> | ||||
|     private static void OpenFile(string folderPath) | ||||
|     { | ||||
|  | ||||
|         Process process = new Process(); | ||||
|  | ||||
|         if (OperatingSystem.IsWindows()) | ||||
|         { | ||||
|             process.StartInfo.FileName = "explorer.exe"; | ||||
|             process.StartInfo.Arguments = folderPath.Replace("/", "\\"); | ||||
|         } | ||||
|         else if (OperatingSystem.IsMacOS()) | ||||
|         { | ||||
|             process.StartInfo.FileName = "open"; | ||||
|             process.StartInfo.Arguments = folderPath; | ||||
|         } | ||||
|         else if (OperatingSystem.IsLinux()) | ||||
|         { | ||||
|             process.StartInfo.FileName = "xdg-open"; | ||||
|             process.StartInfo.Arguments = folderPath; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             throw new NotSupportedException("不支持的操作系统"); | ||||
|         } | ||||
|  | ||||
|         process.Start(); | ||||
|     } | ||||
| } | ||||
| @@ -1,4 +0,0 @@ | ||||
| { | ||||
|   "UpdateServerUri": "127.0.0.1:7400", //管理服务IP | ||||
|   "VerifyToken": "ThingsGateway" //验证Token | ||||
| } | ||||
| @@ -1,41 +0,0 @@ | ||||
| <!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.UpgradeManger</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,104 +0,0 @@ | ||||
| { | ||||
|   "RECORDS": [ | ||||
|     { | ||||
|       "Id": "22222222222222", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_SWAGGER_NAME", | ||||
|       "ConfigValue": "admin", | ||||
|       "Remark": "swagger账号", | ||||
|       "SortCode": "1", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222223", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_SWAGGER_PASSWORD", | ||||
|       "ConfigValue": "123456", | ||||
|       "Remark": "swagger密码", | ||||
|       "SortCode": "2", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222224", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_SWAGGERLOGIN_OPEN", | ||||
|       "ConfigValue": "false", | ||||
|       "Remark": "swagger开启登录", | ||||
|       "SortCode": "3", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "Id": "22222222222226", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_TITLE", | ||||
|       "ConfigValue": "ThingsGateway", | ||||
|       "Remark": "标题", | ||||
|       "SortCode": "5", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222228", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_COPYRIGHT", | ||||
|       "ConfigValue": "ThingsGateway ©2023 Diego", | ||||
|       "Remark": "系统版权", | ||||
|       "SortCode": "6", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222229", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_COPYRIGHT_URL", | ||||
|       "ConfigValue": "https://gitee.com/diego2098/ThingsGateway", | ||||
|       "Remark": "系统版权链接地址", | ||||
|       "SortCode": "7", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222231", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_PASSWORD", | ||||
|       "ConfigValue": "111111", | ||||
|       "Remark": "默认用户密码", | ||||
|       "SortCode": "8", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222227", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_VERIFICAT_EXPIRES", | ||||
|       "ConfigValue": "14400", | ||||
|       "Remark": "Verificat过期时间(分)", | ||||
|       "SortCode": "9", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222232", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_SINGLE_OPEN", | ||||
|       "ConfigValue": "false", | ||||
|       "Remark": "单用户登录开关", | ||||
|       "SortCode": "10", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222230", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_CAPTCHA_OPEN", | ||||
|       "ConfigValue": "true", | ||||
|       "Remark": "登录验证码开关", | ||||
|       "SortCode": "11", | ||||
|       "IsDelete": "false" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "22222222222225", | ||||
|       "Category": "SYS_CONFIGBASEDEFAULT", | ||||
|       "ConfigKey": "CONFIG_REMARK", | ||||
|       "ConfigValue": "边缘采集网关", | ||||
|       "Remark": "说明", | ||||
|       "SortCode": "12", | ||||
|       "IsDelete": "false" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,16 +0,0 @@ | ||||
| { | ||||
|   "RECORDS": [ | ||||
|     { | ||||
|       "Id": "212725263001001", | ||||
|       "Code": "superAdmin", | ||||
|       "Name": "超级管理员", | ||||
|       "SortCode": "1" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "212725263001002", | ||||
|       "Code": "admin", | ||||
|       "Name": "业务管理员", | ||||
|       "SortCode": "2" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| { | ||||
|   "RECORDS": [ | ||||
|     { | ||||
|       "Id": "212725263002001", | ||||
|       "Account": "superAdmin", | ||||
|       "LastLoginDevice": "PC", | ||||
|       "LastLoginIp": "0.0.0.1", | ||||
|       "LastLoginTime": "2023-03-03 21:18:43.7092169", | ||||
|       "LatestLoginDevice": "PC", | ||||
|       "LatestLoginIp": "0.0.0.1", | ||||
|       "LatestLoginTime": "2023-03-03 21:19:16.1043309", | ||||
|       "Password": "7DA385A25A98388E", | ||||
|       "SortCode": "1", | ||||
|       "UserEnable": "true", | ||||
|       "IsDelete": "false", | ||||
|       "UpdateTime": "2023-03-03 21:19:16.1202211" | ||||
|     }, | ||||
|     { | ||||
|       "Id": "201725263002001", | ||||
|       "Account": "admin", | ||||
|       "LastLoginDevice": "PC", | ||||
|       "LastLoginIp": "0.0.0.1", | ||||
|       "LastLoginTime": "2023-03-03 18:20:49.1875384", | ||||
|       "LatestLoginDevice": "PC", | ||||
|       "LatestLoginIp": "0.0.0.1", | ||||
|       "LatestLoginTime": "2023-03-03 18:23:08.6424099", | ||||
|       "Password": "7DA385A25A98388E", | ||||
|       "SortCode": "2", | ||||
|       "UserEnable": "true", | ||||
|       "IsDelete": "false", | ||||
|       "UpdateTime": "2023-03-03 18:23:08.6727296" | ||||
|     } | ||||
|     ] | ||||
| } | ||||
| @@ -1,204 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/admin/role" | ||||
| @using System.Linq.Expressions; | ||||
| @using BlazorComponent; | ||||
| @using Masa.Blazor.Presets; | ||||
| @using Microsoft.AspNetCore.Authorization; | ||||
| @using ThingsGateway.Admin.Application; | ||||
|   | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @attribute [Authorize] | ||||
| @inject UserResoures UserResoures | ||||
| @inherits BaseComponentBase | ||||
| @layout MainLayout | ||||
| <AppDataTable @ref="_datatable" TItem="SysRole" SearchItem="RolePageInput" AddItem="RoleAddInput" EditItem="RoleEditInput" DescFunc=@(memberInfo=>memberInfo.GetCustomAttribute<SqlSugar.SugarColumn>(true)?.ColumnDescription) | ||||
|               SearchModel="@search" IsShowSearchKey | ||||
|               QueryCallAsync="QueryCallAsync" AddCallAsync="AddCallAsync" | ||||
|               EditCallAsync="EditCallAsync" DeleteCallAsync="DeleteCallAsync" | ||||
|                 IsShowQueryButton | ||||
|               IsShowAddButton=@UserResoures.IsHasButtonWithRole("sysroleadd") | ||||
|               IsShowDeleteButton=@UserResoures.IsHasButtonWithRole("sysroledelete") | ||||
|               IsShowEditButton=@UserResoures.IsHasButtonWithRole("sysroleedit")> | ||||
|  | ||||
|     <AddTemplate> | ||||
|         @GetRenderFragment(context) | ||||
|  | ||||
|     </AddTemplate> | ||||
|  | ||||
|     <EditTemplate> | ||||
|         @GetRenderFragment(context) | ||||
|     </EditTemplate> | ||||
|  | ||||
|     <ItemColOperTemplate> | ||||
|         <MList Dense> | ||||
|             @if (@UserResoures.IsHasButtonWithRole("sysroleperresuorce")) | ||||
|             { | ||||
|                 <MListItem OnClick="async()=> | ||||
|                     { | ||||
|                     ChoiceRoleId=context.Item.Id; | ||||
|                     await ResuorceInitAsync(); | ||||
|                     IsShowResuorces=true; | ||||
|                     }"> | ||||
|                     <MListItemTitle Class="ml-2">资源权限</MListItemTitle> | ||||
|                 </MListItem> | ||||
|  | ||||
|             } | ||||
|             @if (@UserResoures.IsHasButtonWithRole("sysroleperuser")) | ||||
|             { | ||||
|                 <MListItem OnClick="async()=> | ||||
|                     { | ||||
|                     ChoiceRoleId=context.Item.Id; | ||||
|                     await UserInitAsync(); | ||||
|                     IsShowUsers=true; | ||||
|                     }"> | ||||
|                     <MListItemTitle Class="ml-2">授权用户</MListItemTitle> | ||||
|                 </MListItem> | ||||
|  | ||||
|             } | ||||
|         </MList> | ||||
|     </ItemColOperTemplate> | ||||
|  | ||||
| </AppDataTable> | ||||
|  | ||||
| <PDrawer @bind-Value="IsShowResuorces" OnCancel="() => IsShowResuorces = false" | ||||
|          Title=资源授权 | ||||
|          Width=@(IsMobile?"100%":"600") | ||||
|          MaxWidth="600" OnSave="OnRoleHasResuorcesSaveAsync"> | ||||
|     @if (IsShowResuorces) | ||||
|     { | ||||
|         <MSheet Outlined Class="ma-0 pa-2"> | ||||
|             <MRow Align="AlignTypes.Center"> | ||||
|                 <MCol>  <MLabel Class="ml-4 font-weight-black">菜单</MLabel>  </MCol> | ||||
|                 <MDivider Vertical /> | ||||
|                 <MCol>  <MLabel Class="ml-4 font-weight-black">按钮</MLabel>    </MCol> | ||||
|             </MRow> | ||||
|         </MSheet> | ||||
|         @foreach (var menu in ResTreeSelectors) | ||||
|         { | ||||
|             <MSheet Outlined Class="ma-0 pa-4"> | ||||
|                 <MRow Align="AlignTypes.Center"> | ||||
|                     <MCol> | ||||
|                         <MListItem IsActive=@(RoleHasResuorces.Any(it=>it.MenuId==menu.Id))> | ||||
|                             <ItemContent> | ||||
|                                 <MListItemContent> | ||||
|                                     <MListItemTitle>@menu.Title</MListItemTitle> | ||||
|                                 </MListItemContent> | ||||
|                                 <MListItemAction> | ||||
|                                     <MCheckbox TValue=bool Value="@context.Active" ValueChanged=@(enable=> | ||||
|                                        { | ||||
|                                        if(!enable) | ||||
|                                        RoleHasResuorces.RemoveWhere(it=>it.MenuId==menu.Id); | ||||
|                                        else if(!RoleHasResuorces.Any(it=>it.MenuId==menu.Id)) | ||||
|                                        RoleHasResuorces.Add(new() {MenuId=menu.Id}); | ||||
|                                        } | ||||
|                                        )></MCheckbox> | ||||
|                                 </MListItemAction> | ||||
|                             </ItemContent> | ||||
|                         </MListItem> | ||||
|                     </MCol> | ||||
|                     <MDivider Vertical /> | ||||
|                     <MCol> | ||||
|                         @GetButtonCore(menu) | ||||
|                     </MCol> | ||||
|  | ||||
|                 </MRow> | ||||
|  | ||||
|             </MSheet> | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
| </PDrawer> | ||||
|  | ||||
| <PDrawer @bind-Value="IsShowUsers" OnCancel="() => IsShowUsers = false" | ||||
|          Title=授权用户 | ||||
|          Width=@(IsMobile?"100%":"500") | ||||
|          MaxWidth="500" OnSave="OnUsersSaveAsync"> | ||||
|  | ||||
|     <MCard Flat Class="ma-0 pa-4"> | ||||
|         <MCardTitle Class="py-2"> | ||||
|             <MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="my-1 mx-2 ml-6" @bind-Value="SearchKey" | ||||
|                         Outlined Label=@typeof(SysUser).GetDescription(nameof(SysUser.Account)) /> | ||||
|         </MCardTitle> | ||||
|         <MTreeview Class="my-1" Dense OpenAll TItem="UserSelectorOutput" TKey="UserSelectorOutput" Selectable @bind-Value="UsersChoice" | ||||
|         Items="AllUsers" ItemText="r=>r.Account" ItemChildren="r=>null" | ||||
|                    ItemKey=@(r=>r)> | ||||
|             <LabelContent> | ||||
|                 <span title=@context.Item.Account> | ||||
|                     @context.Item.Account | ||||
|                 </span> | ||||
|             </LabelContent> | ||||
|         </MTreeview> | ||||
|     </MCard> | ||||
|  | ||||
| </PDrawer> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     RenderFragment GetButtonCore(RoleGrantResourceMenu menu) | ||||
|     { | ||||
|         RenderFragment ViewSubMenu = null; | ||||
|  | ||||
|         foreach (var button in menu.Button ?? new()) | ||||
|         { | ||||
|             ViewSubMenu += | ||||
|     @<MListItem Class="ml-6" IsActive=@(RoleHasResuorces.FirstOrDefault(it=>it.MenuId==menu.Id)?.ButtonInfo?.Contains(button.Id)==true)> | ||||
|         <ItemContent> | ||||
|             <MListItemContent> | ||||
|                 <MListItemTitle>@button.Title</MListItemTitle> | ||||
|             </MListItemContent> | ||||
|             <MListItemAction> | ||||
|                 <MCheckbox TValue=bool Value="@context.Active" ValueChanged=@(a=> | ||||
|                        { | ||||
|                        if(!a) | ||||
|                        { | ||||
|                        RoleHasResuorces.FirstOrDefault(it=>it.MenuId==menu.Id)?.ButtonInfo?.RemoveWhere(it=>it==button.Id); | ||||
|                        } | ||||
|                        else | ||||
|                        { | ||||
|                        if( !(RoleHasResuorces.FirstOrDefault(it=>it.MenuId==menu.Id)?.ButtonInfo?.Any(it=>it==button.Id)==true)) | ||||
|                        { | ||||
|                        if(!RoleHasResuorces.Any(it=>it.MenuId==menu.Id)) | ||||
|                        { | ||||
|                        RoleHasResuorces.Add(new() {MenuId=menu.Id}); | ||||
|                        } | ||||
|                        RoleHasResuorces.FirstOrDefault(it=>it.MenuId==menu.Id).ButtonInfo.Add(button.Id); | ||||
|                        } | ||||
|                        } | ||||
|                        })></MCheckbox> | ||||
|             </MListItemAction> | ||||
|         </ItemContent> | ||||
|     </MListItem> | ||||
|     ; | ||||
|         } | ||||
|  | ||||
|         return ViewSubMenu; | ||||
|     } | ||||
|  | ||||
|     RenderFragment GetRenderFragment(RoleAddInput context) | ||||
|     { | ||||
|         RenderFragment renderFragment = | ||||
|     @<div> | ||||
|         <MSubheader Class="mt-4 font-weight-black">    @(context.Description(x => x.Name))  </MSubheader> | ||||
|         <MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.Name /> | ||||
|  | ||||
|         <MSubheader Class="mt-4 mb-5 font-weight-black">@(context.Description(x => x.SortCode)) </MSubheader> | ||||
|         <MSlider @bind-Value=@context.SortCode Class="mb-5" TValue=int ThumbLabel="@("always")" Dense /> | ||||
|     </div> | ||||
|     ; | ||||
|         return renderFragment; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @@ -1,112 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Masa.Blazor; | ||||
| using Masa.Blazor.Presets; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
|  | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor; | ||||
| /// <summary> | ||||
| /// 角色页面 | ||||
| /// </summary> | ||||
| public partial class Role | ||||
| { | ||||
|     private readonly RolePageInput search = new(); | ||||
|     private IAppDataTable _datatable; | ||||
|     private List<UserSelectorOutput> AllUsers; | ||||
|     long ChoiceRoleId; | ||||
|     bool IsShowResuorces; | ||||
|     bool IsShowUsers; | ||||
|     List<RoleGrantResourceMenu> ResTreeSelectors = new(); | ||||
|     List<RelationRoleResuorce> RoleHasResuorces = new(); | ||||
|     private List<UserSelectorOutput> UsersChoice; | ||||
|  | ||||
|     [CascadingParameter] | ||||
|     MainLayout MainLayout { get; set; } | ||||
|  | ||||
|  | ||||
|  | ||||
|     private string SearchKey { get; set; } | ||||
|  | ||||
|     private Task AddCallAsync(RoleAddInput input) | ||||
|     { | ||||
|         return App.GetService<RoleService>().AddAsync(input); | ||||
|     } | ||||
|     private async Task DeleteCallAsync(IEnumerable<SysRole> sysRoles) | ||||
|     { | ||||
|         await App.GetService<RoleService>().DeleteAsync(sysRoles.Select(a => a.Id).ToArray()); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task EditCallAsync(RoleEditInput input) | ||||
|     { | ||||
|         await App.GetService<RoleService>().EditAsync(input); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|     private async Task OnRoleHasResuorcesSaveAsync(ModalActionEventArgs args) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             GrantResourceInput userGrantRoleInput = new(); | ||||
|             var data = new List<SysResource>(); | ||||
|             userGrantRoleInput.Id = ChoiceRoleId; | ||||
|             userGrantRoleInput.GrantInfoList = RoleHasResuorces; | ||||
|             await App.GetService<RoleService>().GrantResourceAsync(userGrantRoleInput); | ||||
|             IsShowResuorces = false; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             args.Cancel(); | ||||
|             await PopupService.EnqueueSnackbarAsync(ex, false); | ||||
|         } | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|     private async Task OnUsersSaveAsync(ModalActionEventArgs args) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             GrantUserInput userGrantRoleInput = new(); | ||||
|             userGrantRoleInput.Id = ChoiceRoleId; | ||||
|             userGrantRoleInput.GrantInfoList = UsersChoice.Select(it => it.Id).ToList(); | ||||
|             await App.GetService<RoleService>().GrantUserAsync(userGrantRoleInput); | ||||
|             IsShowUsers = false; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             args.Cancel(); | ||||
|             await PopupService.EnqueueSnackbarAsync(ex, false); | ||||
|         } | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private Task<ISqlSugarPagedList<SysRole>> QueryCallAsync(RolePageInput input) | ||||
|     { | ||||
|         return App.GetService<RoleService>().PageAsync(input); | ||||
|     } | ||||
|  | ||||
|     private async Task ResuorceInitAsync() | ||||
|     { | ||||
|         ResTreeSelectors = (await App.GetService<ResourceService>().GetRoleGrantResourceMenusAsync()); | ||||
|         RoleHasResuorces = (await App.GetService<RoleService>().OwnResourceAsync(ChoiceRoleId))?.GrantInfoList; | ||||
|     } | ||||
|  | ||||
|     private async Task<List<UserSelectorOutput>> UserInitAsync() | ||||
|     { | ||||
|         AllUsers = await App.GetService<SysUserService>().UserSelectorAsync(SearchKey); | ||||
|         var data = await App.GetService<RoleService>().OwnUserAsync(ChoiceRoleId); | ||||
|         UsersChoice = AllUsers.Where(a => data.Contains(a.Id)).ToList(); | ||||
|         return AllUsers; | ||||
|     } | ||||
| } | ||||
| @@ -1,100 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Masa.Blazor; | ||||
| using Masa.Blazor.Presets; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Blazor; | ||||
|  | ||||
| /// <summary> | ||||
| /// 用户界面 | ||||
| /// </summary> | ||||
| public partial class User | ||||
| { | ||||
|     private readonly UserPageInput search = new(); | ||||
|     private IAppDataTable _datatable; | ||||
|     private List<SysRole> AllRoles; | ||||
|     long ChoiceUserId; | ||||
|     bool IsShowRoles; | ||||
|     List<SysRole> RolesChoice = new(); | ||||
|     string SearchName; | ||||
|     [CascadingParameter] | ||||
|     MainLayout MainLayout { get; set; } | ||||
|  | ||||
|  | ||||
|     private Task AddCallAsync(UserAddInput input) | ||||
|     { | ||||
|         return App.GetService<SysUserService>().AddAsync(input); | ||||
|     } | ||||
|     private async Task DeleteCallAsync(IEnumerable<SysUser> users) | ||||
|     { | ||||
|         await App.GetService<SysUserService>().DeleteAsync(users.Select(a => a.Id).ToArray()); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task EditCallAsync(UserEditInput users) | ||||
|     { | ||||
|         await App.GetService<SysUserService>().EditAsync(users); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task OnRolesSaveAsync(ModalActionEventArgs args) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             UserGrantRoleInput userGrantRoleInput = new(); | ||||
|             userGrantRoleInput.Id = ChoiceUserId; | ||||
|             userGrantRoleInput.RoleIdList = RolesChoice.Select(it => it.Id).ToList(); | ||||
|             await App.GetService<SysUserService>().GrantRoleAsync(userGrantRoleInput); | ||||
|             IsShowRoles = false; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             args.Cancel(); | ||||
|             await PopupService.EnqueueSnackbarAsync(ex, false); | ||||
|         } | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|     private Task<ISqlSugarPagedList<SysUser>> QueryCallAsync(UserPageInput input) | ||||
|     { | ||||
|         return App.GetService<SysUserService>().PageAsync(input); | ||||
|     } | ||||
|  | ||||
|     private async Task ResetPasswordAsync(SysUser sysUser) | ||||
|     { | ||||
|         await App.GetService<SysUserService>().ResetPasswordAsync(sysUser.Id); | ||||
|         await PopupService.EnqueueSnackbarAsync(new("成功", AlertTypes.Success)); | ||||
|         await MainLayout.StateHasChangedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task RoleInitAsync() | ||||
|     { | ||||
|         AllRoles = await App.GetService<RoleService>().RoleSelectorAsync(); | ||||
|         var data = await App.GetService<RoleService>().GetRoleIdListByUserIdAsync(ChoiceUserId); | ||||
|         RolesChoice = AllRoles.Where(a => data.Contains(a.Id)).ToList(); | ||||
|     } | ||||
|     private async Task UserStatusChangeAsync(SysUser context, bool enable) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (enable) | ||||
|                 await App.GetService<SysUserService>().EnableUserAsync(context.Id); | ||||
|             else | ||||
|                 await App.GetService<SysUserService>().DisableUserAsync(context.Id); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             await _datatable?.QueryClickAsync(); | ||||
|             await MainLayout.StateHasChangedAsync(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,213 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @page "/login" | ||||
| @layout BaseLayout | ||||
| @inherits BaseComponentBase | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @using BlazorComponent; | ||||
| @using Furion.DataEncryption; | ||||
| @using Masa.Blazor.Presets; | ||||
| @using ThingsGateway.Admin.Application; | ||||
|  | ||||
| <Ajax></Ajax> | ||||
| @if (IsMobile) | ||||
| { | ||||
|     <MCard @onkeydown=Enter Height=@("100%")> | ||||
|         @GetLoginCore() | ||||
|     </MCard> | ||||
| } | ||||
| else | ||||
| { | ||||
|     <MRow NoGutters Style="height:100%"> | ||||
|          <MCol Md=7 Sm=12> | ||||
|              <MSheet Elevation=1 Style="width:100%; height:100%;" Class="d-flex align-start flex-column mb-6"> | ||||
|                  <div class="d-flex align-center ml-12 mt-12"> | ||||
|                      <MAvatar Size="40" Color="teal"> | ||||
|                          <span class="white--text text-h6">@CONFIG_TITLE?.GetNameLen2()</span> | ||||
|                      </MAvatar> | ||||
|                      <h1>@CONFIG_TITLE</h1> | ||||
|                  </div> | ||||
|                  <div class="d-flex align-center ml-12 mt-12 mb-auto"> | ||||
|                      <h3>@CONFIG_REMARK</h3> | ||||
|                  </div> | ||||
|                  <div class="d-flex align-center pa-2" style="width:100%;height:100%;"> | ||||
|                      <MImage Src=@(BlazorResourceConst.ResourceUrl+"images/login-left.svg")></MImage> | ||||
|                  </div> | ||||
|              </MSheet> | ||||
|  | ||||
|          </MCol> | ||||
|  | ||||
|          <MCol Md=5 Sm=12 Align="AlignTypes.Center"> | ||||
|              <MRow Md=6 Sm=12 Justify="JustifyTypes.Center" Align="AlignTypes.Center"> | ||||
|                  <MCard Class="px-16 py-12" @onkeydown=Enter> | ||||
|                      @GetLoginCore() | ||||
|                  </MCard> | ||||
|              </MRow> | ||||
|          </MCol> | ||||
|  | ||||
|      </MRow> | ||||
|  | ||||
| } | ||||
|  | ||||
| @code { | ||||
|  | ||||
|     RenderFragment GetLoginCore() | ||||
|     { | ||||
|         RenderFragment ViewSubMenu = | ||||
|     @<div class="mt-2 px-2 py-1 mx-auto text-center my-auto"> | ||||
|         <MAvatar Size=80> | ||||
|             <MImage Src=@UserLogoUrl> | ||||
|             </MImage> | ||||
|         </MAvatar> | ||||
|         <h5 class="mt-2 mb-12">@Welcome 👋</h5> | ||||
|         <MTextField TValue="string" | ||||
|                     Label=账号 | ||||
|                             Outlined | ||||
|                      HideDetails="@("auto")" | ||||
|          @bind-Value=@loginModel.Account> | ||||
|          </MTextField> | ||||
|          <MTextField TValue="string" | ||||
|                      Class="mt-5" | ||||
|                      Label="密码" | ||||
|                      Type="@(_showPassword ? "text" : "password")" | ||||
|                      AppendIcon="@(_showPassword ? "mdi-eye" : "mdi-eye-off")" | ||||
|                      OnAppendClick="()=>_showPassword = !_showPassword" | ||||
|                             Outlined | ||||
|                      HideDetails="@("auto")" | ||||
|          @bind-Value=@Password> | ||||
|          </MTextField> | ||||
|          @if (_showCaptcha) | ||||
|         { | ||||
|             <PImageCaptcha @ref=captcha @bind-Value="CaptchaValue" | ||||
|                            TextFieldClass="mt-5 mx-auto" | ||||
|                            Label=验证码 Outlined Dense | ||||
|                                 OnRefresh="RefreshCode" | ||||
|                                 ErrorMessage=验证码错误> | ||||
|                  </PImageCaptcha> | ||||
|         } | ||||
|         <MButton Class="mt-8 rounded-4" OnClick=LoginAsync Height=45 Width=@("100%") Color="primary">登录</MButton> | ||||
|     </div> | ||||
|         ; | ||||
|         return ViewSubMenu; | ||||
|     } | ||||
| } | ||||
| @code { | ||||
|     private string CaptchaValue; | ||||
|     bool _showPassword; | ||||
|     bool _showCaptcha; | ||||
|     private readonly LoginInput loginModel = new(); | ||||
|  | ||||
|  | ||||
|     [Inject] | ||||
|     AjaxService AjaxService { get; set; } | ||||
|  | ||||
|  | ||||
|     string UserLogoUrl { get; set; } = BlazorResourceConst.ResourceUrl + "images/defaultUser.svg"; | ||||
|  | ||||
|     string Welcome { get; set; } | ||||
|  | ||||
|     private ValidCodeOutput CaptchaInfo { get; set; } | ||||
|  | ||||
|     private string Password { get; set; } | ||||
|  | ||||
|     private string CONFIG_REMARK { get; set; } | ||||
|  | ||||
|     private string CONFIG_TITLE { get; set; } | ||||
|  | ||||
|     private async Task Enter(KeyboardEventArgs e) | ||||
|     { | ||||
|         if (e.Code == "Enter" || e.Code == "NumpadEnter") | ||||
|         { | ||||
|             await LoginAsync(); | ||||
|         } | ||||
|     } | ||||
|     private PImageCaptcha captcha; | ||||
|  | ||||
|     private async Task LoginAsync() | ||||
|     { | ||||
|         loginModel.ValidCodeReqNo = CaptchaInfo.ValidCodeReqNo; | ||||
|         loginModel.ValidCode = CaptchaValue; | ||||
|         loginModel.Password = DESCEncryption.Encrypt(Password, DESCKeyConst.DESCKey); | ||||
|         if (IsMobile) | ||||
|         { | ||||
|             loginModel.Device = AuthDeviceTypeEnum.APP; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             loginModel.Device = AuthDeviceTypeEnum.PC; | ||||
|         } | ||||
|  | ||||
|         var ajaxOption = new AjaxOption { Url = "/auth/b/login", Data = loginModel, }; | ||||
|         var str = await AjaxService.GetMessageAsync(ajaxOption); | ||||
|         if (str != null) | ||||
|         { | ||||
|             var ret = str.FromJsonString<UnifyResult<LoginOutput>>(); | ||||
|             if (ret.Code != 200) | ||||
|             { | ||||
|                 if (captcha != null) | ||||
|                 { | ||||
|                     await captcha.RefreshCode(); | ||||
|                 } | ||||
|                 await PopupService.EnqueueSnackbarAsync(new("登录错误" + ": " + ret.Msg.ToString(), AlertTypes.Error)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 await PopupService.EnqueueSnackbarAsync(new("登录成功", AlertTypes.Success)); | ||||
|                 await Task.Delay(500); | ||||
|                 var userId = await App.GetService<SysUserService>().GetIdByAccountAsync(loginModel.Account); | ||||
|                 var data = await App.GetService<UserCenterService>().GetLoginDefaultRazorAsync(userId); | ||||
|                 var sameLevelMenus = await App.GetService<ResourceService>().GetaMenuAndSpaListAsync(); | ||||
|                 if (NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsolutePath == "/Login" || NavigationManager.ToAbsoluteUri(NavigationManager.Uri).AbsolutePath == "/") | ||||
|                     await AjaxService.GotoAsync(sameLevelMenus.FirstOrDefault(a => a.Id == data)?.Component ?? "index"); | ||||
|                 else | ||||
|                     await AjaxService.GotoAsync(NavigationManager.Uri); | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             if (captcha != null) | ||||
|             { | ||||
|                 await captcha.RefreshCode(); | ||||
|             } | ||||
|             await PopupService.EnqueueSnackbarAsync(new("登录错误", AlertTypes.Error)); | ||||
|         } | ||||
|     } | ||||
|     [Inject] | ||||
|     private NavigationManager NavigationManager { get; set; } | ||||
|  | ||||
|     /// <inheritdoc/> | ||||
|     protected override async Task OnParametersSetAsync() | ||||
|     { | ||||
| #if DEBUG | ||||
|     loginModel.Account = "superAdmin"; | ||||
|     Password = "111111"; | ||||
| #endif | ||||
|         GetCaptchaInfo(); | ||||
|         CONFIG_TITLE = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_TITLE))?.ConfigValue; | ||||
|         CONFIG_REMARK = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_REMARK))?.ConfigValue; | ||||
|         _showCaptcha = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_CAPTCHA_OPEN))?.ConfigValue?.ToBoolean() == true; | ||||
|         Welcome = "欢迎使用" + CONFIG_TITLE + "!"; | ||||
|         await base.OnParametersSetAsync(); | ||||
|     } | ||||
|  | ||||
|     private void GetCaptchaInfo() | ||||
|     { | ||||
|         CaptchaInfo = App.GetService<AuthService>().GetCaptchaInfo(); | ||||
|     } | ||||
|  | ||||
|     private Task<string> RefreshCode() | ||||
|     { | ||||
|         CaptchaInfo = App.GetService<AuthService>().GetCaptchaInfo(); | ||||
|         return Task.FromResult(CaptchaInfo.CodeValue); | ||||
|     } | ||||
| } | ||||
| @@ -1,123 +0,0 @@ | ||||
| @* | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| *@ | ||||
|  | ||||
| @namespace ThingsGateway.Admin.Blazor | ||||
| @using Masa.Blazor.Presets | ||||
| @using ThingsGateway.Admin.Application; | ||||
| @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_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" /> | ||||
|             </MNavigationDrawer> | ||||
|  | ||||
|             <MAppBar Color="barcolor" Style=@($"{(!(IsMobile||_drawerOpen!=true)? "left:200px;":"")}") Elevation="1" App Flat ClippedRight Dense ElevateOnScroll | ||||
|                      MaxHeight="@(BlazorResourceConst.AppBarHeight)" Height="@(BlazorResourceConst.AppBarHeight)"> | ||||
|                 <MButton Class="mr-0" Icon Small=IsMobile OnClick=@(() => _drawerOpen = !_drawerOpen)> | ||||
|                     <MIcon>mdi-menu</MIcon> | ||||
|                 </MButton> | ||||
|                 <AppBarItems BreadcrumbSysResources="BreadcrumbSysResources" SearchSysResources="SearchSysResources" 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" 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"> | ||||
|                         @Body | ||||
|                     </PPageContainer> | ||||
|                 </MCard> | ||||
|                  <MSheet Class="d-flex justify-center align-center rounded-0" Style=@($"height: {BlazorResourceConst.FooterHeight}px; ")> | ||||
|                     <Foter CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE></Foter> | ||||
|                 </MSheet> | ||||
|             </MMain> | ||||
|         </CascadingValue> | ||||
|     </CascadingValue> | ||||
|  | ||||
| </PPageTabsProvider> | ||||
|  | ||||
|  | ||||
| @code { | ||||
|     private bool? _drawerOpen = true; | ||||
|     private PageTabs _pageTabs; | ||||
|     private List<SysResource> BreadcrumbSysResources = new(); | ||||
|     private string CONFIG_COPYRIGHT = ""; | ||||
|     private string CONFIG_COPYRIGHT_URL = ""; | ||||
|     private string CONFIG_TITLE = ""; | ||||
|     private List<SysResource> SearchSysResources = new(); | ||||
|     /// <summary> | ||||
|     /// IsMobile | ||||
|     /// </summary> | ||||
|     [CascadingParameter(Name = "IsMobile")] | ||||
|     public bool IsMobile { get; set; } | ||||
|  | ||||
|     bool Changed { get; set; } | ||||
|     private List<NavItem> Navs { get; set; } = new(); | ||||
|     private List<PageTabItem> PageTabItems { get; set; } = new(); | ||||
|     [Inject] | ||||
|     private UserResoures UserResoures { get; set; } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 页面刷新 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public async Task StateHasChangedAsync() | ||||
|     { | ||||
|         CONFIG_COPYRIGHT = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_COPYRIGHT)).ConfigValue; | ||||
|         CONFIG_TITLE = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_TITLE)).ConfigValue; | ||||
|         CONFIG_COPYRIGHT_URL = (await App.GetService<IConfigService>().GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_COPYRIGHT_URL)).ConfigValue; | ||||
|         await UserResoures.InitUserAsync(); | ||||
|         await UserResoures.InitMenuAsync(); | ||||
|         Navs = UserResoures.Menus.ParseNavItem(); | ||||
|         PageTabItems = UserResoures.PageTabItems; | ||||
|         SearchSysResources = UserResoures.SameLevelMenus; | ||||
|         BreadcrumbSysResources = UserResoures.AllSameLevelMenuSpas; | ||||
|         Changed = !Changed; | ||||
|         await InvokeAsync(StateHasChanged); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// <inheritdoc/> | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     protected override async Task OnInitializedAsync() | ||||
|     { | ||||
|         await StateHasChangedAsync(); | ||||
|         await base.OnInitializedAsync(); | ||||
|     } | ||||
|  } | ||||
| @@ -1,92 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion.DependencyInjection; | ||||
|  | ||||
| using SqlSugar; | ||||
|  | ||||
| using System.Collections; | ||||
| using System.Data; | ||||
| using System.Reflection; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Core; | ||||
|  | ||||
| /// <summary> | ||||
| /// 对象拓展 | ||||
| /// </summary> | ||||
| [SuppressSniffer] | ||||
| public static class ListExtensions | ||||
| { | ||||
|     /// <summary> | ||||
|     /// SqlSugar是否忽略字段 | ||||
|     /// </summary> | ||||
|     /// <param name="pi"></param> | ||||
|     /// <returns></returns> | ||||
|     private static bool IsIgnoreColumn(PropertyInfo pi) | ||||
|     { | ||||
|         return pi.GetCustomAttribute<SugarColumn>(false).IsIgnore == true; | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// SqlSugar是否Json字段 | ||||
|     /// </summary> | ||||
|     /// <param name="pi"></param> | ||||
|     /// <returns></returns> | ||||
|     private static bool IsJsonColumn(PropertyInfo pi) | ||||
|     { | ||||
|         return pi.GetCustomAttribute<SugarColumn>(false).IsJson == true; | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// List转DataTable | ||||
|     /// </summary> | ||||
|     /// <typeparam name="T"></typeparam> | ||||
|     /// <param name="list"></param> | ||||
|     /// <returns></returns> | ||||
|     public static DataTable ToDataTable<T>(this List<T> list) | ||||
|     { | ||||
|         DataTable result = new(); | ||||
|         if (list.Count > 0) | ||||
|         { | ||||
|             var propertys = list[0].GetType().GetPropertiesWithCache(); | ||||
|             foreach (PropertyInfo pi in propertys) | ||||
|             { | ||||
|                 Type colType = pi.PropertyType; | ||||
|                 if (colType.IsGenericType && colType.GetGenericTypeDefinition() == typeof(Nullable<>)) | ||||
|                 { | ||||
|                     colType = colType.GetGenericArguments().First(); | ||||
|                 } | ||||
|                 if (IsIgnoreColumn(pi)) | ||||
|                     continue; | ||||
|                 if (IsJsonColumn(pi))//如果是json特性就是sting类型 | ||||
|                     colType = typeof(string); | ||||
|                 if (colType.IsEnum)//如果是Enum需要转string才会保存Enum字符串 | ||||
|                     colType = typeof(string); | ||||
|                 result.Columns.Add(pi.Name, colType); | ||||
|             } | ||||
|             for (int i = 0; i < list.Count; i++) | ||||
|             { | ||||
|                 ArrayList tempList = new(); | ||||
|                 foreach (PropertyInfo pi in propertys) | ||||
|                 { | ||||
|                     if (IsIgnoreColumn(pi)) | ||||
|                         continue; | ||||
|                     object obj = pi.GetValue(list[i], null); | ||||
|                     if (IsJsonColumn(pi))//如果是json特性就是转化为json格式 | ||||
|                         obj = obj?.ToJsonString();//如果json字符串是空就传null | ||||
|                     tempList.Add(obj); | ||||
|                 } | ||||
|                 object[] array = tempList.ToArray(); | ||||
|                 result.LoadDataRow(array, true); | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
| @@ -1,131 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.ComponentModel; | ||||
| using System.ComponentModel.DataAnnotations; | ||||
| using System.Globalization; | ||||
| using System.Linq.Expressions; | ||||
| using System.Reflection; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Core; | ||||
|  | ||||
| /// <summary> | ||||
| /// 扩展方法 | ||||
| /// </summary> | ||||
| public static class TypeExtensions | ||||
| { | ||||
|     private static SysMemoryCache SysMemoryCache = new SysMemoryCache(); | ||||
|     /// <summary> | ||||
|     /// 获取 DisplayName属性名称 | ||||
|     /// </summary> | ||||
|     /// <typeparam name="T"></typeparam> | ||||
|     /// <param name="item"></param> | ||||
|     /// <param name="accessor"></param> | ||||
|     /// <returns></returns> | ||||
|     /// <exception cref="ArgumentNullException"></exception> | ||||
|     /// <exception cref="ArgumentException"></exception> | ||||
|     public static string Description<T>(this T item, Expression<Func<T, object>> accessor) | ||||
|     { | ||||
|         if (accessor.Body == null) | ||||
|         { | ||||
|             throw new ArgumentNullException(nameof(accessor)); | ||||
|         } | ||||
|  | ||||
|         var expression = accessor.Body; | ||||
|         if (expression is UnaryExpression unaryExpression && unaryExpression.NodeType == ExpressionType.Convert && unaryExpression.Type == typeof(object)) | ||||
|         { | ||||
|             expression = unaryExpression.Operand; | ||||
|         } | ||||
|  | ||||
|         if (expression is not MemberExpression memberExpression) | ||||
|         { | ||||
|             throw new ArgumentException("只能访问字段或属性"); | ||||
|         } | ||||
|  | ||||
|         return typeof(T).GetDescription(memberExpression.Member.Name) ?? memberExpression.Member.Name; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取成员的描述信息 | ||||
|     /// </summary> | ||||
|     /// <param name="memberInfo"></param> | ||||
|     /// <param name="func"></param> | ||||
|     /// <returns></returns> | ||||
|     public static string FindDisplayAttribute(this MemberInfo memberInfo, Func<MemberInfo, string> func = null) | ||||
|     { | ||||
|         var dn = memberInfo.GetCustomAttribute<DisplayNameAttribute>(true)?.DisplayName | ||||
|             ?? memberInfo.GetCustomAttribute<DescriptionAttribute>(true)?.Description | ||||
|             ?? memberInfo.GetCustomAttribute<DisplayAttribute>(true)?.Description | ||||
|             ?? memberInfo.GetCustomAttribute<SqlSugar.SugarColumn>(true)?.ColumnDescription | ||||
|             ?? func?.Invoke(memberInfo) | ||||
|             ?? memberInfo.Name | ||||
|             ; | ||||
|  | ||||
|         return dn; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获得类型属性的描述信息 | ||||
|     /// </summary> | ||||
|     /// <param name="modelType"></param> | ||||
|     /// <param name="name"></param> | ||||
|     /// <returns></returns> | ||||
|     public static string GetDescription(this Type modelType, string name) | ||||
|     { | ||||
|         var cacheKey = $"{nameof(GetDescription)}-{CultureInfo.CurrentUICulture.Name}-{modelType.FullName}-{name}-{modelType.TypeHandle.Value}"; | ||||
|         var str = SysMemoryCache.GetOrCreate(cacheKey, entry => | ||||
|         { | ||||
|             string dn = default; | ||||
|             { | ||||
|  | ||||
|                 var props = modelType.GetPropertiesWithCache(); | ||||
|                 var propertyInfo = props.FirstOrDefault(p => p.Name == name); | ||||
|                 if (propertyInfo != null) | ||||
|                 { | ||||
|                     dn = FindDisplayAttribute(propertyInfo); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var fields = modelType.GetFieldsWithCache(); | ||||
|                     var fieldInfo = fields.FirstOrDefault(p => p.Name == name); | ||||
|                     dn = FindDisplayAttribute(fieldInfo); | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             return dn ?? name; | ||||
|         }, false); | ||||
|  | ||||
|         return str; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取枚举类型的所有项,返回集合 | ||||
|     /// </summary> | ||||
|     public static List<EnumItem> GetEnumList<T>(this T type) where T : Type | ||||
|     { | ||||
|         var enumType = type; | ||||
|         List<EnumItem> list = new(); | ||||
|         var fieldInfos = enumType.GetFieldsWithCache().ToList(); | ||||
|         for (int i = 1; i < fieldInfos.Count; i++) | ||||
|         { | ||||
|             var fieldInfo = fieldInfos[i]; | ||||
|             var des = fieldInfo.FindDisplayAttribute(); | ||||
|             int value = (int)Enum.Parse(enumType, fieldInfo.Name); | ||||
|             list.Add(new(fieldInfo.Name, des, value)); | ||||
|         } | ||||
|         return list; | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,113 +0,0 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Furion; | ||||
| using Furion.Logging; | ||||
|  | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Logging; | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| using LogLevel = Microsoft.Extensions.Logging.LogLevel; | ||||
|  | ||||
| namespace ThingsGateway.Admin.Core; | ||||
|  | ||||
| /// <summary> | ||||
| /// 日志写入文件的组件 | ||||
| /// </summary> | ||||
| public sealed class LoggingFileComponent : IServiceComponent | ||||
| { | ||||
|     /// <inheritdoc/> | ||||
|     public void Load(IServiceCollection services, ComponentContext componentContext) | ||||
|     { | ||||
|         var logFileEnable = App.GetConfig<bool?>("Logging:LogEnable:File"); | ||||
|         if (logFileEnable != true) return; | ||||
|  | ||||
|         //获取默认日志等级 | ||||
|         var defaultLevel = App.GetConfig<LogLevel?>("Logging:LogLevel:File"); | ||||
|         //获取程序根目录 | ||||
|         var rootPath = App.HostEnvironment.ContentRootPath; | ||||
|         if (defaultLevel != null)//如果默认日志等级不是空 | ||||
|         { | ||||
|             //遍历日志等级 | ||||
|             foreach (LogLevel level in Enum.GetValues(typeof(LogLevel))) | ||||
|             { | ||||
|                 //如果日志等级是默认等级和最大等级之间 | ||||
|                 if (level >= defaultLevel && level != LogLevel.None) | ||||
|                 { | ||||
|                     //每天创建一个日志文件 | ||||
|                     services.AddLogging(builder => | ||||
|                    { | ||||
|                        var fileName = $"logs/{level}/{{0:yyyy}}-{{0:MM}}-{{0:dd}}{{0:zz}}.log"; | ||||
|                        builder.AddFile(fileName, options => | ||||
|                        { | ||||
|                            SetLogOptions(options, level);//日志格式化 | ||||
|                        }); | ||||
|                    }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             //添加日志文件 | ||||
|             services.AddFileLogging("logs/{0:yyyy}-{0:MM}-{0:dd}{0:zz}.log", options => | ||||
|             { | ||||
|                 SetLogOptions(options, null);//日志格式化 | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 日志格式化 | ||||
|     /// </summary> | ||||
|     /// <param name="options"></param> | ||||
|     /// <param name="logLevel"></param> | ||||
|     private static void SetLogOptions(FileLoggerOptions options, LogLevel? logLevel) | ||||
|     { | ||||
|         //每天创建一个日志文件 | ||||
|         var rootPath = App.HostEnvironment.ContentRootPath; | ||||
|         if (logLevel != null)//如果日志等级不为空 | ||||
|         { | ||||
|             //过滤日志等级 | ||||
|             options.WriteFilter = (logMsg) => | ||||
|             { | ||||
|                 //不写入LoggingMonitor | ||||
|                 if (logMsg.LogName == "System.Logging.LoggingMonitor") | ||||
|                     return false; | ||||
|                 //只写入NetCore日志 | ||||
|                 if (!logMsg.LogName.StartsWith("System") && !logMsg.LogName.StartsWith("Microsoft")) | ||||
|                     return false; | ||||
|                 return logMsg.LogLevel == logLevel; | ||||
|             }; | ||||
|         } | ||||
|         //定义日志文件名 | ||||
|         options.FileNameRule = fileName => | ||||
|         { | ||||
|             return rootPath + "\\" + string.Format(fileName, DateTimeExtensions.CurrentDateTime); | ||||
|         }; | ||||
|         options.FileSizeLimitBytes = 50 * 1024 * 1024;//日志最大50M | ||||
|         options.MessageFormat = logMsg => | ||||
|             { | ||||
|                 var stringBuilder = new StringBuilder(); | ||||
|                 stringBuilder.AppendLine("【日志级别】:" + logMsg.LogLevel); | ||||
|                 stringBuilder.AppendLine("【日志类名】:" + logMsg.LogName); | ||||
|                 stringBuilder.AppendLine("【日志时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat()); | ||||
|                 stringBuilder.AppendLine("【日志内容】:" + logMsg.Message); | ||||
|                 if (logMsg.Exception != null) | ||||
|                 { | ||||
|                     stringBuilder.AppendLine("【异常信息】:" + logMsg.Exception); | ||||
|                 } | ||||
|                 return stringBuilder.ToString(); | ||||
|             }; | ||||
|     } | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 9.0 KiB | 
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user