Compare commits
	
		
			161 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c6aec3a1af | ||
| 
						 | 
					22e30f7a62 | ||
| 
						 | 
					57711b8ab5 | ||
| 
						 | 
					90ff1259ea | ||
| 
						 | 
					d88fc5ccd7 | ||
| 
						 | 
					5aaca2aa9c | ||
| 
						 | 
					8b9ca56e17 | ||
| 
						 | 
					e4f3772e6d | ||
| 
						 | 
					d58ec81d20 | ||
| 
						 | 
					415aae44b6 | ||
| 
						 | 
					a533286658 | ||
| 
						 | 
					e59f91cd82 | ||
| 
						 | 
					5f8b85d8a4 | ||
| 
						 | 
					47c7b88436 | ||
| 
						 | 
					90006782f2 | ||
| 
						 | 
					c3d49cbe70 | ||
| 
						 | 
					112323a360 | ||
| 
						 | 
					9d08c90fda | ||
| 
						 | 
					602d24deec | ||
| 
						 | 
					a2b9f66785 | ||
| 
						 | 
					7cbf289b50 | ||
| 
						 | 
					4097da79a5 | ||
| 
						 | 
					91b7ae554f | ||
| 
						 | 
					3121aa2542 | ||
| 
						 | 
					4bf895e6e1 | ||
| 
						 | 
					0c5489e920 | ||
| 
						 | 
					d63c3aaa80 | ||
| 
						 | 
					4f188ea6cc | ||
| 
						 | 
					acb17018ae | ||
| 
						 | 
					2affe2988d | ||
| 
						 | 
					4174dd2206 | ||
| 
						 | 
					e1c492f238 | ||
| 
						 | 
					fb08e34fa3 | ||
| 
						 | 
					a1793a0afe | ||
| 
						 | 
					4da9763b49 | ||
| 
						 | 
					81e0918bd0 | ||
| 
						 | 
					c1e064f06d | ||
| 
						 | 
					1c52be8b47 | ||
| 
						 | 
					bcd82055ca | ||
| 
						 | 
					c47d95d170 | ||
| 
						 | 
					3e62f1ad51 | ||
| 
						 | 
					8dcae973ef | ||
| 
						 | 
					4cf35f7294 | ||
| 
						 | 
					94c77d151b | ||
| 
						 | 
					7f600e2b4b | ||
| 
						 | 
					c809d0ba87 | ||
| 
						 | 
					50f038ec89 | ||
| 
						 | 
					9199a255a2 | ||
| 
						 | 
					d324537b47 | ||
| 
						 | 
					d0c05685f7 | ||
| 
						 | 
					1063c930b5 | ||
| 
						 | 
					79cbd44366 | ||
| 
						 | 
					7fdac1c5cb | ||
| 
						 | 
					0c0cf72ebb | ||
| 
						 | 
					8e2fe175ed | ||
| 
						 | 
					d1cff037c9 | ||
| 
						 | 
					fc88a2fafa | ||
| 
						 | 
					45fcceb056 | ||
| 
						 | 
					7043477038 | ||
| 
						 | 
					7dd685cf54 | ||
| 
						 | 
					5f5e4969c0 | ||
| 
						 | 
					8a53fd19e9 | ||
| 
						 | 
					baf4714c36 | ||
| 
						 | 
					7ba9ac7a5b | ||
| 
						 | 
					85b8f26e8e | ||
| 
						 | 
					594a0f1410 | ||
| 
						 | 
					d317d757d7 | ||
| 
						 | 
					fdf0ba6318 | ||
| 
						 | 
					15bf7de5fa | ||
| 
						 | 
					d3402b058e | ||
| 
						 | 
					e7dfdd4031 | ||
| 
						 | 
					b2dd7b6364 | ||
| 
						 | 
					9bd6d9abbf | ||
| 
						 | 
					cd14428fea | ||
| 
						 | 
					19d9f03c2b | ||
| 
						 | 
					0d57e72bbf | ||
| 
						 | 
					329516a61b | ||
| 
						 | 
					d566869589 | ||
| 
						 | 
					9cb8d8e6c7 | ||
| 
						 | 
					9de3c57e5d | ||
| 
						 | 
					f32ff92b0b | ||
| 
						 | 
					88d71e271e | ||
| 
						 | 
					fd9c14612a | ||
| 
						 | 
					e26e5a160f | ||
| 
						 | 
					b836bfed22 | ||
| 
						 | 
					a4b598c6d0 | ||
| 
						 | 
					c9ab755839 | ||
| 
						 | 
					9920edba53 | ||
| 
						 | 
					12bd7280d1 | ||
| 
						 | 
					d30ea7f63b | ||
| 
						 | 
					ebd3390db6 | ||
| 
						 | 
					9a374a9ebc | ||
| 
						 | 
					b1bc22cb08 | ||
| 
						 | 
					4930d53890 | ||
| 
						 | 
					c31327b5bc | ||
| 
						 | 
					3f2aa1f1e1 | ||
| 
						 | 
					6e78c00a96 | ||
| 
						 | 
					c27dde085e | ||
| 
						 | 
					d26cc308c0 | ||
| 
						 | 
					fb1efdf290 | ||
| 
						 | 
					3c99f2a472 | ||
| 
						 | 
					affe9a44e0 | ||
| 
						 | 
					43730fa519 | ||
| 
						 | 
					d39aa22b09 | ||
| 
						 | 
					e232a6b6ea | ||
| 
						 | 
					71ebb36fe9 | ||
| 
						 | 
					78a0b86327 | ||
| 
						 | 
					2636c16a97 | ||
| 
						 | 
					fd77c0242d | ||
| 
						 | 
					e74819a900 | ||
| 
						 | 
					9b7f696c9b | ||
| 
						 | 
					0230d614e7 | ||
| 
						 | 
					252d99ad78 | ||
| 
						 | 
					1ffc200350 | ||
| 
						 | 
					807d89b2b2 | ||
| 
						 | 
					4013afa1f1 | ||
| 
						 | 
					a580927ceb | ||
| 
						 | 
					bf2cf52034 | ||
| 
						 | 
					81bb8b7c31 | ||
| 
						 | 
					a825007fb5 | ||
| 
						 | 
					988124d96a | ||
| 
						 | 
					f0de815296 | ||
| 
						 | 
					0e2d58c887 | ||
| 
						 | 
					b155382626 | ||
| 
						 | 
					f362d740af | ||
| 
						 | 
					4a85e31a4f | ||
| 
						 | 
					302c270ad5 | ||
| 
						 | 
					3c1517d0f3 | ||
| 
						 | 
					f9fb222044 | ||
| 
						 | 
					e8edc02ba3 | ||
| 
						 | 
					95a44e3053 | ||
| 
						 | 
					74a9fe9a87 | ||
| 
						 | 
					4d03f9ea1a | ||
| 
						 | 
					67c96ca991 | ||
| 
						 | 
					88fb793c68 | ||
| 
						 | 
					d6d02d8cc5 | ||
| 
						 | 
					c5a3f8e2e3 | ||
| 
						 | 
					27e8653a1a | ||
| 
						 | 
					863beda82c | ||
| 
						 | 
					bac84c3ecd | ||
| 
						 | 
					2fca2ad9f8 | ||
| 
						 | 
					dd75286fe0 | ||
| 
						 | 
					7f91792cf1 | ||
| 
						 | 
					0e0ccad311 | ||
| 
						 | 
					0691f72e67 | ||
| 
						 | 
					7e38a51720 | ||
| 
						 | 
					34ca8243a3 | ||
| 
						 | 
					112fea7632 | ||
| 
						 | 
					378763e4ee | ||
| 
						 | 
					517bd0394d | ||
| 
						 | 
					70adb97fb5 | ||
| 
						 | 
					623d44cabe | ||
| 
						 | 
					0d479ca00b | ||
| 
						 | 
					8bc49ef437 | ||
| 
						 | 
					f83fcec786 | ||
| 
						 | 
					93690ce40d | ||
| 
						 | 
					f82c5f2f27 | ||
| 
						 | 
					a83c1c3899 | ||
| 
						 | 
					91d6aed109 | ||
| 
						 | 
					db8f8fe51d | ||
| 
						 | 
					4596004b17 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -363,4 +363,6 @@ MigrationBackup/
 | 
			
		||||
FodyWeavers.xsd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/framework/*pro*
 | 
			
		||||
/framework/*Pro*
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
[*.cs]
 | 
			
		||||
 | 
			
		||||
# CA1848: 使用 LoggerMessage 委托
 | 
			
		||||
dotnet_diagnostic.CA1848.severity = none
 | 
			
		||||
 | 
			
		||||
# CA2254: 模板应为静态表达式
 | 
			
		||||
dotnet_diagnostic.CA2254.severity = suggestion
 | 
			
		||||
							
								
								
									
										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,140 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
 | 
			
		||||
		<DefineConstants>Pro</DefineConstants>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
	<ItemGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
 | 
			
		||||
		
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor.cs" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.AllenBradleyCip\ThingsGateway.Foundation.Adapter.AllenBradleyCip.csproj" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Omron\ThingsGateway.Foundation.Adapter.Omron.csproj" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor.cs" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Secs\ThingsGateway.Foundation.Adapter.Secs.csproj" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor.cs" Link="Pages\TS550\TS550DebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor" Link="Pages\TS550\TS550DebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.TS550\ThingsGateway.Foundation.Adapter.TS550.csproj" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor" Link="Pages\Vigor\VigorSerialDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Vigor\ThingsGateway.Foundation.Adapter.Vigor.csproj" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.HZW_QTJC_01\ThingsGateway.Foundation.Adapter.HZW_QTJC_01.csproj" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007DebugPage.razor.cs" />
 | 
			
		||||
		<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\PrivateLogger.cs" Link="Pages\Mqtt\PrivateLogger.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" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor" Link="Pages\DLT645\DLT645_2007DebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor" />
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor" Link="Pages\Modbus\ModbusRtuDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor" Link="Pages\Modbus\ModbusTcpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusUdpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor" Link="Pages\Modbus\ModbusUdpDebugPage.razor" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor.cs" Link="Pages\OPCDA\OPCDAClientDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor.cs" Link="Pages\OPCDA\OPCDAClientPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor.cs" Link="Pages\OPCDA\OPCDAImportVariable.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor" Link="Pages\OPCDA\OPCDAClientDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor" Link="Pages\OPCDA\OPCDAClientPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor" Link="Pages\OPCDA\OPCDAImportVariable.razor" />
 | 
			
		||||
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor" Link="Pages\OPCUA\OPCUAClientDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor" Link="Pages\OPCUA\OPCUAClientPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor" Link="Pages\OPCUA\OPCUAImportVariable.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor.cs" Link="Pages\OPCUA\OPCUAClientDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor.cs" Link="Pages\OPCUA\OPCUAClientPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor.cs" Link="Pages\OPCUA\OPCUAImportVariable.razor.cs" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor" Link="Pages\Siemens\S7_1200DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor" Link="Pages\Siemens\S7_1500DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor" Link="Pages\Siemens\S7_200DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor" Link="Pages\Siemens\S7_200SMARTDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor" Link="Pages\Siemens\S7_300DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor" Link="Pages\Siemens\S7_400DebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor.cs" Link="Pages\Siemens\S7_1200DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor.cs" Link="Pages\Siemens\S7_1500DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor.cs" Link="Pages\Siemens\S7_200DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor.cs" Link="Pages\Siemens\S7_200SMARTDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor.cs" Link="Pages\Siemens\S7_300DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor.cs" Link="Pages\Siemens\S7_400DebugPage.razor.cs" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<Content Update="wwwroot\**">
 | 
			
		||||
			<CopyToOutputDirectory>Always</CopyToOutputDirectory>
 | 
			
		||||
		</Content>
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <Folder Include="Pages\Mqtt\" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <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" />
 | 
			
		||||
	</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,442 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<doc>
 | 
			
		||||
    <assembly>
 | 
			
		||||
        <name>ThingsGateway.Foundation.Adapter.DLT645</name>
 | 
			
		||||
    </assembly>
 | 
			
		||||
    <members>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DataInfo">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析参数
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DataInfo.ByteLength">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析长度
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DataInfo.Digtal">
 | 
			
		||||
            <summary>
 | 
			
		||||
            小数位
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DataInfo.IsSigned">
 | 
			
		||||
            <summary>
 | 
			
		||||
            有符号解析
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645Helper.GetDataInfos(System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取返回的解析信息
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="buffer"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645Helper.GetDLT645_2007Command(System.String,System.Int32,System.Byte,System.String,System.Byte[],System.String[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取DLT645报文
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645_2007
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.#ctor(ThingsGateway.Foundation.Serial.SerialSession)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645_2007
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="serialSession"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.EnableFEHead">
 | 
			
		||||
            <summary>
 | 
			
		||||
            增加FE FE FE FE的报文头部
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.OperCode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写入需操作员代码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.Password">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写入密码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            通讯地址BCD码,一般应该是12个字符
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.SetDataAdapter">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.UInt32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Byte,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Double,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Single,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Int64,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.UInt64,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.UInt16,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Int16,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.BroadcastTime(System.DateTime,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            广播校时
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="dateTime"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.FreezeAsync(System.DateTime,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            冻结
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="dateTime"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.ReadDeviceStationAsync(System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取通信地址
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteBaudRateAsync(System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            修改波特率
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="baudRate"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WriteDeviceStationAsync(System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            更新通信地址
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="station"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007.WritePasswordAsync(System.Byte,System.String,System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            修改密码
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="level"></param>
 | 
			
		||||
            <param name="oldPassword"></param>
 | 
			
		||||
            <param name="newPassword"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.DLT645.ControlCode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            控制码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.Read">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读数据
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.ReadSub">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读后续数据
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.ReadStation">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读站号
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.Write">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写数据
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.WriteStation">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写站号
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.BroadcastTime">
 | 
			
		||||
            <summary>
 | 
			
		||||
            广播校时
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.Freeze">
 | 
			
		||||
            <summary>
 | 
			
		||||
            冻结
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.WriteBaudRate">
 | 
			
		||||
            <summary>
 | 
			
		||||
            更新波特率
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.DLT645.ControlCode.WritePassword">
 | 
			
		||||
            <summary>
 | 
			
		||||
            更新密码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645_2007Address
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.#ctor">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.#ctor(System.String,System.UInt16)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.#ctor(System.String,System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.DataId">
 | 
			
		||||
            <summary>
 | 
			
		||||
            数据标识
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.Reverse">
 | 
			
		||||
            <summary>
 | 
			
		||||
            反转解析
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            站号信息
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.Parse(System.String,System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.ParseFrom(System.String,System.Int32)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析地址
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="address"></param>
 | 
			
		||||
            <param name="length"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007Address.ToString">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645_2007
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter.#ctor(ThingsGateway.Foundation.Core.EndianType)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645_2007
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter.ToDouble(System.Byte[],System.Int32)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645协议转换double
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="buffer">带数据项标识</param>
 | 
			
		||||
            <param name="offset"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter.ToString(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007BitConverter.ToString(System.Byte[],System.Int32,System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645_2007DataHandleAdapter
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter.EnableFEHead">
 | 
			
		||||
            <summary>
 | 
			
		||||
            增加FE FE FE FE的报文头部
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter.PackCommand(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter.GetInstance">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007DataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message,System.Byte[],System.Byte[],System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645_2007
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.#ctor(ThingsGateway.Foundation.Core.TcpClient)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DLT645_2007
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="tcpClient"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.EnableFEHead">
 | 
			
		||||
            <summary>
 | 
			
		||||
            增加FE FE FE FE的报文头部
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.OperCode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写入需操作员代码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.Password">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写入密码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            通讯地址BCD码,一般应该是12个字符
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.SetDataAdapter">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.UInt32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Byte,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Double,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Single,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Int64,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.UInt64,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.UInt16,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Int16,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.BroadcastTime(System.DateTime,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            广播校时
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="dateTime"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.FreezeAsync(System.DateTime,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            冻结
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="dateTime"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.ReadDeviceStationAsync(System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取通信地址
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteBaudRateAsync(System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            修改波特率
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="baudRate"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WriteDeviceStationAsync(System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            更新通信地址
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="station"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.DLT645.DLT645_2007OverTcp.WritePasswordAsync(System.Byte,System.String,System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            修改密码
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="level"></param>
 | 
			
		||||
            <param name="oldPassword"></param>
 | 
			
		||||
            <param name="newPassword"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message.HeadBytesLength">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message.CheckHeadBytes(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.DLT645_2007Message.SendBytesThen">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
    </members>
 | 
			
		||||
</doc>
 | 
			
		||||
@@ -1,420 +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, OperResult> WriteData;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 继电器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 开关输入
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 输入寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 保持寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusSerialServer(SerialSession serialSession) : base(serialSession)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 多站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool MulStation { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 默认站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var item in ModbusServer01ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer02ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer03ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer04ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        ModbusServer01ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer02ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer03ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer04ByteBlocks.Clear();
 | 
			
		||||
        base.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusAddress mAddress;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
        if (MulStation)
 | 
			
		||||
        {
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (Station != mAddress.Station)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>("地址错误");
 | 
			
		||||
            }
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
        int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
 | 
			
		||||
        switch (mAddress.ReadFunction)
 | 
			
		||||
        {
 | 
			
		||||
            case 1:
 | 
			
		||||
                byte[] bytes0 = new byte[len];
 | 
			
		||||
                ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                ModbusServer01ByteBlock.Read(bytes0);
 | 
			
		||||
                return OperResult.CreateSuccessResult(bytes0);
 | 
			
		||||
            case 2:
 | 
			
		||||
                byte[] bytes1 = new byte[len];
 | 
			
		||||
                ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                ModbusServer02ByteBlock.Read(bytes1);
 | 
			
		||||
                return OperResult.CreateSuccessResult(bytes1);
 | 
			
		||||
            case 3:
 | 
			
		||||
 | 
			
		||||
                byte[] bytes3 = new byte[len];
 | 
			
		||||
                ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                ModbusServer03ByteBlock.Read(bytes3);
 | 
			
		||||
                return OperResult.CreateSuccessResult(bytes3);
 | 
			
		||||
            case 4:
 | 
			
		||||
                byte[] bytes4 = new byte[len];
 | 
			
		||||
                ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                ModbusServer04ByteBlock.Read(bytes4);
 | 
			
		||||
                return OperResult.CreateSuccessResult(bytes4);
 | 
			
		||||
        }
 | 
			
		||||
        return new OperResult<byte[]>("功能码错误");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Read(address, length));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusSerialServerDataHandleAdapter dataHandleAdapter = new();
 | 
			
		||||
        dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
 | 
			
		||||
        SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusAddress mAddress;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
        if (MulStation)
 | 
			
		||||
        {
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (Station != mAddress.Station)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult("地址错误");
 | 
			
		||||
            }
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
        }
 | 
			
		||||
        var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
        switch (mAddress.ReadFunction)
 | 
			
		||||
        {
 | 
			
		||||
            case 3:
 | 
			
		||||
                ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                ModbusServer03ByteBlock.Write(value);
 | 
			
		||||
                return OperResult.CreateSuccessResult();
 | 
			
		||||
            case 4:
 | 
			
		||||
                ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                ModbusServer04ByteBlock.Write(value);
 | 
			
		||||
                return OperResult.CreateSuccessResult();
 | 
			
		||||
        }
 | 
			
		||||
        return new OperResult("功能码错误");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusAddress mAddress;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return (new OperResult(ex));
 | 
			
		||||
        }
 | 
			
		||||
        if (MulStation)
 | 
			
		||||
        {
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (Station != mAddress.Station)
 | 
			
		||||
            {
 | 
			
		||||
                return (new OperResult("地址错误"));
 | 
			
		||||
            }
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
        switch (mAddress.ReadFunction)
 | 
			
		||||
        {
 | 
			
		||||
            case 1:
 | 
			
		||||
                ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                return (OperResult.CreateSuccessResult());
 | 
			
		||||
            case 2:
 | 
			
		||||
                ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                return (OperResult.CreateSuccessResult());
 | 
			
		||||
        }
 | 
			
		||||
        return new OperResult("功能码错误");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void Received(ByteBlock byteBlock, IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            //接收外部报文
 | 
			
		||||
            if (requestInfo is ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
            {
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
                if (!modbusServerMessage.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
 | 
			
		||||
                {
 | 
			
		||||
                    var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
 | 
			
		||||
                    if (data.IsSuccess)
 | 
			
		||||
                    {
 | 
			
		||||
                        var coreData = data.Content;
 | 
			
		||||
                        if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                        {
 | 
			
		||||
                            coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
 | 
			
		||||
                        }
 | 
			
		||||
                        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
 | 
			
		||||
                        SerialSession.Send(sendData);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        WriteError(SerialSession, modbusServerMessage);//返回错误码
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else//写入
 | 
			
		||||
                {
 | 
			
		||||
                    var coreData = modbusServerMessage.Content;
 | 
			
		||||
                    if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入继电器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
                            if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            //写入内存区
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入寄存器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                            if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Logger.LogError(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        //返回错误码
 | 
			
		||||
        static void WriteError(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
        {
 | 
			
		||||
            var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2)
 | 
			
		||||
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
 | 
			
		||||
            sendData[1] = (byte)(sendData[1] + 128);
 | 
			
		||||
            SerialSession.Send(sendData);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void WriteSuccess03(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
    {
 | 
			
		||||
        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 6);
 | 
			
		||||
        SerialSession.Send(sendData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Init(ModbusAddress mAddress)
 | 
			
		||||
    {
 | 
			
		||||
        if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
 | 
			
		||||
            ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
 | 
			
		||||
        if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
 | 
			
		||||
            ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
 | 
			
		||||
        if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
 | 
			
		||||
            ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
 | 
			
		||||
        if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
 | 
			
		||||
            ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,440 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using 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, 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);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusAddress mAddress;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
        if (MulStation)
 | 
			
		||||
        {
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (Station != mAddress.Station)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>("地址错误");
 | 
			
		||||
            }
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
        int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
 | 
			
		||||
        switch (mAddress.ReadFunction)
 | 
			
		||||
        {
 | 
			
		||||
            case 1:
 | 
			
		||||
                byte[] bytes0 = new byte[len];
 | 
			
		||||
                ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                ModbusServer01ByteBlock.Read(bytes0);
 | 
			
		||||
                return OperResult.CreateSuccessResult(bytes0);
 | 
			
		||||
            case 2:
 | 
			
		||||
                byte[] bytes1 = new byte[len];
 | 
			
		||||
                ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                ModbusServer02ByteBlock.Read(bytes1);
 | 
			
		||||
                return OperResult.CreateSuccessResult(bytes1);
 | 
			
		||||
            case 3:
 | 
			
		||||
 | 
			
		||||
                byte[] bytes3 = new byte[len];
 | 
			
		||||
                ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                ModbusServer03ByteBlock.Read(bytes3);
 | 
			
		||||
                return OperResult.CreateSuccessResult(bytes3);
 | 
			
		||||
            case 4:
 | 
			
		||||
                byte[] bytes4 = new byte[len];
 | 
			
		||||
                ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                ModbusServer04ByteBlock.Read(bytes4);
 | 
			
		||||
                return OperResult.CreateSuccessResult(bytes4);
 | 
			
		||||
        }
 | 
			
		||||
        return new OperResult<byte[]>("功能码错误");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Read(address, length));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient)
 | 
			
		||||
    {
 | 
			
		||||
        if (socketClient is SocketClient client)
 | 
			
		||||
        {
 | 
			
		||||
            ModbusTcpServerDataHandleAdapter dataHandleAdapter = new();
 | 
			
		||||
            dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
 | 
			
		||||
            client.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var item in TcpService.GetClients())
 | 
			
		||||
            {
 | 
			
		||||
                ModbusTcpDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
                {
 | 
			
		||||
                    CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
 | 
			
		||||
                };
 | 
			
		||||
                item.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusAddress mAddress;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
        if (MulStation)
 | 
			
		||||
        {
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (Station != mAddress.Station)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult("地址错误");
 | 
			
		||||
            }
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
        }
 | 
			
		||||
        var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
        switch (mAddress.ReadFunction)
 | 
			
		||||
        {
 | 
			
		||||
            case 3:
 | 
			
		||||
                ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                ModbusServer03ByteBlock.Write(value);
 | 
			
		||||
                return OperResult.CreateSuccessResult();
 | 
			
		||||
            case 4:
 | 
			
		||||
                ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                ModbusServer04ByteBlock.Write(value);
 | 
			
		||||
                return OperResult.CreateSuccessResult();
 | 
			
		||||
        }
 | 
			
		||||
        return new OperResult("功能码错误");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusAddress mAddress;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return (new OperResult(ex));
 | 
			
		||||
        }
 | 
			
		||||
        if (MulStation)
 | 
			
		||||
        {
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (Station != mAddress.Station)
 | 
			
		||||
            {
 | 
			
		||||
                return (new OperResult("地址错误"));
 | 
			
		||||
            }
 | 
			
		||||
            Init(mAddress);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
        var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
        switch (mAddress.ReadFunction)
 | 
			
		||||
        {
 | 
			
		||||
            case 1:
 | 
			
		||||
                ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                return (OperResult.CreateSuccessResult());
 | 
			
		||||
            case 2:
 | 
			
		||||
                ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                return (OperResult.CreateSuccessResult());
 | 
			
		||||
        }
 | 
			
		||||
        return new OperResult("功能码错误");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void Received(SocketClient client, IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            //接收外部报文
 | 
			
		||||
            if (requestInfo is ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
            {
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
                if (!modbusServerMessage.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
 | 
			
		||||
                {
 | 
			
		||||
                    var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
 | 
			
		||||
                    if (data.IsSuccess)
 | 
			
		||||
                    {
 | 
			
		||||
                        var coreData = data.Content;
 | 
			
		||||
                        if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                        {
 | 
			
		||||
                            coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
 | 
			
		||||
                        }
 | 
			
		||||
                        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
 | 
			
		||||
                        sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
                        client.Send(sendData);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        WriteError(client, modbusServerMessage);//返回错误码
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else//写入
 | 
			
		||||
                {
 | 
			
		||||
                    var coreData = modbusServerMessage.Content;
 | 
			
		||||
                    if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入继电器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
                            if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            //写入内存区
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入寄存器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                            if ((WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Logger.LogError(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        //返回错误码
 | 
			
		||||
        static void WriteError(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
        {
 | 
			
		||||
            var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
 | 
			
		||||
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
 | 
			
		||||
            sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
            sendData[7] = (byte)(sendData[7] + 128);
 | 
			
		||||
            client.Send(sendData);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void WriteSuccess03(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
    {
 | 
			
		||||
        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12);
 | 
			
		||||
        sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
        client.Send(sendData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Init(ModbusAddress mAddress)
 | 
			
		||||
    {
 | 
			
		||||
        if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
 | 
			
		||||
            ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
 | 
			
		||||
        if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
 | 
			
		||||
            ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
 | 
			
		||||
        if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
 | 
			
		||||
            ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
 | 
			
		||||
        if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
 | 
			
		||||
            ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,579 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<doc>
 | 
			
		||||
    <assembly>
 | 
			
		||||
        <name>ThingsGateway.Foundation.Adapter.OPCDA</name>
 | 
			
		||||
    </assembly>
 | 
			
		||||
    <members>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.CollectionExtensions.RemoveWhere``1(System.Collections.Generic.ICollection{``0},System.Func{``0,System.Boolean})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            移除符合条件的元素
 | 
			
		||||
            </summary>
 | 
			
		||||
            <typeparam name="T"></typeparam>
 | 
			
		||||
            <param name="this"></param>
 | 
			
		||||
            <param name="where"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCDA.Comn.ComInterop.LOCALE_SYSTEM_DEFAULT">
 | 
			
		||||
            <summary>
 | 
			
		||||
            The WIN32 system default locale.
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCDA.Comn.ComInterop.LOCALE_USER_DEFAULT">
 | 
			
		||||
            <summary>
 | 
			
		||||
            The WIN32 user default locale.
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Comn.ComInterop.CreateInstance(System.Guid,System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            创建一个COM服务器的实例。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Comn.ComInterop.GetSystemMessage(System.Int32)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            指定错误消息文本检索系统。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Comn.ComInterop.InitializeSecurity">
 | 
			
		||||
            <summary>
 | 
			
		||||
            初始化COM安全。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Comn.ComInterop.ReadClasses(ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCEnumGUID)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            从枚举器读取guid。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Comn.ComInterop.ReadClasses(ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IEnumGUID)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            从枚举器读取guid。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Comn.ComInterop.RealseComServer(System.Object)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            释放 COM 对象
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="m_server"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCDA.Comn.Convert.FILETIME_BaseTime">
 | 
			
		||||
            <summary>
 | 
			
		||||
            windows的filetime是从1601-1-1 00:00:00开始的,datetime是从1-1-1 00:00:00开始的
 | 
			
		||||
            datetime和filetime的滴答单位都是100ns(100纳秒,千万分之一秒),所以转换时只需要考虑开始时间即可
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Da.OnDataChangedHandler">
 | 
			
		||||
            <summary>
 | 
			
		||||
            值变化
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="opcItems"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Da.OnReadCompletedHandler">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="opcItems"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Da.OnWriteCompletedHandler">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写入
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="opcItems"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Da.ItemReadResult">
 | 
			
		||||
            <summary>
 | 
			
		||||
            返回结果
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ItemReadResult.Name">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ID
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ItemReadResult.Quality">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Quality
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ItemReadResult.TimeStamp">
 | 
			
		||||
            <summary>
 | 
			
		||||
            TimeStamp
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ItemReadResult.Value">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Value
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcGroup.InitIoInterfaces(System.Object)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            建立连接
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="handle"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcGroup.ReadAsync">
 | 
			
		||||
            <summary>
 | 
			
		||||
            组读取
 | 
			
		||||
            </summary>
 | 
			
		||||
            <exception cref="T:System.Runtime.InteropServices.ExternalException"></exception>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OpcItem
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.#ctor(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OpcItem
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="itemId"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.AccessPath">
 | 
			
		||||
            <summary>
 | 
			
		||||
            AccessPath
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.Blob">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Blob
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.BlobSize">
 | 
			
		||||
            <summary>
 | 
			
		||||
            BlobSize
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.ClientHandle">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ClientHandle
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.IsActive">
 | 
			
		||||
            <summary>
 | 
			
		||||
            active(1) or not(0)
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.ItemID">
 | 
			
		||||
            <summary>
 | 
			
		||||
            数据项在opc server的完全名称
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.Quality">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Quality
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.RunTimeDataType">
 | 
			
		||||
            <summary>
 | 
			
		||||
            RunTimeDataType
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.ServerHandle">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ServerHandle
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.TimeStamp">
 | 
			
		||||
            <summary>
 | 
			
		||||
            TimeStamp
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem.Value">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Value
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcServer.AddGroup(System.String,System.Boolean,System.Int32,System.Single)">
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcServer.Browse(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取节点
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcServer.GetServerStatus">
 | 
			
		||||
            <summary>
 | 
			
		||||
            服务器状态
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Da.ServerStatus">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ServerStatus
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ServerStatus.CurrentTime">
 | 
			
		||||
            <summary>
 | 
			
		||||
            CurrentTime
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ServerStatus.LastUpdateTime">
 | 
			
		||||
            <summary>
 | 
			
		||||
            LastUpdateTime
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ServerStatus.ServerState">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ServerState
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ServerStatus.StartTime">
 | 
			
		||||
            <summary>
 | 
			
		||||
            StartTime
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ServerStatus.VendorInfo">
 | 
			
		||||
            <summary>
 | 
			
		||||
            VendorInfo
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.Da.ServerStatus.Version">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Version
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Discovery.OpcDiscovery">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OpcDiscovery
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.Discovery.OpcDiscovery.GetOpcServer(System.String,System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            GetOpcServer
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="serverName"></param>
 | 
			
		||||
            <param name="host"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IConnectionPoint">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IConnectionPointContainer">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IEnumConnectionPoints">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IEnumConnections">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IEnumGUID">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IEnumString">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IEnumUnknown">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCCommon">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCEnumGUID">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCServerList">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCServerList2">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCShutdown">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.CONNECTDATA">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCBROWSEDIRECTION">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCBROWSEFILTER">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCBROWSETYPE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCDATASOURCE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCENUMSCOPE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCEUTYPE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCNAMESPACETYPE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCSERVERSTATE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.CATID_OPCDAServer10">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.CATID_OPCDAServer20">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.CATID_OPCDAServer30">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.CATID_XMLDAServer10">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IEnumOPCItemAttributes">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCAsyncIO">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCAsyncIO2">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCAsyncIO3">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCBrowse">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCBrowseServerAddressSpace">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCDataCallback">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCGroupStateMgt">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCGroupStateMgt2">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCItemDeadbandMgt">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCItemIO">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCItemMgt">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCItemProperties">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCItemSamplingMgt">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCPublicGroupStateMgt">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCServer">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCServerPublicGroups">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCSyncIO">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.IOPCSyncIO2">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCBROWSEELEMENT">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCGROUPHEADER">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCGROUPHEADERWRITE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMATTRIBUTES">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMDEF">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMHEADER1">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMHEADER2">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMHEADERWRITE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMPROPERTIES">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMPROPERTY">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMRESULT">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMSTATE">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCITEMVQT">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.OPCSERVERSTATUS">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.Constants">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.Qualities">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.Rcw.Properties">
 | 
			
		||||
            <exclude />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.DataChangedEventHandler">
 | 
			
		||||
            <summary>
 | 
			
		||||
            订阅变化项
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="values"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCDAClient
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.ItemDicts">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前保存的需订阅列表
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.#ctor(ThingsGateway.Foundation.Core.ILog)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="logger"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="E:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.DataChangedHandler">
 | 
			
		||||
            <summary>
 | 
			
		||||
            数据变化事件
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.IsConnected">
 | 
			
		||||
            <summary>
 | 
			
		||||
            是否连接成功
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.OPCNode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前配置
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.AddItems(System.Collections.Generic.Dictionary{System.String,System.Collections.Generic.List{ThingsGateway.Foundation.Adapter.OPCDA.Da.OpcItem}})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            添加节点,需要在连接成功后执行
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="items">组名称/变量节点,注意每次添加的组名称不能相同</param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.AddItemsWithSave(System.Collections.Generic.List{System.String})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            设置节点并保存,每次重连会自动添加节点
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="items"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.Connect">
 | 
			
		||||
            <summary>
 | 
			
		||||
            连接服务器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.Disconnect">
 | 
			
		||||
            <summary>
 | 
			
		||||
            断开连接
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.GetBrowseElements(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            浏览节点
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="itemId"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.GetServerStatus">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取服务状态
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.Init(ThingsGateway.Foundation.Adapter.OPCDA.OPCNode)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            初始化设置
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="node"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.ReadItemsWithGroup(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            按OPC组读取组内变量,结果会在订阅事件中返回
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="groupName">组名称,值为null时读取全部组</param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.RemoveItems(System.Collections.Generic.List{System.String})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            移除节点
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="items"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.ToString">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.WriteItem(System.Collections.Generic.Dictionary{System.String,Newtonsoft.Json.Linq.JToken})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            批量写入值
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient.Dispose(System.Boolean)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCDA连接配置项
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode.ActiveSubscribe">
 | 
			
		||||
            <summary>
 | 
			
		||||
            是否订阅
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode.CheckRate">
 | 
			
		||||
            <summary>
 | 
			
		||||
            内部检测重连间隔/min
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode.DeadBand">
 | 
			
		||||
            <summary>
 | 
			
		||||
            死区
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode.GroupSize">
 | 
			
		||||
            <summary>
 | 
			
		||||
            分组大小
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode.OPCIP">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCIP
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode.OPCName">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCNAME
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode.UpdateRate">
 | 
			
		||||
            <summary>
 | 
			
		||||
            订阅间隔
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCDA.OPCNode.ToString">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
    </members>
 | 
			
		||||
</doc>
 | 
			
		||||
@@ -1,468 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<doc>
 | 
			
		||||
    <assembly>
 | 
			
		||||
        <name>ThingsGateway.Foundation.Adapter.OPCUA</name>
 | 
			
		||||
    </assembly>
 | 
			
		||||
    <members>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.CollectionExtensions.RemoveWhere``1(System.Collections.Generic.ICollection{``0},System.Func{``0,System.Boolean})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            移除符合条件的元素
 | 
			
		||||
            </summary>
 | 
			
		||||
            <typeparam name="T"></typeparam>
 | 
			
		||||
            <param name="this"></param>
 | 
			
		||||
            <param name="where"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.CollectionExtensions.SelectAsync``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,System.Threading.Tasks.Task{``1}})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            异步Select
 | 
			
		||||
            </summary>
 | 
			
		||||
            <typeparam name="T"></typeparam>
 | 
			
		||||
            <typeparam name="TResult"></typeparam>
 | 
			
		||||
            <param name="source"></param>
 | 
			
		||||
            <param name="selector"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils">
 | 
			
		||||
            <summary>
 | 
			
		||||
            辅助类
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.Browse(Opc.Ua.Client.ISession,Opc.Ua.BrowseDescriptionCollection,System.Boolean)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Browses the address space and returns the references found.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="session">The session.</param>
 | 
			
		||||
            <param name="nodesToBrowse">The set of browse operations to perform.</param>
 | 
			
		||||
            <param name="throwOnError">if set to <c>true</c> a exception will be thrown on an error.</param>
 | 
			
		||||
            <returns>
 | 
			
		||||
            The references found. Null if an error occurred.
 | 
			
		||||
            </returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.PrepareBrowseNext(Opc.Ua.BrowseResultCollection)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Create the continuation point collection from the browse result
 | 
			
		||||
            collection for the BrowseNext service.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="browseResultCollection">The browse result collection to use.</param>
 | 
			
		||||
            <returns>The collection of continuation points for the BrowseNext service.</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.BrowseAsync(Opc.Ua.Client.ISession,Opc.Ua.BrowseDescriptionCollection,System.Boolean,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            浏览地址空间
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="session"></param>
 | 
			
		||||
            <param name="nodesToBrowse"></param>
 | 
			
		||||
            <param name="throwOnError"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
            <exception cref="T:Opc.Ua.ServiceResultException"></exception>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.BrowseAsync(Opc.Ua.Client.ISession,Opc.Ua.BrowseDescription,System.Boolean,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            浏览地址空间
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="session"></param>
 | 
			
		||||
            <param name="nodeToBrowse"></param>
 | 
			
		||||
            <param name="throwOnError"></param>
 | 
			
		||||
            <param name="cancellationToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
            <exception cref="T:Opc.Ua.ServiceResultException"></exception>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.BrowseSuperTypesAsync(Opc.Ua.Client.ISession,Opc.Ua.NodeId,System.Boolean)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            浏览地址空间并返回指定类型的所有节点
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="session"></param>
 | 
			
		||||
            <param name="typeId"></param>
 | 
			
		||||
            <param name="throwOnError"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
            <exception cref="T:Opc.Ua.ServiceResultException"></exception>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.CollectFieldsForInstanceAsync(Opc.Ua.Client.ISession,Opc.Ua.NodeId,Opc.Ua.SimpleAttributeOperandCollection,System.Collections.Generic.List{Opc.Ua.NodeId})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Collects the fields for the instance.
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.CollectFieldsForType(Opc.Ua.Client.ISession,Opc.Ua.NodeId,Opc.Ua.SimpleAttributeOperandCollection,System.Collections.Generic.List{Opc.Ua.NodeId})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Collects the fields for the type.
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.ConstructEventAsync(Opc.Ua.Client.ISession,Opc.Ua.Client.MonitoredItem,Opc.Ua.EventFieldList,System.Collections.Generic.Dictionary{Opc.Ua.NodeId,System.Type},System.Collections.Generic.Dictionary{Opc.Ua.NodeId,Opc.Ua.NodeId})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Constructs an event object from a notification.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="session">The session.</param>
 | 
			
		||||
            <param name="monitoredItem">The monitored item that produced the notification.</param>
 | 
			
		||||
            <param name="notification">The notification.</param>
 | 
			
		||||
            <param name="knownEventTypes">The known event types.</param>
 | 
			
		||||
            <param name="eventTypeMappings">Mapping between event types and known event types.</param>
 | 
			
		||||
            <returns>
 | 
			
		||||
            The event object. Null if the notification is not a valid event type.
 | 
			
		||||
            </returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.DiscoverServers(Opc.Ua.ApplicationConfiguration)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Discovers the servers on the local machine.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="configuration">The configuration.</param>
 | 
			
		||||
            <returns>A list of server urls.</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.FindEventType(Opc.Ua.Client.MonitoredItem,Opc.Ua.EventFieldList)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Finds the type of the event for the notification.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="monitoredItem">The monitored item.</param>
 | 
			
		||||
            <param name="notification">The notification.</param>
 | 
			
		||||
            <returns>The NodeId of the EventType.</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.GetAttributeDisplayText(Opc.Ua.Client.ISession,System.UInt32,Opc.Ua.Variant)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            指定的属性的显示文本。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.SelectEndpoint(System.String,System.Boolean)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Finds the endpoint that best matches the current settings.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="discoveryUrl">The discovery URL.</param>
 | 
			
		||||
            <param name="useSecurity">if set to <c>true</c> select an endpoint that uses security.</param>
 | 
			
		||||
            <returns>The best available endpoint.</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.TranslateBrowsePaths(Opc.Ua.Client.ISession,Opc.Ua.NodeId,Opc.Ua.NamespaceTable,System.Threading.CancellationToken,System.String[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            返回一组相对路径的节点id
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.CollectFieldsAsync(Opc.Ua.Client.ISession,Opc.Ua.NodeId,Opc.Ua.QualifiedNameCollection,Opc.Ua.SimpleAttributeOperandCollection,System.Collections.Generic.List{Opc.Ua.NodeId},System.Collections.Generic.Dictionary{Opc.Ua.NodeId,Opc.Ua.QualifiedNameCollection})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Collects the fields for the instance node.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="session">The session.</param>
 | 
			
		||||
            <param name="nodeId">The node id.</param>
 | 
			
		||||
            <param name="parentPath">The parent path.</param>
 | 
			
		||||
            <param name="fields">The event fields.</param>
 | 
			
		||||
            <param name="fieldNodeIds">The node id for the declaration of the field.</param>
 | 
			
		||||
            <param name="foundNodes">The table of found nodes.</param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.ContainsPath(Opc.Ua.SimpleAttributeOperandCollection,Opc.Ua.QualifiedNameCollection)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            判断指定的select子句包含的浏览路径。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.GetAccessLevelDisplayText(System.Byte)">
 | 
			
		||||
             <summary>
 | 
			
		||||
            访问级别属性的显示文本。
 | 
			
		||||
             </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.FormUtils.GetEventNotifierDisplayText(System.Byte)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            事件通知属性的显示文本
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils">
 | 
			
		||||
            <summary>
 | 
			
		||||
            扩展方法
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.Decode(Opc.Ua.IServiceMessageContext,Opc.Ua.NodeId,Opc.Ua.BuiltInType,System.Int32,Newtonsoft.Json.Linq.JToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析获取DataValue
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.DecoderObject(Opc.Ua.IServiceMessageContext,Opc.Ua.NodeId,Opc.Ua.BuiltInType,System.Int32,Newtonsoft.Json.Linq.JToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析获取object
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.DecodeRawData(Opc.Ua.JsonDecoder,Opc.Ua.BuiltInType,System.Int32,System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DecodeRawData
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.Encode(Opc.Ua.IServiceMessageContext,Opc.Ua.BuiltInType,System.Object)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCUAValue解析为Jtoken
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="Context"></param>
 | 
			
		||||
            <param name="type"></param>
 | 
			
		||||
            <param name="value"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.CreateEncoder(Opc.Ua.IServiceMessageContext,System.IO.Stream,System.Boolean,System.Boolean,System.Boolean,System.Boolean)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            CreateEncoder
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.CalculateActualValueRank(Newtonsoft.Json.Linq.JToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            维度
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="jToken"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCUAClient配置项
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.OPCUrl">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCUrl
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.UserName">
 | 
			
		||||
            <summary>
 | 
			
		||||
            登录账号
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.Password">
 | 
			
		||||
            <summary>
 | 
			
		||||
            登录密码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.CheckDomain">
 | 
			
		||||
            <summary>
 | 
			
		||||
            检查域
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.UpdateRate">
 | 
			
		||||
            <summary>
 | 
			
		||||
            更新间隔
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.ActiveSubscribe">
 | 
			
		||||
            <summary>
 | 
			
		||||
            是否订阅
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.GroupSize">
 | 
			
		||||
            <summary>
 | 
			
		||||
            分组大小
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.DeadBand">
 | 
			
		||||
            <summary>
 | 
			
		||||
            死区
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.KeepAliveInterval">
 | 
			
		||||
            <summary>
 | 
			
		||||
            KeepAliveInterval/ms
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.IsUseSecurity">
 | 
			
		||||
            <summary>
 | 
			
		||||
            安全策略
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCNode.ToString">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCUA.DataChangedEventHandler">
 | 
			
		||||
            <summary>
 | 
			
		||||
            订阅委托
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="value"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCUAClient
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.OPCNode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前配置
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ProductUri">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ProductUri
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.Variables">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前保存的变量名称列表
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient._variableDicts">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前的变量名称/OPC变量节点
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.dic_subscriptions">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前的订阅组,组名称/组
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReConnectHandler">
 | 
			
		||||
            <summary>
 | 
			
		||||
            SessionReconnectHandler
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.#ctor(ThingsGateway.Foundation.Core.ILog)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            默认的构造函数,实例化一个新的OPC UA类
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="E:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.DataChangedHandler">
 | 
			
		||||
            <summary>
 | 
			
		||||
            订阅
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.AppConfig">
 | 
			
		||||
            <summary>
 | 
			
		||||
            配置信息
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.Connected">
 | 
			
		||||
            <summary>
 | 
			
		||||
            连接状态
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.OPCUAName">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OPCUAClient
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.Session">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前活动会话。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.AddSubscriptionAsync(System.String,System.String[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            新增订阅,需要指定订阅组名称,订阅的tag名数组
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.RemoveAllSubscription">
 | 
			
		||||
            <summary>
 | 
			
		||||
            移除所有的订阅消息
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.RemoveSubscription(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            移除订阅消息
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="subscriptionName">组名称</param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.BrowseNodeReferenceAsync(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            浏览一个节点的引用
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="tag">节点值</param>
 | 
			
		||||
            <returns>引用节点描述</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.CallMethodByNodeId(System.String,System.String,System.Object[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            调用服务器的方法
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="tagParent">方法的父节点tag</param>
 | 
			
		||||
            <param name="tag">方法的节点tag</param>
 | 
			
		||||
            <param name="args">传递的参数</param>
 | 
			
		||||
            <returns>输出的结果值</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadHistoryRawDataValues(System.String,System.DateTime,System.DateTime,System.UInt32,System.Boolean,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取历史数据
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="tag">节点的索引</param>
 | 
			
		||||
            <param name="start">开始时间</param>
 | 
			
		||||
            <param name="end">结束时间</param>
 | 
			
		||||
            <param name="count">读取的个数</param>
 | 
			
		||||
            <param name="containBound">是否包含边界</param>
 | 
			
		||||
            <param name="cancellationToken">cancellationToken</param>
 | 
			
		||||
            <returns>读取的数据列表</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ConnectAsync">
 | 
			
		||||
            <summary>
 | 
			
		||||
            连接到服务器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.Disconnect">
 | 
			
		||||
            <summary>
 | 
			
		||||
            断开连接。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ConnectAsync(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Creates a new session.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns>The new session object.</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadJTokenValueAsync(System.String[],System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            从服务器读取值
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.WriteNodeAsync(System.Collections.Generic.Dictionary{System.String,Newtonsoft.Json.Linq.JToken},System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            异步写opc标签
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadJTokenValueAsync(Opc.Ua.NodeId[],System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            从服务器读取值
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadNodeAsync(System.String,System.Boolean,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            从服务器或缓存读取节点
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadNoteAttributeAsync(System.String,System.UInt32,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取一个节点的所有属性
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadNoteAttributeAsync(System.Collections.Generic.List{System.String},System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取节点的所有属性
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.ReadNoteAttributes(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取一个节点的所有属性
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="tag">节点信息</param>
 | 
			
		||||
            <returns>节点的特性值</returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.Dispose(System.Boolean)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.Server_ReconnectComplete(System.Object,System.EventArgs)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            连接处理器连接事件处理完成。
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.OPCUA.OPCNodeAttribute">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取属性过程中用于描述的
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNodeAttribute.Name">
 | 
			
		||||
            <summary>
 | 
			
		||||
            属性的名称
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNodeAttribute.StatusCode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            操作结果状态描述
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNodeAttribute.Type">
 | 
			
		||||
            <summary>
 | 
			
		||||
            属性的类型描述
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.OPCUA.OPCNodeAttribute.Value">
 | 
			
		||||
            <summary>
 | 
			
		||||
            属性的值,如果读取错误,返回文本描述
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
    </members>
 | 
			
		||||
</doc>
 | 
			
		||||
@@ -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,336 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<doc>
 | 
			
		||||
    <assembly>
 | 
			
		||||
        <name>ThingsGateway.Foundation.Adapter.Siemens</name>
 | 
			
		||||
    </assembly>
 | 
			
		||||
    <members>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Siemens.S7Area">
 | 
			
		||||
            <summary>
 | 
			
		||||
            区域
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7Area.PE">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7Area.PA">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7Area.MK">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7Area.DB">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7Area.CT">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7Area.TM">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7Area.AI">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7Area.AQ">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress">
 | 
			
		||||
            <summary>
 | 
			
		||||
            西门子PLC地址数据信息
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.BitCode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            bit位偏移
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.DataCode">
 | 
			
		||||
            <summary>
 | 
			
		||||
            数据块代码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.DbBlock">
 | 
			
		||||
            <summary>
 | 
			
		||||
            DB块数据信息
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.GetAddressStart(System.String,System.Boolean)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取起始地址
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="address"></param>
 | 
			
		||||
            <param name="isCounterOrTimer"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.GetBitCode(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取bit
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="address"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.ParseFrom(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析地址
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="address"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.ParseFrom(System.String,System.Int32)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析地址
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="address"></param>
 | 
			
		||||
            <param name="length"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.Parse(System.String,System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensAddress.ToString">
 | 
			
		||||
            <inheritdoc />
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Siemens.DateTime">
 | 
			
		||||
            <summary>
 | 
			
		||||
            https://github.com/S7NetPlus/s7netplus/blob/develop/S7.Net/Types/DateTime.cs
 | 
			
		||||
            Contains the methods to convert between <see cref="T:System.DateTime"/> and S7 representation of datetime values.
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMaximumDateTime">
 | 
			
		||||
            <summary>
 | 
			
		||||
            The maximum <see cref="T:System.DateTime"/> value supported by the specification.
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime">
 | 
			
		||||
            <summary>
 | 
			
		||||
            The minimum <see cref="T:System.DateTime"/> value supported by the specification.
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.DateTime.FromByteArray(System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Parses a <see cref="T:System.DateTime"/> value from bytes.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="bytes">Input bytes read from PLC.</param>
 | 
			
		||||
            <returns>A <see cref="T:System.DateTime"/> object representing the value read from PLC.</returns>
 | 
			
		||||
            <exception cref="T:System.ArgumentOutOfRangeException">Thrown when the length of
 | 
			
		||||
              <paramref name="bytes"/> is not 8 or any value in <paramref name="bytes"/>
 | 
			
		||||
              is outside the valid range of values.</exception>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.DateTime.ToArray(System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Parses an array of <see cref="T:System.DateTime"/> values from bytes.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="bytes">Input bytes read from PLC.</param>
 | 
			
		||||
            <returns>An array of <see cref="T:System.DateTime"/> objects representing the values read from PLC.</returns>
 | 
			
		||||
            <exception cref="T:System.ArgumentOutOfRangeException">Thrown when the length of
 | 
			
		||||
              <paramref name="bytes"/> is not a multiple of 8 or any value in
 | 
			
		||||
              <paramref name="bytes"/> is outside the valid range of values.</exception>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.DateTime.ToByteArray(System.DateTime)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Converts a <see cref="T:System.DateTime"/> value to a byte array.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="dateTime">The DateTime value to convert.</param>
 | 
			
		||||
            <returns>A byte array containing the S7 date time representation of <paramref name="dateTime"/>.</returns>
 | 
			
		||||
            <exception cref="T:System.ArgumentOutOfRangeException">Thrown when the value of
 | 
			
		||||
              <paramref name="dateTime"/> is before <see cref="P:SpecMinimumDateTime"/>
 | 
			
		||||
              or after <see cref="P:SpecMaximumDateTime"/>.</exception>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.DateTime.ToByteArray(System.DateTime[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Converts an array of <see cref="T:System.DateTime"/> values to a byte array.
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="dateTimes">The DateTime values to convert.</param>
 | 
			
		||||
            <returns>A byte array containing the S7 date time representations of <paramref name="dateTimes"/>.</returns>
 | 
			
		||||
            <exception cref="T:System.ArgumentOutOfRangeException">Thrown when any value of
 | 
			
		||||
              <paramref name="dateTimes"/> is before <see cref="P:SpecMinimumDateTime"/>
 | 
			
		||||
              or after <see cref="P:SpecMaximumDateTime"/>.</exception>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.SiemensHelper.S7_MULRW_HEADER">
 | 
			
		||||
            <summary>
 | 
			
		||||
            S7连读写请求头(包含ISO头和COTP头) 
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.Bit">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.Byte">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.Char">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.Word">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.Int">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.DWord">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.DInt">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.Real">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.Counter">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.S7WordLength.Timer">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Siemens.SiemensMessage">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensMessage.HeadBytesLength">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensMessage.CheckHeadBytes(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Siemens.SiemensEnum">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.SiemensEnum.S200">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.SiemensEnum.S200Smart">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.SiemensEnum.S300">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.SiemensEnum.S400">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.SiemensEnum.S1200">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Siemens.SiemensEnum.S1500">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC">
 | 
			
		||||
            <summary>
 | 
			
		||||
            相关命令含义源自网络资料/Shrap7/s7netplus
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.#ctor(ThingsGateway.Foundation.Core.TcpClient,ThingsGateway.Foundation.Adapter.Siemens.SiemensEnum)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            传入PLC类型,程序内会改变相应PLC类型的S7协议LocalTSAP, RemoteTSAP等
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="tcpClient"></param>
 | 
			
		||||
            <param name="siemensPLCEnum"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.CurrentPlc">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前PLC类型
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.GetBitOffset(System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="address"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.DestTSAP">
 | 
			
		||||
            <summary>
 | 
			
		||||
            远程TSAP,需重新连接
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.LocalTSAP">
 | 
			
		||||
            <summary>
 | 
			
		||||
            本地TSAP,需重新连接
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.PDULength">
 | 
			
		||||
            <summary>
 | 
			
		||||
            PDULength
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.Rack">
 | 
			
		||||
            <summary>
 | 
			
		||||
            机架号,需重新连接
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.Slot">
 | 
			
		||||
            <summary>
 | 
			
		||||
            槽号,需重新连接
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.ReadDateAsync(System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取日期
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.ReadDateTimeAsync(System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取时间
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.ReadStringAsync(System.String,System.Text.Encoding,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取变长字符串
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.SetDataAdapter">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.WriteAsync(System.String,System.String,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.WriteDateAsync(System.String,System.DateTime,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写入日期
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLC.WriteDateTimeAsync(System.String,System.DateTime,System.Threading.CancellationToken)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写入时间
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLCDataHandleAdapter">
 | 
			
		||||
            <summary>
 | 
			
		||||
            SiemensS7PLCDataHandleAdapter
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLCDataHandleAdapter.PackCommand(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLCDataHandleAdapter.GetInstance">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Siemens.SiemensS7PLCDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Siemens.SiemensMessage,System.Byte[],System.Byte[],System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
    </members>
 | 
			
		||||
</doc>
 | 
			
		||||
@@ -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,127 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 服务设备
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class ReadWriteDevicesTcpServerBase : ReadWriteDevicesBase
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="ReadWriteDevicesTcpServerBase"/>
 | 
			
		||||
    public ReadWriteDevicesTcpServerBase(TcpService tcpService)
 | 
			
		||||
    {
 | 
			
		||||
        TcpService = tcpService;
 | 
			
		||||
        TcpService.Connecting += Connecting;
 | 
			
		||||
        TcpService.Connected += Connected;
 | 
			
		||||
        TcpService.Received += Received;
 | 
			
		||||
        TcpService.Disconnecting += Disconnecting;
 | 
			
		||||
        TcpService.Disconnected += Disconnected;
 | 
			
		||||
        Logger = TcpService.Logger;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 连接超时时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("连接超时时间")]
 | 
			
		||||
    public ushort ConnectTimeOut { get; set; } = 3000;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 服务管理对象
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public TcpService TcpService { get; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Connect(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        TcpService.Start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task ConnectAsync(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.Run(() => TcpService.Start());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Disconnect()
 | 
			
		||||
    {
 | 
			
		||||
        if (CascadeDisposal)
 | 
			
		||||
            TcpService.Stop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        TcpService.Connecting -= Connecting;
 | 
			
		||||
        TcpService.Connected -= Connected;
 | 
			
		||||
        TcpService.Disconnecting -= Disconnecting;
 | 
			
		||||
        TcpService.Disconnected -= Disconnected;
 | 
			
		||||
        Disconnect();
 | 
			
		||||
        if (CascadeDisposal)
 | 
			
		||||
            TcpService.SafeDispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return TcpService.ServerName;
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接收解析
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected virtual void Received(SocketClient client, IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="client"></param>
 | 
			
		||||
    /// <param name="e"></param>
 | 
			
		||||
    protected virtual void Connected(SocketClient client, ConnectedEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        Logger?.Debug(client.IP + ":" + client.Port + "连接成功");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected virtual void Connecting(SocketClient client, ConnectingEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        Logger?.Debug(client.IP + ":" + client.Port + "正在连接");
 | 
			
		||||
        SetDataAdapter(client);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected virtual void Disconnected(ITcpClientBase client, DisconnectEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected virtual void Disconnecting(ITcpClientBase client, DisconnectEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Received(SocketClient client, ByteBlock byteBlock, IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Received(client, requestInfo);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Logger.Exception(this, ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,23 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='net45'">
 | 
			
		||||
		<Reference Include="Microsoft.CSharp" />
 | 
			
		||||
		<Reference Include="System.Web" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
 | 
			
		||||
		<PackageReference Include="Microsoft.CSharp" Version="4.7.0" PrivateAssets="All" />
 | 
			
		||||
		<PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.7.0" />
 | 
			
		||||
		<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
	
 | 
			
		||||
	<ItemGroup Condition="'$(TargetFramework)'!='net45'">
 | 
			
		||||
		<PackageReference Include="System.IO.Ports" Version="7.0.0" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
 | 
			
		||||
@@ -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,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.Foundation.Dmtp
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// TcpDmtpAdapter
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class TcpDmtpAdapter : CustomFixedHeaderByteBlockDataHandlingAdapter<DmtpMessage>
 | 
			
		||||
    {
 | 
			
		||||
        private SpinLock m_locker = new SpinLock();
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override bool CanSendRequestInfo => true;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override bool CanSplicingSend => false;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override int HeaderLength => 6;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override DmtpMessage GetInstance()
 | 
			
		||||
        {
 | 
			
		||||
            return new DmtpMessage();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override void OnReceivedSuccess(DmtpMessage request)
 | 
			
		||||
        {
 | 
			
		||||
            request.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override void PreviewSend(IRequestInfo requestInfo)
 | 
			
		||||
        {
 | 
			
		||||
            if (!(requestInfo is DmtpMessage message))
 | 
			
		||||
            {
 | 
			
		||||
                throw new Exception($"无法将{nameof(requestInfo)}转换为{nameof(DmtpMessage)}");
 | 
			
		||||
            }
 | 
			
		||||
            if (message.BodyByteBlock != null && message.BodyByteBlock.Length > this.MaxPackageSize)
 | 
			
		||||
            {
 | 
			
		||||
                throw new Exception("发送的BodyLength={requestInfo.BodyLength},大于设定的MaxPackageSize={this.MaxPackageSize}");
 | 
			
		||||
            }
 | 
			
		||||
            using (var byteBlock = new ByteBlock(message.GetLength()))
 | 
			
		||||
            {
 | 
			
		||||
                message.Build(byteBlock);
 | 
			
		||||
                this.GoSend(byteBlock.Buffer, 0, byteBlock.Len);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override void PreviewSend(IList<ArraySegment<byte>> transferBytes)
 | 
			
		||||
        {
 | 
			
		||||
            if (transferBytes.Count == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var length = 0;
 | 
			
		||||
            foreach (var item in transferBytes)
 | 
			
		||||
            {
 | 
			
		||||
                length += item.Count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (length > this.MaxPackageSize)
 | 
			
		||||
            {
 | 
			
		||||
                throw new Exception("发送数据大于设定值,相同解析器可能无法收到有效数据,已终止发送");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var lockTaken = false;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                this.m_locker.Enter(ref lockTaken);
 | 
			
		||||
                foreach (var item in transferBytes)
 | 
			
		||||
                {
 | 
			
		||||
                    this.GoSend(item.Array, item.Offset, item.Count);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                if (lockTaken)
 | 
			
		||||
                {
 | 
			
		||||
                    this.m_locker.Exit(false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,391 +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.Dmtp
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// HttpDmtpClient
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public partial class HttpDmtpClient : HttpClientBase, IHttpDmtpClient
 | 
			
		||||
    {
 | 
			
		||||
        #region 字段
 | 
			
		||||
 | 
			
		||||
        private bool m_allowRoute;
 | 
			
		||||
        private Func<string, IDmtpActor> m_findDmtpActor;
 | 
			
		||||
        private DmtpActor m_smtpActor;
 | 
			
		||||
        private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1);
 | 
			
		||||
        #endregion 字段
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc cref="IDmtpActor.Id"/>
 | 
			
		||||
        public string Id => this.DmtpActor.Id;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc cref="IDmtpActor.IsHandshaked"/>
 | 
			
		||||
        public bool IsHandshaked => this.DmtpActor != null && this.DmtpActor.IsHandshaked;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public IDmtpActor DmtpActor { get => this.m_smtpActor; }
 | 
			
		||||
 | 
			
		||||
        #region 连接
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 建立Tcp连接,并且执行握手。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="timeout"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        /// <exception cref="Exception"></exception>
 | 
			
		||||
        public override ITcpClient Connect(int timeout = 5000)
 | 
			
		||||
        {
 | 
			
		||||
            lock (this.SyncRoot)
 | 
			
		||||
            {
 | 
			
		||||
                if (this.IsHandshaked)
 | 
			
		||||
                {
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
                if (!this.Online)
 | 
			
		||||
                {
 | 
			
		||||
                    base.Connect(timeout);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var request = new HttpRequest()
 | 
			
		||||
                    .SetHost(this.RemoteIPHost.Host);
 | 
			
		||||
                request.Headers.Add(HttpHeaders.Connection, "upgrade");
 | 
			
		||||
                request.Headers.Add(HttpHeaders.Upgrade, DmtpUtility.Dmtp.ToLower());
 | 
			
		||||
 | 
			
		||||
                request.AsMethod(DmtpUtility.Dmtp);
 | 
			
		||||
                var response = this.RequestContent(request);
 | 
			
		||||
                if (response.StatusCode == 101)
 | 
			
		||||
                {
 | 
			
		||||
                    this.SwitchProtocolToDmtp();
 | 
			
		||||
                    this.m_smtpActor.Handshake(this.Config.GetValue(DmtpConfigExtension.VerifyTokenProperty),
 | 
			
		||||
                        this.Config.GetValue(DmtpConfigExtension.DefaultIdProperty),
 | 
			
		||||
                        timeout, this.Config.GetValue(DmtpConfigExtension.MetadataProperty), CancellationToken.None);
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    throw new Exception(response.StatusMessage);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public virtual IHttpDmtpClient Connect(CancellationToken token, int timeout = 5000)
 | 
			
		||||
        {
 | 
			
		||||
            lock (this.SyncRoot)
 | 
			
		||||
            {
 | 
			
		||||
                if (this.IsHandshaked)
 | 
			
		||||
                {
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
                if (!this.Online)
 | 
			
		||||
                {
 | 
			
		||||
                    base.Connect(timeout);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var request = new HttpRequest()
 | 
			
		||||
                    .SetHost(this.RemoteIPHost.Host);
 | 
			
		||||
                request.Headers.Add(HttpHeaders.Connection, "upgrade");
 | 
			
		||||
                request.Headers.Add(HttpHeaders.Upgrade, DmtpUtility.Dmtp.ToLower());
 | 
			
		||||
 | 
			
		||||
                request.AsMethod(DmtpUtility.Dmtp);
 | 
			
		||||
                var response = this.RequestContent(request, timeout: timeout, token: token);
 | 
			
		||||
                if (response.StatusCode == 101)
 | 
			
		||||
                {
 | 
			
		||||
                    this.SwitchProtocolToDmtp();
 | 
			
		||||
                    this.m_smtpActor.Handshake(this.Config.GetValue(DmtpConfigExtension.VerifyTokenProperty),
 | 
			
		||||
                        this.Config.GetValue(DmtpConfigExtension.DefaultIdProperty),
 | 
			
		||||
                        timeout, this.Config.GetValue(DmtpConfigExtension.MetadataProperty), token);
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    throw new Exception(response.StatusMessage);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 建立Tcp连接,并且执行握手。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="timeout"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        /// <exception cref="Exception"></exception>
 | 
			
		||||
        public override async Task<ITcpClient> ConnectAsync(int timeout = 5000)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await this.m_semaphore.WaitAsync();
 | 
			
		||||
                if (this.IsHandshaked)
 | 
			
		||||
                {
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
                if (!this.Online)
 | 
			
		||||
                {
 | 
			
		||||
                    await base.ConnectAsync(timeout);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var request = new HttpRequest()
 | 
			
		||||
                    .SetHost(this.RemoteIPHost.Host);
 | 
			
		||||
                request.Headers.Add(HttpHeaders.Connection, "upgrade");
 | 
			
		||||
                request.Headers.Add(HttpHeaders.Upgrade, DmtpUtility.Dmtp.ToLower());
 | 
			
		||||
 | 
			
		||||
                request.AsMethod(DmtpUtility.Dmtp);
 | 
			
		||||
                var response = this.RequestContent(request);
 | 
			
		||||
                if (response.StatusCode == 101)
 | 
			
		||||
                {
 | 
			
		||||
                    this.SwitchProtocolToDmtp();
 | 
			
		||||
                    await this.m_smtpActor.HandshakeAsync(this.Config.GetValue(DmtpConfigExtension.VerifyTokenProperty),
 | 
			
		||||
                         this.Config.GetValue(DmtpConfigExtension.DefaultIdProperty),
 | 
			
		||||
                         timeout, this.Config.GetValue(DmtpConfigExtension.MetadataProperty), CancellationToken.None);
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    throw new Exception(response.StatusMessage);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                this.m_semaphore.Release();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public virtual async Task<IHttpDmtpClient> ConnectAsync(CancellationToken token, int timeout = 5000)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await this.m_semaphore.WaitAsync();
 | 
			
		||||
                if (this.IsHandshaked)
 | 
			
		||||
                {
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
                if (!this.Online)
 | 
			
		||||
                {
 | 
			
		||||
                    await base.ConnectAsync(timeout);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var request = new HttpRequest()
 | 
			
		||||
                    .SetHost(this.RemoteIPHost.Host);
 | 
			
		||||
                request.Headers.Add(HttpHeaders.Connection, "upgrade");
 | 
			
		||||
                request.Headers.Add(HttpHeaders.Upgrade, DmtpUtility.Dmtp.ToLower());
 | 
			
		||||
 | 
			
		||||
                request.AsMethod(DmtpUtility.Dmtp);
 | 
			
		||||
                var response = this.RequestContent(request, timeout: timeout, token: token);
 | 
			
		||||
                if (response.StatusCode == 101)
 | 
			
		||||
                {
 | 
			
		||||
                    this.SwitchProtocolToDmtp();
 | 
			
		||||
                    await this.m_smtpActor.HandshakeAsync(this.Config.GetValue(DmtpConfigExtension.VerifyTokenProperty),
 | 
			
		||||
                         this.Config.GetValue(DmtpConfigExtension.DefaultIdProperty),
 | 
			
		||||
                         timeout, this.Config.GetValue(DmtpConfigExtension.MetadataProperty), token);
 | 
			
		||||
                    return this;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    throw new Exception(response.StatusMessage);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                this.m_semaphore.Release();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion 连接
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override void Dispose(bool disposing)
 | 
			
		||||
        {
 | 
			
		||||
            this.DmtpActor.SafeDispose();
 | 
			
		||||
            base.Dispose(disposing);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
 | 
			
		||||
        {
 | 
			
		||||
            if (this.Protocol == DmtpUtility.DmtpProtocol && requestInfo is DmtpMessage message)
 | 
			
		||||
            {
 | 
			
		||||
                if (!this.m_smtpActor.InputReceivedData(message))
 | 
			
		||||
                {
 | 
			
		||||
                    if (this.PluginsManager.Enable)
 | 
			
		||||
                    {
 | 
			
		||||
                        this.PluginsManager.Raise(nameof(IDmtpReceivedPlugin.OnDmtpReceived), this, new DmtpMessageEventArgs(message));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            return base.HandleReceivedData(byteBlock, requestInfo);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override void LoadConfig(TouchSocketConfig config)
 | 
			
		||||
        {
 | 
			
		||||
            base.LoadConfig(config);
 | 
			
		||||
            if (this.Container.IsRegistered(typeof(IDmtpRouteService)))
 | 
			
		||||
            {
 | 
			
		||||
                this.m_allowRoute = true;
 | 
			
		||||
                this.m_findDmtpActor = this.Container.Resolve<IDmtpRouteService>().FindDmtpActor;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override void OnDisconnected(DisconnectEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            base.OnDisconnected(e);
 | 
			
		||||
            this.DmtpActor.Close(false, e.Message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #region ResetId
 | 
			
		||||
 | 
			
		||||
        ///<inheritdoc cref="IDmtpActor.ResetId(string)"/>
 | 
			
		||||
        public void ResetId(string id)
 | 
			
		||||
        {
 | 
			
		||||
            this.m_smtpActor.ResetId(id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ///<inheritdoc cref="IDmtpActor.ResetIdAsync(string)"/>
 | 
			
		||||
        public Task ResetIdAsync(string newId)
 | 
			
		||||
        {
 | 
			
		||||
            return this.m_smtpActor.ResetIdAsync(newId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion ResetId
 | 
			
		||||
 | 
			
		||||
        private void SwitchProtocolToDmtp()
 | 
			
		||||
        {
 | 
			
		||||
            this.Protocol = DmtpUtility.DmtpProtocol;
 | 
			
		||||
            this.SetDataHandlingAdapter(new TcpDmtpAdapter());
 | 
			
		||||
            this.m_smtpActor = new SealedDmtpActor(this.m_allowRoute)
 | 
			
		||||
            {
 | 
			
		||||
                OutputSend = DmtpActorSend,
 | 
			
		||||
                OnRouting = OnDmtpActorRouting,
 | 
			
		||||
                OnHandshaking = this.OnDmtpActorHandshaking,
 | 
			
		||||
                OnHandshaked = OnDmtpActorHandshaked,
 | 
			
		||||
                OnClose = OnDmtpActorClose,
 | 
			
		||||
                OnCreateChannel = this.OnDmtpActorCreateChannel,
 | 
			
		||||
                Logger = this.Logger,
 | 
			
		||||
                Client = this,
 | 
			
		||||
                OnFindDmtpActor = this.m_findDmtpActor
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #region 内部委托绑定
 | 
			
		||||
 | 
			
		||||
        private void OnDmtpActorClose(DmtpActor actor, string msg)
 | 
			
		||||
        {
 | 
			
		||||
            base.Close(msg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnDmtpActorCreateChannel(DmtpActor actor, CreateChannelEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            this.OnCreateChannel(e);
 | 
			
		||||
            if (e.Handled)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.PluginsManager.Raise(nameof(IDmtpCreateChannelPlugin.OnCreateChannel), this, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnDmtpActorHandshaked(DmtpActor actor, DmtpVerifyEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            this.OnHandshaked(e);
 | 
			
		||||
            if (e.Handled)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(IDmtpHandshakedPlugin.OnDmtpHandshaked), this, e))
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnDmtpActorHandshaking(DmtpActor actor, DmtpVerifyEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            this.OnHandshaking(e);
 | 
			
		||||
            if (e.Handled)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            this.PluginsManager.Raise(nameof(IDmtpHandshakingPlugin.OnDmtpHandshaking), this, e);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnDmtpActorRouting(DmtpActor actor, PackageRouterEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            this.OnRouting(e);
 | 
			
		||||
            if (e.Handled)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(IDmtpRoutingPlugin.OnDmtpRouting), this, e))
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void DmtpActorSend(DmtpActor actor, ArraySegment<byte>[] transferBytes)
 | 
			
		||||
        {
 | 
			
		||||
            base.Send(transferBytes);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion 内部委托绑定
 | 
			
		||||
 | 
			
		||||
        #region 事件触发
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当创建通道
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="e"></param>
 | 
			
		||||
        protected virtual void OnCreateChannel(CreateChannelEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 在完成握手连接时
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="e"></param>
 | 
			
		||||
        protected virtual void OnHandshaked(DmtpVerifyEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 即将握手连接时
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="e">参数</param>
 | 
			
		||||
        protected virtual void OnHandshaking(DmtpVerifyEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当需要转发路由包时
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="e"></param>
 | 
			
		||||
        protected virtual void OnRouting(PackageRouterEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion 事件触发
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,36 +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.Dmtp
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 基于Dmtp协议的Tcp客户端接口
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public interface ITcpDmtpClient : ITcpDmtpClientBase, ITcpClient
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 建立Tcp连接,并且执行握手。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="token">可取消令箭</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        ITcpDmtpClient Connect(CancellationToken token, int timeout = 5000);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 建立Tcp连接,并且执行握手。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="token">可取消令箭</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        Task<ITcpDmtpClient> ConnectAsync(CancellationToken token, int timeout = 5000);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,246 +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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="byteBlock"></param>
 | 
			
		||||
        /// <param name="requestInfo"></param>
 | 
			
		||||
        protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
 | 
			
		||||
        {
 | 
			
		||||
            if (requestInfo is HttpResponse response)
 | 
			
		||||
            {
 | 
			
		||||
                if (this.m_getContent)
 | 
			
		||||
                {
 | 
			
		||||
                    response.TryGetContent(out _);
 | 
			
		||||
                }
 | 
			
		||||
                this.m_waitData.Set(response);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="e"></param>
 | 
			
		||||
        protected override void OnConnecting(ConnectingEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            this.Protocol = Protocol.Http;
 | 
			
		||||
            this.SetDataHandlingAdapter(new HttpClientDataHandlingAdapter());
 | 
			
		||||
            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,35 +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.Serial;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 串口基接口
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ISerial : IDisposable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送缓存区大小。最小值=1024。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    int SendBufferSize { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接收缓存区大小。最小值=1024。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    int ReceiveBufferSize { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志记录器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    ILog Logger { get; set; }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,41 +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.Serial;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 通讯基类
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class BaseSerial : DependencyObject, ISerial
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 同步根。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected readonly object SyncRoot = new object();
 | 
			
		||||
    private int m_receiveBufferSize = 1024 * 64;
 | 
			
		||||
    private int m_sendBufferSize = 1024 * 64;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual int SendBufferSize
 | 
			
		||||
    {
 | 
			
		||||
        get => m_sendBufferSize;
 | 
			
		||||
        set => m_sendBufferSize = value < 1024 ? 1024 : value;
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual int ReceiveBufferSize
 | 
			
		||||
    {
 | 
			
		||||
        get => m_receiveBufferSize;
 | 
			
		||||
        set => m_receiveBufferSize = value < 1024 ? 1024 : value;
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ILog Logger { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,931 +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.IO.Ports;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Serial;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="SerialSessionBase"/>
 | 
			
		||||
public class SerialSession : SerialSessionBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接收到数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ReceivedEventHandler<SerialSession> Received { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接收数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="byteBlock"></param>
 | 
			
		||||
    /// <param name="requestInfo"></param>
 | 
			
		||||
    protected override bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        this.Received?.Invoke(this, byteBlock, requestInfo);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 串口管理
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SerialSessionBase : BaseSerial, ISerialSession
 | 
			
		||||
{
 | 
			
		||||
    static readonly Protocol SerialPort = new("SerialSession");
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 构造函数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public SerialSessionBase()
 | 
			
		||||
    {
 | 
			
		||||
        this.Protocol = SerialPort;
 | 
			
		||||
        this.m_receiveCounter = new ValueCounter
 | 
			
		||||
        {
 | 
			
		||||
            Period = TimeSpan.FromSeconds(1),
 | 
			
		||||
            OnPeriod = this.OnReceivePeriod
 | 
			
		||||
        };
 | 
			
		||||
        this.m_sendCounter = new ValueCounter
 | 
			
		||||
        {
 | 
			
		||||
            Period = TimeSpan.FromSeconds(1),
 | 
			
		||||
            OnPeriod = this.OnSendPeriod
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #region 变量
 | 
			
		||||
 | 
			
		||||
    private DelaySender m_delaySender;
 | 
			
		||||
    private long m_bufferRate = 1;
 | 
			
		||||
    private bool m_online => MainSerialPort?.IsOpen == true;
 | 
			
		||||
    private ValueCounter m_receiveCounter;
 | 
			
		||||
    private ValueCounter m_sendCounter;
 | 
			
		||||
 | 
			
		||||
    #endregion 变量
 | 
			
		||||
 | 
			
		||||
    #region 事件
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ConnectedEventHandler<ISerialSession> Connected { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public SerialConnectingEventHandler<ISerialSession> Connecting { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public DisconnectEventHandler<ISerialSessionBase> Disconnected { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public DisconnectEventHandler<ISerialSessionBase> Disconnecting { get; set; }
 | 
			
		||||
 | 
			
		||||
    private void PrivateOnConnected(object o)
 | 
			
		||||
    {
 | 
			
		||||
        var e = (ConnectedEventArgs)o;
 | 
			
		||||
        this.OnConnected(e);
 | 
			
		||||
        if (e.Handled)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.PluginsManager.Raise(nameof(ITcpConnectedPlugin.OnTcpConnected), this, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 已经建立Tcp连接
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="e"></param>
 | 
			
		||||
    protected virtual void OnConnected(ConnectedEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            this.Connected?.Invoke(this, e);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Connected)}中发生错误。", ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void PrivateOnConnecting(SerialConnectingEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        if (this.CanSetDataHandlingAdapter)
 | 
			
		||||
        {
 | 
			
		||||
            this.SetDataHandlingAdapter(this.Config.GetValue(TouchSocketConfigExtension.TcpDataHandlingAdapterProperty).Invoke());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.OnConnecting(e);
 | 
			
		||||
        if (e.Handled)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.PluginsManager.Raise(nameof(ITcpConnectingPlugin.OnTcpConnecting), this, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 准备连接的时候,此时已初始化Socket,但是并未建立Tcp连接
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="e"></param>
 | 
			
		||||
    protected virtual void OnConnecting(SerialConnectingEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            this.Connecting?.Invoke(this, e);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.OnConnecting)}中发生错误。", ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void PrivateOnDisconnected(DisconnectEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        this.OnDisconnected(e);
 | 
			
		||||
        if (e.Handled)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.PluginsManager.Raise(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 断开连接。在客户端未设置连接状态时,不会触发
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="e"></param>
 | 
			
		||||
    protected virtual void OnDisconnected(DisconnectEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            this.Disconnected?.Invoke(this, e);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Disconnected)}中发生错误。", ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void PrivateOnDisconnecting(DisconnectEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        this.OnDisconnecting(e);
 | 
			
		||||
        if (e.Handled)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this.PluginsManager.Raise(nameof(ITcpDisconnectingPlugin.OnTcpDisconnecting), this, e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 即将断开连接(仅主动断开时有效)。
 | 
			
		||||
    /// <para>
 | 
			
		||||
    /// 当主动调用Close断开时。
 | 
			
		||||
    /// </para>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="e"></param>
 | 
			
		||||
    protected virtual void OnDisconnecting(DisconnectEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            this.Disconnecting?.Invoke(this, e);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            this.Logger.Log(LogLevel.Error, this, $"在事件{nameof(this.Disconnecting)}中发生错误。", ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 事件
 | 
			
		||||
 | 
			
		||||
    #region 属性
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public DateTime LastReceivedTime => this.m_receiveCounter.LastIncrement;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public DateTime LastSendTime => this.m_sendCounter.LastIncrement;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public Func<ByteBlock, bool> OnHandleRawBuffer { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public Func<ByteBlock, IRequestInfo, bool> OnHandleReceivedData { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IContainer Container { get; private set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual bool CanSetDataHandlingAdapter => true;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public TouchSocketConfig Config { get; private set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public SingleStreamDataHandlingAdapter DataHandlingAdapter { get; private set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public SerialProperty SerialProperty { get; private set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public SerialPort MainSerialPort { get; private set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public bool Online { get => this.m_online; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public bool CanSend => this.m_online;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IPluginsManager PluginsManager { get; private set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ReceiveType ReceiveType { get; private set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public Protocol Protocol { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #endregion 属性
 | 
			
		||||
 | 
			
		||||
    #region 断开操作
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual void Close(string msg = TouchSocketCoreUtility.Empty)
 | 
			
		||||
    {
 | 
			
		||||
        lock (this.SyncRoot)
 | 
			
		||||
        {
 | 
			
		||||
            if (this.m_online)
 | 
			
		||||
            {
 | 
			
		||||
                this.PrivateOnDisconnecting(new DisconnectEventArgs(true, msg));
 | 
			
		||||
 | 
			
		||||
                this.MainSerialPort.TryClose();
 | 
			
		||||
 | 
			
		||||
                this.MainSerialPort.SafeDispose();
 | 
			
		||||
                this.m_delaySender.SafeDispose();
 | 
			
		||||
                this.DataHandlingAdapter.SafeDispose();
 | 
			
		||||
                this.PrivateOnDisconnected(new DisconnectEventArgs(true, msg));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void BreakOut(string msg)
 | 
			
		||||
    {
 | 
			
		||||
        lock (this.SyncRoot)
 | 
			
		||||
        {
 | 
			
		||||
            if (this.m_online)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                this.MainSerialPort.SafeDispose();
 | 
			
		||||
                this.m_delaySender.SafeDispose();
 | 
			
		||||
                this.DataHandlingAdapter.SafeDispose();
 | 
			
		||||
                this.PrivateOnDisconnected(new DisconnectEventArgs(false, msg));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="disposing"></param>
 | 
			
		||||
    protected override void Dispose(bool disposing)
 | 
			
		||||
    {
 | 
			
		||||
        lock (this.SyncRoot)
 | 
			
		||||
        {
 | 
			
		||||
            if (this.m_online)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                this.MainSerialPort.TryClose();
 | 
			
		||||
                this.PrivateOnDisconnecting(new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开"));
 | 
			
		||||
 | 
			
		||||
                this.MainSerialPort.SafeDispose();
 | 
			
		||||
                this.m_delaySender.SafeDispose();
 | 
			
		||||
                this.DataHandlingAdapter.SafeDispose();
 | 
			
		||||
                this.PluginsManager.SafeDispose();
 | 
			
		||||
                this.PrivateOnDisconnected(new DisconnectEventArgs(true, $"{nameof(Dispose)}主动断开"));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        base.Dispose(disposing);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 断开操作
 | 
			
		||||
 | 
			
		||||
    #region Connect
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 打开串口
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected void Open()
 | 
			
		||||
    {
 | 
			
		||||
        lock (this.SyncRoot)
 | 
			
		||||
        {
 | 
			
		||||
            if (this.m_online)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.DisposedValue)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ObjectDisposedException(this.GetType().FullName);
 | 
			
		||||
            }
 | 
			
		||||
            if (this.Config == null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException("配置文件不能为空。");
 | 
			
		||||
            }
 | 
			
		||||
            var serialProperty = this.Config.GetValue(SerialConfigExtension.SerialProperty) ?? throw new ArgumentNullException("串口配置不能为空。");
 | 
			
		||||
 | 
			
		||||
            this.MainSerialPort.SafeDispose();
 | 
			
		||||
            var serialPort = CreateSerial(serialProperty);
 | 
			
		||||
            var args = new SerialConnectingEventArgs(this.MainSerialPort);
 | 
			
		||||
            this.PrivateOnConnecting(args);
 | 
			
		||||
            serialPort.Open();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            this.SetSerialPort(serialPort);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            this.BeginReceive();
 | 
			
		||||
            this.PrivateOnConnected(new ConnectedEventArgs());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual ISerialSession Connect()
 | 
			
		||||
    {
 | 
			
		||||
        this.Open();
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public Task<ISerialSession> ConnectAsync()
 | 
			
		||||
    {
 | 
			
		||||
        return Task.Run(() =>
 | 
			
		||||
        {
 | 
			
		||||
            return this.Connect();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    private void OnReceivePeriod(long value)
 | 
			
		||||
    {
 | 
			
		||||
        this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnSendPeriod(long value)
 | 
			
		||||
    {
 | 
			
		||||
        this.SendBufferSize = TouchSocketUtility.HitBufferLength(value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override int ReceiveBufferSize
 | 
			
		||||
    {
 | 
			
		||||
        get => base.ReceiveBufferSize;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            base.ReceiveBufferSize = value;
 | 
			
		||||
            if (this.MainSerialPort != null && !MainSerialPort.IsOpen)
 | 
			
		||||
            {
 | 
			
		||||
                this.MainSerialPort.ReadBufferSize = base.ReceiveBufferSize;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override int SendBufferSize
 | 
			
		||||
    {
 | 
			
		||||
        get => base.SendBufferSize;
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            base.SendBufferSize = value;
 | 
			
		||||
            if (this.MainSerialPort != null && !MainSerialPort.IsOpen)
 | 
			
		||||
            {
 | 
			
		||||
                this.MainSerialPort.WriteBufferSize = base.SendBufferSize;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual void SetDataHandlingAdapter(SingleStreamDataHandlingAdapter adapter)
 | 
			
		||||
    {
 | 
			
		||||
        if (!this.CanSetDataHandlingAdapter)
 | 
			
		||||
        {
 | 
			
		||||
            throw new Exception($"不允许自由调用{nameof(SetDataHandlingAdapter)}进行赋值。");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.SetAdapter(adapter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ISerialSession Setup(TouchSocketConfig config)
 | 
			
		||||
    {
 | 
			
		||||
        if (config == null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(config));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.ThrowIfDisposed();
 | 
			
		||||
 | 
			
		||||
        this.BuildConfig(config);
 | 
			
		||||
 | 
			
		||||
        this.PluginsManager.Raise(nameof(ILoadingConfigPlugin.OnLoadingConfig), this, new ConfigEventArgs(config));
 | 
			
		||||
        this.LoadConfig(this.Config);
 | 
			
		||||
        this.PluginsManager.Raise(nameof(ILoadedConfigPlugin.OnLoadedConfig), this, new ConfigEventArgs(config));
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void BuildConfig(TouchSocketConfig config)
 | 
			
		||||
    {
 | 
			
		||||
        this.Config = config;
 | 
			
		||||
 | 
			
		||||
        if (!(config.GetValue(TouchSocketCoreConfigExtension.ContainerProperty) is IContainer container))
 | 
			
		||||
        {
 | 
			
		||||
            container = new Container();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!container.IsRegistered(typeof(ILog)))
 | 
			
		||||
        {
 | 
			
		||||
            container.RegisterSingleton<ILog, LoggerGroup>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!(config.GetValue(TouchSocketCoreConfigExtension.PluginsManagerProperty) is IPluginsManager pluginsManager))
 | 
			
		||||
        {
 | 
			
		||||
            pluginsManager = new PluginsManager(container);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (container.IsRegistered(typeof(IPluginsManager)))
 | 
			
		||||
        {
 | 
			
		||||
            pluginsManager = container.Resolve<IPluginsManager>();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            container.RegisterSingleton<IPluginsManager>(pluginsManager);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (config.GetValue(TouchSocketCoreConfigExtension.ConfigureContainerProperty) is Action<IContainer> actionContainer)
 | 
			
		||||
        {
 | 
			
		||||
            actionContainer.Invoke(container);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (config.GetValue(TouchSocketCoreConfigExtension.ConfigurePluginsProperty) is Action<IPluginsManager> actionPluginsManager)
 | 
			
		||||
        {
 | 
			
		||||
            pluginsManager.Enable = true;
 | 
			
		||||
            actionPluginsManager.Invoke(pluginsManager);
 | 
			
		||||
        }
 | 
			
		||||
        this.Container = container;
 | 
			
		||||
        this.PluginsManager = pluginsManager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void PrivateHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        if (this.OnHandleReceivedData?.Invoke(byteBlock, requestInfo) == false)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.HandleReceivedData(byteBlock, requestInfo))
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.PluginsManager.Enable)
 | 
			
		||||
        {
 | 
			
		||||
            var args = new ReceivedDataEventArgs(byteBlock, requestInfo);
 | 
			
		||||
            this.PluginsManager.Raise(nameof(ITcpReceivedPlugin.OnTcpReceived), this, args);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 处理已接收到的数据。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="byteBlock">以二进制流形式传递</param>
 | 
			
		||||
    /// <param name="requestInfo">以解析的数据对象传递</param>
 | 
			
		||||
    /// <returns>如果返回<see langword="true"/>则表示数据已被处理,且不会再向下传递。</returns>
 | 
			
		||||
    protected virtual bool HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 当即将发送时,如果覆盖父类方法,则不会触发插件。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
    /// <param name="offset">偏移</param>
 | 
			
		||||
    /// <param name="length">长度</param>
 | 
			
		||||
    /// <returns>返回值表示是否允许发送</returns>
 | 
			
		||||
    protected virtual bool HandleSendingData(byte[] buffer, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        if (this.PluginsManager.Enable)
 | 
			
		||||
        {
 | 
			
		||||
            var args = new SendingEventArgs(buffer, offset, length);
 | 
			
		||||
            this.PluginsManager.Raise(nameof(ITcpSendingPlugin.OnTcpSending), this, args);
 | 
			
		||||
            return args.IsPermitOperation;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 加载配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="config"></param>
 | 
			
		||||
    protected virtual void LoadConfig(TouchSocketConfig config)
 | 
			
		||||
    {
 | 
			
		||||
        this.SerialProperty = config.GetValue(SerialConfigExtension.SerialProperty);
 | 
			
		||||
        this.Logger ??= this.Container.Resolve<ILog>();
 | 
			
		||||
        this.ReceiveType = config.GetValue(TouchSocketConfigExtension.ReceiveTypeProperty);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 在延迟发生错误
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="ex"></param>
 | 
			
		||||
    protected virtual void OnDelaySenderError(Exception ex)
 | 
			
		||||
    {
 | 
			
		||||
        this.Logger.Log(LogLevel.Error, this, "发送错误", ex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置适配器,该方法不会检验<see cref="CanSetDataHandlingAdapter"/>的值。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="adapter"></param>
 | 
			
		||||
    protected void SetAdapter(SingleStreamDataHandlingAdapter adapter)
 | 
			
		||||
    {
 | 
			
		||||
        this.ThrowIfDisposed();
 | 
			
		||||
        if (adapter is null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(adapter));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.Config != null)
 | 
			
		||||
        {
 | 
			
		||||
            adapter.Config(this.Config);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        adapter.Logger = this.Logger;
 | 
			
		||||
        adapter.OnLoaded(this);
 | 
			
		||||
        adapter.ReceivedCallBack = this.PrivateHandleReceivedData;
 | 
			
		||||
        adapter.SendCallBack = this.DefaultSend;
 | 
			
		||||
        this.DataHandlingAdapter = adapter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void BeginReceive()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (this.ReceiveType == ReceiveType.Iocp)
 | 
			
		||||
        {
 | 
			
		||||
            SerialReceivedEventArgs eventArgs = new();
 | 
			
		||||
            var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize);
 | 
			
		||||
            byteBlock.SetLength(0);
 | 
			
		||||
            eventArgs.UserToken = byteBlock;
 | 
			
		||||
            if (this.MainSerialPort.BytesToRead > 0)
 | 
			
		||||
            {
 | 
			
		||||
                this.ProcessReceived(eventArgs);
 | 
			
		||||
            }
 | 
			
		||||
            MainSerialPort.DataReceived += this.EventArgs_Completed;
 | 
			
		||||
        }
 | 
			
		||||
        else if (this.ReceiveType == ReceiveType.Bio)
 | 
			
		||||
        {
 | 
			
		||||
            new Thread(this.BeginBio)
 | 
			
		||||
            {
 | 
			
		||||
                IsBackground = true
 | 
			
		||||
            }
 | 
			
		||||
            .Start();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private static SerialPort CreateSerial(SerialProperty serialProperty)
 | 
			
		||||
    {
 | 
			
		||||
        SerialPort serialPort = new(serialProperty.PortName, serialProperty.BaudRate, serialProperty.Parity, serialProperty.DataBits, serialProperty.StopBits)
 | 
			
		||||
        {
 | 
			
		||||
            DtrEnable = true,
 | 
			
		||||
            RtsEnable = true
 | 
			
		||||
        };
 | 
			
		||||
        return serialPort;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void EventArgs_Completed(object sender, SerialDataReceivedEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            this.m_bufferRate = 1;
 | 
			
		||||
            SerialReceivedEventArgs eventArgs = new();
 | 
			
		||||
            var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength));
 | 
			
		||||
            newByteBlock.SetLength(0);
 | 
			
		||||
            eventArgs.UserToken = newByteBlock;
 | 
			
		||||
            if (MainSerialPort.BytesToRead > 0)
 | 
			
		||||
            {
 | 
			
		||||
                this.ProcessReceived(eventArgs);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            this.BreakOut(ex.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private void BeginBio()
 | 
			
		||||
    {
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            var byteBlock = new ByteBlock(this.ReceiveBufferSize);
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                int r = MainSerialPort.Read(byteBlock.Buffer, 0, MainSerialPort.BytesToRead);
 | 
			
		||||
                if (r == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    this.BreakOut("远程终端主动关闭");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                byteBlock.SetLength(r);
 | 
			
		||||
                this.HandleBuffer(byteBlock);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                this.BreakOut(ex.Message);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 处理数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private void HandleBuffer(ByteBlock byteBlock)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            this.m_receiveCounter.Increment(byteBlock.Length);
 | 
			
		||||
            if (this.OnHandleRawBuffer?.Invoke(byteBlock) == false)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.DisposedValue)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.PluginsManager.Enable && this.PluginsManager.Raise(nameof(ITcpReceivingPlugin.OnTcpReceiving), this, new ByteBlockEventArgs(byteBlock)))
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.DataHandlingAdapter == null)
 | 
			
		||||
            {
 | 
			
		||||
                this.Logger.Error(this, TouchSocketResource.NullDataAdapter.GetDescription());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            this.DataHandlingAdapter.ReceivedInput(byteBlock);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            this.Logger.Log(LogLevel.Error, this, "在处理数据时发生错误", ex);
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            byteBlock.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 发送
 | 
			
		||||
 | 
			
		||||
    #region 同步发送
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="requestInfo"></param>
 | 
			
		||||
    /// <exception cref="NotConnectedException"></exception>
 | 
			
		||||
    /// <exception cref="OverlengthException"></exception>
 | 
			
		||||
    /// <exception cref="Exception"></exception>
 | 
			
		||||
    public void Send(IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        if (this.DisposedValue)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.DataHandlingAdapter == null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.DataHandlingAdapter.CanSendRequestInfo)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotSupportedException($"当前适配器不支持对象发送。");
 | 
			
		||||
        }
 | 
			
		||||
        this.DataHandlingAdapter.SendInput(requestInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="buffer"><inheritdoc/></param>
 | 
			
		||||
    /// <param name="offset"><inheritdoc/></param>
 | 
			
		||||
    /// <param name="length"><inheritdoc/></param>
 | 
			
		||||
    /// <exception cref="NotConnectedException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="OverlengthException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="Exception"><inheritdoc/></exception>
 | 
			
		||||
    public virtual void Send(byte[] buffer, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        if (this.DataHandlingAdapter == null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
 | 
			
		||||
        }
 | 
			
		||||
        this.DataHandlingAdapter.SendInput(buffer, offset, length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="transferBytes"><inheritdoc/></param>
 | 
			
		||||
    /// <exception cref="NotConnectedException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="OverlengthException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="Exception"><inheritdoc/></exception>
 | 
			
		||||
    public virtual void Send(IList<ArraySegment<byte>> transferBytes)
 | 
			
		||||
    {
 | 
			
		||||
        if (this.DataHandlingAdapter == null)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(this.DataHandlingAdapter), TouchSocketResource.NullDataAdapter.GetDescription());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.DataHandlingAdapter.CanSplicingSend)
 | 
			
		||||
        {
 | 
			
		||||
            this.DataHandlingAdapter.SendInput(transferBytes);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var length = 0;
 | 
			
		||||
            foreach (var item in transferBytes)
 | 
			
		||||
            {
 | 
			
		||||
                length += item.Count;
 | 
			
		||||
            }
 | 
			
		||||
            using var byteBlock = new ByteBlock(length);
 | 
			
		||||
            foreach (var item in transferBytes)
 | 
			
		||||
            {
 | 
			
		||||
                byteBlock.Write(item.Array, item.Offset, item.Count);
 | 
			
		||||
            }
 | 
			
		||||
            this.DataHandlingAdapter.SendInput(byteBlock.Buffer, 0, byteBlock.Len);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 同步发送
 | 
			
		||||
 | 
			
		||||
    #region 异步发送
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="buffer"><inheritdoc/></param>
 | 
			
		||||
    /// <param name="offset"><inheritdoc/></param>
 | 
			
		||||
    /// <param name="length"><inheritdoc/></param>
 | 
			
		||||
    /// <exception cref="NotConnectedException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="OverlengthException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="Exception"><inheritdoc/></exception>
 | 
			
		||||
    public virtual Task SendAsync(byte[] buffer, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.Run(() =>
 | 
			
		||||
        {
 | 
			
		||||
            this.Send(buffer, offset, length);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="requestInfo"></param>
 | 
			
		||||
    /// <exception cref="NotConnectedException"></exception>
 | 
			
		||||
    /// <exception cref="OverlengthException"></exception>
 | 
			
		||||
    /// <exception cref="Exception"></exception>
 | 
			
		||||
    public virtual Task SendAsync(IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.Run(() =>
 | 
			
		||||
        {
 | 
			
		||||
            this.Send(requestInfo);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="transferBytes"><inheritdoc/></param>
 | 
			
		||||
    /// <exception cref="NotConnectedException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="OverlengthException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="Exception"><inheritdoc/></exception>
 | 
			
		||||
    public virtual Task SendAsync(IList<ArraySegment<byte>> transferBytes)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.Run(() =>
 | 
			
		||||
        {
 | 
			
		||||
            this.Send(transferBytes);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 异步发送
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="buffer"><inheritdoc/></param>
 | 
			
		||||
    /// <param name="offset"><inheritdoc/></param>
 | 
			
		||||
    /// <param name="length"><inheritdoc/></param>
 | 
			
		||||
    /// <exception cref="NotConnectedException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="OverlengthException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="Exception"><inheritdoc/></exception>
 | 
			
		||||
    public void DefaultSend(byte[] buffer, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        if (!this.m_online)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotConnectedException(TouchSocketResource.NotConnected.GetDescription());
 | 
			
		||||
        }
 | 
			
		||||
        if (this.HandleSendingData(buffer, offset, length))
 | 
			
		||||
        {
 | 
			
		||||
            if (this.m_delaySender != null && length < this.m_delaySender.DelayLength)
 | 
			
		||||
            {
 | 
			
		||||
                this.m_delaySender.Send(QueueDataBytes.CreateNew(buffer, offset, length));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                this.MainSerialPort.AbsoluteSend(buffer, offset, length);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.m_sendCounter.Increment(length);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="buffer"><inheritdoc/></param>
 | 
			
		||||
    /// <param name="offset"><inheritdoc/></param>
 | 
			
		||||
    /// <param name="length"><inheritdoc/></param>
 | 
			
		||||
    /// <exception cref="NotConnectedException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="OverlengthException"><inheritdoc/></exception>
 | 
			
		||||
    /// <exception cref="Exception"><inheritdoc/></exception>
 | 
			
		||||
    public Task DefaultSendAsync(byte[] buffer, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.Run(() =>
 | 
			
		||||
        {
 | 
			
		||||
            this.DefaultSend(buffer, offset, length);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 发送
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private void ProcessReceived(SerialReceivedEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        if (!this.m_online)
 | 
			
		||||
        {
 | 
			
		||||
            e.UserToken.SafeDispose();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (MainSerialPort.BytesToRead > 0)
 | 
			
		||||
        {
 | 
			
		||||
            byte[] buffer = new byte[2048];
 | 
			
		||||
            var byteBlock = (ByteBlock)e.UserToken;
 | 
			
		||||
            int num = MainSerialPort.Read(buffer, 0, MainSerialPort.BytesToRead);
 | 
			
		||||
            byteBlock.Write(buffer, 0, num);
 | 
			
		||||
            byteBlock.SetLength(num);
 | 
			
		||||
            this.HandleBuffer(byteBlock);
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength));
 | 
			
		||||
                newByteBlock.SetLength(0);
 | 
			
		||||
                e.UserToken = newByteBlock;
 | 
			
		||||
 | 
			
		||||
                if (MainSerialPort.BytesToRead > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    this.m_bufferRate += 2;
 | 
			
		||||
                    this.ProcessReceived(e);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                e.UserToken.SafeDispose();
 | 
			
		||||
                this.BreakOut(ex.Message);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            e.UserToken.SafeDispose();
 | 
			
		||||
            this.BreakOut("远程终端主动关闭");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void SetSerialPort(SerialPort serialPort)
 | 
			
		||||
    {
 | 
			
		||||
        if (serialPort == null)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.MainSerialPort = serialPort;
 | 
			
		||||
        var delaySenderOption = this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty);
 | 
			
		||||
        if (delaySenderOption != null)
 | 
			
		||||
        {
 | 
			
		||||
            this.m_delaySender = new DelaySender(delaySenderOption, this.MainSerialPort.AbsoluteSend);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return SerialProperty?.ToString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,560 +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 System.Net.Security;
 | 
			
		||||
using System.Net.Sockets;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Sockets
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Tcp核心
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class TcpCore : SocketAsyncEventArgs, IDisposable, ISender
 | 
			
		||||
    {
 | 
			
		||||
        private const string m_msg1 = "远程终端主动关闭";
 | 
			
		||||
 | 
			
		||||
        #region 字段
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 同步根
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public readonly object SyncRoot = new object();
 | 
			
		||||
 | 
			
		||||
        private long m_bufferRate;
 | 
			
		||||
        private bool m_disposedValue;
 | 
			
		||||
        private SpinLock m_lock;
 | 
			
		||||
        private volatile bool m_online;
 | 
			
		||||
        private int m_receiveBufferSize = 64 * 1024;
 | 
			
		||||
        private ValueCounter m_receiveCounter;
 | 
			
		||||
        private int m_sendBufferSize = 1024 * 64;
 | 
			
		||||
        private ValueCounter m_sendCounter;
 | 
			
		||||
        private readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1);
 | 
			
		||||
        #endregion 字段
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Tcp核心
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public TcpCore()
 | 
			
		||||
        {
 | 
			
		||||
            this.m_lock = new SpinLock(Debugger.IsAttached);
 | 
			
		||||
            this.m_receiveCounter = new ValueCounter
 | 
			
		||||
            {
 | 
			
		||||
                Period = TimeSpan.FromSeconds(1),
 | 
			
		||||
                OnPeriod = this.OnReceivePeriod
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.m_sendCounter = new ValueCounter
 | 
			
		||||
            {
 | 
			
		||||
                Period = TimeSpan.FromSeconds(1),
 | 
			
		||||
                OnPeriod = this.OnSendPeriod
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 析构函数
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        ~TcpCore()
 | 
			
		||||
        {
 | 
			
		||||
            this.Dispose(disposing: false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public bool CanSend => this.m_online;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当中断Tcp的时候。当为<see langword="true"/>时,意味着是调用<see cref="Close(string)"/>。当为<see langword="false"/>时,则是其他中断。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Action<TcpCore, bool, string> OnBreakOut { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当发生异常的时候
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Action<TcpCore, Exception> OnException { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 在线状态
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool Online { get => this.m_online; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当收到数据的时候
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Action<TcpCore, ByteBlock> OnReceived { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 接收缓存池(可以设定初始值,运行时的值会根据流速自动调整)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int ReceiveBufferSize
 | 
			
		||||
        {
 | 
			
		||||
            get => this.m_receiveBufferSize;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                this.m_receiveBufferSize = value;
 | 
			
		||||
                this.Socket.ReceiveBufferSize = value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 接收计数器
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public ValueCounter ReceiveCounter { get => this.m_receiveCounter; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送缓存池(可以设定初始值,运行时的值会根据流速自动调整)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int SendBufferSize
 | 
			
		||||
        {
 | 
			
		||||
            get => this.m_sendBufferSize;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                this.m_sendBufferSize = value;
 | 
			
		||||
                this.Socket.SendBufferSize = value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送计数器
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public ValueCounter SendCounter { get => this.m_sendCounter; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Socket
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Socket Socket { get; private set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 提供一个用于客户端-服务器通信的流,该流使用安全套接字层 (SSL) 安全协议对服务器和(可选)客户端进行身份验证。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public SslStream SslStream { get; private set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 是否启用了Ssl
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool UseSsl { get; private set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 以Ssl服务器模式授权
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="sslOption"></param>
 | 
			
		||||
        public virtual void Authenticate(ServiceSslOption sslOption)
 | 
			
		||||
        {
 | 
			
		||||
            var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false);
 | 
			
		||||
            sslStream.AuthenticateAsServer(sslOption.Certificate);
 | 
			
		||||
 | 
			
		||||
            this.SslStream = sslStream;
 | 
			
		||||
            this.UseSsl = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 以Ssl客户端模式授权
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="sslOption"></param>
 | 
			
		||||
        public virtual void Authenticate(ClientSslOption sslOption)
 | 
			
		||||
        {
 | 
			
		||||
            var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false);
 | 
			
		||||
            if (sslOption.ClientCertificates == null)
 | 
			
		||||
            {
 | 
			
		||||
                sslStream.AuthenticateAsClient(sslOption.TargetHost);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                sslStream.AuthenticateAsClient(sslOption.TargetHost, sslOption.ClientCertificates, sslOption.SslProtocols, sslOption.CheckCertificateRevocation);
 | 
			
		||||
            }
 | 
			
		||||
            this.SslStream = sslStream;
 | 
			
		||||
            this.UseSsl = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 以Ssl服务器模式授权
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="sslOption"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public virtual async Task AuthenticateAsync(ServiceSslOption sslOption)
 | 
			
		||||
        {
 | 
			
		||||
            var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false);
 | 
			
		||||
            await sslStream.AuthenticateAsServerAsync(sslOption.Certificate);
 | 
			
		||||
 | 
			
		||||
            this.SslStream = sslStream;
 | 
			
		||||
            this.UseSsl = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 以Ssl客户端模式授权
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="sslOption"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public virtual async Task AuthenticateAsync(ClientSslOption sslOption)
 | 
			
		||||
        {
 | 
			
		||||
            var sslStream = (sslOption.CertificateValidationCallback != null) ? new SslStream(new NetworkStream(this.Socket, false), false, sslOption.CertificateValidationCallback) : new SslStream(new NetworkStream(this.Socket, false), false);
 | 
			
		||||
            if (sslOption.ClientCertificates == null)
 | 
			
		||||
            {
 | 
			
		||||
                await sslStream.AuthenticateAsClientAsync(sslOption.TargetHost);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                await sslStream.AuthenticateAsClientAsync(sslOption.TargetHost, sslOption.ClientCertificates, sslOption.SslProtocols, sslOption.CheckCertificateRevocation);
 | 
			
		||||
            }
 | 
			
		||||
            this.SslStream = sslStream;
 | 
			
		||||
            this.UseSsl = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 开始以Iocp方式接收
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public virtual void BeginIocpReceive()
 | 
			
		||||
        {
 | 
			
		||||
            var byteBlock = BytePool.Default.GetByteBlock(this.ReceiveBufferSize);
 | 
			
		||||
            this.UserToken = byteBlock;
 | 
			
		||||
            this.SetBuffer(byteBlock.Buffer, 0, byteBlock.Capacity);
 | 
			
		||||
            if (!this.Socket.ReceiveAsync(this))
 | 
			
		||||
            {
 | 
			
		||||
                this.ProcessReceived(this);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 开始以Ssl接收。
 | 
			
		||||
        /// <para>
 | 
			
		||||
        /// 注意,使用该方法时,应先完成授权。
 | 
			
		||||
        /// </para>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public virtual async Task BeginSslReceive()
 | 
			
		||||
        {
 | 
			
		||||
            if (!this.UseSsl)
 | 
			
		||||
            {
 | 
			
		||||
                throw new Exception("请先完成Ssl验证授权");
 | 
			
		||||
            }
 | 
			
		||||
            while (true)
 | 
			
		||||
            {
 | 
			
		||||
                var byteBlock = new ByteBlock(this.ReceiveBufferSize);
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var r = await Task<int>.Factory.FromAsync(this.SslStream.BeginRead, this.SslStream.EndRead, byteBlock.Buffer, 0, byteBlock.Capacity, default);
 | 
			
		||||
                    if (r == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        this.PrivateBreakOut(false, m_msg1);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    byteBlock.SetLength(r);
 | 
			
		||||
                    this.HandleBuffer(byteBlock);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    byteBlock.Dispose();
 | 
			
		||||
                    this.PrivateBreakOut(false, ex.Message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 请求关闭
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="msg"></param>
 | 
			
		||||
        public virtual void Close(string msg)
 | 
			
		||||
        {
 | 
			
		||||
            this.PrivateBreakOut(true, msg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 释放对象
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public new void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中
 | 
			
		||||
            this.Dispose(disposing: true);
 | 
			
		||||
            GC.SuppressFinalize(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 重置环境,并设置新的<see cref="Socket"/>。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="socket"></param>
 | 
			
		||||
        public virtual void Reset(Socket socket)
 | 
			
		||||
        {
 | 
			
		||||
            if (socket is null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(socket));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!socket.Connected)
 | 
			
		||||
            {
 | 
			
		||||
                throw new Exception("新的Socket必须在连接状态。");
 | 
			
		||||
            }
 | 
			
		||||
            this.Reset();
 | 
			
		||||
            this.m_online = true;
 | 
			
		||||
            this.Socket = socket;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 重置环境。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public virtual void Reset()
 | 
			
		||||
        {
 | 
			
		||||
            this.m_receiveCounter.Reset();
 | 
			
		||||
            this.m_sendCounter.Reset();
 | 
			
		||||
            this.SslStream?.Dispose();
 | 
			
		||||
            this.SslStream = null;
 | 
			
		||||
            this.Socket = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送数据。
 | 
			
		||||
        /// <para>
 | 
			
		||||
        /// 内部会根据是否启用Ssl,进行直接发送,还是Ssl发送。
 | 
			
		||||
        /// </para>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer"></param>
 | 
			
		||||
        /// <param name="offset"></param>
 | 
			
		||||
        /// <param name="length"></param>
 | 
			
		||||
        public virtual void Send(byte[] buffer, int offset, int length)
 | 
			
		||||
        {
 | 
			
		||||
            if (this.UseSsl)
 | 
			
		||||
            {
 | 
			
		||||
                this.SslStream.Write(buffer, offset, length);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var lockTaken = false;
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    this.m_lock.Enter(ref lockTaken);
 | 
			
		||||
                    while (length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        var r = this.Socket.Send(buffer, offset, length, SocketFlags.None);
 | 
			
		||||
                        if (r == 0 && length > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            throw new Exception("发送数据不完全");
 | 
			
		||||
                        }
 | 
			
		||||
                        offset += r;
 | 
			
		||||
                        length -= r;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    if (lockTaken) this.m_lock.Exit(false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this.m_sendCounter.Increment(length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 异步发送数据。
 | 
			
		||||
        /// <para>
 | 
			
		||||
        /// 内部会根据是否启用Ssl,进行直接发送,还是Ssl发送。
 | 
			
		||||
        /// </para>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer"></param>
 | 
			
		||||
        /// <param name="offset"></param>
 | 
			
		||||
        /// <param name="length"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        /// <exception cref="Exception"></exception>
 | 
			
		||||
        public virtual async Task SendAsync(byte[] buffer, int offset, int length)
 | 
			
		||||
        {
 | 
			
		||||
#if NET6_0_OR_GREATER
 | 
			
		||||
            if (this.UseSsl)
 | 
			
		||||
            {
 | 
			
		||||
                await this.SslStream.WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, length), CancellationToken.None);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await this.m_semaphore.WaitAsync();
 | 
			
		||||
 | 
			
		||||
                    while (length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        var r = await this.Socket.SendAsync(new ArraySegment<byte>(buffer, offset, length), SocketFlags.None, CancellationToken.None);
 | 
			
		||||
                        if (r == 0 && length > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            throw new Exception("发送数据不完全");
 | 
			
		||||
                        }
 | 
			
		||||
                        offset += r;
 | 
			
		||||
                        length -= r;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    this.m_semaphore.Release();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
#else
 | 
			
		||||
            if (this.UseSsl)
 | 
			
		||||
            {
 | 
			
		||||
                await this.SslStream.WriteAsync(buffer, offset, length, CancellationToken.None);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    await this.m_semaphore.WaitAsync();
 | 
			
		||||
 | 
			
		||||
                    while (length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        var r = this.Socket.Send(buffer, offset, length, SocketFlags.None);
 | 
			
		||||
                        if (r == 0 && length > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            throw new Exception("发送数据不完全");
 | 
			
		||||
                        }
 | 
			
		||||
                        offset += r;
 | 
			
		||||
                        length -= r;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    this.m_semaphore.Release();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
            this.m_sendCounter.Increment(length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当中断Tcp时。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="manual">当为<see langword="true"/>时,意味着是调用<see cref="Close(string)"/>。当为<see langword="false"/>时,则是其他中断。</param>
 | 
			
		||||
        /// <param name="msg"></param>
 | 
			
		||||
        protected virtual void BreakOut(bool manual, string msg)
 | 
			
		||||
        {
 | 
			
		||||
            this.OnBreakOut?.Invoke(this, manual, msg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 释放对象
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="disposing"></param>
 | 
			
		||||
        protected virtual void Dispose(bool disposing)
 | 
			
		||||
        {
 | 
			
		||||
            if (!this.m_disposedValue)
 | 
			
		||||
            {
 | 
			
		||||
                if (disposing)
 | 
			
		||||
                {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.m_disposedValue = true;
 | 
			
		||||
            }
 | 
			
		||||
            base.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当发生异常的时候
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="ex"></param>
 | 
			
		||||
        protected virtual void Exception(Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            this.OnException?.Invoke(this, ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override sealed void OnCompleted(SocketAsyncEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            if (e.LastOperation == SocketAsyncOperation.Receive)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    this.m_bufferRate = 1;
 | 
			
		||||
                    this.ProcessReceived(e);
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    this.PrivateBreakOut(false, ex.Message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当收到数据的时候
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="byteBlock"></param>
 | 
			
		||||
        protected virtual void Received(ByteBlock byteBlock)
 | 
			
		||||
        {
 | 
			
		||||
            this.OnReceived?.Invoke(this, byteBlock);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void HandleBuffer(ByteBlock byteBlock)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                this.m_receiveCounter.Increment(byteBlock.Length);
 | 
			
		||||
                this.Received(byteBlock);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                this.Exception(ex);
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                byteBlock.Dispose();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnReceivePeriod(long value)
 | 
			
		||||
        {
 | 
			
		||||
            this.ReceiveBufferSize = TouchSocketUtility.HitBufferLength(value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void OnSendPeriod(long value)
 | 
			
		||||
        {
 | 
			
		||||
            this.SendBufferSize = TouchSocketUtility.HitBufferLength(value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void PrivateBreakOut(bool manual, string msg)
 | 
			
		||||
        {
 | 
			
		||||
            lock (this.SyncRoot)
 | 
			
		||||
            {
 | 
			
		||||
                if (this.m_online)
 | 
			
		||||
                {
 | 
			
		||||
                    this.m_online = false;
 | 
			
		||||
                    this.BreakOut(manual, msg);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ProcessReceived(SocketAsyncEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            if (e.SocketError != SocketError.Success)
 | 
			
		||||
            {
 | 
			
		||||
                this.PrivateBreakOut(false, e.SocketError.ToString());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            else if (e.BytesTransferred > 0)
 | 
			
		||||
            {
 | 
			
		||||
                var byteBlock = (ByteBlock)e.UserToken;
 | 
			
		||||
                byteBlock.SetLength(e.BytesTransferred);
 | 
			
		||||
                this.HandleBuffer(byteBlock);
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var newByteBlock = BytePool.Default.GetByteBlock((int)Math.Min(this.ReceiveBufferSize * this.m_bufferRate, TouchSocketUtility.MaxBufferLength));
 | 
			
		||||
                    e.UserToken = newByteBlock;
 | 
			
		||||
                    e.SetBuffer(newByteBlock.Buffer, 0, newByteBlock.Capacity);
 | 
			
		||||
 | 
			
		||||
                    if (!this.Socket.ReceiveAsync(e))
 | 
			
		||||
                    {
 | 
			
		||||
                        this.m_bufferRate += 2;
 | 
			
		||||
                        this.ProcessReceived(e);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    this.PrivateBreakOut(false, ex.Message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                this.PrivateBreakOut(false, m_msg1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,88 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
 | 
			
		||||
//  CSDN博客:https://blog.csdn.net/qq_40374647
 | 
			
		||||
//  哔哩哔哩视频:https://space.bilibili.com/94253567
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/RRQM_Home
 | 
			
		||||
//  Github源代码仓库:https://github.com/RRQM
 | 
			
		||||
//  API首页:http://rrqm_home.gitee.io/touchsocket/
 | 
			
		||||
//  交流QQ群:234762506
 | 
			
		||||
//  感谢您的下载和使用
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
using System.Net;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 显示信息
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="client"></param>
 | 
			
		||||
/// <param name="e"></param>
 | 
			
		||||
public delegate void MessageEventHandler<TClient>(TClient client, MsgPermitEventArgs e);
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 普通通知
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <typeparam name="TClient"></typeparam>
 | 
			
		||||
/// <param name="client"></param>
 | 
			
		||||
/// <param name="e"></param>
 | 
			
		||||
public delegate void TouchSocketEventHandler<TClient>(TClient client, PluginEventArgs e);
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Id修改通知
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <typeparam name="TClient"></typeparam>
 | 
			
		||||
/// <param name="client"></param>
 | 
			
		||||
/// <param name="e"></param>
 | 
			
		||||
public delegate void IdChangedEventHandler<TClient>(TClient client, IdChangedEventArgs e);
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Connecting
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <typeparam name="TClient"></typeparam>
 | 
			
		||||
/// <param name="client"></param>
 | 
			
		||||
/// <param name="e"></param>
 | 
			
		||||
public delegate void ConnectingEventHandler<TClient>(TClient client, ConnectingEventArgs e);
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Connected
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <typeparam name="TClient"></typeparam>
 | 
			
		||||
/// <param name="client"></param>
 | 
			
		||||
/// <param name="e"></param>
 | 
			
		||||
public delegate void ConnectedEventHandler<TClient>(TClient client, ConnectedEventArgs e);
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 客户端断开连接
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <typeparam name="TClient"></typeparam>
 | 
			
		||||
/// <param name="client"></param>
 | 
			
		||||
/// <param name="e"></param>
 | 
			
		||||
public delegate void DisconnectEventHandler<TClient>(TClient client, DisconnectEventArgs e);
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 接收数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="client"></param>
 | 
			
		||||
/// <param name="byteBlock"></param>
 | 
			
		||||
/// <param name="requestInfo"></param>
 | 
			
		||||
public delegate void ReceivedEventHandler<TClient>(TClient client, ByteBlock byteBlock, IRequestInfo requestInfo);
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// UDP接收
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="endpoint"></param>
 | 
			
		||||
/// <param name="byteBlock"></param>
 | 
			
		||||
/// <param name="requestInfo"></param>
 | 
			
		||||
public delegate void UdpReceivedEventHandler(EndPoint endpoint, ByteBlock byteBlock, IRequestInfo requestInfo);
 | 
			
		||||
@@ -1,109 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权(除特别声明或在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.Sockets
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 发送等待接口
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public interface IWaitSender : ISenderBase
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送字节流
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
        /// <param name="offset">偏移</param>
 | 
			
		||||
        /// <param name="length">长度</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        byte[] SendThenReturn(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送字节流
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        byte[] SendThenReturn(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送流中的有效数据
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="byteBlock">数据块载体</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        byte[] SendThenReturn(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 异步发送
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
        /// <param name="offset">偏移</param>
 | 
			
		||||
        /// <param name="length">长度</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 异步发送
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        Task<byte[]> SendThenReturnAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 异步发送
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="byteBlock">数据块载体</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        Task<byte[]> SendThenReturnAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,119 +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.Sockets
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 等待型客户端。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public interface IWaitingClient<TClient> : IWaitSender, IDisposable where TClient : IClient, IDefaultSender, ISender
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 等待设置。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public WaitingOptions WaitingOptions { get; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 客户端终端
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        TClient Client { get; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送字节流
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
        /// <param name="offset">偏移</param>
 | 
			
		||||
        /// <param name="length">长度</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        ResponsedData SendThenResponse(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送字节流
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 发送流中的有效数据
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="byteBlock">数据块载体</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        ResponsedData SendThenResponse(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 异步发送
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
        /// <param name="offset">偏移</param>
 | 
			
		||||
        /// <param name="length">长度</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 异步发送
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">数据缓存区</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 异步发送
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="byteBlock">数据块载体</param>
 | 
			
		||||
        /// <param name="timeout">超时时间</param>
 | 
			
		||||
        /// <param name="cancellationToken">取消令箭</param>
 | 
			
		||||
        /// <exception cref="NotConnectedException">客户端没有连接</exception>
 | 
			
		||||
        /// <exception cref="OverlengthException">发送数据超长</exception>
 | 
			
		||||
        /// <exception cref="Exception">其他异常</exception>
 | 
			
		||||
        /// <returns>返回的数据</returns>
 | 
			
		||||
        Task<ResponsedData> SendThenResponseAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,362 +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.Sockets;
 | 
			
		||||
 | 
			
		||||
internal class WaitingClient<TClient> : DisposableObject, IWaitingClient<TClient> where TClient : IClient, IDefaultSender, ISender
 | 
			
		||||
{
 | 
			
		||||
    private readonly Func<ResponsedData, bool> m_func;
 | 
			
		||||
    private readonly EasyLock easyLock = new();
 | 
			
		||||
    private readonly WaitData<ResponsedData> m_waitData = new();
 | 
			
		||||
    private readonly WaitDataAsync<ResponsedData> m_waitDataAsync = new();
 | 
			
		||||
 | 
			
		||||
    private volatile bool m_breaked;
 | 
			
		||||
 | 
			
		||||
    public WaitingClient(TClient client, WaitingOptions waitingOptions, Func<ResponsedData, bool> func)
 | 
			
		||||
    {
 | 
			
		||||
        this.Client = client ?? throw new ArgumentNullException(nameof(client));
 | 
			
		||||
        this.WaitingOptions = waitingOptions;
 | 
			
		||||
        this.m_func = func;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public WaitingClient(TClient client, WaitingOptions waitingOptions)
 | 
			
		||||
    {
 | 
			
		||||
        this.Client = client ?? throw new ArgumentNullException(nameof(client));
 | 
			
		||||
        this.WaitingOptions = waitingOptions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public bool CanSend
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return this.Client is ITcpClientBase tcpClient ? tcpClient.CanSend : this.Client is IUdpSession;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public TClient Client { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public WaitingOptions WaitingOptions { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected override void Dispose(bool disposing)
 | 
			
		||||
    {
 | 
			
		||||
        this.Client = default;
 | 
			
		||||
        this.m_waitData.SafeDispose();
 | 
			
		||||
        this.m_waitDataAsync.SafeDispose();
 | 
			
		||||
        base.Dispose(disposing);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Cancel()
 | 
			
		||||
    {
 | 
			
		||||
        this.m_waitData.Cancel();
 | 
			
		||||
        this.m_waitDataAsync.Cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnDisconnected(ITcpClientBase client, DisconnectEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        this.m_breaked = true;
 | 
			
		||||
        this.Cancel();
 | 
			
		||||
    }
 | 
			
		||||
    private void OnSerialSessionDisconnected(ISerialSessionBase client, DisconnectEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        this.m_breaked = true;
 | 
			
		||||
        this.Cancel();
 | 
			
		||||
    }
 | 
			
		||||
    private bool OnHandleRawBuffer(ByteBlock byteBlock)
 | 
			
		||||
    {
 | 
			
		||||
        var responsedData = new ResponsedData(byteBlock.ToArray(), null, true);
 | 
			
		||||
        if (this.m_func == null || this.m_func.Invoke(responsedData))
 | 
			
		||||
        {
 | 
			
		||||
            return !this.Set(responsedData);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private bool OnHandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
 | 
			
		||||
    {
 | 
			
		||||
        ResponsedData responsedData;
 | 
			
		||||
        if (byteBlock != null)
 | 
			
		||||
        {
 | 
			
		||||
            responsedData = new ResponsedData(byteBlock.ToArray(), requestInfo, false);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            responsedData = new ResponsedData(null, requestInfo, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.m_func == null || this.m_func.Invoke(responsedData))
 | 
			
		||||
        {
 | 
			
		||||
            return !this.Set(responsedData);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Reset()
 | 
			
		||||
    {
 | 
			
		||||
        this.m_waitData.Reset();
 | 
			
		||||
        this.m_waitDataAsync.Reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 同步Response
 | 
			
		||||
 | 
			
		||||
    public ResponsedData SendThenResponse(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            this.m_breaked = false;
 | 
			
		||||
            this.Reset();
 | 
			
		||||
            if (this.WaitingOptions.BreakTrigger && this.Client is ITcpClientBase tcpClient)
 | 
			
		||||
            {
 | 
			
		||||
                tcpClient.Disconnected += this.OnDisconnected;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.WaitingOptions.BreakTrigger && this.Client is ISerialSessionBase serialSession)
 | 
			
		||||
            {
 | 
			
		||||
                serialSession.Disconnected += this.OnSerialSessionDisconnected;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter)
 | 
			
		||||
            {
 | 
			
		||||
                this.Client.OnHandleReceivedData += this.OnHandleReceivedData;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                this.Client.OnHandleRawBuffer += this.OnHandleRawBuffer;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session)
 | 
			
		||||
            {
 | 
			
		||||
                if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.SendAdapter)
 | 
			
		||||
                {
 | 
			
		||||
                    session.Send(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    session.DefaultSend(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.SendAdapter)
 | 
			
		||||
                {
 | 
			
		||||
                    this.Client.Send(buffer, offset, length);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    this.Client.DefaultSend(buffer, offset, length);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.m_waitData.SetCancellationToken(cancellationToken);
 | 
			
		||||
            switch (this.m_waitData.Wait(timeout))
 | 
			
		||||
            {
 | 
			
		||||
                case WaitDataStatus.SetRunning:
 | 
			
		||||
                    return this.m_waitData.WaitResult;
 | 
			
		||||
 | 
			
		||||
                case WaitDataStatus.Overtime:
 | 
			
		||||
                    throw new TimeoutException();
 | 
			
		||||
                case WaitDataStatus.Canceled:
 | 
			
		||||
                    {
 | 
			
		||||
                        return this.WaitingOptions.ThrowBreakException && this.m_breaked ? throw new Exception("等待已终止。可能是客户端已掉线,或者被注销。") : (ResponsedData)default;
 | 
			
		||||
                    }
 | 
			
		||||
                case WaitDataStatus.Default:
 | 
			
		||||
                case WaitDataStatus.Disposed:
 | 
			
		||||
                default:
 | 
			
		||||
                    throw new Exception("未知错误");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
            if (this.WaitingOptions.BreakTrigger && this.Client is ITcpClientBase tcpClient)
 | 
			
		||||
            {
 | 
			
		||||
                tcpClient.Disconnected -= this.OnDisconnected;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.WaitingOptions.BreakTrigger && this.Client is ISerialSessionBase serialSession)
 | 
			
		||||
            {
 | 
			
		||||
                serialSession.Disconnected -= this.OnSerialSessionDisconnected;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter)
 | 
			
		||||
            {
 | 
			
		||||
                this.Client.OnHandleReceivedData -= this.OnHandleReceivedData;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                this.Client.OnHandleRawBuffer -= this.OnHandleRawBuffer;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return this.SendThenResponse(buffer, 0, buffer.Length, timeout, cancellationToken);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ResponsedData SendThenResponse(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return this.SendThenResponse(byteBlock.Buffer, 0, byteBlock.Len, timeout, cancellationToken);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 同步Response
 | 
			
		||||
 | 
			
		||||
    #region Response异步
 | 
			
		||||
 | 
			
		||||
    public async Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await easyLock.WaitAsync();
 | 
			
		||||
            this.m_breaked = false;
 | 
			
		||||
            this.Reset();
 | 
			
		||||
            if (this.WaitingOptions.BreakTrigger && this.Client is ITcpClientBase tcpClient)
 | 
			
		||||
            {
 | 
			
		||||
                tcpClient.Disconnected += this.OnDisconnected;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.WaitingOptions.BreakTrigger && this.Client is ISerialSessionBase serialSession)
 | 
			
		||||
            {
 | 
			
		||||
                serialSession.Disconnected += this.OnSerialSessionDisconnected;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter)
 | 
			
		||||
            {
 | 
			
		||||
                this.Client.OnHandleReceivedData += this.OnHandleReceivedData;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                this.Client.OnHandleRawBuffer += this.OnHandleRawBuffer;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.WaitingOptions.RemoteIPHost != null && this.Client is IUdpSession session)
 | 
			
		||||
            {
 | 
			
		||||
                if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.SendAdapter)
 | 
			
		||||
                {
 | 
			
		||||
                    session.Send(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    session.DefaultSend(this.WaitingOptions.RemoteIPHost.EndPoint, buffer, offset, length);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.SendAdapter)
 | 
			
		||||
                {
 | 
			
		||||
                    this.Client.Send(buffer, offset, length);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    this.Client.DefaultSend(buffer, offset, length);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.m_waitDataAsync.SetCancellationToken(cancellationToken);
 | 
			
		||||
            switch (await this.m_waitDataAsync.WaitAsync(timeout))
 | 
			
		||||
            {
 | 
			
		||||
                case WaitDataStatus.SetRunning:
 | 
			
		||||
                    return this.m_waitData.WaitResult;
 | 
			
		||||
 | 
			
		||||
                case WaitDataStatus.Overtime:
 | 
			
		||||
                    throw new TimeoutException();
 | 
			
		||||
                case WaitDataStatus.Canceled:
 | 
			
		||||
                    {
 | 
			
		||||
                        return this.WaitingOptions.ThrowBreakException && this.m_breaked ? throw new Exception("等待已终止。可能是客户端已掉线,或者被注销。") : (ResponsedData)default;
 | 
			
		||||
                    }
 | 
			
		||||
                case WaitDataStatus.Default:
 | 
			
		||||
                case WaitDataStatus.Disposed:
 | 
			
		||||
                default:
 | 
			
		||||
                    throw new Exception("未知错误");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
            if (this.WaitingOptions.BreakTrigger && this.Client is ITcpClientBase tcpClient)
 | 
			
		||||
            {
 | 
			
		||||
                tcpClient.Disconnected -= this.OnDisconnected;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.WaitingOptions.BreakTrigger && this.Client is ISerialSessionBase serialSession)
 | 
			
		||||
            {
 | 
			
		||||
                serialSession.Disconnected -= this.OnSerialSessionDisconnected;
 | 
			
		||||
            }
 | 
			
		||||
            if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter)
 | 
			
		||||
            {
 | 
			
		||||
                this.Client.OnHandleReceivedData -= this.OnHandleReceivedData;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                this.Client.OnHandleRawBuffer -= this.OnHandleRawBuffer;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task<ResponsedData> SendThenResponseAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, cancellationToken);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Task<ResponsedData> SendThenResponseAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, cancellationToken);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion Response异步
 | 
			
		||||
 | 
			
		||||
    #region 字节同步
 | 
			
		||||
 | 
			
		||||
    public byte[] SendThenReturn(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return this.SendThenResponse(buffer, offset, length, timeout, cancellationToken).Data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] SendThenReturn(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return this.SendThenReturn(buffer, 0, buffer.Length, timeout, cancellationToken);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte[] SendThenReturn(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return this.SendThenReturn(byteBlock.Buffer, 0, byteBlock.Len, timeout, cancellationToken);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 字节同步
 | 
			
		||||
 | 
			
		||||
    #region 字节异步
 | 
			
		||||
 | 
			
		||||
    public async Task<byte[]> SendThenReturnAsync(byte[] buffer, int offset, int length, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return (await this.SendThenResponseAsync(buffer, offset, length, timeout, cancellationToken)).Data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<byte[]> SendThenReturnAsync(byte[] buffer, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return (await this.SendThenResponseAsync(buffer, 0, buffer.Length, timeout, cancellationToken)).Data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<byte[]> SendThenReturnAsync(ByteBlock byteBlock, int timeout = 1000 * 5, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return (await this.SendThenResponseAsync(byteBlock.Buffer, 0, byteBlock.Len, timeout, cancellationToken)).Data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 字节异步
 | 
			
		||||
 | 
			
		||||
    private bool Set(ResponsedData responsedData)
 | 
			
		||||
    {
 | 
			
		||||
        this.m_waitData.Set(responsedData);
 | 
			
		||||
        this.m_waitDataAsync.Set(responsedData);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,45 +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.Sockets;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// WaitingClientExtensions
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class WaitingClientExtension
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取筛选条件的可等待的客户端。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="TClient"></typeparam>
 | 
			
		||||
    /// <param name="client"></param>
 | 
			
		||||
    /// <param name="waitingOptions"></param>
 | 
			
		||||
    /// <param name="func">当条件成立时返回</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static IWaitingClient<TClient> GetWaitingClientEx<TClient>(this TClient client, WaitingOptions waitingOptions, Func<ResponsedData, bool> func) where TClient : IClient, IDefaultSender, ISender
 | 
			
		||||
    {
 | 
			
		||||
        return new WaitingClient<TClient>(client, waitingOptions, func);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取可等待的客户端。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="TClient"></typeparam>
 | 
			
		||||
    /// <param name="client"></param>
 | 
			
		||||
    /// <param name="waitingOptions"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static IWaitingClient<TClient> GetWaitingClientEx<TClient>(this TClient client, WaitingOptions waitingOptions) where TClient : IClient, IDefaultSender, ISender
 | 
			
		||||
    {
 | 
			
		||||
        var waitingClient = new WaitingClient<TClient>(client, waitingOptions);
 | 
			
		||||
        return waitingClient;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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, ToString());
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                    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, ToString());
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                            if (isSuccess)
 | 
			
		||||
                                producer.Flush(cancellationToken);
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        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, ToString());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    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, ToString());
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        if (isSuccess)
 | 
			
		||||
                            producer.Flush(cancellationToken);
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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, ToString());
 | 
			
		||||
            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,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
 | 
			
		||||
 | 
			
		||||
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, ToString());
 | 
			
		||||
            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 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 resultTask1 = RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusSerialServer)}-{CurrentDevice.Name}",
 | 
			
		||||
               new Dictionary<string, string>
 | 
			
		||||
{
 | 
			
		||||
    {
 | 
			
		||||
                       tag.Value.Name,
 | 
			
		||||
                       thingsGatewayBitConverter.GetDataFormBytes(addressStr ,  bytes,result).ToString()
 | 
			
		||||
 | 
			
		||||
                   },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
                );
 | 
			
		||||
                var result1 = resultTask1.ConfigureAwait(true).GetAwaiter().GetResult();
 | 
			
		||||
                return result1.FirstOrDefault().Value;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var resultTask1 = RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusSerialServer)}-{CurrentDevice.Name}",
 | 
			
		||||
               new Dictionary<string, string>
 | 
			
		||||
{
 | 
			
		||||
    {
 | 
			
		||||
                       tag.Value.Name,
 | 
			
		||||
                       thingsGatewayBitConverter.GetDataFormBytes(addressStr ,   bytes,result).ToString()
 | 
			
		||||
                   },
 | 
			
		||||
}
 | 
			
		||||
                );
 | 
			
		||||
                var result1 = resultTask1.ConfigureAwait(true).GetAwaiter().GetResult();
 | 
			
		||||
                return result1.FirstOrDefault().Value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex.Message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,239 +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, ToString());
 | 
			
		||||
            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 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 resultTask1 = RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{client.IP + ":" + client.Port}",
 | 
			
		||||
               new Dictionary<string, string>
 | 
			
		||||
{
 | 
			
		||||
    {
 | 
			
		||||
                       tag.Value.Name,
 | 
			
		||||
                       thingsGatewayBitConverter.GetDataFormBytes(addressStr ,  bytes,result).ToString()
 | 
			
		||||
 | 
			
		||||
                   },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
                );
 | 
			
		||||
                var result1 = resultTask1.ConfigureAwait(true).GetAwaiter().GetResult();
 | 
			
		||||
                return result1.FirstOrDefault().Value;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var resultTask1 = RpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusTcpServer)}-{CurrentDevice.Name}-{client.IP + ":" + client.Port}",
 | 
			
		||||
               new Dictionary<string, string>
 | 
			
		||||
{
 | 
			
		||||
    {
 | 
			
		||||
                       tag.Value.Name,
 | 
			
		||||
                       thingsGatewayBitConverter.GetDataFormBytes(addressStr ,   bytes,result).ToString()
 | 
			
		||||
                   },
 | 
			
		||||
}
 | 
			
		||||
                );
 | 
			
		||||
                var result1 = resultTask1.ConfigureAwait(true).GetAwaiter().GetResult();
 | 
			
		||||
                return result1.FirstOrDefault().Value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex.Message);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,601 +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 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, ToString());
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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, ToString());
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        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, ToString());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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, ToString());
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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, ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    private List<CollectDeviceRunTime> _collectDevice;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void Init(UploadDeviceRunTime device)
 | 
			
		||||
    {
 | 
			
		||||
        var mqttFactory = new MqttFactory(new PrivateLogger(LogMessage));
 | 
			
		||||
        _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 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,569 +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 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, ToString());
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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, ToString());
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        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, ToString());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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, ToString());
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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, ToString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void Init(UploadDeviceRunTime device)
 | 
			
		||||
    {
 | 
			
		||||
        var mqttFactory = new MqttFactory(new PrivateLogger(LogMessage));
 | 
			
		||||
        _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 async Task AllPublishAsync(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        //保留消息
 | 
			
		||||
        //分解List,避免超出mqtt字节大小限制
 | 
			
		||||
        var varData = _globalDeviceData.AllVariables.Adapt<List<VariableData>>().ChunkTrivialBetter(driverPropertys.SplitSize);
 | 
			
		||||
        var devData = _globalDeviceData.AllVariables.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, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        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,364 +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.Internal;
 | 
			
		||||
using MQTTnet.Protocol;
 | 
			
		||||
using MQTTnet.Server;
 | 
			
		||||
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Application;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Plugin.Mqtt;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// MqttServer
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class MqttServer : UpLoadBase
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    private readonly MqttServerProperty driverPropertys = new();
 | 
			
		||||
    private readonly MqttClientVariableProperty variablePropertys = new();
 | 
			
		||||
    private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new();
 | 
			
		||||
    private ConcurrentQueue<VariableData> _collectVariableRunTimes = new();
 | 
			
		||||
    private GlobalDeviceData _globalDeviceData;
 | 
			
		||||
 | 
			
		||||
    private MQTTnet.Server.MqttServer _mqttServer;
 | 
			
		||||
    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;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override Task AfterStopAsync()
 | 
			
		||||
    {
 | 
			
		||||
        return Task.CompletedTask;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task BeforStartAsync(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (_mqttServer != null)
 | 
			
		||||
        {
 | 
			
		||||
            _mqttServer.ValidatingConnectionAsync += MqttServer_ValidatingConnectionAsync;
 | 
			
		||||
            _mqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync;
 | 
			
		||||
            _mqttServer.LoadingRetainedMessageAsync += MqttServer_LoadingRetainedMessageAsync;
 | 
			
		||||
            _mqttServer.InterceptingSubscriptionAsync += MqttServer_InterceptingSubscriptionAsync; ;
 | 
			
		||||
            await _mqttServer.StartAsync();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task ExecuteAsync(CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            ////变化推送
 | 
			
		||||
            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)
 | 
			
		||||
                        {
 | 
			
		||||
                            var message = new MqttApplicationMessageBuilder()
 | 
			
		||||
.WithTopic($"{driverPropertys.VariableTopic}")
 | 
			
		||||
.WithPayload(item.GetSciptListValue(driverPropertys.BigTextScriptVariableModel)).Build();
 | 
			
		||||
                            await _mqttServer.InjectApplicationMessage(
 | 
			
		||||
                                    new InjectedMqttApplicationMessage(message), cancellationToken);
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            ////变化推送
 | 
			
		||||
            var devList = _collectDeviceRunTimes.ToListWithDequeue();
 | 
			
		||||
            if (devList?.Count != 0)
 | 
			
		||||
            {
 | 
			
		||||
                //分解List,避免超出mqtt字节大小限制
 | 
			
		||||
                var varData = devList.ChunkTrivialBetter(driverPropertys.SplitSize);
 | 
			
		||||
                foreach (var item in varData)
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        if (!cancellationToken.IsCancellationRequested)
 | 
			
		||||
                        {
 | 
			
		||||
                            var message = new MqttApplicationMessageBuilder()
 | 
			
		||||
                        .WithTopic($"{driverPropertys.DeviceTopic}")
 | 
			
		||||
                        .WithPayload(item.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel)).Build();
 | 
			
		||||
                            await _mqttServer.InjectApplicationMessage(
 | 
			
		||||
                                    new InjectedMqttApplicationMessage(message), cancellationToken);
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken);
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override bool IsConnected() => _mqttServer?.IsStarted == true;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return $" {nameof(MqttServer)} IP:{driverPropertys.IP} Port:{driverPropertys.Port}";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void Dispose(bool disposing)
 | 
			
		||||
    {
 | 
			
		||||
        if (_mqttServer != null)
 | 
			
		||||
        {
 | 
			
		||||
            _mqttServer.ValidatingConnectionAsync -= MqttServer_ValidatingConnectionAsync;
 | 
			
		||||
            _mqttServer.InterceptingPublishAsync -= MqttServer_InterceptingPublishAsync;
 | 
			
		||||
            _mqttServer.LoadingRetainedMessageAsync -= MqttServer_LoadingRetainedMessageAsync;
 | 
			
		||||
            _mqttServer.InterceptingSubscriptionAsync -= MqttServer_InterceptingSubscriptionAsync; ;
 | 
			
		||||
            _mqttServer?.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
 | 
			
		||||
        _globalDeviceData?.CollectDevices?.ForEach(a =>
 | 
			
		||||
        {
 | 
			
		||||
            a.DeviceStatusChange -= DeviceStatusChange;
 | 
			
		||||
        });
 | 
			
		||||
        _uploadVariables = null;
 | 
			
		||||
        _collectDeviceRunTimes.Clear();
 | 
			
		||||
        _collectVariableRunTimes.Clear();
 | 
			
		||||
        _collectDeviceRunTimes = null;
 | 
			
		||||
        _collectVariableRunTimes = null;
 | 
			
		||||
        base.Dispose(disposing);
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void Init(UploadDeviceRunTime device)
 | 
			
		||||
    {
 | 
			
		||||
        var mqttFactory = new MqttFactory(new PrivateLogger(LogMessage));
 | 
			
		||||
        var mqttServerOptions = mqttFactory.CreateServerOptionsBuilder()
 | 
			
		||||
            .WithDefaultEndpointBoundIPAddress(string.IsNullOrEmpty(driverPropertys.IP) ? null : IPAddress.Parse(driverPropertys.IP))
 | 
			
		||||
            .WithDefaultEndpointPort(driverPropertys.Port)
 | 
			
		||||
            .WithDefaultEndpoint()
 | 
			
		||||
            .Build();
 | 
			
		||||
        _mqttServer = mqttFactory.CreateMqttServer(mqttServerOptions);
 | 
			
		||||
 | 
			
		||||
        _globalDeviceData = App.GetService<GlobalDeviceData>();
 | 
			
		||||
        _rpcCore = App.GetService<RpcSingletonService>();
 | 
			
		||||
 | 
			
		||||
        var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id))
 | 
			
		||||
                .Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).GetBoolValue())
 | 
			
		||||
                .ToList();
 | 
			
		||||
 | 
			
		||||
        _uploadVariables = tags;
 | 
			
		||||
 | 
			
		||||
        _globalDeviceData.CollectDevices.Where(a => _uploadVariables.Select(b => b.DeviceId).Contains(a.Id)).ForEach(a =>
 | 
			
		||||
        {
 | 
			
		||||
            a.DeviceStatusChange += DeviceStatusChange;
 | 
			
		||||
        });
 | 
			
		||||
        _uploadVariables.ForEach(a =>
 | 
			
		||||
        {
 | 
			
		||||
            a.VariableValueChange += VariableValueChange;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void DeviceStatusChange(CollectDeviceRunTime collectDeviceRunTime)
 | 
			
		||||
    {
 | 
			
		||||
        _collectDeviceRunTimes.Enqueue(collectDeviceRunTime.Adapt<DeviceData>());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task MqttServer_InterceptingPublishAsync(InterceptingPublishEventArgs arg)
 | 
			
		||||
    {
 | 
			
		||||
        if (!driverPropertys.DeviceRpcEnable || string.IsNullOrEmpty(arg.ClientId))
 | 
			
		||||
            return;
 | 
			
		||||
        if (arg.ApplicationMessage.Topic != driverPropertys.RpcWriteTopic)
 | 
			
		||||
            return;
 | 
			
		||||
        var rpcDatas = Encoding.UTF8.GetString(arg.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 OperResult("权限不足,变量不支持写入"));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    mqttRpcResult.Success = false;
 | 
			
		||||
                    mqttRpcResult.Message.Add(rpcData.Key, new OperResult("不存在该变量"));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var result = await _rpcCore.InvokeDeviceMethodAsync(ToString() + "-" + arg.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, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var variableMessage = new MqttApplicationMessageBuilder()
 | 
			
		||||
.WithTopic($"{driverPropertys.RpcSubTopic}")
 | 
			
		||||
.WithPayload(mqttRpcResult.ToJsonString()).Build();
 | 
			
		||||
            await _mqttServer.InjectApplicationMessage(
 | 
			
		||||
                     new InjectedMqttApplicationMessage(variableMessage));
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task MqttServer_InterceptingSubscriptionAsync(InterceptingSubscriptionEventArgs arg)
 | 
			
		||||
    {
 | 
			
		||||
        if (arg.TopicFilter.Topic == driverPropertys.RpcWriteTopic)
 | 
			
		||||
        {
 | 
			
		||||
            arg.Response.ReasonCode = MqttSubscribeReasonCode.UnspecifiedError;
 | 
			
		||||
        }
 | 
			
		||||
        return CompletedTask.Instance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Task MqttServer_LoadingRetainedMessageAsync(LoadingRetainedMessagesEventArgs arg)
 | 
			
		||||
    {
 | 
			
		||||
        //首次连接时的保留消息
 | 
			
		||||
        //分解List,避免超出mqtt字节大小限制
 | 
			
		||||
        var varData = _globalDeviceData.AllVariables.Adapt<List<VariableData>>().ChunkTrivialBetter(driverPropertys.SplitSize);
 | 
			
		||||
        var devData = _globalDeviceData.AllVariables.Adapt<List<DeviceData>>().ChunkTrivialBetter(driverPropertys.SplitSize);
 | 
			
		||||
        List<MqttApplicationMessage> Messages = new();
 | 
			
		||||
        foreach (var item in varData)
 | 
			
		||||
        {
 | 
			
		||||
            Messages.Add(new MqttApplicationMessageBuilder()
 | 
			
		||||
        .WithTopic($"{driverPropertys.VariableTopic}")
 | 
			
		||||
        .WithPayload(item.GetSciptListValue(driverPropertys.BigTextScriptVariableModel)).Build());
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in devData)
 | 
			
		||||
        {
 | 
			
		||||
            Messages.Add(new MqttApplicationMessageBuilder()
 | 
			
		||||
.WithTopic($"{driverPropertys.DeviceTopic}")
 | 
			
		||||
.WithPayload(item.GetSciptListValue(driverPropertys.BigTextScriptDeviceModel)).Build());
 | 
			
		||||
        }
 | 
			
		||||
        arg.LoadedRetainedMessages = Messages;
 | 
			
		||||
        return CompletedTask.Instance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task MqttServer_ValidatingConnectionAsync(ValidatingConnectionEventArgs arg)
 | 
			
		||||
    {
 | 
			
		||||
        if (!arg.ClientId.StartsWith(driverPropertys.StartWithId))
 | 
			
		||||
        {
 | 
			
		||||
            arg.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        var _openApiUserService = App.GetService<IOpenApiUserService>();
 | 
			
		||||
        var userInfo = await _openApiUserService.GetUserByAccountAsync(arg.UserName);//获取用户信息
 | 
			
		||||
        if (userInfo == null)
 | 
			
		||||
        {
 | 
			
		||||
            arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (userInfo.Password != arg.Password)
 | 
			
		||||
        {
 | 
			
		||||
            arg.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        LogMessage?.LogInformation($"{ToString()}-{arg.ClientId}-客户端已连接成功");
 | 
			
		||||
    }
 | 
			
		||||
    private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime)
 | 
			
		||||
    {
 | 
			
		||||
        _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,154 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using 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]
 | 
			
		||||
    private InitTimezone InitTimezone { get; set; }
 | 
			
		||||
 | 
			
		||||
    [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.Message));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    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.Message));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    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.Message));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    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.Message));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,119 +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 System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Plugin.Mqtt;
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
    {
 | 
			
		||||
        MqttFactory = new MqttFactory(new PrivateLogger(new EasyLogger(LogAction) { LogLevel = LogLevel.Trace }));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        MqttClient = MqttFactory.CreateMqttClient();
 | 
			
		||||
        MqttClient.ApplicationMessageReceivedAsync += MqttClient_ApplicationMessageReceivedAsync;
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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,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 MQTTnet.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Plugin.Mqtt
 | 
			
		||||
{
 | 
			
		||||
    internal class PrivateLogger : IMqttNetLogger
 | 
			
		||||
    {
 | 
			
		||||
        readonly ILog LogMessage;
 | 
			
		||||
        public PrivateLogger(ILog logger)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage = logger;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsEnabled => true;
 | 
			
		||||
        public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
 | 
			
		||||
        {
 | 
			
		||||
            switch (logLevel)
 | 
			
		||||
            {
 | 
			
		||||
                case MqttNetLogLevel.Verbose:
 | 
			
		||||
                    LogMessage?.Log(LogLevel.Trace, source, message != null ? (parameters != null ? message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty : message) : string.Empty, exception);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MqttNetLogLevel.Info:
 | 
			
		||||
                    LogMessage?.Log(LogLevel.Info, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MqttNetLogLevel.Warning:
 | 
			
		||||
                    LogMessage?.Log(LogLevel.Warning, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case MqttNetLogLevel.Error:
 | 
			
		||||
                    LogMessage?.Log(LogLevel.Warning, source, message != null ? (parameters != null ? string.Format(message, parameters) : message) : string.Empty, exception);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +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
 | 
			
		||||
 | 
			
		||||
// Licensed to the .NET Foundation under one or more agreements.
 | 
			
		||||
// The .NET Foundation licenses this file to you under the MIT license.
 | 
			
		||||
// See the LICENSE file in the project root for more information.
 | 
			
		||||
 | 
			
		||||
using MQTTnet.Protocol;
 | 
			
		||||
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace MQTTnet.Extensions.Rpc
 | 
			
		||||
{
 | 
			
		||||
    public static class MqttRpcClientExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static Task<byte[]> ExecuteAsync(this MqttRpcClient client, MqttRpcTopicPair mqttRpcTopicPair, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel, TimeSpan timeout)
 | 
			
		||||
        {
 | 
			
		||||
            if (client == null) throw new ArgumentNullException(nameof(client));
 | 
			
		||||
 | 
			
		||||
            var buffer = Encoding.UTF8.GetBytes(payload ?? string.Empty);
 | 
			
		||||
 | 
			
		||||
            return client.ExecuteAsync(mqttRpcTopicPair, buffer, qualityOfServiceLevel, timeout);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +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
 | 
			
		||||
 | 
			
		||||
// Licensed to the .NET Foundation under one or more agreements.
 | 
			
		||||
// The .NET Foundation licenses this file to you under the MIT license.
 | 
			
		||||
// See the LICENSE file in the project root for more information.
 | 
			
		||||
 | 
			
		||||
namespace MQTTnet.Extensions.Rpc
 | 
			
		||||
{
 | 
			
		||||
    public sealed class MqttRpcTopicPair
 | 
			
		||||
    {
 | 
			
		||||
        public string RequestTopic { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string ResponseTopic { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,57 +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 Outlined 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 />
 | 
			
		||||
 | 
			
		||||
         <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,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, ToString());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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, ToString());
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    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, ToString());
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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, ToString());
 | 
			
		||||
                                    }
 | 
			
		||||
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            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, ToString());
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (Exception ex)
 | 
			
		||||
                    {
 | 
			
		||||
                        LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        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, ToString());
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    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, ToString());
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            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, ToString());
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        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, ToString());
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogMessage?.LogWarning(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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, ToString());
 | 
			
		||||
            await CacheDb.AddCacheData(queueName, data, driverPropertys.CacheMaxCount);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime)
 | 
			
		||||
    {
 | 
			
		||||
        if (driverPropertys?.IsInterval != true)
 | 
			
		||||
            _collectVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<VariableData>());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,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_300DebugPage
 | 
			
		||||
{
 | 
			
		||||
    /// <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.S300);
 | 
			
		||||
            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_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,54 +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.DestTSAP != 0)
 | 
			
		||||
            {
 | 
			
		||||
                _plc.DestTSAP = driverPropertys.DestTSAP;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,53 +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.DestTSAP != 0)
 | 
			
		||||
            {
 | 
			
		||||
                _plc.DestTSAP = driverPropertys.DestTSAP;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,53 +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.DestTSAP != 0)
 | 
			
		||||
            {
 | 
			
		||||
                _plc.DestTSAP = driverPropertys.DestTSAP;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,52 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
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.DestTSAP != 0)
 | 
			
		||||
            {
 | 
			
		||||
                _plc.DestTSAP = driverPropertys.DestTSAP;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,53 +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.DestTSAP != 0)
 | 
			
		||||
            {
 | 
			
		||||
                _plc.DestTSAP = driverPropertys.DestTSAP;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,53 +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.DestTSAP != 0)
 | 
			
		||||
            {
 | 
			
		||||
                _plc.DestTSAP = driverPropertys.DestTSAP;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -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,58 +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}
 | 
			
		||||
		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
 | 
			
		||||
		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
 | 
			
		||||
		{51313113-7BB8-494E-9C24-6787BECE39BB} = {BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}
 | 
			
		||||
		{79E7042F-F9E3-4D87-BFA9-4B7DD9736735} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
 | 
			
		||||
		{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,107 +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("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{79E7042F-F9E3-4D87-BFA9-4B7DD9736735}"
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCUA", "plugin\ThingsGateway.Plugin.OPCUA\ThingsGateway.Plugin.OPCUA.csproj", "{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateway", "Gateway", "{000C3C62-345E-451C-8CEE-6F2C6A087116}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.ApiController", "Web\ThingsGateway.Gateway.ApiController\ThingsGateway.Gateway.ApiController.csproj", "{5D7BE567-2345-46C8-9F54-DDC1DA96D198}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "Web\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F}"
 | 
			
		||||
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}") = "Core", "Core", "{BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugin", "Plugin", "{CC8D0880-B73E-4DFC-9052-86504728708E}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		Plugin\Directory.Build.props = Plugin\Directory.Build.props
 | 
			
		||||
	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}"
 | 
			
		||||
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
 | 
			
		||||
@@ -135,102 +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
 | 
			
		||||
		{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
 | 
			
		||||
@@ -242,33 +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} = {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}
 | 
			
		||||
		{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
 | 
			
		||||
		{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
 | 
			
		||||
		{51313113-7BB8-494E-9C24-6787BECE39BB} = {BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}
 | 
			
		||||
		{79E7042F-F9E3-4D87-BFA9-4B7DD9736735} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
 | 
			
		||||
		{000C3C62-345E-451C-8CEE-6F2C6A087116} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
 | 
			
		||||
		{5D7BE567-2345-46C8-9F54-DDC1DA96D198} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
 | 
			
		||||
		{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
 | 
			
		||||
		{CD0F211A-F65B-4026-9750-68AC3C70D012} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
 | 
			
		||||
		{5CD79F91-7182-4A9D-9BEF-4DF410C782D2} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
 | 
			
		||||
		{BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
 | 
			
		||||
		{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}
 | 
			
		||||
		{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,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 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.Message, AlertTypes.Error);
 | 
			
		||||
        }
 | 
			
		||||
        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,265 +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, ToString());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        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");
 | 
			
		||||
        };//有客户端正在连接
 | 
			
		||||
        service.Connected = (client, e) => { service.Logger.Info($"{client.GetIPPort()}:Connected"); };//有客户端连接
 | 
			
		||||
        service.Disconnected = (client, e) => { service.Logger.Info($"{client.GetIPPort()}:Disconnected"); };//有客户端断开连接
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user