Compare commits
	
		
			703 Commits
		
	
	
		
			10.11.22.0
			...
			3.0.0.26
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7ba9ac7a5b | ||
| 
						 | 
					85b8f26e8e | ||
| 
						 | 
					594a0f1410 | ||
| 
						 | 
					d317d757d7 | ||
| 
						 | 
					fdf0ba6318 | ||
| 
						 | 
					15bf7de5fa | ||
| 
						 | 
					d3402b058e | ||
| 
						 | 
					e7dfdd4031 | ||
| 
						 | 
					b2dd7b6364 | ||
| 
						 | 
					9bd6d9abbf | ||
| 
						 | 
					cd14428fea | ||
| 
						 | 
					19d9f03c2b | ||
| 
						 | 
					0d57e72bbf | ||
| 
						 | 
					329516a61b | ||
| 
						 | 
					d566869589 | ||
| 
						 | 
					9cb8d8e6c7 | ||
| 
						 | 
					9de3c57e5d | ||
| 
						 | 
					f32ff92b0b | ||
| 
						 | 
					88d71e271e | ||
| 
						 | 
					fd9c14612a | ||
| 
						 | 
					e26e5a160f | ||
| 
						 | 
					b836bfed22 | ||
| 
						 | 
					a4b598c6d0 | ||
| 
						 | 
					c9ab755839 | ||
| 
						 | 
					9920edba53 | ||
| 
						 | 
					12bd7280d1 | ||
| 
						 | 
					d30ea7f63b | ||
| 
						 | 
					ebd3390db6 | ||
| 
						 | 
					9a374a9ebc | ||
| 
						 | 
					b1bc22cb08 | ||
| 
						 | 
					4930d53890 | ||
| 
						 | 
					c31327b5bc | ||
| 
						 | 
					3f2aa1f1e1 | ||
| 
						 | 
					6e78c00a96 | ||
| 
						 | 
					c27dde085e | ||
| 
						 | 
					d26cc308c0 | ||
| 
						 | 
					fb1efdf290 | ||
| 
						 | 
					3c99f2a472 | ||
| 
						 | 
					affe9a44e0 | ||
| 
						 | 
					43730fa519 | ||
| 
						 | 
					d39aa22b09 | ||
| 
						 | 
					e232a6b6ea | ||
| 
						 | 
					71ebb36fe9 | ||
| 
						 | 
					78a0b86327 | ||
| 
						 | 
					2636c16a97 | ||
| 
						 | 
					fd77c0242d | ||
| 
						 | 
					e74819a900 | ||
| 
						 | 
					9b7f696c9b | ||
| 
						 | 
					0230d614e7 | ||
| 
						 | 
					252d99ad78 | ||
| 
						 | 
					1ffc200350 | ||
| 
						 | 
					807d89b2b2 | ||
| 
						 | 
					4013afa1f1 | ||
| 
						 | 
					a580927ceb | ||
| 
						 | 
					bf2cf52034 | ||
| 
						 | 
					81bb8b7c31 | ||
| 
						 | 
					a825007fb5 | ||
| 
						 | 
					988124d96a | ||
| 
						 | 
					f0de815296 | ||
| 
						 | 
					0e2d58c887 | ||
| 
						 | 
					b155382626 | ||
| 
						 | 
					f362d740af | ||
| 
						 | 
					4a85e31a4f | ||
| 
						 | 
					302c270ad5 | ||
| 
						 | 
					3c1517d0f3 | ||
| 
						 | 
					f9fb222044 | ||
| 
						 | 
					e8edc02ba3 | ||
| 
						 | 
					95a44e3053 | ||
| 
						 | 
					74a9fe9a87 | ||
| 
						 | 
					4d03f9ea1a | ||
| 
						 | 
					67c96ca991 | ||
| 
						 | 
					88fb793c68 | ||
| 
						 | 
					d6d02d8cc5 | ||
| 
						 | 
					c5a3f8e2e3 | ||
| 
						 | 
					27e8653a1a | ||
| 
						 | 
					863beda82c | ||
| 
						 | 
					bac84c3ecd | ||
| 
						 | 
					2fca2ad9f8 | ||
| 
						 | 
					dd75286fe0 | ||
| 
						 | 
					7f91792cf1 | ||
| 
						 | 
					0e0ccad311 | ||
| 
						 | 
					0691f72e67 | ||
| 
						 | 
					7e38a51720 | ||
| 
						 | 
					34ca8243a3 | ||
| 
						 | 
					112fea7632 | ||
| 
						 | 
					378763e4ee | ||
| 
						 | 
					517bd0394d | ||
| 
						 | 
					70adb97fb5 | ||
| 
						 | 
					623d44cabe | ||
| 
						 | 
					0d479ca00b | ||
| 
						 | 
					8bc49ef437 | ||
| 
						 | 
					f83fcec786 | ||
| 
						 | 
					93690ce40d | ||
| 
						 | 
					f82c5f2f27 | ||
| 
						 | 
					a83c1c3899 | ||
| 
						 | 
					91d6aed109 | ||
| 
						 | 
					db8f8fe51d | ||
| 
						 | 
					4596004b17 | ||
| 
						 | 
					d5540906cb | ||
| 
						 | 
					90796a979d | ||
| 
						 | 
					2190a87772 | ||
| 
						 | 
					c5953b83f8 | ||
| 
						 | 
					24bc60abf0 | ||
| 
						 | 
					31eee6b009 | ||
| 
						 | 
					c5da565a8f | ||
| 
						 | 
					947cd712e1 | ||
| 
						 | 
					edc208f96b | ||
| 
						 | 
					1fb0296ee7 | ||
| 
						 | 
					6488d3df87 | ||
| 
						 | 
					56189d78e0 | ||
| 
						 | 
					bff18127b8 | ||
| 
						 | 
					363206e0ba | ||
| 
						 | 
					fd3e378501 | ||
| 
						 | 
					4ba2fe4c9d | ||
| 
						 | 
					2c499626ad | ||
| 
						 | 
					2b581a03c3 | ||
| 
						 | 
					450c15210a | ||
| 
						 | 
					65fed8cc93 | ||
| 
						 | 
					4b64771ea2 | ||
| 
						 | 
					f39977a6ff | ||
| 
						 | 
					933b535caa | ||
| 
						 | 
					8abc5d2f20 | ||
| 
						 | 
					d8783cd994 | ||
| 
						 | 
					d5d087feb5 | ||
| 
						 | 
					6ba3399df7 | ||
| 
						 | 
					65124b3aa8 | ||
| 
						 | 
					98597f4726 | ||
| 
						 | 
					e7981f0d8e | ||
| 
						 | 
					cf654427c3 | ||
| 
						 | 
					ff2f628282 | ||
| 
						 | 
					ae818ca265 | ||
| 
						 | 
					0f2aed458e | ||
| 
						 | 
					d486c44ff6 | ||
| 
						 | 
					ca7b9980bf | ||
| 
						 | 
					3c71e6a8e3 | ||
| 
						 | 
					542442864c | ||
| 
						 | 
					5edb64fa85 | ||
| 
						 | 
					8dc1c898a3 | ||
| 
						 | 
					1ed35726b0 | ||
| 
						 | 
					27fae9ebaa | ||
| 
						 | 
					b103f25c94 | ||
| 
						 | 
					abff450274 | ||
| 
						 | 
					c260736a11 | ||
| 
						 | 
					166ac2307a | ||
| 
						 | 
					b21a4e1a4d | ||
| 
						 | 
					f7dc943fa3 | ||
| 
						 | 
					bfbd2693ec | ||
| 
						 | 
					819e71c993 | ||
| 
						 | 
					9fd0b489a2 | ||
| 
						 | 
					f5fe9f8dae | ||
| 
						 | 
					f9ffc18145 | ||
| 
						 | 
					08db5b983a | ||
| 
						 | 
					5b3b4c8c50 | ||
| 
						 | 
					73f914ffc4 | ||
| 
						 | 
					d6bdd73ed6 | ||
| 
						 | 
					7370ee7349 | ||
| 
						 | 
					4574596bac | ||
| 
						 | 
					4d16855e36 | ||
| 
						 | 
					13a0d4d282 | ||
| 
						 | 
					b9cd06b829 | ||
| 
						 | 
					5b460e8fa2 | ||
| 
						 | 
					41087edf17 | ||
| 
						 | 
					2afcc38e38 | ||
| 
						 | 
					e59ccce25f | ||
| 
						 | 
					d7425890e8 | ||
| 
						 | 
					a989a837fb | ||
| 
						 | 
					db1221da50 | ||
| 
						 | 
					cf794569ed | ||
| 
						 | 
					51e5bbab0d | ||
| 
						 | 
					2c197ed2b2 | ||
| 
						 | 
					d8fc6665b3 | ||
| 
						 | 
					c671a79822 | ||
| 
						 | 
					9d93ce4c41 | ||
| 
						 | 
					a6d99fe227 | ||
| 
						 | 
					923b8bca31 | ||
| 
						 | 
					e2c30d1c88 | ||
| 
						 | 
					b6d9f2a04e | ||
| 
						 | 
					57306ea664 | ||
| 
						 | 
					cd7f3fd02f | ||
| 
						 | 
					0482e077a8 | ||
| 
						 | 
					5f986a45ca | ||
| 
						 | 
					ca7b49c0d5 | ||
| 
						 | 
					52dd555e6c | ||
| 
						 | 
					579b1a59f9 | ||
| 
						 | 
					5299c5c4be | ||
| 
						 | 
					f7756bccef | ||
| 
						 | 
					a6b874d160 | ||
| 
						 | 
					3e5fb3ddcf | ||
| 
						 | 
					5e6bcb12d3 | ||
| 
						 | 
					14303f1429 | ||
| 
						 | 
					96711ba022 | ||
| 
						 | 
					cbfc0fdbdc | ||
| 
						 | 
					6e81886c0e | ||
| 
						 | 
					2d976bc132 | ||
| 
						 | 
					57f6a476af | ||
| 
						 | 
					8491ed296e | ||
| 
						 | 
					cd1288afdc | ||
| 
						 | 
					ec6c830cb0 | ||
| 
						 | 
					2f86ccc4bf | ||
| 
						 | 
					8ca445aec0 | ||
| 
						 | 
					1e1f27c8a5 | ||
| 
						 | 
					2b84bde367 | ||
| 
						 | 
					b52e58551d | ||
| 
						 | 
					9aceed00bf | ||
| 
						 | 
					58814f7f74 | ||
| 
						 | 
					6a70ef9f31 | ||
| 
						 | 
					82cc4ca500 | ||
| 
						 | 
					4567fa04ed | ||
| 
						 | 
					8b98b5d818 | ||
| 
						 | 
					176d0351af | ||
| 
						 | 
					d63dc3384b | ||
| 
						 | 
					1ccd704e30 | ||
| 
						 | 
					f5d23dbe79 | ||
| 
						 | 
					75bfe53ac3 | ||
| 
						 | 
					3308f916dd | ||
| 
						 | 
					e7140279ca | ||
| 
						 | 
					1034719f5e | ||
| 
						 | 
					2c00043a7f | ||
| 
						 | 
					65c695d9ce | ||
| 
						 | 
					57253fe46a | ||
| 
						 | 
					4e5c443440 | ||
| 
						 | 
					0b3b73d8ec | ||
| 
						 | 
					921eabc134 | ||
| 
						 | 
					0faa428751 | ||
| 
						 | 
					f71a2fdd63 | ||
| 
						 | 
					4eb9ed8aba | ||
| 
						 | 
					d7b549abb8 | ||
| 
						 | 
					95d723c578 | ||
| 
						 | 
					2fcd853e86 | ||
| 
						 | 
					07eef7c812 | ||
| 
						 | 
					b01e0757fa | ||
| 
						 | 
					32844a20c6 | ||
| 
						 | 
					5b6532c601 | ||
| 
						 | 
					2c5b4b4027 | ||
| 
						 | 
					72d7ecf195 | ||
| 
						 | 
					2cfa6b4306 | ||
| 
						 | 
					6f6ffde0ab | ||
| 
						 | 
					1694739a16 | ||
| 
						 | 
					95d1e8bfca | ||
| 
						 | 
					60dec08e3c | ||
| 
						 | 
					a99d71be93 | ||
| 
						 | 
					f1331b6a0c | ||
| 
						 | 
					10d66b642b | ||
| 
						 | 
					cd2310e4a8 | ||
| 
						 | 
					1b399cf6b0 | ||
| 
						 | 
					877445bc0a | ||
| 
						 | 
					9a5b345bde | ||
| 
						 | 
					fc9e8ea7b3 | ||
| 
						 | 
					32be6fcfc1 | ||
| 
						 | 
					49847236c2 | ||
| 
						 | 
					d8424443e6 | ||
| 
						 | 
					f3b571ec3f | ||
| 
						 | 
					99318bb5d7 | ||
| 
						 | 
					1aa154c9aa | ||
| 
						 | 
					c65d8a445b | ||
| 
						 | 
					80f4f85570 | ||
| 
						 | 
					5beee43a6b | ||
| 
						 | 
					8d6ae203a0 | ||
| 
						 | 
					4353479a5c | ||
| 
						 | 
					34d7687f9e | ||
| 
						 | 
					b1dc3cf4af | ||
| 
						 | 
					6a58b95933 | ||
| 
						 | 
					d3badfd02b | ||
| 
						 | 
					0098be057b | ||
| 
						 | 
					6f972aa515 | ||
| 
						 | 
					7407ba6313 | ||
| 
						 | 
					1c79de207b | ||
| 
						 | 
					257c79db92 | ||
| 
						 | 
					9d1934a308 | ||
| 
						 | 
					d70f959902 | ||
| 
						 | 
					e4d810222f | ||
| 
						 | 
					bc1af4ae07 | ||
| 
						 | 
					6e688ef43f | ||
| 
						 | 
					f0fe1b23dc | ||
| 
						 | 
					aaf2006401 | ||
| 
						 | 
					b821e26935 | ||
| 
						 | 
					7ae4287157 | ||
| 
						 | 
					c6fcc38a65 | ||
| 
						 | 
					ab2d5c8853 | ||
| 
						 | 
					5e557ff0bc | ||
| 
						 | 
					918ca449a1 | ||
| 
						 | 
					8e73368008 | ||
| 
						 | 
					f3c1faf672 | ||
| 
						 | 
					d6df04dd6a | ||
| 
						 | 
					b1b9e51ab6 | ||
| 
						 | 
					e49d4770ac | ||
| 
						 | 
					8fa1075511 | ||
| 
						 | 
					9a70169b94 | ||
| 
						 | 
					fefb928237 | ||
| 
						 | 
					ad7e700d0d | ||
| 
						 | 
					1699c69147 | ||
| 
						 | 
					1695f7cece | ||
| 
						 | 
					052c27f907 | ||
| 
						 | 
					dc46c32b30 | ||
| 
						 | 
					fa63349bb2 | ||
| 
						 | 
					ffe26448a6 | ||
| 
						 | 
					4af51e8a84 | ||
| 
						 | 
					1e453cf5a5 | ||
| 
						 | 
					591282b87d | ||
| 
						 | 
					e87528d520 | ||
| 
						 | 
					d79eb0411d | ||
| 
						 | 
					ac1e0a4cf7 | ||
| 
						 | 
					9525eab130 | ||
| 
						 | 
					89b317496c | ||
| 
						 | 
					13be91e78b | ||
| 
						 | 
					f68c1437f3 | ||
| 
						 | 
					4c64c969bb | ||
| 
						 | 
					b4bf3b5138 | ||
| 
						 | 
					083bc4b400 | ||
| 
						 | 
					e8683c5bcc | ||
| 
						 | 
					80e0d1de91 | ||
| 
						 | 
					dbe841037e | ||
| 
						 | 
					bdd537c33c | ||
| 
						 | 
					c0c3846094 | ||
| 
						 | 
					9e8710e7d2 | ||
| 
						 | 
					475553fdf6 | ||
| 
						 | 
					9d570f5b45 | ||
| 
						 | 
					af7fafd34f | ||
| 
						 | 
					d43130f4fc | ||
| 
						 | 
					7500194620 | ||
| 
						 | 
					eb27c29144 | ||
| 
						 | 
					43260b3e24 | ||
| 
						 | 
					f80713f0aa | ||
| 
						 | 
					0c4bdc7ad1 | ||
| 
						 | 
					811cff7bd0 | ||
| 
						 | 
					30269aa75c | ||
| 
						 | 
					e345ef7083 | ||
| 
						 | 
					f559c9b8f7 | ||
| 
						 | 
					f4af0916b2 | ||
| 
						 | 
					f15f14f28d | ||
| 
						 | 
					834f44f58d | ||
| 
						 | 
					b36f45dcf4 | ||
| 
						 | 
					11ba21c9a8 | ||
| 
						 | 
					b045557ce1 | ||
| 
						 | 
					0dd251a3f6 | ||
| 
						 | 
					793acb1725 | ||
| 
						 | 
					921243e8bd | ||
| 
						 | 
					bd9d7a90d9 | ||
| 
						 | 
					cc444a4cea | ||
| 
						 | 
					38ca1fa168 | ||
| 
						 | 
					7a552b87ec | ||
| 
						 | 
					36923d3190 | ||
| 
						 | 
					a9d3017123 | ||
| 
						 | 
					313acd4976 | ||
| 
						 | 
					a4c91bb268 | ||
| 
						 | 
					f9b566984b | ||
| 
						 | 
					8dd261854d | ||
| 
						 | 
					7351e62d87 | ||
| 
						 | 
					0593ae720b | ||
| 
						 | 
					a0a7b08e08 | ||
| 
						 | 
					9a3bc6b8b3 | ||
| 
						 | 
					5acae17f71 | ||
| 
						 | 
					f1e5b76ef2 | ||
| 
						 | 
					53c628fde9 | ||
| 
						 | 
					baca0a70c0 | ||
| 
						 | 
					3e8d0af404 | ||
| 
						 | 
					cf9a91d9d5 | ||
| 
						 | 
					02b9e282c6 | ||
| 
						 | 
					9ce87f235f | ||
| 
						 | 
					e329bea1b2 | ||
| 
						 | 
					8086e7b54d | ||
| 
						 | 
					f7a875606e | ||
| 
						 | 
					196eaf85f4 | ||
| 
						 | 
					876a55668e | ||
| 
						 | 
					05bd21bdd5 | ||
| 
						 | 
					fb51a08cc6 | ||
| 
						 | 
					dd83d7f4d3 | ||
| 
						 | 
					842a56f7ce | ||
| 
						 | 
					9246a6e797 | ||
| 
						 | 
					8ad693f717 | ||
| 
						 | 
					f4c2ee7cc4 | ||
| 
						 | 
					6043441faa | ||
| 
						 | 
					4a065c3710 | ||
| 
						 | 
					0ef800bdd7 | ||
| 
						 | 
					56eaa1910d | ||
| 
						 | 
					201788e286 | ||
| 
						 | 
					506e0f144f | ||
| 
						 | 
					72f68bfdd9 | ||
| 
						 | 
					2f9869b11d | ||
| 
						 | 
					8ffcf6498c | ||
| 
						 | 
					d224ae1923 | ||
| 
						 | 
					fed2063a19 | ||
| 
						 | 
					db2810cdd7 | ||
| 
						 | 
					4f1a6781ef | ||
| 
						 | 
					beffa5d5a4 | ||
| 
						 | 
					7a20f1de07 | ||
| 
						 | 
					cd25cf726b | ||
| 
						 | 
					d6b1bc3842 | ||
| 
						 | 
					a4385fb9bb | ||
| 
						 | 
					7045f2b8ea | ||
| 
						 | 
					07ca1a4de8 | ||
| 
						 | 
					24f289e692 | ||
| 
						 | 
					01bcdaae2d | ||
| 
						 | 
					55890008d1 | ||
| 
						 | 
					5ab9b01879 | ||
| 
						 | 
					e4abb333b3 | ||
| 
						 | 
					09f476c745 | ||
| 
						 | 
					8806e68dce | ||
| 
						 | 
					2ef1e25cd8 | ||
| 
						 | 
					10e7f202aa | ||
| 
						 | 
					ccd7000c09 | ||
| 
						 | 
					8ee7b798cf | ||
| 
						 | 
					7733cf5bf0 | ||
| 
						 | 
					a05ce86dd7 | ||
| 
						 | 
					91f51c32e8 | ||
| 
						 | 
					f910202bba | ||
| 
						 | 
					6d77194a8f | ||
| 
						 | 
					9deb89c15f | ||
| 
						 | 
					4b62a092b4 | ||
| 
						 | 
					81c8f626f9 | ||
| 
						 | 
					3e846c42fb | ||
| 
						 | 
					63ad7fd766 | ||
| 
						 | 
					9ff1e9aa34 | ||
| 
						 | 
					8d162b6f3d | ||
| 
						 | 
					9844d10bef | ||
| 
						 | 
					b908fa8489 | ||
| 
						 | 
					15a10643a7 | ||
| 
						 | 
					299617aca1 | ||
| 
						 | 
					45647d697a | ||
| 
						 | 
					48f5105d38 | ||
| 
						 | 
					fe1c741d68 | ||
| 
						 | 
					fa42cc1f00 | ||
| 
						 | 
					42cf5e7a81 | ||
| 
						 | 
					47905e1aa1 | ||
| 
						 | 
					9a8e907df3 | ||
| 
						 | 
					106fe85582 | ||
| 
						 | 
					4b3571bd57 | ||
| 
						 | 
					96b537401a | ||
| 
						 | 
					721c9eb057 | ||
| 
						 | 
					51701bf6d6 | ||
| 
						 | 
					dbde68bd56 | ||
| 
						 | 
					ad2c9f585a | ||
| 
						 | 
					562093c468 | ||
| 
						 | 
					b0295584a3 | ||
| 
						 | 
					208c54de98 | ||
| 
						 | 
					63e2d941a1 | ||
| 
						 | 
					3956838e9c | ||
| 
						 | 
					abeee58bb0 | ||
| 
						 | 
					d5b1b49722 | ||
| 
						 | 
					564ed03ff8 | ||
| 
						 | 
					70db4c76b4 | ||
| 
						 | 
					d059f7975b | ||
| 
						 | 
					4e74e6dc2d | ||
| 
						 | 
					b6deb96658 | ||
| 
						 | 
					3839e966be | ||
| 
						 | 
					3dd035849c | ||
| 
						 | 
					3d6532b5d6 | ||
| 
						 | 
					bf7c175ee7 | ||
| 
						 | 
					f84af35ed6 | ||
| 
						 | 
					99063b3eb1 | ||
| 
						 | 
					3bec18f28d | ||
| 
						 | 
					15de7a7894 | ||
| 
						 | 
					e20e04e677 | ||
| 
						 | 
					5fc6ae2835 | ||
| 
						 | 
					7d281b8c96 | ||
| 
						 | 
					4880b801a7 | ||
| 
						 | 
					74e354456a | ||
| 
						 | 
					af2e03aa36 | ||
| 
						 | 
					d8fa660ab6 | ||
| 
						 | 
					1a62d48297 | ||
| 
						 | 
					7ba01be13d | ||
| 
						 | 
					1a83d64db7 | ||
| 
						 | 
					5b53014c40 | ||
| 
						 | 
					83685340af | ||
| 
						 | 
					31e0cc4dec | ||
| 
						 | 
					56b87fc1f5 | ||
| 
						 | 
					6b956a2dd7 | ||
| 
						 | 
					1937623d7d | ||
| 
						 | 
					3b60b10945 | ||
| 
						 | 
					7173acd350 | ||
| 
						 | 
					6310d87338 | ||
| 
						 | 
					49a1ed7c18 | ||
| 
						 | 
					d426e280d9 | ||
| 
						 | 
					6154fb29f1 | ||
| 
						 | 
					97d48ef9d6 | ||
| 
						 | 
					88992625c4 | ||
| 
						 | 
					bc6eb44218 | ||
| 
						 | 
					cf9ccd799d | ||
| 
						 | 
					ffa0e4e771 | ||
| 
						 | 
					60fa9c196c | ||
| 
						 | 
					df860d22fb | ||
| 
						 | 
					cb46ff326c | ||
| 
						 | 
					f277a853ef | ||
| 
						 | 
					9ae34f67c3 | ||
| 
						 | 
					c9223218cc | ||
| 
						 | 
					c0dd645aba | ||
| 
						 | 
					2e948eb5b6 | ||
| 
						 | 
					c3276889cf | ||
| 
						 | 
					a76ca8282d | ||
| 
						 | 
					8ce6b8362f | ||
| 
						 | 
					842fb12f05 | ||
| 
						 | 
					d63e1511af | ||
| 
						 | 
					278783b8e0 | ||
| 
						 | 
					d24e3c922d | ||
| 
						 | 
					1d02cd2283 | ||
| 
						 | 
					8edeb82a87 | ||
| 
						 | 
					146e9279de | ||
| 
						 | 
					47105f50a9 | ||
| 
						 | 
					16c9c80f37 | ||
| 
						 | 
					8e7e4bc95a | ||
| 
						 | 
					0aa3d2f930 | ||
| 
						 | 
					ce77755a1e | ||
| 
						 | 
					0f31f20c87 | ||
| 
						 | 
					ee6da2aaa5 | ||
| 
						 | 
					a35f087cd9 | ||
| 
						 | 
					6e029b44dd | ||
| 
						 | 
					973c0cff34 | ||
| 
						 | 
					2027eea6ac | ||
| 
						 | 
					2f43692f33 | ||
| 
						 | 
					6d24992f88 | ||
| 
						 | 
					b4388a58d6 | ||
| 
						 | 
					158aa05fac | ||
| 
						 | 
					f2731bf55e | ||
| 
						 | 
					7304e99fce | ||
| 
						 | 
					02700b83eb | ||
| 
						 | 
					676b25acf9 | ||
| 
						 | 
					556359ea2d | ||
| 
						 | 
					b72923e0f5 | ||
| 
						 | 
					115ac9f75e | ||
| 
						 | 
					32e36f6708 | ||
| 
						 | 
					d949b7a4f9 | ||
| 
						 | 
					eae1171ff5 | ||
| 
						 | 
					76a1b75a51 | ||
| 
						 | 
					8882c0daea | ||
| 
						 | 
					07ebc16d59 | ||
| 
						 | 
					0ceb109964 | ||
| 
						 | 
					118b0d0038 | ||
| 
						 | 
					5e87067792 | ||
| 
						 | 
					c946a252e8 | ||
| 
						 | 
					f9ad2ba1dd | ||
| 
						 | 
					0d0ecd33bd | ||
| 
						 | 
					e4b98fd05b | ||
| 
						 | 
					95a5933303 | ||
| 
						 | 
					da3b55fa64 | ||
| 
						 | 
					fbbabfb90e | ||
| 
						 | 
					f13da6830d | ||
| 
						 | 
					f560a8e2f8 | ||
| 
						 | 
					56f1139c2f | ||
| 
						 | 
					773bdfc1e2 | ||
| 
						 | 
					f449666628 | ||
| 
						 | 
					3f282de0ab | ||
| 
						 | 
					440dd8d22f | ||
| 
						 | 
					dcff9de2f7 | ||
| 
						 | 
					a192866543 | ||
| 
						 | 
					10081416de | ||
| 
						 | 
					e2bed618f9 | ||
| 
						 | 
					03ab1f3823 | ||
| 
						 | 
					ac8aeb63d9 | ||
| 
						 | 
					2e16d822fa | ||
| 
						 | 
					e407d873fa | ||
| 
						 | 
					fd712a1dbe | ||
| 
						 | 
					e9028b40ce | ||
| 
						 | 
					c9da3dee7c | ||
| 
						 | 
					c8c224e202 | ||
| 
						 | 
					f34559daaf | ||
| 
						 | 
					9fefbf4c27 | ||
| 
						 | 
					1af9fd73ea | ||
| 
						 | 
					75ef394eff | ||
| 
						 | 
					ec6cc2c63e | ||
| 
						 | 
					06bc2e192b | ||
| 
						 | 
					78701ec7c1 | ||
| 
						 | 
					c925fab7e4 | ||
| 
						 | 
					42fd72c164 | ||
| 
						 | 
					7fd160e1a2 | ||
| 
						 | 
					97a0d940eb | ||
| 
						 | 
					efaa099d81 | ||
| 
						 | 
					47864a804b | ||
| 
						 | 
					91136c0e43 | ||
| 
						 | 
					28c3b1bd61 | ||
| 
						 | 
					551352bc40 | ||
| 
						 | 
					e73c24c925 | ||
| 
						 | 
					7ec4c286cc | ||
| 
						 | 
					6705e2ec4b | ||
| 
						 | 
					6f0373063b | ||
| 
						 | 
					f64eef60b5 | ||
| 
						 | 
					89546bf86b | ||
| 
						 | 
					793678feca | ||
| 
						 | 
					923cc3019a | ||
| 
						 | 
					10eb98a5f6 | ||
| 
						 | 
					bd9e89d8dd | ||
| 
						 | 
					1926b4ce73 | ||
| 
						 | 
					4ef3062d74 | ||
| 
						 | 
					abb6e0f60f | ||
| 
						 | 
					f204d8d84e | ||
| 
						 | 
					fa301656f1 | ||
| 
						 | 
					7e1221028f | ||
| 
						 | 
					41308cb2dd | ||
| 
						 | 
					130600521c | ||
| 
						 | 
					cd57548a48 | ||
| 
						 | 
					efacc99f76 | ||
| 
						 | 
					f0d236e172 | ||
| 
						 | 
					a8118bd8c6 | ||
| 
						 | 
					0e58f2ef53 | ||
| 
						 | 
					f4b22b3a0c | ||
| 
						 | 
					df5bd281c7 | ||
| 
						 | 
					a3f23837ce | ||
| 
						 | 
					612d989b97 | ||
| 
						 | 
					42c01ee9a2 | ||
| 
						 | 
					14074db591 | ||
| 
						 | 
					43dfdd7942 | ||
| 
						 | 
					f397b97ccf | ||
| 
						 | 
					95f8716144 | ||
| 
						 | 
					17ba472b2e | ||
| 
						 | 
					42d82571ab | ||
| 
						 | 
					9119a28141 | ||
| 
						 | 
					a32263d838 | ||
| 
						 | 
					208ae2bb88 | ||
| 
						 | 
					4d85462a85 | ||
| 
						 | 
					f601aa9ca0 | ||
| 
						 | 
					8aee3ad455 | ||
| 
						 | 
					6a2a1e9561 | ||
| 
						 | 
					5f8786c9dc | ||
| 
						 | 
					73f1d3eead | ||
| 
						 | 
					2bf21bb3c3 | ||
| 
						 | 
					f80f0dbb11 | ||
| 
						 | 
					37518c70c4 | ||
| 
						 | 
					e5951b5bef | ||
| 
						 | 
					ab320bd90b | ||
| 
						 | 
					7bd36b5371 | ||
| 
						 | 
					b882b0f2bc | ||
| 
						 | 
					38d7ae73cc | ||
| 
						 | 
					4527c6ee5d | ||
| 
						 | 
					85829e70c1 | ||
| 
						 | 
					256c08d82a | ||
| 
						 | 
					c2ce03c047 | ||
| 
						 | 
					f2af19e198 | ||
| 
						 | 
					930b7c092d | ||
| 
						 | 
					00757c69c6 | ||
| 
						 | 
					55f267d0fc | ||
| 
						 | 
					6b96aff6e8 | ||
| 
						 | 
					32b773a8fa | ||
| 
						 | 
					03089adad6 | ||
| 
						 | 
					4a1fe746ab | ||
| 
						 | 
					aa52c05d2c | ||
| 
						 | 
					26407a43e7 | ||
| 
						 | 
					a02934bf19 | ||
| 
						 | 
					09c65fba09 | ||
| 
						 | 
					4305c727d0 | ||
| 
						 | 
					188339897f | ||
| 
						 | 
					4ecff9a707 | ||
| 
						 | 
					355aed49c6 | ||
| 
						 | 
					4717b6b0f0 | ||
| 
						 | 
					45ebe9048d | ||
| 
						 | 
					b2170c49a3 | ||
| 
						 | 
					dc2f4d6115 | ||
| 
						 | 
					1eb132440f | ||
| 
						 | 
					a464bbc37a | ||
| 
						 | 
					ed995697c2 | ||
| 
						 | 
					163cd84c7b | ||
| 
						 | 
					293d7cc292 | ||
| 
						 | 
					5de1b4e74c | ||
| 
						 | 
					7b474975da | ||
| 
						 | 
					beab51516b | ||
| 
						 | 
					fe8685a50c | ||
| 
						 | 
					f9af5d0885 | ||
| 
						 | 
					e8136a9720 | ||
| 
						 | 
					531e5d4556 | ||
| 
						 | 
					e66255963a | ||
| 
						 | 
					246aac8ee4 | ||
| 
						 | 
					23cfeff685 | ||
| 
						 | 
					a5e7e0d126 | ||
| 
						 | 
					5bebc30ba0 | ||
| 
						 | 
					0e7057f5b9 | ||
| 
						 | 
					7c6c365ba4 | ||
| 
						 | 
					424c9bb0c5 | ||
| 
						 | 
					9d0f26594c | ||
| 
						 | 
					99c17de079 | ||
| 
						 | 
					b1e3dd0af6 | ||
| 
						 | 
					261cb89530 | ||
| 
						 | 
					ff6773ba37 | ||
| 
						 | 
					bdfbbfcbbd | ||
| 
						 | 
					0c4cd56758 | ||
| 
						 | 
					4a36658321 | ||
| 
						 | 
					7aae938685 | ||
| 
						 | 
					3723401e7a | ||
| 
						 | 
					70631366a9 | ||
| 
						 | 
					0e40bbda3e | ||
| 
						 | 
					e9aa475398 | ||
| 
						 | 
					8d2a811184 | ||
| 
						 | 
					dd7f5b6700 | ||
| 
						 | 
					a4f6277737 | ||
| 
						 | 
					c2bfaacbb7 | ||
| 
						 | 
					a17cbfa2d4 | ||
| 
						 | 
					fb9a101555 | ||
| 
						 | 
					e319cf0200 | ||
| 
						 | 
					0a8395ef6a | ||
| 
						 | 
					38df5e01be | ||
| 
						 | 
					ebd891a868 | ||
| 
						 | 
					4ab2395cbe | ||
| 
						 | 
					5f1f989fc9 | ||
| 
						 | 
					44b709eee3 | ||
| 
						 | 
					d0d7726597 | ||
| 
						 | 
					054c342aeb | ||
| 
						 | 
					c79c33baf7 | ||
| 
						 | 
					23b00e35b2 | ||
| 
						 | 
					fe51079266 | ||
| 
						 | 
					0791b0bbee | ||
| 
						 | 
					dbf04c8eeb | ||
| 
						 | 
					6204256df8 | ||
| 
						 | 
					93cc8c2327 | ||
| 
						 | 
					68a2e5bbbc | ||
| 
						 | 
					72792153f2 | ||
| 
						 | 
					88b6ef1897 | 
							
								
								
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -362,10 +362,5 @@ MigrationBackup/
 | 
			
		||||
# Fody - auto-generated XML schema
 | 
			
		||||
FodyWeavers.xsd
 | 
			
		||||
 | 
			
		||||
/src/*Pro*/
 | 
			
		||||
/src/*Pro*
 | 
			
		||||
/src/**/*Pro*
 | 
			
		||||
/src/*pro*
 | 
			
		||||
/src/*pro*/
 | 
			
		||||
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
 | 
			
		||||
/src/.idea/
 | 
			
		||||
 | 
			
		||||
/framework/*Pro*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										202
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										202
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,202 +0,0 @@
 | 
			
		||||
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is field.
 | 
			
		||||
 | 
			
		||||
   4. Cachetribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright 2023-present Diego
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
							
								
								
									
										202
									
								
								LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright 2023-present Diego
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
							
								
								
									
										103
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								README.md
									
									
									
									
									
								
							@@ -1,101 +1,38 @@
 | 
			
		||||
 | 
			
		||||
# ThingsGateway
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 介绍
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 **NetCore** 跨平台边缘采集网关(工业设备采集)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
A cross-platform, high-performance edge data collection gateway based on net9.
 | 
			
		||||
 | 
			
		||||
 **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[Documentation](https://thingsgateway.cn/).
 | 
			
		||||
 | 
			
		||||
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
 **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Demo
 | 
			
		||||
## 文档
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[Demo](https://demo.thingsgateway.cn/)
 | 
			
		||||
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Account: **SuperAdmin**
 | 
			
		||||
## 协议
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Password: **111111**
 | 
			
		||||
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
 | 
			
		||||
## 演示
 | 
			
		||||
 | 
			
		||||
## Docker
 | 
			
		||||
[ThingsGateway演示地址](http://120.24.62.140:5000/)
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
账户	:  **superAdmin**	
 | 
			
		||||
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
 | 
			
		||||
密码 : **111111**
 | 
			
		||||
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
 | 
			
		||||
```
 | 
			
		||||
## 赞助
 | 
			
		||||
 | 
			
		||||
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
 | 
			
		||||
 | 
			
		||||
## 社区
 | 
			
		||||
 | 
			
		||||
QQ群:605534569
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Plugin List
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### Data Collection Plugins
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| Plugin Name | Remarks                                                       |
 | 
			
		||||
| ----------- | ------------------------------------------------------------- |
 | 
			
		||||
| Modbus      | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links   |
 | 
			
		||||
| SiemensS7   | Siemens PLC S7 series                                         |
 | 
			
		||||
| Dlt6452007  | Supports Serial/Tcp/Udp links                                 |
 | 
			
		||||
| OpcDaMaster | Compiled for 64-bit                                           |
 | 
			
		||||
| OpcUaMaster | Supports certificate login, object extension, Json read/write |
 | 
			
		||||
| Webhook          | Webhook                                             |
 | 
			
		||||
 | 
			
		||||
#### Business Plugins
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| Plugin Name      | Remarks                                                                                           |
 | 
			
		||||
| ---------------- | ------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| ModbusSlave      | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links, supports Rpc reverse writing         |
 | 
			
		||||
| OpcUaServer      | OpcUa server, supports Rpc reverse writing                                                        |
 | 
			
		||||
| MqttClient       | Mqtt client, supports Rpc reverse writing, script-customizable upload content                     |
 | 
			
		||||
| MqttServer       | Mqtt server, supports WebSocket, supports Rpc reverse writing, script-customizable upload content |
 | 
			
		||||
| KafkaProducer    | Script-customizable upload content                                                                |
 | 
			
		||||
| RabbitMQProducer | Script-customizable upload content                                                                |
 | 
			
		||||
| SqlDB            | Relational database storage, supports historical storage and real-time data updates               |
 | 
			
		||||
| SqlHistoryAlarm      | Alarm historical data relational database storage                                                 |
 | 
			
		||||
| TDengineDB       | Time-series database storage                                                                      |
 | 
			
		||||
| QuestDB          | Time-series database storage                                                                      |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[License](https://thingsgateway.cn/docs/1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Sponsorship
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[Sponsorship Approach](https://thingsgateway.cn/docs/1000)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Community
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Pro Plugins
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[Plugin List](https://thingsgateway.cn/docs/1001)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
# ThingsGateway
 | 
			
		||||
 | 
			
		||||
## 介绍
 | 
			
		||||
 | 
			
		||||
基于net9的跨平台高性能边缘采集网关
 | 
			
		||||
 | 
			
		||||
## 文档
 | 
			
		||||
 | 
			
		||||
[文档](https://thingsgateway.cn/)
 | 
			
		||||
 | 
			
		||||
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 演示
 | 
			
		||||
 | 
			
		||||
[ThingsGateway演示地址](https://demo.thingsgateway.cn/)
 | 
			
		||||
 | 
			
		||||
账户	:  **SuperAdmin**
 | 
			
		||||
 | 
			
		||||
密码 : **111111**
 | 
			
		||||
 | 
			
		||||
**右上角个人弹出框中,切换到物联网关模块**
 | 
			
		||||
 | 
			
		||||
## Docker
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
 | 
			
		||||
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 插件列表
 | 
			
		||||
 | 
			
		||||
#### 采集插件
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 插件名称    | 备注                                  |
 | 
			
		||||
| ----------- | ------------------------------------- |
 | 
			
		||||
| Modbus      | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路 |
 | 
			
		||||
| SiemensS7   | 西门子PLC S7系列                      |
 | 
			
		||||
| Dlt6452007  | 支持串口/Tcp/Udp链路                  |
 | 
			
		||||
| OpcDaMaster | 64位编译                              |
 | 
			
		||||
| OpcUaMaster | 支持证书登录,扩展对象,Json读写      |
 | 
			
		||||
 | 
			
		||||
#### 业务插件
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 插件名称         | 备注                                                       |
 | 
			
		||||
| ---------------- | ---------------------------------------------------------- |
 | 
			
		||||
| ModbusSlave      | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路,支持Rpc反写         |
 | 
			
		||||
| OpcUaServer      | OpcUa服务端,支持Rpc反写                                   |
 | 
			
		||||
| MqttClient       | Mqtt客户端,支持Rpc反写,脚本自定义上传内容                |
 | 
			
		||||
| MqttServer       | Mqtt服务端,支持WebSocket,支持Rpc反写,脚本自定义上传内容 |
 | 
			
		||||
| KafkaProducer    | 脚本自定义上传内容                                         |
 | 
			
		||||
| RabbitMQProducer | 脚本自定义上传内容                                         |
 | 
			
		||||
| SqlDB            | 关系数据库存储,支持历史存储和实时数据更新                 |
 | 
			
		||||
| SqlHistoryAlarm      | 报警历史数据关系数据库存储                                 |
 | 
			
		||||
| TDengineDB       | 时序数据库存储                                             |
 | 
			
		||||
| QuestDB          | 时序数据库存储                                             |
 | 
			
		||||
| Webhook          | Webhook                                             |
 | 
			
		||||
 | 
			
		||||
## 协议
 | 
			
		||||
 | 
			
		||||
[版权声明](https://thingsgateway.cn/docs/1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 赞助
 | 
			
		||||
 | 
			
		||||
[赞助途径](https://thingsgateway.cn/docs/1000)
 | 
			
		||||
 | 
			
		||||
## 社区
 | 
			
		||||
 | 
			
		||||
QQ群:605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
 | 
			
		||||
 | 
			
		||||
## Pro插件
 | 
			
		||||
 | 
			
		||||
[插件列表](https://thingsgateway.cn/docs/1001)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 特别声明
 | 
			
		||||
 | 
			
		||||
ThingsGateway 项目已加入 [dotNET China](https://gitee.com/dotnetchina)  组织。<br/>
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
							
								
								
									
										103
									
								
								framework/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								framework/.editorconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
			
		||||
[*.cs]
 | 
			
		||||
 | 
			
		||||
# CA1848: 使用 LoggerMessage 委托
 | 
			
		||||
dotnet_diagnostic.CA1848.severity = none
 | 
			
		||||
 | 
			
		||||
# CA2254: 模板应为静态表达式
 | 
			
		||||
dotnet_diagnostic.CA2254.severity = suggestion
 | 
			
		||||
 | 
			
		||||
[*.cs]
 | 
			
		||||
#### 命名样式 ####
 | 
			
		||||
 | 
			
		||||
# 命名规则
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
 | 
			
		||||
 | 
			
		||||
# 符号规范
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_kinds = interface
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.interface.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
 | 
			
		||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.types.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
 | 
			
		||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.non_field_members.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
# 命名样式
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.begins_with_i.required_prefix = I
 | 
			
		||||
dotnet_naming_style.begins_with_i.required_suffix = 
 | 
			
		||||
dotnet_naming_style.begins_with_i.word_separator = 
 | 
			
		||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.pascal_case.required_prefix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.required_suffix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.word_separator = 
 | 
			
		||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.pascal_case.required_prefix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.required_suffix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.word_separator = 
 | 
			
		||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
[*.vb]
 | 
			
		||||
#### 命名样式 ####
 | 
			
		||||
 | 
			
		||||
# 命名规则
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
 | 
			
		||||
 | 
			
		||||
# 符号规范
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_kinds = interface
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.interface.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
 | 
			
		||||
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.类型.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
 | 
			
		||||
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.非字段成员.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
# 命名样式
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.以_i_开始.required_prefix = I
 | 
			
		||||
dotnet_naming_style.以_i_开始.required_suffix = 
 | 
			
		||||
dotnet_naming_style.以_i_开始.word_separator = 
 | 
			
		||||
dotnet_naming_style.以_i_开始.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_prefix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_suffix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.word_separator = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_prefix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_suffix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.word_separator = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
 | 
			
		||||
							
								
								
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
###############################################################################
 | 
			
		||||
# Set default behavior to automatically normalize line endings.
 | 
			
		||||
###############################################################################
 | 
			
		||||
* text=auto
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# Set default behavior for command prompt diff.
 | 
			
		||||
#
 | 
			
		||||
# This is need for earlier builds of msysgit that does not have it on by
 | 
			
		||||
# default for csharp files.
 | 
			
		||||
# Note: This is only used by command line
 | 
			
		||||
###############################################################################
 | 
			
		||||
#*.cs     diff=csharp
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# Set the merge driver for project and solution files
 | 
			
		||||
#
 | 
			
		||||
# Merging from the command prompt will add diff markers to the files if there
 | 
			
		||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
 | 
			
		||||
# the diff markers are never inserted). Diff markers may cause the following 
 | 
			
		||||
# file extensions to fail to load in VS. An alternative would be to treat
 | 
			
		||||
# these files as binary and thus will always conflict and require user
 | 
			
		||||
# intervention with every merge. To do so, just uncomment the entries below
 | 
			
		||||
###############################################################################
 | 
			
		||||
#*.sln       merge=binary
 | 
			
		||||
#*.csproj    merge=binary
 | 
			
		||||
#*.vbproj    merge=binary
 | 
			
		||||
#*.vcxproj   merge=binary
 | 
			
		||||
#*.vcproj    merge=binary
 | 
			
		||||
#*.dbproj    merge=binary
 | 
			
		||||
#*.fsproj    merge=binary
 | 
			
		||||
#*.lsproj    merge=binary
 | 
			
		||||
#*.wixproj   merge=binary
 | 
			
		||||
#*.modelproj merge=binary
 | 
			
		||||
#*.sqlproj   merge=binary
 | 
			
		||||
#*.wwaproj   merge=binary
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# behavior for image files
 | 
			
		||||
#
 | 
			
		||||
# image files are treated as binary by default.
 | 
			
		||||
###############################################################################
 | 
			
		||||
#*.jpg   binary
 | 
			
		||||
#*.png   binary
 | 
			
		||||
#*.gif   binary
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# diff behavior for common document formats
 | 
			
		||||
# 
 | 
			
		||||
# Convert binary document formats to text before diffing them. This feature
 | 
			
		||||
# is only available from the command line. Turn it on by uncommenting the 
 | 
			
		||||
# entries below.
 | 
			
		||||
###############################################################################
 | 
			
		||||
#*.doc   diff=astextplain
 | 
			
		||||
#*.DOC   diff=astextplain
 | 
			
		||||
#*.docx  diff=astextplain
 | 
			
		||||
#*.DOCX  diff=astextplain
 | 
			
		||||
#*.dot   diff=astextplain
 | 
			
		||||
#*.DOT   diff=astextplain
 | 
			
		||||
#*.pdf   diff=astextplain
 | 
			
		||||
#*.PDF   diff=astextplain
 | 
			
		||||
#*.rtf   diff=astextplain
 | 
			
		||||
#*.RTF   diff=astextplain
 | 
			
		||||
							
								
								
									
										364
									
								
								framework/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								framework/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,364 @@
 | 
			
		||||
## Ignore Visual Studio temporary files, build results, and
 | 
			
		||||
## files generated by popular Visual Studio add-ons.
 | 
			
		||||
##
 | 
			
		||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
 | 
			
		||||
 | 
			
		||||
# User-specific files
 | 
			
		||||
*.rsuser
 | 
			
		||||
*.suo
 | 
			
		||||
*.user
 | 
			
		||||
*.userosscache
 | 
			
		||||
*.sln.docstates
 | 
			
		||||
 | 
			
		||||
# User-specific files (MonoDevelop/Xamarin Studio)
 | 
			
		||||
*.userprefs
 | 
			
		||||
 | 
			
		||||
# Mono auto generated files
 | 
			
		||||
mono_crash.*
 | 
			
		||||
 | 
			
		||||
# Build results
 | 
			
		||||
[Dd]ebug/
 | 
			
		||||
[Dd]ebugPublic/
 | 
			
		||||
[Rr]elease/
 | 
			
		||||
[Rr]eleases/
 | 
			
		||||
x64/
 | 
			
		||||
x86/
 | 
			
		||||
[Ww][Ii][Nn]32/
 | 
			
		||||
[Aa][Rr][Mm]/
 | 
			
		||||
[Aa][Rr][Mm]64/
 | 
			
		||||
bld/
 | 
			
		||||
[Bb]in/
 | 
			
		||||
[Oo]bj/
 | 
			
		||||
[Oo]ut/
 | 
			
		||||
[Ll]og/
 | 
			
		||||
[Ll]ogs/
 | 
			
		||||
 | 
			
		||||
# Visual Studio 2015/2017 cache/options directory
 | 
			
		||||
.vs/
 | 
			
		||||
# Uncomment if you have tasks that create the project's static files in wwwroot
 | 
			
		||||
#wwwroot/
 | 
			
		||||
 | 
			
		||||
# Visual Studio 2017 auto generated files
 | 
			
		||||
Generated\ Files/
 | 
			
		||||
 | 
			
		||||
# MSTest test Results
 | 
			
		||||
[Tt]est[Rr]esult*/
 | 
			
		||||
[Bb]uild[Ll]og.*
 | 
			
		||||
 | 
			
		||||
# NUnit
 | 
			
		||||
*.VisualState.xml
 | 
			
		||||
TestResult.xml
 | 
			
		||||
nunit-*.xml
 | 
			
		||||
 | 
			
		||||
# Build Results of an ATL Project
 | 
			
		||||
[Dd]ebugPS/
 | 
			
		||||
[Rr]eleasePS/
 | 
			
		||||
dlldata.c
 | 
			
		||||
 | 
			
		||||
# Benchmark Results
 | 
			
		||||
BenchmarkDotNet.Artifacts/
 | 
			
		||||
 | 
			
		||||
# .NET Core
 | 
			
		||||
project.lock.json
 | 
			
		||||
project.fragment.lock.json
 | 
			
		||||
artifacts/
 | 
			
		||||
 | 
			
		||||
# ASP.NET Scaffolding
 | 
			
		||||
ScaffoldingReadMe.txt
 | 
			
		||||
 | 
			
		||||
# StyleCop
 | 
			
		||||
StyleCopReport.xml
 | 
			
		||||
 | 
			
		||||
# Files built by Visual Studio
 | 
			
		||||
*_i.c
 | 
			
		||||
*_p.c
 | 
			
		||||
*_h.h
 | 
			
		||||
*.ilk
 | 
			
		||||
*.meta
 | 
			
		||||
*.obj
 | 
			
		||||
*.iobj
 | 
			
		||||
*.pch
 | 
			
		||||
*.pdb
 | 
			
		||||
*.ipdb
 | 
			
		||||
*.pgc
 | 
			
		||||
*.pgd
 | 
			
		||||
*.rsp
 | 
			
		||||
*.sbr
 | 
			
		||||
*.tlb
 | 
			
		||||
*.tli
 | 
			
		||||
*.tlh
 | 
			
		||||
*.tmp
 | 
			
		||||
*.tmp_proj
 | 
			
		||||
*_wpftmp.csproj
 | 
			
		||||
*.log
 | 
			
		||||
*.vspscc
 | 
			
		||||
*.vssscc
 | 
			
		||||
.builds
 | 
			
		||||
*.pidb
 | 
			
		||||
*.svclog
 | 
			
		||||
*.scc
 | 
			
		||||
 | 
			
		||||
# Chutzpah Test files
 | 
			
		||||
_Chutzpah*
 | 
			
		||||
 | 
			
		||||
# Visual C++ cache files
 | 
			
		||||
ipch/
 | 
			
		||||
*.aps
 | 
			
		||||
*.ncb
 | 
			
		||||
*.opendb
 | 
			
		||||
*.opensdf
 | 
			
		||||
*.sdf
 | 
			
		||||
*.cachefile
 | 
			
		||||
*.VC.db
 | 
			
		||||
*.VC.VC.opendb
 | 
			
		||||
 | 
			
		||||
# Visual Studio profiler
 | 
			
		||||
*.psess
 | 
			
		||||
*.vsp
 | 
			
		||||
*.vspx
 | 
			
		||||
*.sap
 | 
			
		||||
 | 
			
		||||
# Visual Studio Trace Files
 | 
			
		||||
*.e2e
 | 
			
		||||
 | 
			
		||||
# TFS 2012 Local Workspace
 | 
			
		||||
$tf/
 | 
			
		||||
 | 
			
		||||
# Guidance Automation Toolkit
 | 
			
		||||
*.gpState
 | 
			
		||||
 | 
			
		||||
# ReSharper is a .NET coding add-in
 | 
			
		||||
_ReSharper*/
 | 
			
		||||
*.[Rr]e[Ss]harper
 | 
			
		||||
*.DotSettings.user
 | 
			
		||||
 | 
			
		||||
# TeamCity is a build add-in
 | 
			
		||||
_TeamCity*
 | 
			
		||||
 | 
			
		||||
# DotCover is a Code Coverage Tool
 | 
			
		||||
*.dotCover
 | 
			
		||||
 | 
			
		||||
# AxoCover is a Code Coverage Tool
 | 
			
		||||
.axoCover/*
 | 
			
		||||
!.axoCover/settings.json
 | 
			
		||||
 | 
			
		||||
# Coverlet is a free, cross platform Code Coverage Tool
 | 
			
		||||
coverage*.json
 | 
			
		||||
coverage*.xml
 | 
			
		||||
coverage*.info
 | 
			
		||||
 | 
			
		||||
# Visual Studio code coverage results
 | 
			
		||||
*.coverage
 | 
			
		||||
*.coveragexml
 | 
			
		||||
 | 
			
		||||
# NCrunch
 | 
			
		||||
_NCrunch_*
 | 
			
		||||
.*crunch*.local.xml
 | 
			
		||||
nCrunchTemp_*
 | 
			
		||||
 | 
			
		||||
# MightyMoose
 | 
			
		||||
*.mm.*
 | 
			
		||||
AutoTest.Net/
 | 
			
		||||
 | 
			
		||||
# Web workbench (sass)
 | 
			
		||||
.sass-cache/
 | 
			
		||||
 | 
			
		||||
# Installshield output folder
 | 
			
		||||
[Ee]xpress/
 | 
			
		||||
 | 
			
		||||
# DocProject is a documentation generator add-in
 | 
			
		||||
DocProject/buildhelp/
 | 
			
		||||
DocProject/Help/*.HxT
 | 
			
		||||
DocProject/Help/*.HxC
 | 
			
		||||
DocProject/Help/*.hhc
 | 
			
		||||
DocProject/Help/*.hhk
 | 
			
		||||
DocProject/Help/*.hhp
 | 
			
		||||
DocProject/Help/Html2
 | 
			
		||||
DocProject/Help/html
 | 
			
		||||
 | 
			
		||||
# Click-Once directory
 | 
			
		||||
publish/
 | 
			
		||||
 | 
			
		||||
# Publish Web Output
 | 
			
		||||
*.[Pp]ublish.xml
 | 
			
		||||
*.azurePubxml
 | 
			
		||||
# Note: Comment the next line if you want to checkin your web deploy settings,
 | 
			
		||||
# but database connection strings (with potential passwords) will be unencrypted
 | 
			
		||||
*.pubxml
 | 
			
		||||
*.publishproj
 | 
			
		||||
 | 
			
		||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
 | 
			
		||||
# checkin your Azure Web App publish settings, but sensitive information contained
 | 
			
		||||
# in these scripts will be unencrypted
 | 
			
		||||
PublishScripts/
 | 
			
		||||
 | 
			
		||||
# NuGet Packages
 | 
			
		||||
*.nupkg
 | 
			
		||||
# NuGet Symbol Packages
 | 
			
		||||
*.snupkg
 | 
			
		||||
# The packages folder can be ignored because of Package Restore
 | 
			
		||||
**/[Pp]ackages/*
 | 
			
		||||
# except build/, which is used as an MSBuild target.
 | 
			
		||||
!**/[Pp]ackages/build/
 | 
			
		||||
# Uncomment if necessary however generally it will be regenerated when needed
 | 
			
		||||
#!**/[Pp]ackages/repositories.config
 | 
			
		||||
# NuGet v3's project.json files produces more ignorable files
 | 
			
		||||
*.nuget.props
 | 
			
		||||
*.nuget.targets
 | 
			
		||||
 | 
			
		||||
# Microsoft Azure Build Output
 | 
			
		||||
csx/
 | 
			
		||||
*.build.csdef
 | 
			
		||||
 | 
			
		||||
# Microsoft Azure Emulator
 | 
			
		||||
ecf/
 | 
			
		||||
rcf/
 | 
			
		||||
 | 
			
		||||
# Windows Store app package directories and files
 | 
			
		||||
AppPackages/
 | 
			
		||||
BundleArtifacts/
 | 
			
		||||
Package.StoreAssociation.xml
 | 
			
		||||
_pkginfo.txt
 | 
			
		||||
*.appx
 | 
			
		||||
*.appxbundle
 | 
			
		||||
*.appxupload
 | 
			
		||||
 | 
			
		||||
# Visual Studio cache files
 | 
			
		||||
# files ending in .cache can be ignored
 | 
			
		||||
*.[Cc]ache
 | 
			
		||||
# but keep track of directories ending in .cache
 | 
			
		||||
!?*.[Cc]ache/
 | 
			
		||||
 | 
			
		||||
# Others
 | 
			
		||||
ClientBin/
 | 
			
		||||
~$*
 | 
			
		||||
*~
 | 
			
		||||
*.dbmdl
 | 
			
		||||
*.dbproj.schemaview
 | 
			
		||||
*.jfm
 | 
			
		||||
*.pfx
 | 
			
		||||
*.publishsettings
 | 
			
		||||
orleans.codegen.cs
 | 
			
		||||
 | 
			
		||||
# Including strong name files can present a security risk
 | 
			
		||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
 | 
			
		||||
#*.snk
 | 
			
		||||
 | 
			
		||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
 | 
			
		||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
 | 
			
		||||
#bower_components/
 | 
			
		||||
 | 
			
		||||
# RIA/Silverlight projects
 | 
			
		||||
Generated_Code/
 | 
			
		||||
 | 
			
		||||
# Backup & report files from converting an old project file
 | 
			
		||||
# to a newer Visual Studio version. Backup files are not needed,
 | 
			
		||||
# because we have git ;-)
 | 
			
		||||
_UpgradeReport_Files/
 | 
			
		||||
Backup*/
 | 
			
		||||
UpgradeLog*.XML
 | 
			
		||||
UpgradeLog*.htm
 | 
			
		||||
ServiceFabricBackup/
 | 
			
		||||
*.rptproj.bak
 | 
			
		||||
 | 
			
		||||
# SQL Server files
 | 
			
		||||
*.mdf
 | 
			
		||||
*.ldf
 | 
			
		||||
*.ndf
 | 
			
		||||
 | 
			
		||||
# Business Intelligence projects
 | 
			
		||||
*.rdl.data
 | 
			
		||||
*.bim.layout
 | 
			
		||||
*.bim_*.settings
 | 
			
		||||
*.rptproj.rsuser
 | 
			
		||||
*- [Bb]ackup.rdl
 | 
			
		||||
*- [Bb]ackup ([0-9]).rdl
 | 
			
		||||
*- [Bb]ackup ([0-9][0-9]).rdl
 | 
			
		||||
 | 
			
		||||
# Microsoft Fakes
 | 
			
		||||
FakesAssemblies/
 | 
			
		||||
 | 
			
		||||
# GhostDoc plugin setting file
 | 
			
		||||
*.GhostDoc.xml
 | 
			
		||||
 | 
			
		||||
# Node.js Tools for Visual Studio
 | 
			
		||||
.ntvs_analysis.dat
 | 
			
		||||
node_modules/
 | 
			
		||||
 | 
			
		||||
# Visual Studio 6 build log
 | 
			
		||||
*.plg
 | 
			
		||||
 | 
			
		||||
# Visual Studio 6 workspace options file
 | 
			
		||||
*.opt
 | 
			
		||||
 | 
			
		||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
 | 
			
		||||
*.vbw
 | 
			
		||||
 | 
			
		||||
# Visual Studio LightSwitch build output
 | 
			
		||||
**/*.HTMLClient/GeneratedArtifacts
 | 
			
		||||
**/*.DesktopClient/GeneratedArtifacts
 | 
			
		||||
**/*.DesktopClient/ModelManifest.xml
 | 
			
		||||
**/*.Server/GeneratedArtifacts
 | 
			
		||||
**/*.Server/ModelManifest.xml
 | 
			
		||||
_Pvt_Extensions
 | 
			
		||||
 | 
			
		||||
# Paket dependency manager
 | 
			
		||||
.paket/paket.exe
 | 
			
		||||
paket-files/
 | 
			
		||||
 | 
			
		||||
# FAKE - F# Make
 | 
			
		||||
.fake/
 | 
			
		||||
 | 
			
		||||
# CodeRush personal settings
 | 
			
		||||
.cr/personal
 | 
			
		||||
 | 
			
		||||
# Python Tools for Visual Studio (PTVS)
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.pyc
 | 
			
		||||
 | 
			
		||||
# Cake - Uncomment if you are using it
 | 
			
		||||
# tools/**
 | 
			
		||||
# !tools/packages.config
 | 
			
		||||
 | 
			
		||||
# Tabs Studio
 | 
			
		||||
*.tss
 | 
			
		||||
 | 
			
		||||
# Telerik's JustMock configuration file
 | 
			
		||||
*.jmconfig
 | 
			
		||||
 | 
			
		||||
# BizTalk build output
 | 
			
		||||
*.btp.cs
 | 
			
		||||
*.btm.cs
 | 
			
		||||
*.odx.cs
 | 
			
		||||
*.xsd.cs
 | 
			
		||||
 | 
			
		||||
# OpenCover UI analysis results
 | 
			
		||||
OpenCover/
 | 
			
		||||
 | 
			
		||||
# Azure Stream Analytics local run output
 | 
			
		||||
ASALocalRun/
 | 
			
		||||
 | 
			
		||||
# MSBuild Binary and Structured Log
 | 
			
		||||
*.binlog
 | 
			
		||||
 | 
			
		||||
# NVidia Nsight GPU debugger configuration file
 | 
			
		||||
*.nvuser
 | 
			
		||||
 | 
			
		||||
# MFractors (Xamarin productivity tool) working folder
 | 
			
		||||
.mfractor/
 | 
			
		||||
 | 
			
		||||
# Local History for Visual Studio
 | 
			
		||||
.localhistory/
 | 
			
		||||
 | 
			
		||||
# BeatPulse healthcheck temp database
 | 
			
		||||
healthchecksdb
 | 
			
		||||
 | 
			
		||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
 | 
			
		||||
MigrationBackup/
 | 
			
		||||
 | 
			
		||||
# Ionide (cross platform F# VS Code tools) working folder
 | 
			
		||||
.ionide/
 | 
			
		||||
 | 
			
		||||
# Fody - auto-generated XML schema
 | 
			
		||||
FodyWeavers.xsd
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								framework/Demo/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								framework/Demo/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
<Project>
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<Version>3.0.0.25</Version>
 | 
			
		||||
		<LangVersion>latest</LangVersion>
 | 
			
		||||
		<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
 | 
			
		||||
		<Authors>Diego</Authors>
 | 
			
		||||
		<Product>ThingsGateway</Product>
 | 
			
		||||
		<Copyright>© 2023-present Diego</Copyright>
 | 
			
		||||
		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
 | 
			
		||||
		<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
 | 
			
		||||
		<GenerateDocumentationFile>False</GenerateDocumentationFile>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
#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 ThingsGateway.Components;
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Photino.Blazor;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
internal class Program
 | 
			
		||||
{
 | 
			
		||||
    [STAThread]
 | 
			
		||||
    private static void Main(string[] args)
 | 
			
		||||
    {
 | 
			
		||||
        System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory);
 | 
			
		||||
 | 
			
		||||
        var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args);
 | 
			
		||||
 | 
			
		||||
        appBuilder.RootComponents.Add<App>("#app");
 | 
			
		||||
 | 
			
		||||
        appBuilder.Services.ThingsGatewayComponentsConfigureServices();
 | 
			
		||||
        var app = appBuilder.Build();
 | 
			
		||||
        app.MainWindow.SetTitle("ThingsGateway.Foundation.Demo");
 | 
			
		||||
        app.MainWindow.SetIconFile("wwwroot/favicon.ico");
 | 
			
		||||
        AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
 | 
			
		||||
        {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        app.Run();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<OutputType>WinExe</OutputType>
 | 
			
		||||
		<ApplicationIcon>favicon.ico</ApplicationIcon>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <None Remove="favicon.ico" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <Content Include="favicon.ico">
 | 
			
		||||
	    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
 | 
			
		||||
	    <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
 | 
			
		||||
	    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
 | 
			
		||||
	  </Content>
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<PackageReference Include="Photino.Blazor" Version="2.6.0" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										26
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Rcl/App.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Rcl/App.razor
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
 | 
			
		||||
<Router AppAssembly="@typeof(App).Assembly">
 | 
			
		||||
    <Found Context="routeData">
 | 
			
		||||
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
 | 
			
		||||
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
 | 
			
		||||
    </Found>
 | 
			
		||||
    <NotFound>
 | 
			
		||||
        <LayoutView Layout="@typeof(MainLayout)">
 | 
			
		||||
            <p role="alert">Sorry, there's nothing at this address.</p>
 | 
			
		||||
        </LayoutView>
 | 
			
		||||
    </NotFound>
 | 
			
		||||
</Router>
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,148 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Components;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Components;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 调试UI
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class DriverDebugUIBase : ComponentBase, IDisposable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ConcurrentLinkedList<(LogLevel level, string message)> Messages = new();
 | 
			
		||||
 | 
			
		||||
    private PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(1));
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    ~DriverDebugUIBase()
 | 
			
		||||
    {
 | 
			
		||||
        this.SafeDispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 变量地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public virtual string Address { get; set; } = "40001";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 长度
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public virtual int Length { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 默认读写设备
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public virtual IReadWrite Plc { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写入值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public virtual string WriteValue { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 数据类型
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected virtual DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Int16;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Inject]
 | 
			
		||||
    public InitTimezone InitTimezone { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        _periodicTimer?.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void LogOut(ThingsGateway.Foundation.Core.LogLevel logLevel, object source, string message, Exception exception)
 | 
			
		||||
    {
 | 
			
		||||
        Messages.Add(((LogLevel)logLevel,
 | 
			
		||||
            $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {message} {exception}"));
 | 
			
		||||
        if (Messages.Count > 2500)
 | 
			
		||||
        {
 | 
			
		||||
            Messages.Clear();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual async Task ReadAsync()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var data = await Plc.ReadAsync(Address, Length, DataTypeEnum);
 | 
			
		||||
            if (data.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                Messages.Add((LogLevel.Information,
 | 
			
		||||
            $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 对应类型值:{Environment.NewLine}{data.Content.ToJsonString(true)} "));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                Messages.Add((LogLevel.Warning,
 | 
			
		||||
            $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Messages.Add((LogLevel.Error,
 | 
			
		||||
            $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex}"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public virtual async Task WriteAsync()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var data = await Plc.WriteAsync(Address, WriteValue, Length, DataTypeEnum);
 | 
			
		||||
            if (data.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                Messages.Add((LogLevel.Information,
 | 
			
		||||
            $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                Messages.Add((LogLevel.Warning,
 | 
			
		||||
            $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Messages.Add((LogLevel.Error,
 | 
			
		||||
            $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex}"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        _ = RunTimerAsync();
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
    }
 | 
			
		||||
    private async Task RunTimerAsync()
 | 
			
		||||
    {
 | 
			
		||||
        while (await _periodicTimer.WaitForNextTickAsync())
 | 
			
		||||
        {
 | 
			
		||||
            await InvokeAsync(StateHasChanged);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,205 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@using BlazorComponent;
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web;
 | 
			
		||||
@using Microsoft.JSInterop;
 | 
			
		||||
@using ThingsGateway.Foundation.Core;
 | 
			
		||||
@using Masa.Blazor;
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@inherits DriverDebugUIBase
 | 
			
		||||
 | 
			
		||||
<MCard Elevation="1" Rounded="false" Class=" pa-2" Style="width:100%">
 | 
			
		||||
    <MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters>
 | 
			
		||||
 | 
			
		||||
        <MCol Md="5">
 | 
			
		||||
            <MTabs @bind-Value="tab" Class="ma-2">
 | 
			
		||||
                <MTab Value=1>   读写测试    </MTab>
 | 
			
		||||
                <MTab Value=2>    特殊功能    </MTab>
 | 
			
		||||
                <MTab Value=3>    代码示例    </MTab>
 | 
			
		||||
            </MTabs>
 | 
			
		||||
 | 
			
		||||
            <MTabsItems Value="tab">
 | 
			
		||||
                <MTabItem Value="1">
 | 
			
		||||
                    @if (tab == 1)
 | 
			
		||||
                    {
 | 
			
		||||
                        @if (ReadWriteContent == null)
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                            <div class="my-1 py-1">
 | 
			
		||||
                                <MTooltip Bottom Context="tip">
 | 
			
		||||
                                    <ActivatorContent>
 | 
			
		||||
                                        <MTextarea Class="mx-1 my-1" Label="变量地址" @attributes="@tip.Attrs" Dense Outlined HideDetails="@("auto")" @bind-Value=@Address />
 | 
			
		||||
                                    </ActivatorContent>
 | 
			
		||||
                                    <ChildContent>
 | 
			
		||||
                                        <span style="white-space: pre-wrap;">@Plc?.GetAddressDescription()</span>
 | 
			
		||||
                                    </ChildContent>
 | 
			
		||||
                                </MTooltip>
 | 
			
		||||
                                <MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters>
 | 
			
		||||
                                <MTextField Class="mx-1 my-1" Style="max-width:200px" Label="读取长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@Length />
 | 
			
		||||
                                    <MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="DataTypeEnum" Outlined Label="数据类型"
 | 
			
		||||
                                             Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar())
 | 
			
		||||
                                             MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
 | 
			
		||||
                                             ItemText=@((u) =>u.Description)
 | 
			
		||||
                                             ItemValue=@(u =>(DataTypeEnum)u.Value)
 | 
			
		||||
                                             HideDetails=@("auto") Height="30"
 | 
			
		||||
                                              Dense>
 | 
			
		||||
                                </MSelect>
 | 
			
		||||
                            </MRow>
 | 
			
		||||
 | 
			
		||||
                            <MButton Class="mx-1 my-1" Color="primary" OnClick="ReadAsync">
 | 
			
		||||
                                读取
 | 
			
		||||
                            </MButton>
 | 
			
		||||
                            <MTextarea Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@WriteValue />
 | 
			
		||||
                                <MButton Class="mx-1 my-1" Color="primary" OnClick="WriteAsync">
 | 
			
		||||
                                    写入
 | 
			
		||||
                                </MButton>
 | 
			
		||||
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            @ReadWriteContent
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                </MTabItem>
 | 
			
		||||
 | 
			
		||||
                <MTabItem Value="2">
 | 
			
		||||
                    @if (tab == 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        @if (ShowDefaultOtherContent)
 | 
			
		||||
                        {
 | 
			
		||||
                            <MSubheader>
 | 
			
		||||
                                连读打包
 | 
			
		||||
                            </MSubheader>
 | 
			
		||||
                            <MContainer>
 | 
			
		||||
 | 
			
		||||
                                @foreach (var item in DeviceVariableRunTimes)
 | 
			
		||||
                                {
 | 
			
		||||
                                    <MRow Dense Align="AlignTypes.Center">
 | 
			
		||||
                                        <MTextField Class="ma-1" Outlined Style="min-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.VariableAddress)) Dense HideDetails="@("auto")" @bind-Value=@item.VariableAddress></MTextField>
 | 
			
		||||
                                        <MSelect Class="mx-1 my-1" Style="max-width:120px" @bind-Value="item.DataTypeEnum" Outlined Label=@(item.DescriptionWithOutSugar(x => x.DataTypeEnum))
 | 
			
		||||
                                                 Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar())
 | 
			
		||||
                                                 MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
 | 
			
		||||
                                                 ItemText=@((u) =>u.Description)
 | 
			
		||||
                                                 ItemValue=@(u =>(DataTypeEnum)u.Value)
 | 
			
		||||
                                                 HideDetails=@("auto") Height="30"
 | 
			
		||||
                                              Dense>
 | 
			
		||||
                                    </MSelect>
 | 
			
		||||
 | 
			
		||||
                                    <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.IntervalTime)) Dense HideDetails="@("auto")" @bind-Value=@item.IntervalTime></MTextField>
 | 
			
		||||
 | 
			
		||||
                                        <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=实时值 Readonly ClearIcon="" Dense HideDetails="@("auto")" Value=item.Value?.ToJsonString()></MTextField>
 | 
			
		||||
 | 
			
		||||
                                    </MRow>
 | 
			
		||||
                                }
 | 
			
		||||
                                <MRow Dense>
 | 
			
		||||
                                <MTextField Class="ma-1" Outlined Style="max-width:100px" Label="打包长度" Dense HideDetails="@("auto")" @bind-Value=@MaxPack></MTextField>
 | 
			
		||||
                                    <MButton Class="ma-1" Color="primary" OnClick="MulReadAsync">
 | 
			
		||||
                                        读取
 | 
			
		||||
                                    </MButton>
 | 
			
		||||
                                </MRow>
 | 
			
		||||
                            </MContainer>
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        @if (OtherContent != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            <MSheet Style="height:100%;overflow-y:auto">
 | 
			
		||||
                                @OtherContent
 | 
			
		||||
                            </MSheet>
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                </MTabItem>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                <MTabItem Value="3">
 | 
			
		||||
                    @if (tab == 3)
 | 
			
		||||
                    {
 | 
			
		||||
                        @if (CodeContent != null)
 | 
			
		||||
                            @CodeContent
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                            <MRow Align="AlignTypes.Center">
 | 
			
		||||
                                <MContainer>
 | 
			
		||||
 | 
			
		||||
                                    <MItemGroup @bind-Value="_selected" Class="shrink mr-6" Mandatory>
 | 
			
		||||
                                    @{
 | 
			
		||||
 | 
			
		||||
                                            int index = 0;
 | 
			
		||||
 | 
			
		||||
                                        }
 | 
			
		||||
                                        <MRow>
 | 
			
		||||
 | 
			
		||||
                                            @foreach (var item in Sections)
 | 
			
		||||
                                            {
 | 
			
		||||
                                                <MItem Value="@(index++)">
 | 
			
		||||
                                                    <div>
 | 
			
		||||
                                                        <MButton IsActive="@context.Active" Icon OnClick="@context.Toggle">
 | 
			
		||||
                                                            <MIcon>mdi-record</MIcon>
 | 
			
		||||
                                                        </MButton>
 | 
			
		||||
                                                    </div>
 | 
			
		||||
                                                </MItem>
 | 
			
		||||
                                            }
 | 
			
		||||
                                        </MRow>
 | 
			
		||||
                                    </MItemGroup>
 | 
			
		||||
                                </MContainer>
 | 
			
		||||
 | 
			
		||||
                                <MCol>
 | 
			
		||||
                                    <MWindow Value="_selected" Vertical Class="elevation-1 grey lighten-5 rounded-b" Style=@($"height:450px;overflow:auto")>
 | 
			
		||||
                                        @{
 | 
			
		||||
                                            int index = 0;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        @foreach (var item in Sections)
 | 
			
		||||
                                        {
 | 
			
		||||
                                            <MWindowItem Value="@(index++)">
 | 
			
		||||
                                                <AppCode RoundedTop0 Code="@item.Code" Language="@item.Language" />
 | 
			
		||||
                                            </MWindowItem>
 | 
			
		||||
                                        }
 | 
			
		||||
                                    </MWindow>
 | 
			
		||||
                                </MCol>
 | 
			
		||||
                            </MRow>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    }
 | 
			
		||||
                </MTabItem>
 | 
			
		||||
            </MTabsItems>
 | 
			
		||||
 | 
			
		||||
        </MCol>
 | 
			
		||||
 | 
			
		||||
        <MCol Md="7">
 | 
			
		||||
            <MCard Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
 | 
			
		||||
                <ConsoleTxt Messages="Messages" Height=500></ConsoleTxt>
 | 
			
		||||
            </MCard>
 | 
			
		||||
        </MCol>
 | 
			
		||||
 | 
			
		||||
    </MRow>
 | 
			
		||||
 | 
			
		||||
</MCard>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    StringNumber tab;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,236 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using BlazorComponent;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Components;
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public partial class DriverDebugUIPage : DriverDebugUIBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// DeviceVariableRunTimes
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public List<DeviceVariableRunTime> DeviceVariableRunTimes;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// MaxPack
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int MaxPack = 100;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// MulReadAsync
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task MulReadAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack);
 | 
			
		||||
        foreach (var item in deviceVariableSourceReads)
 | 
			
		||||
        {
 | 
			
		||||
            var result = await Plc.ReadAsync(item.VariableAddress, item.Length);
 | 
			
		||||
            if (result.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    item.DeviceVariableRunTimes.PraseStructContent(Plc, result.Content);
 | 
			
		||||
                    Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Content.ToHexString(' ')));
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Message));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private StringNumber _selected = 0;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Sections
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public List<(string Code, string Language)> Sections { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ShowDefaultOtherContent
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public bool ShowDefaultOtherContent { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        DeviceVariableRunTimes = new()
 | 
			
		||||
            {
 | 
			
		||||
                                new DeviceVariableRunTime()
 | 
			
		||||
                                {
 | 
			
		||||
                                    DataTypeEnum=DataTypeEnum.Int16,
 | 
			
		||||
                                    VariableAddress="40001",
 | 
			
		||||
                                    IntervalTime=1000,
 | 
			
		||||
                                },
 | 
			
		||||
                                   new DeviceVariableRunTime()
 | 
			
		||||
                                {
 | 
			
		||||
                                    DataTypeEnum=DataTypeEnum.Int16,
 | 
			
		||||
                                    VariableAddress="40011",
 | 
			
		||||
                                    IntervalTime=1000,
 | 
			
		||||
                                },
 | 
			
		||||
                                   new DeviceVariableRunTime()
 | 
			
		||||
                                {
 | 
			
		||||
                                    DataTypeEnum=DataTypeEnum.Int16,
 | 
			
		||||
                                    VariableAddress="40031",
 | 
			
		||||
                                    IntervalTime=1000,
 | 
			
		||||
                                },
 | 
			
		||||
                                   new DeviceVariableRunTime()
 | 
			
		||||
                                {
 | 
			
		||||
                                    DataTypeEnum=DataTypeEnum.Int16,
 | 
			
		||||
                                    VariableAddress="40101",
 | 
			
		||||
                                    IntervalTime=1000,
 | 
			
		||||
                                },
 | 
			
		||||
            };
 | 
			
		||||
        Sections.Add((
 | 
			
		||||
"""
 | 
			
		||||
                /// <inheritdoc/>
 | 
			
		||||
                public class DeviceVariableSourceRead : IDeviceVariableSourceRead<DeviceVariableRunTime>
 | 
			
		||||
                {
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    public TimerTick TimerTick { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    public string VariableAddress { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    public int Length { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    public List<DeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<DeviceVariableRunTime>();
 | 
			
		||||
                }
 | 
			
		||||
                /// <inheritdoc/>
 | 
			
		||||
                public class DeviceVariableRunTime : IDeviceVariableRunTime
 | 
			
		||||
                {
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    [Description("读取间隔")]
 | 
			
		||||
                    public int IntervalTime { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    [Description("变量地址")]
 | 
			
		||||
                    public string VariableAddress { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    public int Index { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    [Description("数据类型")]
 | 
			
		||||
                    public DataTypeEnum DataTypeEnum { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    [Description("实时值")]
 | 
			
		||||
                    public object Value { get; set; }
 | 
			
		||||
                    /// <inheritdoc/>
 | 
			
		||||
                    public OperResult SetValue(object value)
 | 
			
		||||
                    {
 | 
			
		||||
                        Value = value;
 | 
			
		||||
                        return OperResult.CreateSuccessResult();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                public List<DeviceVariableRunTime> DeviceVariableRunTimes;
 | 
			
		||||
                                
 | 
			
		||||
                private static async Task ModbusClientAsync(IReadWrite plc)
 | 
			
		||||
                {
 | 
			
		||||
                DeviceVariableRunTimes = new()
 | 
			
		||||
                {
 | 
			
		||||
                                new DeviceVariableRunTime()
 | 
			
		||||
                                {
 | 
			
		||||
                                    DataTypeEnum=DataTypeEnum.Int16,
 | 
			
		||||
                                    VariableAddress="40001",
 | 
			
		||||
                                    IntervalTime=1000,
 | 
			
		||||
                                },
 | 
			
		||||
                                   new DeviceVariableRunTime()
 | 
			
		||||
                                {
 | 
			
		||||
                                    DataTypeEnum=DataTypeEnum.Int16,
 | 
			
		||||
                                    VariableAddress="40011",
 | 
			
		||||
                                    IntervalTime=1000,
 | 
			
		||||
                                },
 | 
			
		||||
                                   new DeviceVariableRunTime()
 | 
			
		||||
                                {
 | 
			
		||||
                                    DataTypeEnum=DataTypeEnum.Int16,
 | 
			
		||||
                                    VariableAddress="40031",
 | 
			
		||||
                                    IntervalTime=1000,
 | 
			
		||||
                                },
 | 
			
		||||
                                   new DeviceVariableRunTime()
 | 
			
		||||
                                {
 | 
			
		||||
                                    DataTypeEnum=DataTypeEnum.Int16,
 | 
			
		||||
                                    VariableAddress="40101",
 | 
			
		||||
                                    IntervalTime=1000,
 | 
			
		||||
                                },
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                    #region 连读
 | 
			
		||||
                var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack);
 | 
			
		||||
                foreach (var item in deviceVariableSourceReads)
 | 
			
		||||
                {
 | 
			
		||||
                    var result = await Plc.ReadAsync(item.VariableAddress, item.Length);
 | 
			
		||||
                    if (result.IsSuccess)
 | 
			
		||||
                    {
 | 
			
		||||
                        item.DeviceVariableRunTimes.PraseStructContent(result.Content);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                    #endregion
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
""", "csharp"));
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 自定义模板
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public RenderFragment ReadWriteContent { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 自定义模板
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public RenderFragment OtherContent { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 自定义模板
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Parameter]
 | 
			
		||||
    public RenderFragment CodeContent { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    ~DriverDebugUIPage()
 | 
			
		||||
    {
 | 
			
		||||
        this.SafeDispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public override IReadWrite Plc { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        Plc?.SafeDispose();
 | 
			
		||||
        base.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="firstRender"></param>
 | 
			
		||||
    protected override void OnAfterRender(bool firstRender)
 | 
			
		||||
    {
 | 
			
		||||
        base.OnAfterRender(firstRender);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,47 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@using BlazorComponent;
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web;
 | 
			
		||||
@using System.IO.Ports;
 | 
			
		||||
@using Masa.Blazor
 | 
			
		||||
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
 | 
			
		||||
    <div class="mb-4">通道配置</div>
 | 
			
		||||
    <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
 | 
			
		||||
        <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.PortName)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.PortName />
 | 
			
		||||
        <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.BaudRate)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.BaudRate />
 | 
			
		||||
        <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.DataBits)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.DataBits />
 | 
			
		||||
        <MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.Parity" Label="@(serialProperty.DescriptionWithOutSugar(x => x.Parity))"
 | 
			
		||||
                 Items=@(typeof(Parity).GetEnumListWithOutSugar())
 | 
			
		||||
                 MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
 | 
			
		||||
                 ItemText=@((u) =>u.Description)
 | 
			
		||||
                 ItemValue=@(u =>(Parity)u.Value)
 | 
			
		||||
                 HideDetails=@("auto") Height="30"
 | 
			
		||||
                 Dense>
 | 
			
		||||
        </MSelect>
 | 
			
		||||
        <MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.StopBits" Label="@(serialProperty.DescriptionWithOutSugar(x => x.StopBits))"
 | 
			
		||||
                 Items=@(typeof(StopBits).GetEnumListWithOutSugar())
 | 
			
		||||
                 MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
 | 
			
		||||
                 ItemText=@((u) =>u.Description)
 | 
			
		||||
                 ItemValue=@(u =>(StopBits)u.Value)
 | 
			
		||||
                 HideDetails=@("auto") Height="30"
 | 
			
		||||
                 Dense>
 | 
			
		||||
        </MSelect>
 | 
			
		||||
        <MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
 | 
			
		||||
            连接
 | 
			
		||||
        </MButton>
 | 
			
		||||
        <MButton Class="ma-1" OnClick=@DisConnect Color="red">
 | 
			
		||||
            断开
 | 
			
		||||
        </MButton>
 | 
			
		||||
    </MRow>
 | 
			
		||||
</MCard>
 | 
			
		||||
@@ -0,0 +1,106 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public partial class SerialSessionPage : IDisposable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志输出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Action<LogLevel, object, string, Exception> LogAction;
 | 
			
		||||
 | 
			
		||||
    private TouchSocketConfig config;
 | 
			
		||||
 | 
			
		||||
    private readonly SerialProperty serialProperty = new();
 | 
			
		||||
 | 
			
		||||
    private SerialSession SerialSession { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取对象
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public SerialSession GetSerialSession()
 | 
			
		||||
    {
 | 
			
		||||
        config ??= new TouchSocketConfig();
 | 
			
		||||
        var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
 | 
			
		||||
        LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
 | 
			
		||||
        config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
 | 
			
		||||
        config.SetSerialProperty(serialProperty);
 | 
			
		||||
        //载入配置
 | 
			
		||||
        SerialSession.Setup(config);
 | 
			
		||||
        return SerialSession;
 | 
			
		||||
    }
 | 
			
		||||
    private async Task ConnectAsync()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            SerialSession.Close();
 | 
			
		||||
            await GetSerialSession().ConnectAsync();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogAction?.Invoke(LogLevel.Error, null, null, ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    private void DisConnect()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            SerialSession.Close();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogAction?.Invoke(LogLevel.Error, null, null, ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        config ??= new TouchSocketConfig();
 | 
			
		||||
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="firstRender"></param>
 | 
			
		||||
    protected override void OnAfterRender(bool firstRender)
 | 
			
		||||
    {
 | 
			
		||||
        if (firstRender)
 | 
			
		||||
        {
 | 
			
		||||
            var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
 | 
			
		||||
            LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
 | 
			
		||||
            config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
 | 
			
		||||
            SerialSession.Setup(config);
 | 
			
		||||
        }
 | 
			
		||||
        base.OnAfterRender(firstRender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        SerialSession.SafeDispose();
 | 
			
		||||
    }
 | 
			
		||||
    internal void StateHasChangedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        StateHasChanged();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,34 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人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 Masa.Blazor
 | 
			
		||||
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
 | 
			
		||||
    <div class="mb-4">通道配置</div>
 | 
			
		||||
    <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
 | 
			
		||||
 | 
			
		||||
        <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP />
 | 
			
		||||
        <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port />
 | 
			
		||||
 | 
			
		||||
        <MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
 | 
			
		||||
            连接
 | 
			
		||||
        </MButton>
 | 
			
		||||
        <MButton Class="ma-1" OnClick=@DisConnect Color="red">
 | 
			
		||||
            断开
 | 
			
		||||
        </MButton>
 | 
			
		||||
    </MRow>
 | 
			
		||||
</MCard>
 | 
			
		||||
@@ -0,0 +1,116 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public partial class TcpClientPage : IDisposable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志输出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Action<LogLevel, object, string, Exception> LogAction;
 | 
			
		||||
 | 
			
		||||
    private TouchSocketConfig config;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// IP
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private string IP = "127.0.0.1";
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Port
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Port { get; set; } = 502;
 | 
			
		||||
 | 
			
		||||
    private TcpClient TcpClient { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    private async Task ConnectAsync()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            TcpClient.Close();
 | 
			
		||||
            await GetTcpClient().ConnectAsync();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            LogAction?.Invoke(LogLevel.Error, null, null, ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    private void DisConnect()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            TcpClient.Close();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            LogAction?.Invoke(LogLevel.Error, null, null, ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取对象
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public TcpClient GetTcpClient()
 | 
			
		||||
    {
 | 
			
		||||
        config ??= new TouchSocketConfig();
 | 
			
		||||
        var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
 | 
			
		||||
        LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
 | 
			
		||||
        config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
 | 
			
		||||
        config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
 | 
			
		||||
        //载入配置
 | 
			
		||||
        TcpClient.Setup(config);
 | 
			
		||||
        return TcpClient;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        config ??= new TouchSocketConfig();
 | 
			
		||||
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="firstRender"></param>
 | 
			
		||||
    protected override void OnAfterRender(bool firstRender)
 | 
			
		||||
    {
 | 
			
		||||
        if (firstRender)
 | 
			
		||||
        {
 | 
			
		||||
            var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
 | 
			
		||||
            LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
 | 
			
		||||
            config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
 | 
			
		||||
            config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
 | 
			
		||||
            TcpClient.Setup(config);
 | 
			
		||||
        }
 | 
			
		||||
        base.OnAfterRender(firstRender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        TcpClient.SafeDispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal void StateHasChangedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        StateHasChanged();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人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 Masa.Blazor
 | 
			
		||||
 | 
			
		||||
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
 | 
			
		||||
    <div class="mb-4">通道配置</div>
 | 
			
		||||
    <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
 | 
			
		||||
 | 
			
		||||
        <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
 | 
			
		||||
        <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
 | 
			
		||||
 | 
			
		||||
        <MButton Class="ma-1" OnClick=@Connect Color="primary">
 | 
			
		||||
            启动
 | 
			
		||||
        </MButton>
 | 
			
		||||
        <MButton Class="ma-1" OnClick=@DisConnect Color="red">
 | 
			
		||||
            停止
 | 
			
		||||
        </MButton>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    </MRow>
 | 
			
		||||
</MCard>
 | 
			
		||||
@@ -0,0 +1,108 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public partial class TcpServerPage : IDisposable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志输出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Action<LogLevel, object, string, Exception> LogAction;
 | 
			
		||||
 | 
			
		||||
    private TouchSocketConfig config;
 | 
			
		||||
 | 
			
		||||
    private string ip = "127.0.0.1";
 | 
			
		||||
 | 
			
		||||
    private int port = 502;
 | 
			
		||||
 | 
			
		||||
    private TcpService TcpServer { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    private void Connect()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            TcpServer.Stop();
 | 
			
		||||
            GetTcpServer().Start();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            LogAction?.Invoke(LogLevel.Error, null, null, ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    private void DisConnect()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            TcpServer.Stop();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogAction?.Invoke(LogLevel.Error, null, null, ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取对象
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public TcpService GetTcpServer()
 | 
			
		||||
    {
 | 
			
		||||
        config ??= new TouchSocketConfig();
 | 
			
		||||
        var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
 | 
			
		||||
        LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
 | 
			
		||||
        config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
 | 
			
		||||
        config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
 | 
			
		||||
        //载入配置
 | 
			
		||||
        TcpServer.Setup(config);
 | 
			
		||||
        return TcpServer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        config ??= new TouchSocketConfig();
 | 
			
		||||
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="firstRender"></param>
 | 
			
		||||
    protected override void OnAfterRender(bool firstRender)
 | 
			
		||||
    {
 | 
			
		||||
        if (firstRender)
 | 
			
		||||
        {
 | 
			
		||||
            var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
 | 
			
		||||
            LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
 | 
			
		||||
            config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
 | 
			
		||||
            config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
 | 
			
		||||
            TcpServer.Setup(config);
 | 
			
		||||
        }
 | 
			
		||||
        base.OnAfterRender(firstRender);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        TcpServer.SafeDispose();
 | 
			
		||||
    }
 | 
			
		||||
    internal void StateHasChangedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        StateHasChanged();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,37 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人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 Masa.Blazor
 | 
			
		||||
 | 
			
		||||
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
 | 
			
		||||
    <div class="mb-4">通道配置</div>
 | 
			
		||||
    <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
 | 
			
		||||
 | 
			
		||||
        <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP />
 | 
			
		||||
        <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port />
 | 
			
		||||
 | 
			
		||||
        <MButton Class="ma-1" OnClick=Connect Color="primary">
 | 
			
		||||
            连接
 | 
			
		||||
        </MButton>
 | 
			
		||||
        <MButton Class="ma-1" OnClick=DisConnect Color="red">
 | 
			
		||||
            断开
 | 
			
		||||
        </MButton>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    </MRow>
 | 
			
		||||
</MCard>
 | 
			
		||||
@@ -0,0 +1,113 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public partial class UdpSessionPage : IDisposable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志输出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Action<LogLevel, object, string, Exception> LogAction;
 | 
			
		||||
 | 
			
		||||
    private TouchSocketConfig config;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// IP
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string IP = "127.0.0.1";
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Port
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Port = 502;
 | 
			
		||||
 | 
			
		||||
    private UdpSession UdpSession { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    private void Connect()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            UdpSession.Stop();
 | 
			
		||||
            GetUdpSession().Start();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            LogAction?.Invoke(LogLevel.Error, null, null, ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    private void DisConnect()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            UdpSession.Stop();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            LogAction?.Invoke(LogLevel.Error, null, null, ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取对象
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public UdpSession GetUdpSession()
 | 
			
		||||
    {
 | 
			
		||||
        config ??= new TouchSocketConfig();
 | 
			
		||||
        var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
 | 
			
		||||
        LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
 | 
			
		||||
        config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
 | 
			
		||||
        config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
 | 
			
		||||
        config.SetBindIPHost(new IPHost(0));
 | 
			
		||||
        //载入配置
 | 
			
		||||
        UdpSession.Setup(config);
 | 
			
		||||
        return UdpSession;
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        config ??= new TouchSocketConfig();
 | 
			
		||||
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="firstRender"></param>
 | 
			
		||||
    protected override void OnAfterRender(bool firstRender)
 | 
			
		||||
    {
 | 
			
		||||
        if (firstRender)
 | 
			
		||||
        {
 | 
			
		||||
            var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
 | 
			
		||||
            LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
 | 
			
		||||
            config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
 | 
			
		||||
            config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
 | 
			
		||||
            config.SetBindIPHost(new IPHost(0));
 | 
			
		||||
            UdpSession.Setup(config);
 | 
			
		||||
        }
 | 
			
		||||
        base.OnAfterRender(firstRender);
 | 
			
		||||
    }
 | 
			
		||||
    private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        UdpSession.SafeDispose();
 | 
			
		||||
    }
 | 
			
		||||
    internal void StateHasChangedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        StateHasChanged();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,42 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class DeviceVariableRunTime : IDeviceVariableRunTime
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [Description("读取间隔")]
 | 
			
		||||
    public int IntervalTime { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [Description("变量地址")]
 | 
			
		||||
    public string VariableAddress { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public int Index { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [Description("数据类型")]
 | 
			
		||||
    public DataTypeEnum DataTypeEnum { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [Description("实时值")]
 | 
			
		||||
    public object Value { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public OperResult SetValue(object value, DateTime dateTime = default, bool isOnline = true)
 | 
			
		||||
    {
 | 
			
		||||
        Value = value;
 | 
			
		||||
        return OperResult.CreateSuccessResult();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,28 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class DeviceVariableSourceRead : IDeviceVariableSourceRead<IDeviceVariableRunTime>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public TimerTick TimerTick { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public string VariableAddress { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public int Length { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public List<IDeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<IDeviceVariableRunTime>();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,21 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
global using ThingsGateway.Components;
 | 
			
		||||
global using ThingsGateway.Foundation.Core;
 | 
			
		||||
global using ThingsGateway.Foundation.Serial;
 | 
			
		||||
global using ThingsGateway.Foundation.Sockets;
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,36 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@page "/"
 | 
			
		||||
@layout BaseLayout
 | 
			
		||||
@inject NavigationManager NavigationManager
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="firstRender"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    protected override async Task OnAfterRenderAsync(bool firstRender)
 | 
			
		||||
    {
 | 
			
		||||
        if (firstRender)
 | 
			
		||||
        {
 | 
			
		||||
            NavigationManager.NavigateTo("index");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await base.OnAfterRenderAsync(firstRender);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@page "/index"
 | 
			
		||||
 | 
			
		||||
<div class="ml-2">
 | 
			
		||||
    <div class="my-6 ">
 | 
			
		||||
        <MLabel Class="text-h3" Color="primary">ThingsGateway</MLabel>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <strong class="text--lighten-1 text-h5 my-1">文档</strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="my-2 ml-4">
 | 
			
		||||
        <PCopyableText>
 | 
			
		||||
            https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
        </PCopyableText>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <strong class="text--lighten-1 text-h5 my-1">协议</strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="my-2 ml-4">
 | 
			
		||||
        <PCopyableText Text="https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh">
 | 
			
		||||
            Apache-2.0开源协议
 | 
			
		||||
        </PCopyableText>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
        <strong class="text--lighten-1 text-h5 my-1">赞助</strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="my-2 ml-4">
 | 
			
		||||
        <PCopyableText Text="https://diego2098.gitee.io/thingsgateway-docs/docs/donate">
 | 
			
		||||
            ThingsGateway赞助途径
 | 
			
		||||
        </PCopyableText>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
        <strong class="text--lighten-1 text-h5 my-1">社区</strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="my-2 ml-4">
 | 
			
		||||
        <PCopyableText Text="605534569">
 | 
			
		||||
            QQ群:605534569
 | 
			
		||||
        </PCopyableText>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,42 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@inherits LayoutComponentBase
 | 
			
		||||
 | 
			
		||||
<CascadingValue Value="IsMobile" Name="IsMobile">
 | 
			
		||||
    <MApp>
 | 
			
		||||
        <MErrorHandler>
 | 
			
		||||
            @Body
 | 
			
		||||
        </MErrorHandler>
 | 
			
		||||
    </MApp>
 | 
			
		||||
</CascadingValue>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    public bool IsMobile { get; set; }
 | 
			
		||||
    [Inject]
 | 
			
		||||
    public MasaBlazor MasaBlazor { get; set; }
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
        MasaBlazor.BreakpointChanged += BreakpointOnOnUpdate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void BreakpointOnOnUpdate(object sender, BreakpointChangedEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        IsMobile = MasaBlazor.Breakpoint.Mobile;
 | 
			
		||||
        if (e.MobileChanged)
 | 
			
		||||
        {
 | 
			
		||||
            StateHasChanged();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,77 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人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 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";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,232 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
public partial class MainLayout
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    private List<NavItem> Navs { get; set; } = new();
 | 
			
		||||
    private List<PageTabItem> pageTabItems { get; set; } = new();
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        var dataString =
 | 
			
		||||
"""
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "Href": "/index",
 | 
			
		||||
    "Title": "首页"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Modbus",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusRtu",
 | 
			
		||||
        "Title": "ModbusRtu"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusTcp",
 | 
			
		||||
        "Title": "ModbusTcp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusRtuOverTcp",
 | 
			
		||||
        "Title": "ModbusRtuOverTcp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusRtuOverUdp",
 | 
			
		||||
        "Title": "ModbusRtuOverUdp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusUdp",
 | 
			
		||||
        "Title": "ModbusUdp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusTcpDtu",
 | 
			
		||||
        "Title": "ModbusTcpDtu"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusTcpServer",
 | 
			
		||||
        "Title": "ModbusTcpServer"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusSerialServer",
 | 
			
		||||
        "Title": "ModbusSerialServer"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Siemens",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_1500",
 | 
			
		||||
        "Title": "S7_1500"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_1200",
 | 
			
		||||
        "Title": "S7_1200"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_200",
 | 
			
		||||
        "Title": "S7_200"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_200SMART",
 | 
			
		||||
        "Title": "S7_200SMART"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_300",
 | 
			
		||||
        "Title": "S7_400"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_400",
 | 
			
		||||
        "Title": "S7_400"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "DLT645",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/DLT645_2007",
 | 
			
		||||
        "Title": "DLT645_2007"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/DLT645_2007OverTcp",
 | 
			
		||||
        "Title": "DLT645_2007OverTcp"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "OPCDA",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/OPCDAClient",
 | 
			
		||||
        "Title": "OPCDAClient"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "OPCUA",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/OPCUAClient",
 | 
			
		||||
        "Title": "OPCUAClient"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Mqtt",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/MqttClient",
 | 
			
		||||
        "Title": "MqttClient"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
""";
 | 
			
		||||
        Navs = dataString.FromJsonString<List<NavItem>>();
 | 
			
		||||
 | 
			
		||||
#if Pro
 | 
			
		||||
        var dataStringPro =
 | 
			
		||||
"""
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Melsec",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/QnA3E_Binary",
 | 
			
		||||
        "Title": "QnA3E_Binary"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "ABCIP",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ABCIPTCP",
 | 
			
		||||
        "Title": "ABCIPTCP"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Omron",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/OmronFinsTcp",
 | 
			
		||||
        "Title": "OmronFinsTcp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/OmronFinsUdp",
 | 
			
		||||
        "Title": "OmronFinsUdp"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Secs",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/SecsTcp",
 | 
			
		||||
        "Title": "SecsTcp"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "TS550",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/TS550",
 | 
			
		||||
        "Title": "TS550"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Vigor",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/VigorSerial",
 | 
			
		||||
        "Title": "VigorSerial"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/VigorSerialOverTcp",
 | 
			
		||||
        "Title": "VigorSerialOverTcp"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "GasCustom",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/GasCustomSerial",
 | 
			
		||||
        "Title": "GasCustomSerial"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/GasCustomSerialOverTcp",
 | 
			
		||||
        "Title": "GasCustomSerialOverTcp"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
""";
 | 
			
		||||
        Navs.AddRange(dataStringPro.FromJsonString<List<NavItem>>());
 | 
			
		||||
#endif
 | 
			
		||||
        pageTabItems = Navs.PasePageTabItem();
 | 
			
		||||
        base.OnInitialized();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,152 @@
 | 
			
		||||
<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.Melsec\Page\QnA3E_BinaryDebugPage.razor.cs" Link="Pages\Melsec\QnA3E_BinaryDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Melsec\Page\QnA3E_BinaryDebugPage.razor" Link="Pages\Melsec\QnA3E_BinaryDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Melsec\ThingsGateway.Foundation.Adapter.Melsec.csproj" />
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor.cs" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.AllenBradleyCip\ThingsGateway.Foundation.Adapter.AllenBradleyCip.csproj" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Omron\ThingsGateway.Foundation.Adapter.Omron.csproj" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor.cs" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Secs\ThingsGateway.Foundation.Adapter.Secs.csproj" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor.cs" Link="Pages\TS550\TS550DebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor" Link="Pages\TS550\TS550DebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.TS550\ThingsGateway.Foundation.Adapter.TS550.csproj" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor" Link="Pages\Vigor\VigorSerialDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Vigor\ThingsGateway.Foundation.Adapter.Vigor.csproj" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor" />
 | 
			
		||||
		<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.HZW_QTJC_01\ThingsGateway.Foundation.Adapter.HZW_QTJC_01.csproj" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<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\RpcClass\MqttRpcClient.cs" Link="Pages\Mqtt\MqttRpcClient.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClientExtensions.cs" Link="Pages\Mqtt\MqttRpcClientExtensions.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcTopicPair.cs" Link="Pages\Mqtt\MqttRpcTopicPair.cs" />
 | 
			
		||||
		<Compile Include="..\..\Web\ThingsGateway.Gateway.Application\Workers\ManageGateway\MqttLoggerExtensions.cs" Link="Pages\Mqtt\MqttLoggerExtensions.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor" Link="Pages\DLT645\DLT645_2007DebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor" />
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor" Link="Pages\Modbus\ModbusRtuDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor" Link="Pages\Modbus\ModbusTcpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusUdpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor" Link="Pages\Modbus\ModbusUdpDebugPage.razor" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor.cs" Link="Pages\OPCDA\OPCDAClientDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor.cs" Link="Pages\OPCDA\OPCDAClientPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor.cs" Link="Pages\OPCDA\OPCDAImportVariable.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor" Link="Pages\OPCDA\OPCDAClientDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor" Link="Pages\OPCDA\OPCDAClientPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor" Link="Pages\OPCDA\OPCDAImportVariable.razor" />
 | 
			
		||||
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor" Link="Pages\OPCUA\OPCUAClientDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor" Link="Pages\OPCUA\OPCUAClientPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor" Link="Pages\OPCUA\OPCUAImportVariable.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor.cs" Link="Pages\OPCUA\OPCUAClientDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor.cs" Link="Pages\OPCUA\OPCUAClientPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor.cs" Link="Pages\OPCUA\OPCUAImportVariable.razor.cs" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor" Link="Pages\Siemens\S7_1200DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor" Link="Pages\Siemens\S7_1500DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor" Link="Pages\Siemens\S7_200DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor" Link="Pages\Siemens\S7_200SMARTDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor" Link="Pages\Siemens\S7_300DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor" Link="Pages\Siemens\S7_400DebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor.cs" Link="Pages\Siemens\S7_1200DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor.cs" Link="Pages\Siemens\S7_1500DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor.cs" Link="Pages\Siemens\S7_200DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor.cs" Link="Pages\Siemens\S7_200SMARTDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor.cs" Link="Pages\Siemens\S7_300DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor.cs" Link="Pages\Siemens\S7_400DebugPage.razor.cs" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<!--<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" />
 | 
			
		||||
		<PackageReference Include="ThingsGateway.Foundation.Adapter.Modbus" Version="*" />
 | 
			
		||||
		<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCDA" Version="*" />
 | 
			
		||||
		<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCUA" Version="*" />
 | 
			
		||||
		<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />-->
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
 | 
			
		||||
		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
 | 
			
		||||
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<Content Update="wwwroot\**">
 | 
			
		||||
			<CopyToOutputDirectory>Always</CopyToOutputDirectory>
 | 
			
		||||
		</Content>
 | 
			
		||||
	</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>
 | 
			
		||||
@@ -0,0 +1,29 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@using System.Net.Http
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Forms
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Routing
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
 | 
			
		||||
@using Microsoft.JSInterop
 | 
			
		||||
@using BlazorComponent
 | 
			
		||||
@using Masa.Blazor
 | 
			
		||||
@using Masa.Blazor.Presets
 | 
			
		||||
@using ThingsGateway.Foundation.Core;
 | 
			
		||||
@using ThingsGateway.Components;
 | 
			
		||||
@using ThingsGateway.Core;
 | 
			
		||||
@using System.Net.Http.Json
 | 
			
		||||
@using System.IO;
 | 
			
		||||
@using System.Text.Json;
 | 
			
		||||
@using ThingsGateway.Foundation.Serial;
 | 
			
		||||
@using ThingsGateway.Foundation.Sockets;
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB  | 
| 
		 Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB  | 
@@ -0,0 +1,41 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
 | 
			
		||||
    <title>ThingsGateway.Foundation.Demo</title>
 | 
			
		||||
    <base href="/" />
 | 
			
		||||
 | 
			
		||||
    <link rel="icon" href="favicon.ico" type="image/x-icon">
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet" />
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/css/materialdesign/v7.1.96/css/materialdesignicons.min.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/css/material/icons.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/css/fontawesome/v6.4.0/css/all.min.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/style/custom.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/prism/prism-material-dark-for-masa.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/prism/prism-line-highlight.min.css" rel="stylesheet">
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <div id="app"></div>
 | 
			
		||||
 | 
			
		||||
    <div id="blazor-error-ui">
 | 
			
		||||
        <span>
 | 
			
		||||
            <environment include="Staging,Production">
 | 
			
		||||
                An error has occurred. This application may no longer respond until reloaded.
 | 
			
		||||
            </environment>
 | 
			
		||||
            <environment include="Development">
 | 
			
		||||
                An unhandled exception has occurred. See browser dev tools for details.
 | 
			
		||||
            </environment>
 | 
			
		||||
        </span>
 | 
			
		||||
        <a href="" class="reload">Reload</a>
 | 
			
		||||
        <a class="dismiss">🗙</a>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <script src="_framework/blazor.webview.js" autostart="true"></script>
 | 
			
		||||
    <script src="_content/ThingsGateway.Components/prism/prism.min.js"></script>
 | 
			
		||||
    <script src="_content/BlazorComponent/js/blazor-component.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										53
									
								
								framework/Foundation/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								framework/Foundation/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
<Project>
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<Version>3.0.0.25</Version>
 | 
			
		||||
		<GenerateDocumentationFile>True</GenerateDocumentationFile>
 | 
			
		||||
		<LangVersion>latest</LangVersion>
 | 
			
		||||
		<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks>
 | 
			
		||||
		<Description>
 | 
			
		||||
			ThingsGateway.Foundation是工业设备通讯类库,归属于ThingsGateway边缘网关项目,说明文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
		</Description>
 | 
			
		||||
		<Authors>Diego</Authors>
 | 
			
		||||
		<Product>ThingsGateway</Product>
 | 
			
		||||
		<Copyright>© 2023-present Diego</Copyright>
 | 
			
		||||
		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
 | 
			
		||||
		<PublishRepositoryUrl>true</PublishRepositoryUrl>
 | 
			
		||||
		<EmbedUntrackedSource>true</EmbedUntrackedSource>
 | 
			
		||||
		<EmbedAllSources>true</EmbedAllSources>
 | 
			
		||||
		<RepositoryType>Gitee</RepositoryType>
 | 
			
		||||
		<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
			
		||||
		<PackageReadmeFile>README.md</PackageReadmeFile>
 | 
			
		||||
		<PackageIcon>icon.png</PackageIcon>
 | 
			
		||||
		<IncludeSymbols>true</IncludeSymbols>
 | 
			
		||||
		<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
 | 
			
		||||
		<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl>
 | 
			
		||||
		<PackageTags>ThingsGateway;Diego;dotNET China;Blazor;设备采集;边缘网关</PackageTags>
 | 
			
		||||
		<SignAssembly>True</SignAssembly>
 | 
			
		||||
		<DelaySign>False</DelaySign>
 | 
			
		||||
		<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
 | 
			
		||||
		<PackageOutputPath>../../nupkgs</PackageOutputPath>
 | 
			
		||||
		<AssemblyOriginatorKeyFile>../../../snks/ThingsGateway.snk</AssemblyOriginatorKeyFile>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<None Include="..\..\..\README.md" Pack="true" PackagePath="\" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<None Include="..\..\..\icon.png">
 | 
			
		||||
			<Pack>True</Pack>
 | 
			
		||||
			<PackagePath></PackagePath>
 | 
			
		||||
		</None>
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup Condition="'$(Configuration)' == 'Release'">
 | 
			
		||||
		<DebugSymbols>True</DebugSymbols>
 | 
			
		||||
		<DebugType>Embedded</DebugType>
 | 
			
		||||
		<EmbedAllSources>True</EmbedAllSources>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
	
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,14 +1,16 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Dlt645;
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 控制码
 | 
			
		||||
@@ -19,44 +21,36 @@ public enum ControlCode : byte
 | 
			
		||||
    /// 读数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Read = 0x11,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读后续数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    ReadSub = 0x12,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读站号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    ReadStation = 0x13,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Write = 0x14,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写站号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    WriteStation = 0x15,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 广播校时
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    BroadcastTime = 0x08,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 冻结
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Freeze = 0x16,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新波特率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    WriteBaudRate = 0x17,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    WritePassword = 0x18,
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,480 @@
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,109 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// DLT645_2007Address
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class DLT645_2007Address : DeviceAddressBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DLT645_2007Address()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 数据标识
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte[] DataId { get; set; } = new byte[0];
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 反转解析
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool Reverse { get; set; } = true;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 站号信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte[] Station { get; set; } = new byte[0];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 解析地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static DLT645_2007Address ParseFrom(string address)
 | 
			
		||||
    {
 | 
			
		||||
        DLT645_2007Address dLT645_2007Address = new();
 | 
			
		||||
        byte[] array;
 | 
			
		||||
        array = new byte[0];
 | 
			
		||||
        if (address.IndexOf(';') < 0)
 | 
			
		||||
        {
 | 
			
		||||
            array = address.ByHexStringToBytes().Reverse().ToArray();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
 | 
			
		||||
            for (int index = 0; index < strArray.Length; ++index)
 | 
			
		||||
            {
 | 
			
		||||
                if (strArray[index].ToUpper().StartsWith("S="))
 | 
			
		||||
                {
 | 
			
		||||
                    var station = strArray[index].Substring(2);
 | 
			
		||||
                    if (station.IsNullOrEmpty()) station = string.Empty;
 | 
			
		||||
                    if (station.Length < 12)
 | 
			
		||||
                        station = station.PadLeft(12, '0');
 | 
			
		||||
                    dLT645_2007Address.Station = station.ByHexStringToBytes().Reverse().ToArray();
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArray[index].Contains("r="))
 | 
			
		||||
                {
 | 
			
		||||
                    dLT645_2007Address.Reverse = strArray[index].Substring(2).GetBoolValue();
 | 
			
		||||
                }
 | 
			
		||||
                else if (!strArray[index].Contains("="))
 | 
			
		||||
                {
 | 
			
		||||
                    array = strArray[index].ByHexStringToBytes().Reverse().ToArray();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        dLT645_2007Address.DataId = array;
 | 
			
		||||
        return dLT645_2007Address;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        StringBuilder stringGeter = new();
 | 
			
		||||
        if (Station.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            stringGeter.Append($"s={Station.Reverse().ToArray().ToHexString()};");
 | 
			
		||||
        }
 | 
			
		||||
        if (DataId.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            stringGeter.Append($"{DataId.Reverse().ToArray().ToHexString()};");
 | 
			
		||||
        }
 | 
			
		||||
        if (!Reverse)
 | 
			
		||||
        {
 | 
			
		||||
            stringGeter.Append($"s={Reverse.ToString()};");
 | 
			
		||||
        }
 | 
			
		||||
        return stringGeter.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,117 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// DLT645_2007
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class DLT645_2007BitConverter : ThingsGatewayBitConverter
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// DLT645_2007
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DLT645_2007BitConverter(EndianType endianType) : base(endianType)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// DLT645协议转换double
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="buffer">带数据项标识</param>
 | 
			
		||||
    /// <param name="offset"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override double ToDouble(byte[] buffer, int offset)
 | 
			
		||||
    {
 | 
			
		||||
        return Convert.ToDouble(this.ToString(buffer, offset, buffer.Length));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override IThingsGatewayBitConverter CopyNew()
 | 
			
		||||
    {
 | 
			
		||||
        return new DLT645_2007BitConverter(EndianType)
 | 
			
		||||
        {
 | 
			
		||||
            DataFormat = DataFormat,
 | 
			
		||||
            BcdFormat = BcdFormat,
 | 
			
		||||
            Encoding = Encoding,
 | 
			
		||||
            IsStringReverseByteWord = IsStringReverseByteWord,
 | 
			
		||||
            Length = Length,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString(byte[] buffer)
 | 
			
		||||
    {
 | 
			
		||||
        return this.ToString(buffer, 0, buffer.Length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString(byte[] buffer, int offset, int length)
 | 
			
		||||
    {
 | 
			
		||||
        buffer = buffer.RemoveBegin(offset);
 | 
			
		||||
        buffer = buffer.BytesAdd(-0x33);
 | 
			
		||||
        var dataInfos = DLT645Helper.GetDataInfos(buffer);
 | 
			
		||||
        StringBuilder stringBuilder = new();
 | 
			
		||||
        foreach (var dataInfo in dataInfos)
 | 
			
		||||
        {
 | 
			
		||||
            //实际数据
 | 
			
		||||
            var content = buffer.SelectMiddle(4, dataInfo.ByteLength).Reverse().ToArray();
 | 
			
		||||
            if (dataInfo.IsSigned)//可能为负数
 | 
			
		||||
            {
 | 
			
		||||
                if (content[0] > 0x80)//最高位是表示正负
 | 
			
		||||
                {
 | 
			
		||||
                    content[0] = (byte)(content[0] - 0x80);
 | 
			
		||||
                    if (dataInfo.Digtal == 0)//无小数点
 | 
			
		||||
                    {
 | 
			
		||||
                        stringBuilder.Append($"-{content.ToHexString()}");
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        stringBuilder.Append((-(Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ToString(stringBuilder, dataInfo, content);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                ToString(stringBuilder, dataInfo, content);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return stringBuilder.ToString();
 | 
			
		||||
 | 
			
		||||
        static void ToString(StringBuilder stringBuilder, DataInfo dataInfo, byte[] content)
 | 
			
		||||
        {
 | 
			
		||||
            if (dataInfo.Digtal < 0)
 | 
			
		||||
            {
 | 
			
		||||
                stringBuilder.Append($"{Encoding.ASCII.GetString(content)}");
 | 
			
		||||
            }
 | 
			
		||||
            else if (dataInfo.Digtal == 0)//无小数点
 | 
			
		||||
            {
 | 
			
		||||
                stringBuilder.Append($"{content.ToHexString()}");
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                stringBuilder.Append(((Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,191 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// DLT645_2007DataHandleAdapter
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<DLT645_2007Message>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 增加FE FE FE FE的报文头部
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("前导符报文头")]
 | 
			
		||||
    public bool EnableFEHead { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override byte[] PackCommand(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        //打包时加上4个FE字节
 | 
			
		||||
        if (EnableFEHead)
 | 
			
		||||
        {
 | 
			
		||||
            return DataTransUtil.SpliceArray(new byte[4] { 0xFE, 0xFE, 0xFE, 0xFE }, command);
 | 
			
		||||
        }
 | 
			
		||||
        return command;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override DLT645_2007Message GetInstance()
 | 
			
		||||
    {
 | 
			
		||||
        return new DLT645_2007Message();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override FilterResult UnpackResponse(DLT645_2007Message request, byte[] send, byte[] body, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        //因为设备可能带有FE前导符开头,这里找到0x68的位置
 | 
			
		||||
        int headCodeIndex = -1;
 | 
			
		||||
        if (response != null)
 | 
			
		||||
        {
 | 
			
		||||
            for (int index = 0; index < response.Length; index++)
 | 
			
		||||
            {
 | 
			
		||||
                if (response[index] == 0x68)
 | 
			
		||||
                {
 | 
			
		||||
                    headCodeIndex = index;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        int sendHeadCodeIndex = 0;
 | 
			
		||||
        if (send != null)
 | 
			
		||||
        {
 | 
			
		||||
            for (int index = 0; index < send.Length; index++)
 | 
			
		||||
            {
 | 
			
		||||
                if (send[index] == 0x68)
 | 
			
		||||
                {
 | 
			
		||||
                    sendHeadCodeIndex = index;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //帧起始符 地址域  帧起始符 控制码 数据域长度共10个字节
 | 
			
		||||
        if (headCodeIndex < 0 || headCodeIndex + 10 > response.Length)
 | 
			
		||||
            return FilterResult.Cache;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var len = 10 + response[headCodeIndex + 9] + 2;
 | 
			
		||||
 | 
			
		||||
        if (response.Length - headCodeIndex < len)
 | 
			
		||||
        {
 | 
			
		||||
            return FilterResult.Cache;
 | 
			
		||||
        }
 | 
			
		||||
        if (response.Length - headCodeIndex >= len && response[len + headCodeIndex - 1] == 0x16)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            //检查校验码
 | 
			
		||||
            int sumCheck = 0;
 | 
			
		||||
            for (int i = headCodeIndex; i < len + headCodeIndex - 2; i++)
 | 
			
		||||
                sumCheck += response[i];
 | 
			
		||||
            if ((byte)sumCheck != response[len + headCodeIndex - 2])
 | 
			
		||||
            {
 | 
			
		||||
                //校验错误
 | 
			
		||||
                request.Message = "和校验错误";
 | 
			
		||||
                request.ErrorCode = 999;
 | 
			
		||||
                return FilterResult.Success;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (
 | 
			
		||||
                (response[headCodeIndex + 1] != send[sendHeadCodeIndex + 1]) ||
 | 
			
		||||
                (response[headCodeIndex + 2] != send[sendHeadCodeIndex + 2]) ||
 | 
			
		||||
                (response[headCodeIndex + 3] != send[sendHeadCodeIndex + 3]) ||
 | 
			
		||||
                (response[headCodeIndex + 4] != send[sendHeadCodeIndex + 4]) ||
 | 
			
		||||
                (response[headCodeIndex + 5] != send[sendHeadCodeIndex + 5]) ||
 | 
			
		||||
                (response[headCodeIndex + 6] != send[sendHeadCodeIndex + 6])
 | 
			
		||||
                )//设备地址不符合时,返回错误
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (
 | 
			
		||||
                (send[sendHeadCodeIndex + 1] == 0xAA) &&
 | 
			
		||||
                (send[sendHeadCodeIndex + 2] == 0xAA) &&
 | 
			
		||||
                (send[sendHeadCodeIndex + 3] == 0xAA) &&
 | 
			
		||||
                (send[sendHeadCodeIndex + 4] == 0xAA) &&
 | 
			
		||||
                (send[sendHeadCodeIndex + 5] == 0xAA) &&
 | 
			
		||||
                (send[sendHeadCodeIndex + 6] == 0xAA)
 | 
			
		||||
                )//读写通讯地址例外
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    request.Message = "返回地址不符合规则";
 | 
			
		||||
                    request.ErrorCode = 999;
 | 
			
		||||
                    return FilterResult.Success;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if ((response[headCodeIndex + 8] != send[sendHeadCodeIndex + 8] + 0x80))//控制码不符合时,返回错误
 | 
			
		||||
            {
 | 
			
		||||
                request.Message = $"返回控制码:0x{response[headCodeIndex + 8]:X2},请求控制码:0x{send[sendHeadCodeIndex + 8]:X2},不符合规则";
 | 
			
		||||
                request.ErrorCode = 999;
 | 
			
		||||
                return FilterResult.Success;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if ((response[headCodeIndex + 8] & 0x40) == 0x40)//控制码bit6为1时,返回错误
 | 
			
		||||
            {
 | 
			
		||||
                byte byte1 = (byte)(response[headCodeIndex + 10] - 0x33);
 | 
			
		||||
                var error = DLT645Helper.Get2007ErrorMessage(byte1);
 | 
			
		||||
                request.Message = $"异常控制码:0x{response[headCodeIndex + 8]:X2},错误信息:{error}";
 | 
			
		||||
                request.ErrorCode = 999;
 | 
			
		||||
                return FilterResult.Success;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (send[sendHeadCodeIndex + 8] == (byte)ControlCode.Read ||
 | 
			
		||||
    send[sendHeadCodeIndex + 8] == (byte)ControlCode.Write
 | 
			
		||||
    )
 | 
			
		||||
            {
 | 
			
		||||
                //数据标识不符合时,返回错误
 | 
			
		||||
                if (
 | 
			
		||||
                (response[headCodeIndex + 10] == send[sendHeadCodeIndex + 10]) &&
 | 
			
		||||
                (response[headCodeIndex + 11] == send[sendHeadCodeIndex + 11]) &&
 | 
			
		||||
                (response[headCodeIndex + 12] == send[sendHeadCodeIndex + 12]) &&
 | 
			
		||||
                (response[headCodeIndex + 13] == send[sendHeadCodeIndex + 13])
 | 
			
		||||
                )
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    request.Message = "返回数据标识不符合规则";
 | 
			
		||||
                    request.ErrorCode = 999;
 | 
			
		||||
                    return FilterResult.Success;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            request.Content = response.RemoveBegin(headCodeIndex + 10).RemoveLast(response.Length + 2 - len - headCodeIndex);
 | 
			
		||||
            request.ErrorCode = 0;
 | 
			
		||||
            return FilterResult.Success;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            request.ErrorCode = 999;
 | 
			
		||||
            return FilterResult.Success;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class DLT645_2007Message : MessageBase, IMessage
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override int HeadBytesLength => -1;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override bool CheckHeadBytes(byte[] heads)
 | 
			
		||||
    {
 | 
			
		||||
        BodyLength = -1;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,480 @@
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
 | 
			
		||||
internal static class PackHelper
 | 
			
		||||
{
 | 
			
		||||
    public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
 | 
			
		||||
    {
 | 
			
		||||
        var byteConverter = device.ThingsGatewayBitConverter;
 | 
			
		||||
        var result = new List<T>();
 | 
			
		||||
        //需要先剔除额外信息,比如dataformat等
 | 
			
		||||
        foreach (var item in deviceVariables)
 | 
			
		||||
        {
 | 
			
		||||
            var address = item.VariableAddress;
 | 
			
		||||
 | 
			
		||||
            IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
 | 
			
		||||
            item.ThingsGatewayBitConverter = transformParameter;
 | 
			
		||||
            //item.VariableAddress = address;
 | 
			
		||||
            item.Index = device.GetBitOffset(item.VariableAddress);
 | 
			
		||||
 | 
			
		||||
            result.Add(new()
 | 
			
		||||
            {
 | 
			
		||||
                DeviceVariableRunTimes = new() { item },
 | 
			
		||||
                VariableAddress = address,
 | 
			
		||||
                Length = 1,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,21 @@
 | 
			
		||||
#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.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
global using ThingsGateway.Foundation.Core;
 | 
			
		||||
global using ThingsGateway.Foundation.Serial;
 | 
			
		||||
global using ThingsGateway.Foundation.Sockets;
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -0,0 +1,21 @@
 | 
			
		||||
#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.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
global using ThingsGateway.Foundation.Core;
 | 
			
		||||
global using ThingsGateway.Foundation.Serial;
 | 
			
		||||
global using ThingsGateway.Foundation.Sockets;
 | 
			
		||||
@@ -0,0 +1,175 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Modbus协议地址
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusAddress : DeviceAddressBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ModbusAddress()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读取功能码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ushort AddressStart => Address.ToUShort();
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读取功能码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte ReadFunction { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 站号信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte Station { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写入功能码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte WriteFunction { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 打包临时写入,需要读取的字节长度
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int ByteLength { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// BitIndex
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int BitIndex => (int)(Address.SplitDot().LastOrDefault().ToInt());
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读取功能码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ushort AddressEnd => (ushort)(AddressStart + (Math.Ceiling(ByteLength / 2.0) > 0 ? Math.Ceiling(ByteLength / 2.0) : 1));
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 作为Slave时需提供的SocketId,用于分辨Socket客户端,通常对比的是初始链接时的注册包
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string SocketId { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 解析地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static ModbusAddress ParseFrom(string address, byte station)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusAddress modbusAddress = new()
 | 
			
		||||
        {
 | 
			
		||||
            Station = station
 | 
			
		||||
        };
 | 
			
		||||
        return ParseFrom(address, modbusAddress);
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 解析地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static ModbusAddress ParseFrom(string address, ModbusAddress modbusAddress = null)
 | 
			
		||||
    {
 | 
			
		||||
        modbusAddress ??= new();
 | 
			
		||||
        if (address.IndexOf(';') < 0)
 | 
			
		||||
        {
 | 
			
		||||
            Address(address);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
            for (int index = 0; index < strArray.Length; ++index)
 | 
			
		||||
            {
 | 
			
		||||
                if (strArray[index].ToUpper().StartsWith("S="))
 | 
			
		||||
                {
 | 
			
		||||
                    if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
 | 
			
		||||
                        modbusAddress.Station = byte.Parse(strArray[index].Substring(2));
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArray[index].ToUpper().StartsWith("W="))
 | 
			
		||||
                {
 | 
			
		||||
                    if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
 | 
			
		||||
                        modbusAddress.WriteFunction = byte.Parse(strArray[index].Substring(2));
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArray[index].ToUpper().StartsWith("ID="))
 | 
			
		||||
                {
 | 
			
		||||
                    modbusAddress.SocketId = strArray[index].Substring(3);
 | 
			
		||||
                }
 | 
			
		||||
                else if (!strArray[index].Contains("="))
 | 
			
		||||
                {
 | 
			
		||||
                    Address(strArray[index]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return modbusAddress;
 | 
			
		||||
 | 
			
		||||
        void Address(string address)
 | 
			
		||||
        {
 | 
			
		||||
            var readF = ushort.Parse(address.Substring(0, 1));
 | 
			
		||||
            if (readF > 4)
 | 
			
		||||
                throw new("功能码错误");
 | 
			
		||||
            switch (readF)
 | 
			
		||||
            {
 | 
			
		||||
                case 0:
 | 
			
		||||
                    modbusAddress.ReadFunction = 1;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 1:
 | 
			
		||||
                    modbusAddress.ReadFunction = 2;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 3:
 | 
			
		||||
                    modbusAddress.ReadFunction = 4;
 | 
			
		||||
                    break;
 | 
			
		||||
                case 4:
 | 
			
		||||
                    modbusAddress.ReadFunction = 3;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            modbusAddress.Address = (double.Parse(address.Substring(1)) - 1).ToString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        StringBuilder stringGeter = new();
 | 
			
		||||
        if (Station > 0)
 | 
			
		||||
        {
 | 
			
		||||
            stringGeter.Append($"s={Station.ToString()};");
 | 
			
		||||
        }
 | 
			
		||||
        if (WriteFunction > 0)
 | 
			
		||||
        {
 | 
			
		||||
            stringGeter.Append($"w={WriteFunction.ToString()};");
 | 
			
		||||
        }
 | 
			
		||||
        if (!string.IsNullOrEmpty(SocketId))
 | 
			
		||||
        {
 | 
			
		||||
            stringGeter.Append($"id={SocketId};");
 | 
			
		||||
        }
 | 
			
		||||
        stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString());
 | 
			
		||||
        return stringGeter.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private string GetFunctionString(int readF)
 | 
			
		||||
    {
 | 
			
		||||
        return readF switch
 | 
			
		||||
        {
 | 
			
		||||
            1 => "0",
 | 
			
		||||
            2 => "1",
 | 
			
		||||
            3 => "4",
 | 
			
		||||
            4 => "3",
 | 
			
		||||
            _ => "4",
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,334 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Bool;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
internal class ModbusHelper
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加Crc16
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static byte[] AddCrc(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        return EasyCRC16.CRC16(command);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加ModbusTcp报文头
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
 | 
			
		||||
    {
 | 
			
		||||
        byte[] tcp = new byte[modbus.Length + 6];
 | 
			
		||||
        tcp[0] = BitConverter.GetBytes(id)[1];
 | 
			
		||||
        tcp[1] = BitConverter.GetBytes(id)[0];
 | 
			
		||||
        tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
 | 
			
		||||
        tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
 | 
			
		||||
        modbus.CopyTo(tcp, 6);
 | 
			
		||||
        return tcp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// modbus地址格式说明
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        StringBuilder stringBuilder = new();
 | 
			
		||||
        stringBuilder.AppendLine("Modbus寄存器");
 | 
			
		||||
        stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。");
 | 
			
		||||
        stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。");
 | 
			
		||||
        stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。");
 | 
			
		||||
        stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。");
 | 
			
		||||
        stringBuilder.AppendLine("举例:");
 | 
			
		||||
        stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
 | 
			
		||||
        stringBuilder.AppendLine("额外格式:");
 | 
			
		||||
        stringBuilder.AppendLine("设备站号 ,比如40001;s=2; ,代表设备地址为2的保持寄存器第一个寄存器");
 | 
			
		||||
        stringBuilder.AppendLine("写入功能码 ,比如40001;w=16; ,代表保持寄存器第一个寄存器,写入值时采用0x10功能码,而不是默认的0x06功能码");
 | 
			
		||||
        return stringBuilder.ToString();
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通过错误码来获取到对应的文本消息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static string GetDescriptionByErrorCode(byte code)
 | 
			
		||||
    {
 | 
			
		||||
        return code switch
 | 
			
		||||
        {
 | 
			
		||||
            1 => "不支持的功能码",
 | 
			
		||||
            2 => "读取寄存器越界",
 | 
			
		||||
            3 => "读取长度超限",
 | 
			
		||||
            4 => "读写异常",
 | 
			
		||||
            _ => "未知错误",
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="send">发送数据</param>
 | 
			
		||||
    /// <param name="response">返回数据</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static OperResult<byte[], FilterResult> GetModbusData(byte[] send, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (response.Length < 3)
 | 
			
		||||
                return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if (response[1] >= 0x80)//错误码
 | 
			
		||||
                return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
 | 
			
		||||
            if (response[1] <= 0x05)
 | 
			
		||||
            {
 | 
			
		||||
                if ((response.Length < response[2] + 3))
 | 
			
		||||
                    return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if ((response.Length < 6))
 | 
			
		||||
                    return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (send.Length == 0)
 | 
			
		||||
            {
 | 
			
		||||
                var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
 | 
			
		||||
                result.Message = "接收数据正确,但主机并没有主动请求数据";
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
            if (send[0] != response[0])
 | 
			
		||||
                return new OperResult<byte[], FilterResult>(string.Format("站号不一致", send[0], response[0])) { Content2 = FilterResult.Success };
 | 
			
		||||
            if (send[1] != response[1])
 | 
			
		||||
                return new OperResult<byte[], FilterResult>() { Message = "功能码不一致", Content2 = FilterResult.Success };
 | 
			
		||||
            return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[], FilterResult>(ex) { Content2 = FilterResult.Success };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 去除Crc,返回modbus数据区
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="send"></param>
 | 
			
		||||
    /// <param name="response"></param>
 | 
			
		||||
    /// <param name="crcCheck"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static OperResult<byte[], FilterResult> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
 | 
			
		||||
    {
 | 
			
		||||
        if (response.Length < 3)
 | 
			
		||||
            return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
 | 
			
		||||
 | 
			
		||||
        if (response[1] >= 0x80)//错误码
 | 
			
		||||
            return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
 | 
			
		||||
        if (response[1] <= 0x05)
 | 
			
		||||
        {
 | 
			
		||||
            if ((response.Length < response[2] + 5))
 | 
			
		||||
                return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if ((response.Length < 8))
 | 
			
		||||
                return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var data = response.SelectMiddle(0, response[2] != 0 ? response[2] + 5 : 8);
 | 
			
		||||
        if (crcCheck && !EasyCRC16.CheckCRC16(data))
 | 
			
		||||
            return new OperResult<byte[], FilterResult>("Crc校验失败" + DataTransUtil.ByteToHexString(data, ' ')) { Content2 = FilterResult.Success };
 | 
			
		||||
        return GetModbusData(send, data.RemoveLast(2));
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取读取报文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, station);
 | 
			
		||||
            return OperResult.CreateSuccessResult(GetReadModbusCommand(mAddress, length));
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取写入布尔量报文,根据地址识别功能码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, station);
 | 
			
		||||
 | 
			
		||||
            //功能码或实际长度
 | 
			
		||||
            if (values?.Length > 1 || mAddress.WriteFunction == 15)
 | 
			
		||||
                return GetWriteBoolModbusCommand(mAddress, values, values.Length);
 | 
			
		||||
            else
 | 
			
		||||
                return GetWriteBoolModbusCommand(address, values[0], station);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取写入字报文,根据地址识别功能码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, station);
 | 
			
		||||
 | 
			
		||||
            //功能码或实际长度
 | 
			
		||||
            if (value?.Length > 2 || mAddress.WriteFunction == 16)
 | 
			
		||||
                return OperResult.CreateSuccessResult(GetWriteModbusCommand(mAddress, value));
 | 
			
		||||
            else
 | 
			
		||||
                return OperResult.CreateSuccessResult(GetWriteOneModbusCommand(mAddress, value));
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取读取报文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static byte[] GetReadModbusCommand(ModbusAddress mAddress, int length)
 | 
			
		||||
    {
 | 
			
		||||
        byte[] array = new byte[6]
 | 
			
		||||
        {
 | 
			
		||||
        (byte) mAddress.Station,
 | 
			
		||||
        (byte) mAddress.ReadFunction,
 | 
			
		||||
        BitConverter.GetBytes(mAddress.AddressStart)[1],
 | 
			
		||||
        BitConverter.GetBytes(mAddress.AddressStart)[0],
 | 
			
		||||
        BitConverter.GetBytes(length)[1],
 | 
			
		||||
        BitConverter.GetBytes(length)[0]
 | 
			
		||||
        };
 | 
			
		||||
        return array;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取05写入布尔量报文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (address.IndexOf('.') <= 0)
 | 
			
		||||
            {
 | 
			
		||||
                var mAddress = ModbusAddress.ParseFrom(address, station);
 | 
			
		||||
 | 
			
		||||
                return OperResult.CreateSuccessResult(GetWriteBoolModbusCommand(mAddress, value));
 | 
			
		||||
            }
 | 
			
		||||
            return new("不支持写入字寄存器的某一位");
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取05写入布尔量报文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static byte[] GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
 | 
			
		||||
    {
 | 
			
		||||
        byte[] array = new byte[6]
 | 
			
		||||
        {
 | 
			
		||||
    (byte) mAddress.Station,
 | 
			
		||||
    (byte)5,
 | 
			
		||||
    BitConverter.GetBytes(mAddress.AddressStart)[1],
 | 
			
		||||
    BitConverter.GetBytes(mAddress.AddressStart)[0],
 | 
			
		||||
     0,
 | 
			
		||||
     0
 | 
			
		||||
        };
 | 
			
		||||
        if (value)
 | 
			
		||||
        {
 | 
			
		||||
            array[4] = 0xFF;
 | 
			
		||||
            array[5] = 0;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            array[4] = 0;
 | 
			
		||||
            array[5] = 0;
 | 
			
		||||
        }
 | 
			
		||||
        return array;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取15写入布尔量报文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            byte[] numArray1 = values.BoolArrayToByte();
 | 
			
		||||
            byte[] numArray2 = new byte[7 + numArray1.Length];
 | 
			
		||||
            numArray2[0] = (byte)mAddress.Station;
 | 
			
		||||
            numArray2[1] = (byte)15;
 | 
			
		||||
            numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
 | 
			
		||||
            numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
 | 
			
		||||
            numArray2[4] = (byte)(length / 256);
 | 
			
		||||
            numArray2[5] = (byte)(length % 256);
 | 
			
		||||
            numArray2[6] = (byte)numArray1.Length;
 | 
			
		||||
            numArray1.CopyTo(numArray2, 7);
 | 
			
		||||
            return OperResult.CreateSuccessResult(numArray2);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取16写入字报文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static byte[] GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
 | 
			
		||||
    {
 | 
			
		||||
        byte[] numArray = new byte[7 + values.Length];
 | 
			
		||||
        numArray[0] = (byte)mAddress.Station;
 | 
			
		||||
        numArray[1] = (byte)16;
 | 
			
		||||
        numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
 | 
			
		||||
        numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
 | 
			
		||||
        numArray[4] = (byte)(values.Length / 2 / 256);
 | 
			
		||||
        numArray[5] = (byte)(values.Length / 2 % 256);
 | 
			
		||||
        numArray[6] = (byte)values.Length;
 | 
			
		||||
        values.CopyTo(numArray, 7);
 | 
			
		||||
        return numArray;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取6写入字报文
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal static byte[] GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
 | 
			
		||||
    {
 | 
			
		||||
        byte[] numArray = new byte[4 + values.Length];
 | 
			
		||||
        numArray[0] = (byte)mAddress.Station;
 | 
			
		||||
        numArray[1] = (byte)6;
 | 
			
		||||
        numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
 | 
			
		||||
        numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
 | 
			
		||||
        values.CopyTo(numArray, 4);
 | 
			
		||||
        return numArray;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,191 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// ModbusRtu
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusRtu : ReadWriteDevicesSerialSessionBase
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ModbusRtu
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="serialSession"></param>
 | 
			
		||||
    public ModbusRtu(SerialSession serialSession) : base(serialSession)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Crc校验
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("Crc校验")]
 | 
			
		||||
    public bool Crc16CheckEnable { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 站号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("站号")]
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusRtuDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
        {
 | 
			
		||||
            Crc16CheckEnable = Crc16CheckEnable,
 | 
			
		||||
            CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
 | 
			
		||||
        };
 | 
			
		||||
        SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            if (FrameTime != 0)
 | 
			
		||||
                Thread.Sleep(FrameTime);
 | 
			
		||||
            var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            await Task.Delay(FrameTime, cancellationToken);
 | 
			
		||||
            var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,85 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Rtu适配器
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusRtuDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusRtuMessage>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检测CRC
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool Crc16CheckEnable { get; set; } = true;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="command"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override byte[] PackCommand(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        return ModbusHelper.AddCrc(command);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override ModbusRtuMessage GetInstance()
 | 
			
		||||
    {
 | 
			
		||||
        return new ModbusRtuMessage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override FilterResult UnpackResponse(ModbusRtuMessage request, byte[] send, byte[] body, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        //链路干扰时需剔除前缀中的多于字节,初步按站号+功能码找寻初始字节
 | 
			
		||||
        if (send?.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            int index = -1;
 | 
			
		||||
            for (int i = 0; i < response.Length - 1; i++)
 | 
			
		||||
            {
 | 
			
		||||
                if (response[i] == send[0] && (response[i + 1] == send[1] || response[i + 1] == (send[1] + 0x80)))
 | 
			
		||||
                {
 | 
			
		||||
                    index = i;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (index >= 0)
 | 
			
		||||
            {
 | 
			
		||||
                response = response.RemoveBegin(index);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //理想状态检测
 | 
			
		||||
            var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
 | 
			
		||||
            if (result.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                request.ErrorCode = result.ErrorCode;
 | 
			
		||||
                request.Message = result.Message;
 | 
			
		||||
                request.Content = result.Content;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                request.ErrorCode = result.ErrorCode;
 | 
			
		||||
                request.Message = result.Message;
 | 
			
		||||
            }
 | 
			
		||||
            return result.Content2;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return FilterResult.Success;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusRtuMessage : MessageBase, IMessage
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override int HeadBytesLength => -1;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override bool CheckHeadBytes(byte[] heads)
 | 
			
		||||
    {
 | 
			
		||||
        BodyLength = -1;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,185 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusRtuOverTcp(TcpClient tcpClient) : base(tcpClient)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Crc校验
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("Crc校验")]
 | 
			
		||||
    public bool Crc16CheckEnable { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 站号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("站号")]
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusRtuDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
        {
 | 
			
		||||
            Crc16CheckEnable = Crc16CheckEnable,
 | 
			
		||||
            CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
 | 
			
		||||
        };
 | 
			
		||||
        TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            if (FrameTime != 0)
 | 
			
		||||
                Thread.Sleep(FrameTime);
 | 
			
		||||
            var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            await Task.Delay(FrameTime, cancellationToken);
 | 
			
		||||
            var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,184 @@
 | 
			
		||||
#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.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class ModbusRtuOverUdp : ReadWriteDevicesUdpSessionBase
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusRtuOverUdp(UdpSession udpSession) : base(udpSession)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Crc校验
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("Crc校验")]
 | 
			
		||||
    public bool Crc16CheckEnable { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 站号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("站号")]
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusRtuOverUdpDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
        {
 | 
			
		||||
            Crc16CheckEnable = Crc16CheckEnable,
 | 
			
		||||
        };
 | 
			
		||||
        UdpSession.Config.SetUdpDataHandlingAdapter(() =>
 | 
			
		||||
        {
 | 
			
		||||
            return dataHandleAdapter;
 | 
			
		||||
        });
 | 
			
		||||
        UdpSession.Setup(UdpSession.Config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            if (FrameTime != 0)
 | 
			
		||||
                Thread.Sleep(FrameTime);
 | 
			
		||||
            var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            await Task.Delay(FrameTime, cancellationToken);
 | 
			
		||||
            var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusRtuMessage>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检测CRC
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool Crc16CheckEnable { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override byte[] PackCommand(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        return ModbusHelper.AddCrc(command);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override ModbusRtuMessage GetInstance()
 | 
			
		||||
    {
 | 
			
		||||
        return new ModbusRtuMessage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,452 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Bool;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SerialSession, Task<OperResult>> WriteData;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 继电器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 开关输入
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 输入寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 保持寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusSerialServer(SerialSession serialSession) : base(serialSession)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 多站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool MulStation { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 默认站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var item in ModbusServer01ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer02ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer03ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer04ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        ModbusServer01ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer02ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer03ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer04ByteBlocks.Clear();
 | 
			
		||||
        base.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EasyLock easyLock = new();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult<byte[]>("地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
            int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    byte[] bytes0 = new byte[len];
 | 
			
		||||
                    ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer01ByteBlock.Read(bytes0);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes0);
 | 
			
		||||
                case 2:
 | 
			
		||||
                    byte[] bytes1 = new byte[len];
 | 
			
		||||
                    ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer02ByteBlock.Read(bytes1);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes1);
 | 
			
		||||
                case 3:
 | 
			
		||||
 | 
			
		||||
                    byte[] bytes3 = new byte[len];
 | 
			
		||||
                    ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer03ByteBlock.Read(bytes3);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes3);
 | 
			
		||||
                case 4:
 | 
			
		||||
                    byte[] bytes4 = new byte[len];
 | 
			
		||||
                    ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer04ByteBlock.Read(bytes4);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes4);
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult<byte[]>("功能码错误");
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Read(address, length));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusSerialServerDataHandleAdapter dataHandleAdapter = new();
 | 
			
		||||
        dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
 | 
			
		||||
        SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(ex);
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult("地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 3:
 | 
			
		||||
                    ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer03ByteBlock.Write(value);
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
                case 4:
 | 
			
		||||
                    ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer04ByteBlock.Write(value);
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult("功能码错误");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return (new OperResult(ex));
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return (new OperResult("地址错误"));
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                    return (OperResult.CreateSuccessResult());
 | 
			
		||||
                case 2:
 | 
			
		||||
                    ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                    return (OperResult.CreateSuccessResult());
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult("功能码错误");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override async Task Received(SerialSession client, ReceivedDataEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var requestInfo = e.RequestInfo;
 | 
			
		||||
            //接收外部报文
 | 
			
		||||
            if (requestInfo is ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
            {
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
                if (!modbusServerMessage.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
 | 
			
		||||
                {
 | 
			
		||||
                    var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
 | 
			
		||||
                    if (data.IsSuccess)
 | 
			
		||||
                    {
 | 
			
		||||
                        var coreData = data.Content;
 | 
			
		||||
                        if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                        {
 | 
			
		||||
                            coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
 | 
			
		||||
                        }
 | 
			
		||||
                        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
 | 
			
		||||
                        SerialSession.Send(sendData);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        WriteError(SerialSession, modbusServerMessage);//返回错误码
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else//写入
 | 
			
		||||
                {
 | 
			
		||||
                    var coreData = modbusServerMessage.Content;
 | 
			
		||||
                    if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入继电器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
                            if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            //写入内存区
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入寄存器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                            if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Logger.LogError(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        //返回错误码
 | 
			
		||||
        static void WriteError(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
        {
 | 
			
		||||
            var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2)
 | 
			
		||||
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
 | 
			
		||||
            sendData[1] = (byte)(sendData[1] + 128);
 | 
			
		||||
            SerialSession.Send(sendData);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void WriteSuccess03(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
    {
 | 
			
		||||
        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 6);
 | 
			
		||||
        SerialSession.Send(sendData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Init(ModbusAddress mAddress)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,141 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class ModbusSerialServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusSerialServerMessage>
 | 
			
		||||
{
 | 
			
		||||
    private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="command"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override byte[] PackCommand(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        return ModbusHelper.AddCrc(command);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取modbus写入数据区内容
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="response">返回数据</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal OperResult<byte[]> GetModbusData(byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var func = ThingsGatewayBitConverter.ToByte(response, 1);
 | 
			
		||||
            if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6)
 | 
			
		||||
            {
 | 
			
		||||
                if (response.Length == 6)
 | 
			
		||||
                    return OperResult.CreateSuccessResult(response);
 | 
			
		||||
            }
 | 
			
		||||
            else if (func == 15 || func == 16)
 | 
			
		||||
            {
 | 
			
		||||
                var length = ThingsGatewayBitConverter.ToByte(response, 6);
 | 
			
		||||
                if (response.Length == 7 + length)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult(response);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" };
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override ModbusSerialServerMessage GetInstance()
 | 
			
		||||
    {
 | 
			
		||||
        return new ModbusSerialServerMessage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override FilterResult UnpackResponse(ModbusSerialServerMessage request, byte[] send, byte[] body, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        var result1 = ModbusHelper.GetModbusRtuData(new byte[0], response, true);
 | 
			
		||||
        if (result1.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var result = GetModbusData(response.RemoveLast(2));
 | 
			
		||||
            if (result.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                //解析01 03 00 00 00 0A
 | 
			
		||||
                var station = ThingsGatewayBitConverter.ToByte(response, 0);
 | 
			
		||||
                var function = ThingsGatewayBitConverter.ToByte(response, 1);
 | 
			
		||||
                int addressStart = ThingsGatewayBitConverter.ToInt16(response, 2);
 | 
			
		||||
                if (addressStart == -1)
 | 
			
		||||
                {
 | 
			
		||||
                    addressStart = 65535;
 | 
			
		||||
                }
 | 
			
		||||
                if (function > 4)
 | 
			
		||||
                {
 | 
			
		||||
                    if (function > 6)
 | 
			
		||||
                    {
 | 
			
		||||
                        request.CurModbusAddress = new ModbusAddress()
 | 
			
		||||
                        {
 | 
			
		||||
                            Station = station,
 | 
			
		||||
                            Address = addressStart.ToString(),
 | 
			
		||||
                            WriteFunction = function,
 | 
			
		||||
                            ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3),
 | 
			
		||||
                        };
 | 
			
		||||
                        request.Length = ThingsGatewayBitConverter.ToByte(response, 5);
 | 
			
		||||
                        request.Content = result.Content.RemoveBegin(7);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        request.CurModbusAddress = new ModbusAddress()
 | 
			
		||||
                        {
 | 
			
		||||
                            Station = station,
 | 
			
		||||
                            Address = addressStart.ToString(),
 | 
			
		||||
                            WriteFunction = function,
 | 
			
		||||
                            ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3),
 | 
			
		||||
                        };
 | 
			
		||||
                        request.Length = 1;
 | 
			
		||||
                        request.Content = result.Content.RemoveBegin(4);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    request.CurModbusAddress = new ModbusAddress()
 | 
			
		||||
                    {
 | 
			
		||||
                        Station = station,
 | 
			
		||||
                        Address = addressStart.ToString(),
 | 
			
		||||
                        ReadFunction = function,
 | 
			
		||||
                    };
 | 
			
		||||
                    request.Length = ThingsGatewayBitConverter.ToByte(response, 5);
 | 
			
		||||
                }
 | 
			
		||||
                request.ErrorCode = result.ErrorCode;
 | 
			
		||||
                request.Message = result.Message;
 | 
			
		||||
                return FilterResult.Success;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                request.ErrorCode = result.ErrorCode;
 | 
			
		||||
                request.Message = result.Message;
 | 
			
		||||
                return FilterResult.Cache;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return result1.Content2;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusSerialServerMessage : MessageBase, IMessage
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 当前关联的地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ModbusAddress CurModbusAddress { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 当前读写的数据长度
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Length { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override int HeadBytesLength => -1;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override bool CheckHeadBytes(byte[] heads)
 | 
			
		||||
    {
 | 
			
		||||
        BodyLength = -1;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,184 @@
 | 
			
		||||
#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.Adapter.Modbus;
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class ModbusTcp : ReadWriteDevicesTcpClientBase
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusTcp(TcpClient tcpClient) : base(tcpClient)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检测事务标识符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("检测事务标识符")]
 | 
			
		||||
    public bool IsCheckMessageId { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 站号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("站号")]
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            var data = SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            var data = await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusTcpDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
        {
 | 
			
		||||
            IsCheckMessageId = IsCheckMessageId,
 | 
			
		||||
            CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
 | 
			
		||||
        };
 | 
			
		||||
        TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            if (FrameTime != 0)
 | 
			
		||||
                Thread.Sleep(FrameTime);
 | 
			
		||||
            var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            await Task.Delay(FrameTime, cancellationToken);
 | 
			
		||||
            var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// ModbusTcpDataHandleAdapter
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage>
 | 
			
		||||
{
 | 
			
		||||
    private readonly EasyIncrementCount easyIncrementCount = new(ushort.MaxValue);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检测事务标识符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool IsCheckMessageId
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return Request?.IsCheckMessageId ?? false;
 | 
			
		||||
        }
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            Request.IsCheckMessageId = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override byte[] PackCommand(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override ModbusTcpMessage GetInstance()
 | 
			
		||||
    {
 | 
			
		||||
        return new ModbusTcpMessage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override FilterResult UnpackResponse(ModbusTcpMessage request, byte[] send, byte[] body, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        //理想状态检测
 | 
			
		||||
        var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
 | 
			
		||||
        if (result.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            request.ErrorCode = result.ErrorCode;
 | 
			
		||||
            request.Message = result.Message;
 | 
			
		||||
            request.Content = result.Content;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            request.ErrorCode = result.ErrorCode;
 | 
			
		||||
            request.Message = result.Message;
 | 
			
		||||
        }
 | 
			
		||||
        return result.Content2;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,41 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusTcpMessage : MessageBase, IMessage
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override int HeadBytesLength => 6;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检测事务标识符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool IsCheckMessageId { get; set; } = false;
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override bool CheckHeadBytes(byte[] heads)
 | 
			
		||||
    {
 | 
			
		||||
        if (heads == null || heads.Length <= 0) return false;
 | 
			
		||||
        HeadBytes = heads;
 | 
			
		||||
 | 
			
		||||
        int num = (HeadBytes[4] * 256) + HeadBytes[5];
 | 
			
		||||
        BodyLength = num;
 | 
			
		||||
 | 
			
		||||
        if (!IsCheckMessageId)
 | 
			
		||||
            return true;
 | 
			
		||||
        else
 | 
			
		||||
            return SendBytes[0] == HeadBytes[0] && SendBytes[1] == HeadBytes[1] && HeadBytes[2] == 0 && HeadBytes[3] == 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,249 @@
 | 
			
		||||
#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.Adapter.Modbus;
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusTcpDtu(TcpService tcpService) : base(tcpService)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
        ModbusTcpDtuPlugin modbusTcpSalvePlugin = new ModbusTcpDtuPlugin();
 | 
			
		||||
        tcpService.Config.ConfigurePlugins(a =>
 | 
			
		||||
         {
 | 
			
		||||
             a.Add(modbusTcpSalvePlugin);
 | 
			
		||||
         });
 | 
			
		||||
        tcpService.Setup(tcpService.Config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检测事务标识符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("检测事务标识符")]
 | 
			
		||||
    public bool IsCheckMessageId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 站号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("站号")]
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
    {
 | 
			
		||||
        if (socketClient is SocketClient client)
 | 
			
		||||
        {
 | 
			
		||||
            ModbusTcpDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
            {
 | 
			
		||||
                IsCheckMessageId = IsCheckMessageId,
 | 
			
		||||
                CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
 | 
			
		||||
            };
 | 
			
		||||
            client.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var item in TcpService.GetClients())
 | 
			
		||||
            {
 | 
			
		||||
                ModbusTcpDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
                {
 | 
			
		||||
                    IsCheckMessageId = IsCheckMessageId,
 | 
			
		||||
                    CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
 | 
			
		||||
                };
 | 
			
		||||
                item.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OperResult<byte[]> SendThenReturn(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            if (TcpService.TryGetSocketClient($"ID={id}", out var client))
 | 
			
		||||
            {
 | 
			
		||||
                SetDataAdapter(client);
 | 
			
		||||
 | 
			
		||||
                var item = commandResult.Content;
 | 
			
		||||
                if (FrameTime != 0)
 | 
			
		||||
                    Thread.Sleep(FrameTime);
 | 
			
		||||
                var WaitingClientEx = client.CreateWaitingClient(new() { });
 | 
			
		||||
                var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>("客户端未连接");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return commandResult;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<OperResult<byte[]>> SendThenReturnAsync(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            if (TcpService.TryGetSocketClient($"ID={id}", out var client))
 | 
			
		||||
            {
 | 
			
		||||
                SetDataAdapter(client);
 | 
			
		||||
 | 
			
		||||
                var item = commandResult.Content;
 | 
			
		||||
                await Task.Delay(FrameTime, cancellationToken);
 | 
			
		||||
                var WaitingClientEx = client.CreateWaitingClient(new() { });
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>("客户端未连接");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return commandResult;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal class ModbusTcpDtuPlugin : PluginBase, ITcpReceivingPlugin
 | 
			
		||||
    {
 | 
			
		||||
        public Task OnTcpReceiving(ITcpClientBase client, ByteBlockEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            if (client is ISocketClient socket)
 | 
			
		||||
            {
 | 
			
		||||
                if (!socket.Id.StartsWith("ID="))
 | 
			
		||||
                {
 | 
			
		||||
                    ByteBlock byteBlock = e.ByteBlock;
 | 
			
		||||
                    var id = $"ID={byteBlock.ToArray().ToHexString()}";
 | 
			
		||||
                    socket.ResetId(id);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return e.InvokeNext();//如果本插件无法处理当前数据,请将数据转至下一个插件。
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,470 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Bool;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusTcpServer : ReadWriteDevicesTcpServerBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<OperResult>> WriteData;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 继电器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 开关输入
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 输入寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 保持寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusTcpServer(TcpService tcpService) : base(tcpService)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 多站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("多站点")]
 | 
			
		||||
    public bool MulStation { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 默认站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("默认站点")]
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var item in ModbusServer01ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer02ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer03ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer04ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        ModbusServer01ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer02ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer03ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer04ByteBlocks.Clear();
 | 
			
		||||
        base.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EasyLock easyLock = new();
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult<byte[]>("地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
            int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    byte[] bytes0 = new byte[len];
 | 
			
		||||
                    ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer01ByteBlock.Read(bytes0);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes0);
 | 
			
		||||
                case 2:
 | 
			
		||||
                    byte[] bytes1 = new byte[len];
 | 
			
		||||
                    ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer02ByteBlock.Read(bytes1);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes1);
 | 
			
		||||
                case 3:
 | 
			
		||||
 | 
			
		||||
                    byte[] bytes3 = new byte[len];
 | 
			
		||||
                    ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer03ByteBlock.Read(bytes3);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes3);
 | 
			
		||||
                case 4:
 | 
			
		||||
                    byte[] bytes4 = new byte[len];
 | 
			
		||||
                    ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer04ByteBlock.Read(bytes4);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes4);
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult<byte[]>("功能码错误");
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Read(address, length));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient)
 | 
			
		||||
    {
 | 
			
		||||
        if (socketClient is SocketClient client)
 | 
			
		||||
        {
 | 
			
		||||
            ModbusTcpServerDataHandleAdapter dataHandleAdapter = new();
 | 
			
		||||
            dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
 | 
			
		||||
            client.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var item in TcpService.GetClients())
 | 
			
		||||
            {
 | 
			
		||||
                ModbusTcpDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
                {
 | 
			
		||||
                    CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
 | 
			
		||||
                };
 | 
			
		||||
                item.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(ex);
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult("地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 3:
 | 
			
		||||
                    ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer03ByteBlock.Write(value);
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
                case 4:
 | 
			
		||||
                    ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer04ByteBlock.Write(value);
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult("功能码错误");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return (new OperResult(ex));
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return (new OperResult("地址错误"));
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                    return (OperResult.CreateSuccessResult());
 | 
			
		||||
                case 2:
 | 
			
		||||
                    ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                    return (OperResult.CreateSuccessResult());
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult("功能码错误");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override async Task Received(SocketClient client, ReceivedDataEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var requestInfo = e.RequestInfo;
 | 
			
		||||
            //接收外部报文
 | 
			
		||||
            if (requestInfo is ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
            {
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
                if (!modbusServerMessage.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
 | 
			
		||||
                {
 | 
			
		||||
                    var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
 | 
			
		||||
                    if (data.IsSuccess)
 | 
			
		||||
                    {
 | 
			
		||||
                        var coreData = data.Content;
 | 
			
		||||
                        if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                        {
 | 
			
		||||
                            coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
 | 
			
		||||
                        }
 | 
			
		||||
                        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
 | 
			
		||||
                        sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
                        client.Send(sendData);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        WriteError(client, modbusServerMessage);//返回错误码
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else//写入
 | 
			
		||||
                {
 | 
			
		||||
                    var coreData = modbusServerMessage.Content;
 | 
			
		||||
                    if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入继电器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
                            if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            //写入内存区
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入寄存器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                            if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Logger.LogError(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        //返回错误码
 | 
			
		||||
        static void WriteError(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
        {
 | 
			
		||||
            var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
 | 
			
		||||
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
 | 
			
		||||
            sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
            sendData[7] = (byte)(sendData[7] + 128);
 | 
			
		||||
            client.Send(sendData);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void WriteSuccess03(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
    {
 | 
			
		||||
        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12);
 | 
			
		||||
        sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
        client.Send(sendData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Init(ModbusAddress mAddress)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,132 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpServerMessage>
 | 
			
		||||
{
 | 
			
		||||
    private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="command"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override byte[] PackCommand(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        return command;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取modbus写入数据区内容
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="response">返回数据</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal OperResult<byte[]> GetModbusData(byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var func = ThingsGatewayBitConverter.ToByte(response, 1);
 | 
			
		||||
            if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6)
 | 
			
		||||
            {
 | 
			
		||||
                if (response.Length == 6)
 | 
			
		||||
                    return OperResult.CreateSuccessResult(response);
 | 
			
		||||
            }
 | 
			
		||||
            else if (func == 15 || func == 16)
 | 
			
		||||
            {
 | 
			
		||||
                var length = ThingsGatewayBitConverter.ToByte(response, 6);
 | 
			
		||||
                if (response.Length == 7 + length)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult(response);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" };
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override ModbusTcpServerMessage GetInstance()
 | 
			
		||||
    {
 | 
			
		||||
        return new ModbusTcpServerMessage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override FilterResult UnpackResponse(ModbusTcpServerMessage request, byte[] send, byte[] body, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        var result = GetModbusData(response.RemoveBegin(6));
 | 
			
		||||
        if (result.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            //解析01 03 00 00 00 0A
 | 
			
		||||
            var station = ThingsGatewayBitConverter.ToByte(response, 6);
 | 
			
		||||
            var function = ThingsGatewayBitConverter.ToByte(response, 7);
 | 
			
		||||
            int addressStart = ThingsGatewayBitConverter.ToInt16(response, 8);
 | 
			
		||||
            if (addressStart == -1)
 | 
			
		||||
            {
 | 
			
		||||
                addressStart = 65535;
 | 
			
		||||
            }
 | 
			
		||||
            if (function > 4)
 | 
			
		||||
            {
 | 
			
		||||
                if (function > 6)
 | 
			
		||||
                {
 | 
			
		||||
                    request.CurModbusAddress = new ModbusAddress()
 | 
			
		||||
                    {
 | 
			
		||||
                        Station = station,
 | 
			
		||||
                        Address = addressStart.ToString(),
 | 
			
		||||
                        WriteFunction = function,
 | 
			
		||||
                        ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3),
 | 
			
		||||
                    };
 | 
			
		||||
                    request.Length = ThingsGatewayBitConverter.ToByte(response, 11);
 | 
			
		||||
                    request.Content = result.Content.RemoveBegin(7);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    request.CurModbusAddress = new ModbusAddress()
 | 
			
		||||
                    {
 | 
			
		||||
                        Station = station,
 | 
			
		||||
                        Address = addressStart.ToString(),
 | 
			
		||||
                        WriteFunction = function,
 | 
			
		||||
                        ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3),
 | 
			
		||||
                    };
 | 
			
		||||
                    request.Length = 1;
 | 
			
		||||
                    request.Content = result.Content.RemoveBegin(4);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                request.CurModbusAddress = new ModbusAddress()
 | 
			
		||||
                {
 | 
			
		||||
                    Station = station,
 | 
			
		||||
                    Address = addressStart.ToString(),
 | 
			
		||||
                    ReadFunction = function,
 | 
			
		||||
                };
 | 
			
		||||
                request.Length = ThingsGatewayBitConverter.ToByte(response, 11);
 | 
			
		||||
            }
 | 
			
		||||
            request.ErrorCode = result.ErrorCode;
 | 
			
		||||
            request.Message = result.Message;
 | 
			
		||||
            return FilterResult.Success;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            request.ErrorCode = result.ErrorCode;
 | 
			
		||||
            request.Message = result.Message;
 | 
			
		||||
            return FilterResult.Cache;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class ModbusTcpServerMessage : MessageBase, IMessage
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当前关联的地址
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public ModbusAddress CurModbusAddress { get; set; }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当前读写的数据长度
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int Length { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override int HeadBytesLength => 6;
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override bool CheckHeadBytes(byte[] heads)
 | 
			
		||||
        {
 | 
			
		||||
            if (heads == null || heads.Length != 6) return false;
 | 
			
		||||
            HeadBytes = heads;
 | 
			
		||||
 | 
			
		||||
            int num = (HeadBytes[4] * 256) + HeadBytes[5];
 | 
			
		||||
            BodyLength = num;
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,187 @@
 | 
			
		||||
#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.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class ModbusUdp : ReadWriteDevicesUdpSessionBase
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusUdp(UdpSession udpSession) : base(udpSession)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检测事务标识符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("检测事务标识符")]
 | 
			
		||||
    public bool IsCheckMessageId { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 站号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("站号")]
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusUdpDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
        {
 | 
			
		||||
            IsCheckMessageId = IsCheckMessageId
 | 
			
		||||
        };
 | 
			
		||||
        UdpSession.Config.SetUdpDataHandlingAdapter(() =>
 | 
			
		||||
        {
 | 
			
		||||
            return dataHandleAdapter;
 | 
			
		||||
        });
 | 
			
		||||
        UdpSession.Setup(UdpSession.Config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return SendThenReturn(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
 | 
			
		||||
            return await SendThenReturnAsync(commandResult, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            if (FrameTime != 0)
 | 
			
		||||
                Thread.Sleep(FrameTime);
 | 
			
		||||
            var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (commandResult.IsSuccess)
 | 
			
		||||
        {
 | 
			
		||||
            var item = commandResult.Content;
 | 
			
		||||
            await Task.Delay(FrameTime, cancellationToken);
 | 
			
		||||
            var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
 | 
			
		||||
            return (MessageBase)result.RequestInfo;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(commandResult.Message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusTcpMessage>
 | 
			
		||||
{
 | 
			
		||||
    private readonly EasyIncrementCount easyIncrementCount = new(ushort.MaxValue);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检测事务标识符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool IsCheckMessageId
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
        {
 | 
			
		||||
            return Request?.IsCheckMessageId ?? false;
 | 
			
		||||
        }
 | 
			
		||||
        set
 | 
			
		||||
        {
 | 
			
		||||
            Request.IsCheckMessageId = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override byte[] PackCommand(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override ModbusTcpMessage GetInstance()
 | 
			
		||||
    {
 | 
			
		||||
        return new ModbusTcpMessage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,169 @@
 | 
			
		||||
#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;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// PackHelper
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class PackHelper
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 打包变量,添加到<see href="deviceVariableSourceReads"></see>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="device"></param>
 | 
			
		||||
    /// <param name="deviceVariables"></param>
 | 
			
		||||
    /// <param name="maxPack">最大打包长度</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
 | 
			
		||||
    {
 | 
			
		||||
        if (deviceVariables == null)
 | 
			
		||||
            throw new ArgumentNullException(nameof(deviceVariables));
 | 
			
		||||
 | 
			
		||||
        var deviceVariableSourceReads = new List<T>();
 | 
			
		||||
        var byteConverter = device.ThingsGatewayBitConverter;
 | 
			
		||||
        //需要先剔除额外信息,比如dataformat等
 | 
			
		||||
        foreach (var item in deviceVariables)
 | 
			
		||||
        {
 | 
			
		||||
            var address = item.VariableAddress;
 | 
			
		||||
            IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
 | 
			
		||||
            item.ThingsGatewayBitConverter = transformParameter;
 | 
			
		||||
            //item.VariableAddress = address;
 | 
			
		||||
            item.Index = device.GetBitOffset(item.VariableAddress);
 | 
			
		||||
        }
 | 
			
		||||
        var deviceVariableRunTimeGroups = deviceVariables.GroupBy(it => it.IntervalTime);
 | 
			
		||||
        foreach (var group in deviceVariableRunTimeGroups)
 | 
			
		||||
        {
 | 
			
		||||
            Dictionary<ModbusAddress, T2> map = group.ToDictionary(it =>
 | 
			
		||||
            {
 | 
			
		||||
                var lastLen = it.DataTypeEnum.GetByteLength();
 | 
			
		||||
                if (lastLen <= 0)
 | 
			
		||||
                {
 | 
			
		||||
                    switch (it.DataTypeEnum)
 | 
			
		||||
                    {
 | 
			
		||||
                        case DataTypeEnum.String:
 | 
			
		||||
                            lastLen = it.ThingsGatewayBitConverter.Length == null ? throw new("数据类型为字符串时,必须指定字符串长度,才能进行打包") : it.ThingsGatewayBitConverter.Length.Value;
 | 
			
		||||
                            break;
 | 
			
		||||
                        default:
 | 
			
		||||
                            lastLen = 2;
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (it.ThingsGatewayBitConverter.Length != null && it.DataTypeEnum != DataTypeEnum.String)
 | 
			
		||||
                {
 | 
			
		||||
                    lastLen *= it.ThingsGatewayBitConverter.Length.Value;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var address = it.VariableAddress;
 | 
			
		||||
                if (address.IndexOf('.') > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    var addressSplits = address.SplitDot();
 | 
			
		||||
                    address = addressSplits.RemoveLast(1).ArrayToString(".");
 | 
			
		||||
                }
 | 
			
		||||
                var result = ModbusAddress.ParseFrom(address);
 | 
			
		||||
                result.ByteLength = lastLen;
 | 
			
		||||
                return result;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            //获取变量的地址
 | 
			
		||||
            var modbusAddressList = map.Keys;
 | 
			
		||||
 | 
			
		||||
            //获取功能码
 | 
			
		||||
            var functionCodes = modbusAddressList.Select(t => t.ReadFunction).Distinct();
 | 
			
		||||
            foreach (var functionCode in functionCodes)
 | 
			
		||||
            {
 | 
			
		||||
                var modbusAddressSameFunList = modbusAddressList.Where(t => t.ReadFunction == functionCode);
 | 
			
		||||
                var stationNumbers = modbusAddressSameFunList.Select(t => t.Station).Distinct();
 | 
			
		||||
                foreach (var stationNumber in stationNumbers)
 | 
			
		||||
                {
 | 
			
		||||
                    var addressList = modbusAddressSameFunList
 | 
			
		||||
                        .Where(t => t.Station == stationNumber)
 | 
			
		||||
                        .ToDictionary(t => t, t => map[t]);
 | 
			
		||||
 | 
			
		||||
                    var tempResult = LoadSourceRead<T, T2>(addressList, functionCode, group.Key, maxPack);
 | 
			
		||||
                    deviceVariableSourceReads.AddRange(tempResult);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return deviceVariableSourceReads;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static List<T> LoadSourceRead<T, T2>(Dictionary<ModbusAddress, T2> addressList, int functionCode, int intervalTime, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
 | 
			
		||||
    {
 | 
			
		||||
        List<T> sourceReads = new();
 | 
			
		||||
        //按地址和长度排序
 | 
			
		||||
        var orderByAddressEnd = addressList.Keys.OrderBy(it => it.AddressEnd);
 | 
			
		||||
        //按地址和长度排序
 | 
			
		||||
        var orderByAddressStart = addressList.Keys.OrderBy(it => it.AddressStart);
 | 
			
		||||
        //地址最小,在循环中更改
 | 
			
		||||
        var minAddress = orderByAddressStart.First().AddressStart;
 | 
			
		||||
        //地址最大
 | 
			
		||||
        var maxAddress = orderByAddressStart.Last().AddressStart;
 | 
			
		||||
 | 
			
		||||
        while (maxAddress >= minAddress)
 | 
			
		||||
        {
 | 
			
		||||
            //最大的打包长度
 | 
			
		||||
            int readLength = maxPack;
 | 
			
		||||
            if (functionCode == 1 || functionCode == 2)
 | 
			
		||||
            {
 | 
			
		||||
                readLength = maxPack * 8 * 2;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            //获取当前的一组打包地址信息,
 | 
			
		||||
            var tempAddressEnd = orderByAddressEnd.Where(t => t.AddressEnd <= minAddress + readLength).ToList();
 | 
			
		||||
            //起始地址
 | 
			
		||||
            var startAddress = tempAddressEnd.OrderBy(it => it.AddressStart).First();
 | 
			
		||||
            //读取寄存器长度
 | 
			
		||||
            var sourceLen = tempAddressEnd.Last().AddressEnd - startAddress.AddressStart;
 | 
			
		||||
 | 
			
		||||
            T sourceRead = new()
 | 
			
		||||
            {
 | 
			
		||||
                TimerTick = new TimerTick(intervalTime),
 | 
			
		||||
                //这里只需要根据地址排序的第一个地址,作为实际打包报文中的起始地址
 | 
			
		||||
                VariableAddress = startAddress.ToString(),
 | 
			
		||||
                Length = sourceLen
 | 
			
		||||
            };
 | 
			
		||||
            foreach (var item in tempAddressEnd)
 | 
			
		||||
            {
 | 
			
		||||
                var readNode = addressList[item];
 | 
			
		||||
                if ((functionCode == -1 || functionCode == 3 || functionCode == 4) && readNode.DataTypeEnum == DataTypeEnum.Boolean)
 | 
			
		||||
                {
 | 
			
		||||
                    readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 16) + readNode.Index;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    if (functionCode == 1 || functionCode == 2)
 | 
			
		||||
                        readNode.Index = item.AddressStart - startAddress.AddressStart + readNode.Index;
 | 
			
		||||
                    else
 | 
			
		||||
                        readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 2) + readNode.Index;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                sourceRead.DeviceVariableRunTimes.Add(readNode);
 | 
			
		||||
                addressList.Remove(item);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            sourceReads.Add(sourceRead);
 | 
			
		||||
            if (orderByAddressEnd.Count() > 0)
 | 
			
		||||
                minAddress = orderByAddressStart.First().AddressStart;
 | 
			
		||||
            else
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        return sourceReads;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -0,0 +1,611 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<doc>
 | 
			
		||||
    <assembly>
 | 
			
		||||
        <name>ThingsGateway.Foundation.Adapter.Modbus</name>
 | 
			
		||||
    </assembly>
 | 
			
		||||
    <members>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Modbus协议地址
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressStart">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取功能码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ReadFunction">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取功能码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            站号信息
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.WriteFunction">
 | 
			
		||||
            <summary>
 | 
			
		||||
            写入功能码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ByteLength">
 | 
			
		||||
            <summary>
 | 
			
		||||
            打包临时写入,需要读取的字节长度
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressEnd">
 | 
			
		||||
            <summary>
 | 
			
		||||
            读取功能码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Parse(System.String)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,System.Byte)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析地址
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            解析地址
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ToString">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.AddCrc(System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            添加Crc16
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.AddModbusTcpHead(System.Byte[],System.UInt16)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            添加ModbusTcp报文头
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetAddressDescription">
 | 
			
		||||
            <summary>
 | 
			
		||||
            modbus地址格式说明
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetDescriptionByErrorCode(System.Byte)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            通过错误码来获取到对应的文本消息
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetModbusData(System.Byte[],System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="send">发送数据</param>
 | 
			
		||||
            <param name="response">返回数据</param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetModbusRtuData(System.Byte[],System.Byte[],System.Boolean)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            去除Crc,返回modbus数据区
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="send"></param>
 | 
			
		||||
            <param name="response"></param>
 | 
			
		||||
            <param name="crcCheck"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetReadModbusCommand(System.String,System.Int32,System.Byte)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取读取报文
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(System.String,System.Boolean[],System.Byte)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取写入布尔量报文,根据地址识别功能码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteModbusCommand(System.String,System.Byte[],System.Byte)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取写入字报文,根据地址识别功能码
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetReadModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Int32)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取读取报文
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(System.String,System.Boolean,System.Byte)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取05写入布尔量报文
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Boolean)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取05写入布尔量报文
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Boolean[],System.Int32)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取15写入布尔量报文
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取16写入字报文
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteOneModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取6写入字报文
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Crc16CheckEnable">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Crc校验
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            站号
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SetDataAdapter(System.Object)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Crc16CheckEnable">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Crc校验
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            站号
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SetDataAdapter(System.Object)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.Crc16CheckEnable">
 | 
			
		||||
            <summary>
 | 
			
		||||
            检测CRC
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.PackCommand(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.GetInstance">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.UnpackResponse(System.Byte[],System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ModbusRtu
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.#ctor(ThingsGateway.Foundation.Serial.SerialSession)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ModbusRtu
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="serialSession"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Crc16CheckEnable">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Crc校验
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            站号
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Read(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.SetDataAdapter(System.Object)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Rtu适配器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.Crc16CheckEnable">
 | 
			
		||||
            <summary>
 | 
			
		||||
            检测CRC
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.PackCommand(System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="command"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.GetInstance">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage,System.Byte[],System.Byte[],System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.HeadBytesLength">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.CheckHeadBytes(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.SendBytesThen">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer01ByteBlocks">
 | 
			
		||||
            <summary>
 | 
			
		||||
            继电器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer02ByteBlocks">
 | 
			
		||||
            <summary>
 | 
			
		||||
            开关输入
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer03ByteBlocks">
 | 
			
		||||
            <summary>
 | 
			
		||||
            输入寄存器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer04ByteBlocks">
 | 
			
		||||
            <summary>
 | 
			
		||||
            保持寄存器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteData">
 | 
			
		||||
            <summary>
 | 
			
		||||
            接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(ThingsGateway.Foundation.Sockets.TcpService)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.MulStation">
 | 
			
		||||
            <summary>
 | 
			
		||||
            多站点
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            默认站点
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Dispose">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Read(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.SetDataAdapter(System.Object)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Received(ThingsGateway.Foundation.Sockets.SocketClient,ThingsGateway.Foundation.Core.IRequestInfo)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.PackCommand(System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="command"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.GetModbusData(System.Byte[])">
 | 
			
		||||
            <summary>
 | 
			
		||||
            获取modbus写入数据区内容
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="response">返回数据</param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.GetInstance">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage,System.Byte[],System.Byte[],System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.CurModbusAddress">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前关联的地址
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.Length">
 | 
			
		||||
            <summary>
 | 
			
		||||
            当前读写的数据长度
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.HeadBytesLength">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.CheckHeadBytes(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.IsCheckMessageId">
 | 
			
		||||
            <summary>
 | 
			
		||||
            检测事务标识符
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            站号
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SetDataAdapter(System.Object)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter">
 | 
			
		||||
            <summary>
 | 
			
		||||
            ModbusTcpDataHandleAdapter
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.IsCheckMessageId">
 | 
			
		||||
            <summary>
 | 
			
		||||
            检测事务标识符
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.PackCommand(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.GetInstance">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage,System.Byte[],System.Byte[],System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.HeadBytesLength">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.IsCheckMessageId">
 | 
			
		||||
            <summary>
 | 
			
		||||
            检测事务标识符
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.CheckHeadBytes(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.IsCheckMessageId">
 | 
			
		||||
            <summary>
 | 
			
		||||
            检测事务标识符
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Station">
 | 
			
		||||
            <summary>
 | 
			
		||||
            站号
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.GetAddressDescription">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SetDataAdapter(System.Object)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.IsCheckMessageId">
 | 
			
		||||
            <summary>
 | 
			
		||||
            检测事务标识符
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.PackCommand(System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.GetInstance">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.UnpackResponse(System.Byte[],System.Byte[])">
 | 
			
		||||
            <inheritdoc/>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Foundation.Adapter.Modbus.PackHelper">
 | 
			
		||||
            <summary>
 | 
			
		||||
            PackHelper
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Foundation.Adapter.Modbus.PackHelper.ModbusLoadSourceRead``2(ThingsGateway.Foundation.Core.IReadWrite,System.Collections.Generic.List{``1},System.Int32)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            打包变量,添加到<see href="deviceVariableSourceReads"></see>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="device"></param>
 | 
			
		||||
            <param name="deviceVariables"></param>
 | 
			
		||||
            <param name="MaxPack">最大打包长度</param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
    </members>
 | 
			
		||||
</doc>
 | 
			
		||||
@@ -1,23 +1,25 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Comn;
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn;
 | 
			
		||||
#pragma warning disable CA1416 // 验证平台兼容性
 | 
			
		||||
#pragma warning disable IDE0090
 | 
			
		||||
#pragma warning disable IDE0051
 | 
			
		||||
 | 
			
		||||
internal sealed class ComInterop
 | 
			
		||||
internal class ComInterop
 | 
			
		||||
{
 | 
			
		||||
    private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
 | 
			
		||||
 | 
			
		||||
@@ -31,7 +33,6 @@ internal sealed class ComInterop
 | 
			
		||||
    private const uint EOAC_NONE = 0x00;
 | 
			
		||||
    private const uint EOAC_SECURE_REFS = 0x02;
 | 
			
		||||
    private const uint EOAC_STATIC_CLOAKING = 0x20;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The WIN32 system default locale.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -70,7 +71,6 @@ internal sealed class ComInterop
 | 
			
		||||
    private const uint RPC_C_IMP_LEVEL_DELEGATE = 4;
 | 
			
		||||
    private const uint RPC_C_IMP_LEVEL_IDENTIFY = 2;
 | 
			
		||||
    private const uint RPC_C_IMP_LEVEL_IMPERSONATE = 3;
 | 
			
		||||
 | 
			
		||||
    #endregion const
 | 
			
		||||
 | 
			
		||||
    #region struct
 | 
			
		||||
@@ -147,7 +147,6 @@ internal sealed class ComInterop
 | 
			
		||||
 | 
			
		||||
    [DllImport("Kernel32.dll")]
 | 
			
		||||
    private static extern int GetUserDefaultLangID();
 | 
			
		||||
 | 
			
		||||
    #endregion win32 api
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -176,7 +175,7 @@ internal sealed class ComInterop
 | 
			
		||||
            // 检查是否在本地或远程连接。
 | 
			
		||||
            uint clsctx = 0x01 | 0x04;
 | 
			
		||||
 | 
			
		||||
            if (hostName?.Length > 0 && !hostName.Equals("localhost", StringComparison.OrdinalIgnoreCase) && !hostName.Equals("127.0.0.1", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            if (hostName != null && hostName.Length > 0 && hostName.ToLower() != "localhost" && hostName != "127.0.0.1")
 | 
			
		||||
            {
 | 
			
		||||
                clsctx = 0x04 | 0x10;
 | 
			
		||||
            }
 | 
			
		||||
@@ -254,10 +253,9 @@ internal sealed class ComInterop
 | 
			
		||||
 | 
			
		||||
        if (error != 0)
 | 
			
		||||
        {
 | 
			
		||||
            throw new ExternalException("InitializeSecurity fail: " + GetSystemMessage(error), error);
 | 
			
		||||
            throw new ExternalException("COM初始化安全: " + GetSystemMessage(error), error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 从枚举器读取guid。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -365,9 +363,9 @@ internal sealed class ComInterop
 | 
			
		||||
    /// <param name="m_server"></param>
 | 
			
		||||
    internal static void RealseComServer(object m_server)
 | 
			
		||||
    {
 | 
			
		||||
        if (m_server?.GetType().IsCOMObject == true)
 | 
			
		||||
        if (m_server != null && m_server.GetType().IsCOMObject)
 | 
			
		||||
        {
 | 
			
		||||
            Marshal.ReleaseComObject(m_server);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +1,16 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Comn;
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn;
 | 
			
		||||
 | 
			
		||||
internal static class Convert
 | 
			
		||||
{
 | 
			
		||||
@@ -52,7 +54,6 @@ internal static class Convert
 | 
			
		||||
            throw new NotSupportedException("Object cannot be cloned.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static DateTime FileTimeToDateTime(System.Runtime.InteropServices.ComTypes.FILETIME filetime)
 | 
			
		||||
    {
 | 
			
		||||
        long num = filetime.dwHighDateTime;
 | 
			
		||||
@@ -79,4 +80,4 @@ internal static class Convert
 | 
			
		||||
        DateTime fILETIME_BaseTime2 = FILETIME_BaseTime;
 | 
			
		||||
        return fILETIME_BaseTime2.Add(new TimeSpan(num2)).ToLocalTime();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 值变化
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="opcItems"></param>
 | 
			
		||||
public delegate void OnDataChangedHandler(List<ItemReadResult> opcItems);
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 读取
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="opcItems"></param>
 | 
			
		||||
public delegate void OnReadCompletedHandler(List<ItemReadResult> opcItems);
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 写入
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="opcItems"></param>
 | 
			
		||||
internal delegate void OnWriteCompletedHandler(List<ItemWriteResult> opcItems);
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 返回结果
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ItemReadResult
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Name { get; set; } = "";
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Quality
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public short Quality { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// TimeStamp
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime TimeStamp { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Value
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public object Value { get; set; } = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal class ItemWriteResult
 | 
			
		||||
{
 | 
			
		||||
    internal int Exception { get; set; } = 0;
 | 
			
		||||
    internal string Name { get; set; } = "";
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +1,23 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Da;
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
 | 
			
		||||
#pragma warning disable CA1416 // 验证平台兼容性
 | 
			
		||||
 | 
			
		||||
internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
internal class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
{
 | 
			
		||||
    internal object groupPointer = null;
 | 
			
		||||
    internal int revisedUpdateRate = 0;
 | 
			
		||||
@@ -33,7 +35,6 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
    private IOPCSyncIO m_SyncIO = null;
 | 
			
		||||
    private GCHandle percendDeadBand = GCHandle.Alloc(0, GCHandleType.Pinned);
 | 
			
		||||
    private GCHandle timeBias = GCHandle.Alloc(0, GCHandleType.Pinned);
 | 
			
		||||
 | 
			
		||||
    internal OpcGroup(string name)
 | 
			
		||||
    {
 | 
			
		||||
        Name = name;
 | 
			
		||||
@@ -53,11 +54,11 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
 | 
			
		||||
    internal event CancelCompletedHandler OnCancelCompleted;
 | 
			
		||||
 | 
			
		||||
    internal event DataChangedHandler OnDataChanged;
 | 
			
		||||
    internal event OnDataChangedHandler OnDataChanged;
 | 
			
		||||
 | 
			
		||||
    internal event ReadCompletedHandler OnReadCompleted;
 | 
			
		||||
    internal event OnReadCompletedHandler OnReadCompleted;
 | 
			
		||||
 | 
			
		||||
    internal event WriteCompletedHandler OnWriteCompleted;
 | 
			
		||||
    internal event OnWriteCompletedHandler OnWriteCompleted;
 | 
			
		||||
 | 
			
		||||
    internal bool ActiveSubscribe
 | 
			
		||||
    {
 | 
			
		||||
@@ -77,7 +78,6 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
    internal object GroupPointer => groupPointer;
 | 
			
		||||
 | 
			
		||||
    internal bool IsActive { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
    internal int LCID
 | 
			
		||||
    {
 | 
			
		||||
        get => lcid;
 | 
			
		||||
@@ -85,8 +85,7 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal string Name { get; private set; } = string.Empty;
 | 
			
		||||
    internal List<OpcItem> OpcItems { get; private set; } = new List<OpcItem>();
 | 
			
		||||
 | 
			
		||||
    internal List<OpcItem> OpcItems { get; private set; } = new List<OpcItem> { };
 | 
			
		||||
    internal GCHandle PercendDeadBand
 | 
			
		||||
    {
 | 
			
		||||
        get => percendDeadBand;
 | 
			
		||||
@@ -96,13 +95,11 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
    internal int RequestUpdateRate { get; set; } = 1000;
 | 
			
		||||
    internal int RevisedUpdateRate => revisedUpdateRate;
 | 
			
		||||
    internal int ServerGroupHandle => serverGroupHandle;
 | 
			
		||||
 | 
			
		||||
    internal GCHandle TimeBias
 | 
			
		||||
    {
 | 
			
		||||
        get => timeBias;
 | 
			
		||||
        set => timeBias = value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        Dispose(disposing: true);
 | 
			
		||||
@@ -143,7 +140,7 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        OnDataChanged?.Invoke(Name, ServerGroupHandle, itemChanged);
 | 
			
		||||
        OnDataChanged?.Invoke(itemChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void OnReadComplete(int dwTransid,
 | 
			
		||||
@@ -175,7 +172,7 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        OnReadCompleted?.Invoke(Name, ServerGroupHandle, itemChanged);
 | 
			
		||||
        OnReadCompleted?.Invoke(itemChanged);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void OnWriteComplete(int dwTransid,
 | 
			
		||||
@@ -198,7 +195,7 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        OnWriteCompleted?.Invoke(Name, ServerGroupHandle, itemwrite);
 | 
			
		||||
        OnWriteCompleted?.Invoke(itemwrite);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal List<Tuple<OpcItem, int>> AddOpcItem(OpcItem[] items)
 | 
			
		||||
@@ -226,7 +223,7 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
            }
 | 
			
		||||
            //添加OPC项组
 | 
			
		||||
            m_ItemManagement?.AddItems(items.Length, itemDefyArray, out pResults, out pErrors);
 | 
			
		||||
            IntPtr Position = pResults;
 | 
			
		||||
            IntPtr Pos = pResults;
 | 
			
		||||
            Marshal.Copy(pErrors, errors, 0, items.Length);
 | 
			
		||||
            List<Tuple<OpcItem, int>> results = new();
 | 
			
		||||
            for (int j = 0; j < items.Length; j++)
 | 
			
		||||
@@ -235,16 +232,16 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
                {
 | 
			
		||||
                    if (j != 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        Position = IntPtr.Add(Position, Marshal.SizeOf(typeof(OPCITEMRESULT)));
 | 
			
		||||
                        Pos = IntPtr.Add(Pos, Marshal.SizeOf(typeof(OPCITEMRESULT)));
 | 
			
		||||
                    }
 | 
			
		||||
                    object o = Marshal.PtrToStructure(Position, typeof(OPCITEMRESULT));
 | 
			
		||||
                    object o = Marshal.PtrToStructure(Pos, typeof(OPCITEMRESULT));
 | 
			
		||||
                    if (o != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var result = (OPCITEMRESULT)o;
 | 
			
		||||
 | 
			
		||||
                        items[j].RunTimeDataType = result.vtCanonicalDataType;
 | 
			
		||||
                        itemServerHandle[j] = items[j].ServerHandle = result.hServer;
 | 
			
		||||
                        Marshal.DestroyStructure(Position, typeof(OPCITEMRESULT));
 | 
			
		||||
                        Marshal.DestroyStructure(Pos, typeof(OPCITEMRESULT));
 | 
			
		||||
                        OpcItems.Add(items[j]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -267,7 +264,6 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 建立连接
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -285,7 +281,6 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
        //创建客户端与服务端之间的连接
 | 
			
		||||
        m_ConnectionPoint.Advise(this, out m_connectionpoint_cookie);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 组读取
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -307,11 +302,11 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
                Marshal.Copy(pErrors, PErrors, 0, OpcItems.Count);
 | 
			
		||||
                if (PErrors.Any(a => a > 0))
 | 
			
		||||
                {
 | 
			
		||||
                    throw new("Read fail,Code:" + pErrors);
 | 
			
		||||
                    throw new("读取错误,错误代码:" + pErrors);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                throw new ArgumentNullException(nameof(m_Async2IO));
 | 
			
		||||
                throw new("连接无效");
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
@@ -358,6 +353,7 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
        return results;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    internal List<Tuple<int, int>> Write(object[] values, int[] serverHandle)
 | 
			
		||||
    {
 | 
			
		||||
        IntPtr pErrors = IntPtr.Zero;
 | 
			
		||||
@@ -387,10 +383,9 @@ internal sealed class OpcGroup : IOPCDataCallback, IDisposable
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            throw new ArgumentNullException(nameof(m_Async2IO));
 | 
			
		||||
            throw new("连接无效");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Dispose(bool disposing)
 | 
			
		||||
    protected virtual void Dispose(bool disposing)
 | 
			
		||||
    {
 | 
			
		||||
        if (!disposedValue)
 | 
			
		||||
        {
 | 
			
		||||
@@ -1,24 +1,24 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Da;
 | 
			
		||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// OpcItem
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpcItem
 | 
			
		||||
{
 | 
			
		||||
    private static int _hanle = 0;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OpcItem
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -28,22 +28,18 @@ public class OpcItem
 | 
			
		||||
        ItemID = itemId;
 | 
			
		||||
        ClientHandle = ++_hanle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// AccessPath
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string AccessPath { get; private set; } = "";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Blob
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public IntPtr Blob { get; set; } = IntPtr.Zero;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// BlobSize
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int BlobSize { get; set; } = 0;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ClientHandle
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -58,29 +54,24 @@ public class OpcItem
 | 
			
		||||
    /// 数据项在opc server的完全名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string ItemID { get; private set; } = String.Empty;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Quality
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Quality { get; set; } = Qualities.OPC_QUALITY_BAD;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// RunTimeDataType
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public short RunTimeDataType { get; set; } = 0;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ServerHandle
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int ServerHandle { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// TimeStamp
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime TimeStamp { get; set; } = new DateTime(0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Value
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public object Value { get; set; }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,23 +1,25 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Da;
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
 | 
			
		||||
#pragma warning disable CA1416 // 验证平台兼容性
 | 
			
		||||
 | 
			
		||||
internal sealed class OpcServer : IDisposable
 | 
			
		||||
internal class OpcServer : IDisposable
 | 
			
		||||
{
 | 
			
		||||
    private object lockThis = new();
 | 
			
		||||
 | 
			
		||||
    private bool disposedValue;
 | 
			
		||||
 | 
			
		||||
    private IOPCServer m_OpcServer = null;
 | 
			
		||||
@@ -52,10 +54,11 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
        return AddGroup(groupName, true, 1000, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal OpcGroup AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand)
 | 
			
		||||
    {
 | 
			
		||||
        if (null == m_OpcServer || IsConnected == false)
 | 
			
		||||
            throw new("Uninitialized connection");
 | 
			
		||||
            throw new("未初始化连接!");
 | 
			
		||||
        OpcGroup group = new(groupName, active, reqUpdateRate, deadBand);
 | 
			
		||||
        Guid riid = typeof(IOPCItemMgt).GUID;
 | 
			
		||||
        m_OpcServer?.AddGroup(group.Name,
 | 
			
		||||
@@ -76,9 +79,10 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            throw new("Error adding OPC group, OPC server returns null");
 | 
			
		||||
            throw new("添加OPC组错误,OPC服务器返回null");
 | 
			
		||||
        }
 | 
			
		||||
        return group;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -86,10 +90,10 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    internal List<BrowseElement> Browse(string itemId = null)
 | 
			
		||||
    {
 | 
			
		||||
        lock (lockThis)
 | 
			
		||||
        lock (this)
 | 
			
		||||
        {
 | 
			
		||||
            if (null == m_OpcServer || IsConnected == false)
 | 
			
		||||
                throw new("Uninitialized connection");
 | 
			
		||||
                throw new("未初始化连接!");
 | 
			
		||||
 | 
			
		||||
            var count = 0;
 | 
			
		||||
            var moreElements = 0;
 | 
			
		||||
@@ -106,6 +110,7 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
                           new PropertyID(101),
 | 
			
		||||
                         };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            var server = m_OpcServer as IOPCBrowse;
 | 
			
		||||
            server.Browse(
 | 
			
		||||
                     string.IsNullOrEmpty(itemId) ? "" : itemId,
 | 
			
		||||
@@ -124,7 +129,7 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
            BrowseElement[] browseElements = Interop.GetBrowseElements(ref pElements, count, true);
 | 
			
		||||
            string stringUni = Marshal.PtrToStringUni(pContinuationPoint);
 | 
			
		||||
            Marshal.FreeCoTaskMem(pContinuationPoint);
 | 
			
		||||
            OpcServer.ProcessResults(browseElements, filterId);
 | 
			
		||||
            this.ProcessResults(browseElements, filterId);
 | 
			
		||||
            return browseElements?.ToList();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -137,13 +142,13 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
            object o = Comn.ComInterop.CreateInstance(info.CLSID, Host);
 | 
			
		||||
            if (o == null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new(string.Format("{0} {1} Unable to create com object", info.CLSID, Host));
 | 
			
		||||
                throw new(string.Format("{0}{1}无法创建com对象", info.CLSID, Host));
 | 
			
		||||
            }
 | 
			
		||||
            m_OpcServer = (IOPCServer)o;
 | 
			
		||||
            IsConnected = true;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            throw new("Host and Name should be initialized");
 | 
			
		||||
            throw new("应初始化Host与Name");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@@ -156,12 +161,13 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (null == m_OpcServer || IsConnected == false)
 | 
			
		||||
                throw new("Uninitialized connection");
 | 
			
		||||
                throw new("未初始化连接!");
 | 
			
		||||
            IntPtr statusPtr = IntPtr.Zero;
 | 
			
		||||
            m_OpcServer?.GetStatus(out statusPtr);
 | 
			
		||||
            OPCSERVERSTATUS status;
 | 
			
		||||
            if (statusPtr != IntPtr.Zero)
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS));
 | 
			
		||||
 | 
			
		||||
                Marshal.FreeCoTaskMem(statusPtr);
 | 
			
		||||
@@ -170,7 +176,7 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
                {
 | 
			
		||||
                    status = (OPCSERVERSTATUS)o;
 | 
			
		||||
                    serverStatus = new();
 | 
			
		||||
                    serverStatus.Version = $"{status.wMajorVersion}.{status.wMinorVersion}.{status.wBuildNumber}";
 | 
			
		||||
                    serverStatus.Version = $"{status.wMajorVersion.ToString()}.{status.wMinorVersion.ToString()}.{status.wBuildNumber.ToString()}";
 | 
			
		||||
                    serverStatus.ServerState = status.dwServerState;
 | 
			
		||||
                    serverStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime);
 | 
			
		||||
                    serverStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime);
 | 
			
		||||
@@ -183,13 +189,15 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    IsConnected = false;
 | 
			
		||||
                    throw new("GetServerStatus error");
 | 
			
		||||
                    throw new("未知错误");
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                IsConnected = false;
 | 
			
		||||
                throw new("GetServerStatus error");
 | 
			
		||||
                throw new("未知错误");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
@@ -200,6 +208,8 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
                IsConnected = false;
 | 
			
		||||
            ServerStatus = serverStatus;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal void RemoveGroup(OpcGroup group)
 | 
			
		||||
@@ -211,7 +221,7 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Dispose(bool disposing)
 | 
			
		||||
    protected virtual void Dispose(bool disposing)
 | 
			
		||||
    {
 | 
			
		||||
        if (!disposedValue)
 | 
			
		||||
        {
 | 
			
		||||
@@ -222,6 +232,7 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
            }
 | 
			
		||||
            catch
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            if (m_OpcServer != null)
 | 
			
		||||
            {
 | 
			
		||||
@@ -236,7 +247,7 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void ProcessResults(BrowseElement[] elements, PropertyID[] propertyIDs)
 | 
			
		||||
    private void ProcessResults(BrowseElement[] elements, PropertyID[] propertyIDs)
 | 
			
		||||
    {
 | 
			
		||||
        if (elements == null)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -261,4 +272,4 @@ internal sealed class OpcServer : IDisposable
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +1,18 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Da;
 | 
			
		||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// ServerStatus
 | 
			
		||||
/// </summary>
 | 
			
		||||
@@ -21,29 +22,24 @@ public class ServerStatus
 | 
			
		||||
    /// CurrentTime
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime CurrentTime { get; internal set; } = new DateTime(0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// LastUpdateTime
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime LastUpdateTime { get; internal set; } = new DateTime(0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ServerState
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public OPCSERVERSTATE ServerState { get; internal set; } = OPCSERVERSTATE.OPC_STATUS_NOCONFIG;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// StartTime
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime StartTime { get; internal set; } = new DateTime(0);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// VendorInfo
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string VendorInfo { get; internal set; } = "UNKOWN";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Version
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Version { get; internal set; } = "UNKOWN";
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,24 +1,25 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Discovery;
 | 
			
		||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Discovery;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// OpcDiscovery
 | 
			
		||||
/// </summary>
 | 
			
		||||
internal sealed class OpcDiscovery
 | 
			
		||||
public class OpcDiscovery
 | 
			
		||||
{
 | 
			
		||||
    private static readonly Guid CATID_OPC_DA10 = new("63D5F430-CFE4-11d1-B2C8-0060083BA1FB");
 | 
			
		||||
 | 
			
		||||
@@ -27,7 +28,6 @@ internal sealed class OpcDiscovery
 | 
			
		||||
    private static readonly Guid CATID_OPC_DA30 = new("CC603642-66D7-48f1-B69A-B625E73652D7");
 | 
			
		||||
 | 
			
		||||
    private static readonly Guid OPCEnumCLSID = new("13486D51-4821-11D2-A494-3CB306C10000");
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// GetOpcServer
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@@ -36,15 +36,16 @@ internal sealed class OpcDiscovery
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static ServerInfo GetOpcServer(string serverName, string host)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (string.IsNullOrEmpty(serverName))
 | 
			
		||||
        {
 | 
			
		||||
            throw new ArgumentNullException(nameof(serverName));
 | 
			
		||||
            throw new("检索失败,需提供OPCName");
 | 
			
		||||
        }
 | 
			
		||||
        ServerInfo result = null;
 | 
			
		||||
        ServerInfo[] serverInfos = null;
 | 
			
		||||
        object o_Server = Comn.ComInterop.CreateInstance(OPCEnumCLSID, host);
 | 
			
		||||
        if (o_Server == null)
 | 
			
		||||
            throw new("GetOpcServer failed, please check if OPC runtime is installed");
 | 
			
		||||
            throw new("检索失败,请检查是否安装OPC Runtime");
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Guid catid = CATID_OPC_DA20;
 | 
			
		||||
@@ -72,7 +73,10 @@ internal sealed class OpcDiscovery
 | 
			
		||||
                {
 | 
			
		||||
                    sb.AppendLine(item.ToString());
 | 
			
		||||
                }
 | 
			
		||||
                throw new($"Unable to create OPCServer connection. Please check if the OPC name is consistent. The following is a list of OPCServers in {host}:{Environment.NewLine}{sb}");
 | 
			
		||||
                throw new($"无法创建OPCServer连接,请检查OPC名称是否一致,以下为{host}中的OPC列表:"
 | 
			
		||||
                    + Environment.NewLine +
 | 
			
		||||
                  sb.ToString()
 | 
			
		||||
                    );
 | 
			
		||||
            }
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
@@ -81,6 +85,7 @@ internal sealed class OpcDiscovery
 | 
			
		||||
            Comn.ComInterop.RealseComServer(o_Server);
 | 
			
		||||
            o_Server = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList m_server, Guid catid)
 | 
			
		||||
@@ -89,7 +94,7 @@ internal sealed class OpcDiscovery
 | 
			
		||||
        //2
 | 
			
		||||
        m_server.EnumClassesOfCategories(
 | 
			
		||||
            1,
 | 
			
		||||
            [catid],
 | 
			
		||||
            new Guid[] { catid },
 | 
			
		||||
            0,
 | 
			
		||||
            null,
 | 
			
		||||
            out enumerator);
 | 
			
		||||
@@ -101,14 +106,15 @@ internal sealed class OpcDiscovery
 | 
			
		||||
        serverInfos = GetServerDetails(clsids?.ToArray(), host, m_server);
 | 
			
		||||
        for (int i = 0; i < serverInfos.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            if (serverInfos[i].CLSID.ToString().Equals(serverName, StringComparison.OrdinalIgnoreCase) ||
 | 
			
		||||
                         serverInfos[i].ProgID.Equals(serverName, StringComparison.OrdinalIgnoreCase) ||
 | 
			
		||||
                         serverInfos[i].VerIndProgID.Equals(serverName, StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            if (serverInfos[i].CLSID.ToString().ToLower() == serverName.ToLower() ||
 | 
			
		||||
                    serverInfos[i].ProgID.ToLower() == serverName.ToLower() ||
 | 
			
		||||
                    serverInfos[i].VerIndProgID.ToLower() == serverName.ToLower())
 | 
			
		||||
            {
 | 
			
		||||
                result = serverInfos[i];
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList2 m_server, Guid catid)
 | 
			
		||||
@@ -117,7 +123,7 @@ internal sealed class OpcDiscovery
 | 
			
		||||
        IOPCEnumGUID enumerator = null;
 | 
			
		||||
        m_server.EnumClassesOfCategories(
 | 
			
		||||
            1,
 | 
			
		||||
            [catid],
 | 
			
		||||
            new Guid[] { catid },
 | 
			
		||||
            0,
 | 
			
		||||
            null,
 | 
			
		||||
            out enumerator);
 | 
			
		||||
@@ -129,16 +135,18 @@ internal sealed class OpcDiscovery
 | 
			
		||||
        serverInfos = GetServerDetails(clsids?.ToArray(), host, m_server);
 | 
			
		||||
        for (int i = 0; i < serverInfos.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            if (serverInfos[i].CLSID.ToString().Equals(serverName, StringComparison.OrdinalIgnoreCase) ||
 | 
			
		||||
                         serverInfos[i].ProgID.Equals(serverName, StringComparison.OrdinalIgnoreCase) ||
 | 
			
		||||
                         serverInfos[i].VerIndProgID.Equals(serverName, StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            if (serverInfos[i].CLSID.ToString().ToLower() == serverName.ToLower() ||
 | 
			
		||||
                    serverInfos[i].ProgID.ToLower() == serverName.ToLower() ||
 | 
			
		||||
                    serverInfos[i].VerIndProgID.ToLower() == serverName.ToLower())
 | 
			
		||||
            {
 | 
			
		||||
                result = serverInfos[i];
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static ServerInfo[] GetServerDetails(Guid[] clsids, string host, IOPCServerList m_server)
 | 
			
		||||
    {
 | 
			
		||||
        ArrayList servers = new ArrayList();
 | 
			
		||||
@@ -222,9 +230,12 @@ internal sealed class OpcDiscovery
 | 
			
		||||
        }
 | 
			
		||||
        return (ServerInfo[])servers.ToArray(typeof(ServerInfo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal sealed class ServerInfo
 | 
			
		||||
 | 
			
		||||
internal class ServerInfo
 | 
			
		||||
{
 | 
			
		||||
    internal Guid CLSID { get; set; }
 | 
			
		||||
    internal string Description { get; set; } = string.Empty;
 | 
			
		||||
@@ -242,4 +253,4 @@ internal sealed class ServerInfo
 | 
			
		||||
        stringBuilder.AppendLine($"{nameof(VerIndProgID)}:{VerIndProgID}");
 | 
			
		||||
        return stringBuilder.ToString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +1,16 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
 | 
			
		||||
 | 
			
		||||
[Serializable]
 | 
			
		||||
@@ -19,7 +21,7 @@ public class BrowseElement : ICloneable
 | 
			
		||||
    private string m_itemName;
 | 
			
		||||
    private string m_itemPath;
 | 
			
		||||
    private string m_name;
 | 
			
		||||
    private ItemProperty[] m_properties = [];
 | 
			
		||||
    private ItemProperty[] m_properties = new ItemProperty[0];
 | 
			
		||||
 | 
			
		||||
    public bool HasChildren
 | 
			
		||||
    {
 | 
			
		||||
@@ -80,7 +82,6 @@ public class BrowseElement : ICloneable
 | 
			
		||||
            m_name = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ItemProperty[] Properties
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
@@ -1,21 +1,22 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.
 | 
			
		||||
 | 
			
		||||
Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
 | 
			
		||||
 | 
			
		||||
/// <exclude />
 | 
			
		||||
[ComImport]
 | 
			
		||||
[GuidAttribute("B196B286-BAB4-101A-B69C-00AA00341D07")]
 | 
			
		||||
@@ -326,8 +327,7 @@ public interface IOPCShutdown
 | 
			
		||||
public struct CONNECTDATA
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.IUnknown)]
 | 
			
		||||
    private object pUnk;
 | 
			
		||||
 | 
			
		||||
    object pUnk;
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    private int dwCookie;
 | 
			
		||||
}
 | 
			
		||||
    int dwCookie;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +1,21 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.
 | 
			
		||||
 | 
			
		||||
Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
 | 
			
		||||
 | 
			
		||||
@@ -88,30 +91,25 @@ public enum OPCSERVERSTATE
 | 
			
		||||
[ComImport]
 | 
			
		||||
[GuidAttribute("63D5F430-CFE4-11d1-B2C8-0060083BA1FB")]
 | 
			
		||||
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
public interface CATID_OPCDAServer10
 | 
			
		||||
{ }
 | 
			
		||||
public interface CATID_OPCDAServer10 { }
 | 
			
		||||
 | 
			
		||||
/// <exclude />
 | 
			
		||||
[ComImport]
 | 
			
		||||
[GuidAttribute("63D5F432-CFE4-11d1-B2C8-0060083BA1FB")]
 | 
			
		||||
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
public interface CATID_OPCDAServer20
 | 
			
		||||
{ }
 | 
			
		||||
public interface CATID_OPCDAServer20 { }
 | 
			
		||||
 | 
			
		||||
/// <exclude />
 | 
			
		||||
[ComImport]
 | 
			
		||||
[GuidAttribute("CC603642-66D7-48f1-B69A-B625E73652D7")]
 | 
			
		||||
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
public interface CATID_OPCDAServer30
 | 
			
		||||
{ }
 | 
			
		||||
public interface CATID_OPCDAServer30 { }
 | 
			
		||||
 | 
			
		||||
/// <exclude />
 | 
			
		||||
[ComImport]
 | 
			
		||||
[GuidAttribute("3098EDA4-A006-48b2-A27F-247453959408")]
 | 
			
		||||
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
public interface CATID_XMLDAServer10
 | 
			
		||||
{ }
 | 
			
		||||
 | 
			
		||||
public interface CATID_XMLDAServer10 { }
 | 
			
		||||
/// <exclude />
 | 
			
		||||
[ComImport]
 | 
			
		||||
[GuidAttribute("39c13a55-011e-11d0-9675-0020afd8adb3")]
 | 
			
		||||
@@ -1004,16 +1002,12 @@ public struct OPCBROWSEELEMENT
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szName;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szItemID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwFlagValue;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwReserved;
 | 
			
		||||
 | 
			
		||||
    public OPCITEMPROPERTIES ItemProperties;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1023,16 +1017,12 @@ public struct OPCGROUPHEADER
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwSize;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwItemCount;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hClientGroup;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwTransactionID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hrStatus;
 | 
			
		||||
}
 | 
			
		||||
@@ -1043,13 +1033,10 @@ public struct OPCGROUPHEADERWRITE
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwItemCount;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hClientGroup;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwTransactionID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hrStatus;
 | 
			
		||||
}
 | 
			
		||||
@@ -1060,35 +1047,24 @@ public struct OPCITEMATTRIBUTES
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szAccessPath;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szItemID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int bActive;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hClient;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hServer;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwAccessRights;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwBlobSize;
 | 
			
		||||
 | 
			
		||||
    public IntPtr pBlob;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short vtRequestedDataType;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short vtCanonicalDataType;
 | 
			
		||||
 | 
			
		||||
    public OPCEUTYPE dwEUType;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.Struct)]
 | 
			
		||||
    public object vEUInfo;
 | 
			
		||||
}
 | 
			
		||||
@@ -1099,24 +1075,17 @@ public struct OPCITEMDEF
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szAccessPath;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szItemID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int bActive;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hClient;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwBlobSize;
 | 
			
		||||
 | 
			
		||||
    public IntPtr pBlob;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short vtRequestedDataType;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wReserved;
 | 
			
		||||
};
 | 
			
		||||
@@ -1127,16 +1096,12 @@ public struct OPCITEMHEADER1
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hClient;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwValueOffset;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wQuality;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wReserved;
 | 
			
		||||
 | 
			
		||||
    public System.Runtime.InteropServices.ComTypes.FILETIME ftTimeStampItem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1146,24 +1111,19 @@ public struct OPCITEMHEADER2
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hClient;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwValueOffset;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wQuality;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wReserved;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <exclude />
 | 
			
		||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
 | 
			
		||||
public struct OPCITEMHEADERWRITE
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hClient;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwError;
 | 
			
		||||
}
 | 
			
		||||
@@ -1174,12 +1134,9 @@ public struct OPCITEMPROPERTIES
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hrErrorID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwNumProperties;
 | 
			
		||||
 | 
			
		||||
    public IntPtr pItemProperties;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwReserved;
 | 
			
		||||
}
 | 
			
		||||
@@ -1190,25 +1147,18 @@ public struct OPCITEMPROPERTY
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short vtDataType;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wReserved;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwPropertyID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szItemID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szDescription;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.Struct)]
 | 
			
		||||
    public object vValue;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hrErrorID;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwReserved;
 | 
			
		||||
}
 | 
			
		||||
@@ -1219,19 +1169,14 @@ public struct OPCITEMRESULT
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hServer;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short vtCanonicalDataType;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wReserved;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwAccessRights;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwBlobSize;
 | 
			
		||||
 | 
			
		||||
    public IntPtr pBlob;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1241,15 +1186,11 @@ public struct OPCITEMSTATE
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int hClient;
 | 
			
		||||
 | 
			
		||||
    public System.Runtime.InteropServices.ComTypes.FILETIME ftTimeStamp;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wQuality;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wReserved;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.Struct)]
 | 
			
		||||
    public object vDataValue;
 | 
			
		||||
}
 | 
			
		||||
@@ -1260,22 +1201,16 @@ public struct OPCITEMVQT
 | 
			
		||||
{
 | 
			
		||||
    [MarshalAs(UnmanagedType.Struct)]
 | 
			
		||||
    public object vDataValue;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int bQualitySpecified;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wQuality;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wReserved;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int bTimeStampSpecified;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwReserved;
 | 
			
		||||
 | 
			
		||||
    public System.Runtime.InteropServices.ComTypes.FILETIME ftTimeStamp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1287,29 +1222,21 @@ public struct OPCSERVERSTATUS
 | 
			
		||||
    public System.Runtime.InteropServices.ComTypes.FILETIME ftCurrentTime;
 | 
			
		||||
    public System.Runtime.InteropServices.ComTypes.FILETIME ftLastUpdateTime;
 | 
			
		||||
    public OPCSERVERSTATE dwServerState;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwGroupCount;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I4)]
 | 
			
		||||
    public int dwBandWidth;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wMajorVersion;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wMinorVersion;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wBuildNumber;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.I2)]
 | 
			
		||||
    public short wReserved;
 | 
			
		||||
 | 
			
		||||
    [MarshalAs(UnmanagedType.LPWStr)]
 | 
			
		||||
    public string szVendorInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <exclude />
 | 
			
		||||
public static class Constants
 | 
			
		||||
{
 | 
			
		||||
@@ -1320,7 +1247,6 @@ public static class Constants
 | 
			
		||||
 | 
			
		||||
    // category description strings.
 | 
			
		||||
    public const string OPC_CATEGORY_DESCRIPTION_DA10 = "OPC Data Access Servers Version 1.0";
 | 
			
		||||
 | 
			
		||||
    public const string OPC_CATEGORY_DESCRIPTION_DA20 = "OPC Data Access Servers Version 2.0";
 | 
			
		||||
    public const string OPC_CATEGORY_DESCRIPTION_DA30 = "OPC Data Access Servers Version 3.0";
 | 
			
		||||
    public const string OPC_CATEGORY_DESCRIPTION_XMLDA10 = "OPC XML Data Access Servers Version 1.0";
 | 
			
		||||
@@ -1332,13 +1258,11 @@ public static class Constants
 | 
			
		||||
 | 
			
		||||
    // values for access rights mask.
 | 
			
		||||
    public const int OPC_READABLE = 0x01;
 | 
			
		||||
 | 
			
		||||
    // well known complex type description systems.
 | 
			
		||||
    // well known complex type description systems.   
 | 
			
		||||
    public const string OPC_TYPE_SYSTEM_OPCBINARY = "OPCBinary";
 | 
			
		||||
 | 
			
		||||
    public const string OPC_TYPE_SYSTEM_XMLSCHEMA = "XMLSchema";
 | 
			
		||||
    public const string OPC_WRITE_BEHAVIOR_ALL_OR_NOTHING = "All or Nothing";
 | 
			
		||||
 | 
			
		||||
    // complex data write behavior values.
 | 
			
		||||
    public const string OPC_WRITE_BEHAVIOR_BEST_EFFORT = "Best Effort";
 | 
			
		||||
 | 
			
		||||
@@ -1356,7 +1280,7 @@ public static class Qualities
 | 
			
		||||
 | 
			
		||||
    public const short OPC_LIMIT_MASK = 0x03;
 | 
			
		||||
 | 
			
		||||
    // Values for Limit Bitfield
 | 
			
		||||
    // Values for Limit Bitfield 
 | 
			
		||||
    public const short OPC_LIMIT_OK = 0x00;
 | 
			
		||||
 | 
			
		||||
    // Values for QUALITY_MASK bit field
 | 
			
		||||
@@ -1383,7 +1307,6 @@ public static class Qualities
 | 
			
		||||
 | 
			
		||||
    // Values for fields in the quality word
 | 
			
		||||
    public const short OPC_QUALITY_MASK = 0xC0;
 | 
			
		||||
 | 
			
		||||
    public const short OPC_QUALITY_NOT_CONNECTED = 0x08;
 | 
			
		||||
    public const short OPC_QUALITY_OUT_OF_SERVICE = 0x1C;
 | 
			
		||||
    public const short OPC_QUALITY_SENSOR_CAL = 0x50;
 | 
			
		||||
@@ -1417,7 +1340,6 @@ public static class Properties
 | 
			
		||||
 | 
			
		||||
    // property ids.
 | 
			
		||||
    public const int OPC_PROPERTY_DATATYPE = 1;
 | 
			
		||||
 | 
			
		||||
    public const int OPC_PROPERTY_DEADBAND = 306;
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_ACCESS_RIGHTS = "Item Access Rights";
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_ALARM_AREA_LIST = "Alarm Area List";
 | 
			
		||||
@@ -1428,7 +1350,6 @@ public static class Properties
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_CONDITION_STATUS = "Condition Status";
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_CONSISTENCY_WINDOW = "Consistency Window";
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_DATA_FILTER_VALUE = "Data Filter Value";
 | 
			
		||||
 | 
			
		||||
    // property descriptions.
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_DATATYPE = "Item Canonical Data Type";
 | 
			
		||||
 | 
			
		||||
@@ -1458,7 +1379,6 @@ public static class Properties
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_TIMEZONE = "Item Timezone";
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_TYPE_DESCRIPTION = "Type Description";
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_TYPE_ID = "Type ID";
 | 
			
		||||
 | 
			
		||||
    // complex data properties.
 | 
			
		||||
    public const string OPC_PROPERTY_DESC_TYPE_SYSTEM_ID = "Type System ID";
 | 
			
		||||
 | 
			
		||||
@@ -1491,7 +1411,6 @@ public static class Properties
 | 
			
		||||
    public const int OPC_PROPERTY_TIMEZONE = 108;
 | 
			
		||||
    public const int OPC_PROPERTY_TYPE_DESCRIPTION = 604;
 | 
			
		||||
    public const int OPC_PROPERTY_TYPE_ID = 602;
 | 
			
		||||
 | 
			
		||||
    // complex data properties.
 | 
			
		||||
    public const int OPC_PROPERTY_TYPE_SYSTEM_ID = 600;
 | 
			
		||||
 | 
			
		||||
@@ -1499,4 +1418,4 @@ public static class Properties
 | 
			
		||||
    public const int OPC_PROPERTY_UNFILTERED_ITEM_ID = 608;
 | 
			
		||||
    public const int OPC_PROPERTY_VALUE = 2;
 | 
			
		||||
    public const int OPC_PROPERTY_WRITE_BEHAVIOR = 606;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +1,31 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
 | 
			
		||||
#pragma warning disable CS8605 // 取消装箱可能为 null 的值。
 | 
			
		||||
 | 
			
		||||
public static class Interop
 | 
			
		||||
public class Interop
 | 
			
		||||
{
 | 
			
		||||
    public static PropertyID GetPropertyID(int input)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (FieldInfo field in typeof(Property).GetFields(BindingFlags.Static | BindingFlags.Public))
 | 
			
		||||
        {
 | 
			
		||||
            PropertyID propertyId = (PropertyID)field.GetValue(typeof(PropertyID));
 | 
			
		||||
            PropertyID propertyId = (PropertyID)field.GetValue((object)typeof(PropertyID));
 | 
			
		||||
            if (input == propertyId.Code)
 | 
			
		||||
                return propertyId;
 | 
			
		||||
        }
 | 
			
		||||
@@ -32,13 +34,13 @@ public static class Interop
 | 
			
		||||
 | 
			
		||||
    internal static BrowseElement GetBrowseElement(IntPtr pInput, bool deallocate)
 | 
			
		||||
    {
 | 
			
		||||
        BrowseElement browseElement = null;
 | 
			
		||||
        BrowseElement browseElement = (BrowseElement)null;
 | 
			
		||||
        if (pInput != IntPtr.Zero)
 | 
			
		||||
        {
 | 
			
		||||
            OPCBROWSEELEMENT structure = (OPCBROWSEELEMENT)Marshal.PtrToStructure(pInput, typeof(OPCBROWSEELEMENT));
 | 
			
		||||
            browseElement = new BrowseElement();
 | 
			
		||||
            browseElement.Name = structure.szName;
 | 
			
		||||
            browseElement.ItemPath = null;
 | 
			
		||||
            browseElement.ItemPath = (string)null;
 | 
			
		||||
            browseElement.ItemName = structure.szItemID;
 | 
			
		||||
            browseElement.IsItem = (structure.dwFlagValue & 2) != 0;
 | 
			
		||||
            browseElement.HasChildren = (structure.dwFlagValue & 1) != 0;
 | 
			
		||||
@@ -73,7 +75,7 @@ public static class Interop
 | 
			
		||||
      int count,
 | 
			
		||||
      bool deallocate)
 | 
			
		||||
    {
 | 
			
		||||
        BrowseElement[] browseElements = null;
 | 
			
		||||
        BrowseElement[] browseElements = (BrowseElement[])null;
 | 
			
		||||
        if (pInput != IntPtr.Zero && count > 0)
 | 
			
		||||
        {
 | 
			
		||||
            browseElements = new BrowseElement[count];
 | 
			
		||||
@@ -81,7 +83,7 @@ public static class Interop
 | 
			
		||||
            for (int index = 0; index < count; ++index)
 | 
			
		||||
            {
 | 
			
		||||
                browseElements[index] = Interop.GetBrowseElement(pInput1, deallocate);
 | 
			
		||||
                pInput1 = (nint)(pInput1.ToInt64() + Marshal.SizeOf(typeof(OPCBROWSEELEMENT)));
 | 
			
		||||
                pInput1 = (IntPtr)(pInput1.ToInt64() + (long)Marshal.SizeOf(typeof(OPCBROWSEELEMENT)));
 | 
			
		||||
            }
 | 
			
		||||
            if (deallocate)
 | 
			
		||||
            {
 | 
			
		||||
@@ -102,17 +104,16 @@ public static class Interop
 | 
			
		||||
            for (int index = 0; index < input.Length; ++index)
 | 
			
		||||
            {
 | 
			
		||||
                Marshal.StructureToPtr((object)Interop.GetBrowseElement(input[index], propertiesRequested), ptr, false);
 | 
			
		||||
                ptr = (nint)(ptr.ToInt64() + Marshal.SizeOf(typeof(OPCBROWSEELEMENT)));
 | 
			
		||||
                ptr = (IntPtr)(ptr.ToInt64() + (long)Marshal.SizeOf(typeof(OPCBROWSEELEMENT)));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return browseElements;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static ItemProperty[] GetItemProperties(
 | 
			
		||||
      ref OPCITEMPROPERTIES input,
 | 
			
		||||
      bool deallocate)
 | 
			
		||||
    {
 | 
			
		||||
        ItemProperty[] itemProperties = null;
 | 
			
		||||
        ItemProperty[] itemProperties = (ItemProperty[])null;
 | 
			
		||||
        if (input.dwNumProperties > 0)
 | 
			
		||||
        {
 | 
			
		||||
            itemProperties = new ItemProperty[input.dwNumProperties];
 | 
			
		||||
@@ -129,7 +130,7 @@ public static class Interop
 | 
			
		||||
                    itemProperties[index].Description = ex.Message;
 | 
			
		||||
                    itemProperties[index].ResultID = ResultID.E_FAIL;
 | 
			
		||||
                }
 | 
			
		||||
                pInput = (nint)(pInput.ToInt64() + Marshal.SizeOf(typeof(OPCITEMPROPERTY)));
 | 
			
		||||
                pInput = (IntPtr)(pInput.ToInt64() + (long)Marshal.SizeOf(typeof(OPCITEMPROPERTY)));
 | 
			
		||||
            }
 | 
			
		||||
            if (deallocate)
 | 
			
		||||
            {
 | 
			
		||||
@@ -154,7 +155,7 @@ public static class Interop
 | 
			
		||||
            for (int index = 0; index < input.Length; ++index)
 | 
			
		||||
            {
 | 
			
		||||
                Marshal.StructureToPtr((object)Interop.GetItemProperty(input[index]), ptr, false);
 | 
			
		||||
                ptr = (nint)(ptr.ToInt64() + Marshal.SizeOf(typeof(OPCITEMPROPERTY)));
 | 
			
		||||
                ptr = (IntPtr)(ptr.ToInt64() + (long)Marshal.SizeOf(typeof(OPCITEMPROPERTY)));
 | 
			
		||||
                if (input[index].ResultID.Failed())
 | 
			
		||||
                    flag = true;
 | 
			
		||||
            }
 | 
			
		||||
@@ -166,7 +167,7 @@ public static class Interop
 | 
			
		||||
 | 
			
		||||
    internal static ItemProperty GetItemProperty(IntPtr pInput, bool deallocate)
 | 
			
		||||
    {
 | 
			
		||||
        ItemProperty itemProperty = null;
 | 
			
		||||
        ItemProperty itemProperty = (ItemProperty)null;
 | 
			
		||||
        if (pInput != IntPtr.Zero)
 | 
			
		||||
        {
 | 
			
		||||
            OPCITEMPROPERTY structure = (OPCITEMPROPERTY)Marshal.PtrToStructure(pInput, typeof(OPCITEMPROPERTY));
 | 
			
		||||
@@ -175,7 +176,7 @@ public static class Interop
 | 
			
		||||
                ID = Interop.GetPropertyID(structure.dwPropertyID),
 | 
			
		||||
                Description = structure.szDescription,
 | 
			
		||||
                DataType = Interop.GetType((VarEnum)structure.vtDataType),
 | 
			
		||||
                ItemPath = null,
 | 
			
		||||
                ItemPath = (string)null,
 | 
			
		||||
                ItemName = structure.szItemID
 | 
			
		||||
            };
 | 
			
		||||
            itemProperty.Value = Interop.UnmarshalPropertyValue(itemProperty.ID, structure.vValue);
 | 
			
		||||
@@ -187,7 +188,6 @@ public static class Interop
 | 
			
		||||
        }
 | 
			
		||||
        return itemProperty;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static OPCITEMPROPERTY GetItemProperty(ItemProperty input)
 | 
			
		||||
    {
 | 
			
		||||
        OPCITEMPROPERTY itemProperty = new OPCITEMPROPERTY();
 | 
			
		||||
@@ -197,7 +197,7 @@ public static class Interop
 | 
			
		||||
            itemProperty.szDescription = input.Description;
 | 
			
		||||
            itemProperty.vtDataType = (short)Interop.GetType(input.DataType);
 | 
			
		||||
            itemProperty.vValue = Interop.MarshalPropertyValue(input.ID, input.Value);
 | 
			
		||||
            itemProperty.wReserved = 0;
 | 
			
		||||
            itemProperty.wReserved = (short)0;
 | 
			
		||||
            itemProperty.hrErrorID = Interop.GetResultID(input.ResultID);
 | 
			
		||||
            PropertyDescription propertyDescription = PropertyDescription.Find(input.ID);
 | 
			
		||||
            if (propertyDescription != null)
 | 
			
		||||
@@ -214,7 +214,7 @@ public static class Interop
 | 
			
		||||
        if (propertyIDs != null)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (PropertyID propertyId in propertyIDs)
 | 
			
		||||
                arrayList.Add(propertyId.Code);
 | 
			
		||||
                arrayList.Add((object)propertyId.Code);
 | 
			
		||||
        }
 | 
			
		||||
        return (int[])arrayList.ToArray(typeof(int));
 | 
			
		||||
    }
 | 
			
		||||
@@ -224,306 +224,208 @@ public static class Interop
 | 
			
		||||
        switch (input)
 | 
			
		||||
        {
 | 
			
		||||
            case -2147467262:
 | 
			
		||||
                return new ResultID(ResultID.E_NOTSUPPORTED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.E_NOTSUPPORTED, (long)input);
 | 
			
		||||
            case -2147467259:
 | 
			
		||||
                return new ResultID(ResultID.E_FAIL, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.E_FAIL, (long)input);
 | 
			
		||||
            case -2147352571:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_BADTYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_BADTYPE, (long)input);
 | 
			
		||||
            case -2147352566:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_RANGE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_RANGE, (long)input);
 | 
			
		||||
            case -2147217401:
 | 
			
		||||
                return new ResultID(ResultID.Hda.W_NOFILTER, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.W_NOFILTER, (long)input);
 | 
			
		||||
            case -2147024882:
 | 
			
		||||
                return new ResultID(ResultID.E_OUTOFMEMORY, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.E_OUTOFMEMORY, (long)input);
 | 
			
		||||
            case -2147024809:
 | 
			
		||||
                return new ResultID(ResultID.E_INVALIDARG, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.E_INVALIDARG, (long)input);
 | 
			
		||||
            case -1073479679:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALIDHANDLE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALIDHANDLE, (long)input);
 | 
			
		||||
            case -1073479676:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_BADTYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_BADTYPE, (long)input);
 | 
			
		||||
            case -1073479673:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_UNKNOWN_ITEM_NAME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_UNKNOWN_ITEM_NAME, (long)input);
 | 
			
		||||
            case -1073479672:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALID_ITEM_NAME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALID_ITEM_NAME, (long)input);
 | 
			
		||||
            case -1073479671:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALID_FILTER, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALID_FILTER, (long)input);
 | 
			
		||||
            case -1073479670:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_UNKNOWN_ITEM_PATH, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_UNKNOWN_ITEM_PATH, (long)input);
 | 
			
		||||
            case -1073479669:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_RANGE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_RANGE, (long)input);
 | 
			
		||||
            case -1073479165:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALID_PID, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALID_PID, (long)input);
 | 
			
		||||
            case -1073479164:
 | 
			
		||||
                return new ResultID(ResultID.Ae.E_INVALIDTIME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Ae.E_INVALIDTIME, (long)input);
 | 
			
		||||
            case -1073479163:
 | 
			
		||||
                return new ResultID(ResultID.Ae.E_BUSY, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Ae.E_BUSY, (long)input);
 | 
			
		||||
            case -1073479162:
 | 
			
		||||
                return new ResultID(ResultID.Ae.E_NOINFO, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Ae.E_NOINFO, (long)input);
 | 
			
		||||
            case -1073478655:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_NO_ITEM_DEADBAND, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_NO_ITEM_DEADBAND, (long)input);
 | 
			
		||||
            case -1073478654:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_NO_ITEM_BUFFERING, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_NO_ITEM_BUFFERING, (long)input);
 | 
			
		||||
            case -1073478653:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALIDCONTINUATIONPOINT, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_INVALIDCONTINUATIONPOINT, (long)input);
 | 
			
		||||
            case -1073478650:
 | 
			
		||||
                return new ResultID(ResultID.Da.E_NO_WRITEQT, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.E_NO_WRITEQT, (long)input);
 | 
			
		||||
            case -1073478649:
 | 
			
		||||
                return new ResultID(ResultID.Cpx.E_TYPE_CHANGED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Cpx.E_TYPE_CHANGED, (long)input);
 | 
			
		||||
            case -1073478648:
 | 
			
		||||
                return new ResultID(ResultID.Cpx.E_FILTER_DUPLICATE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Cpx.E_FILTER_DUPLICATE, (long)input);
 | 
			
		||||
            case -1073478647:
 | 
			
		||||
                return new ResultID(ResultID.Cpx.E_FILTER_INVALID, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Cpx.E_FILTER_INVALID, (long)input);
 | 
			
		||||
            case -1073478646:
 | 
			
		||||
                return new ResultID(ResultID.Cpx.E_FILTER_ERROR, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Cpx.E_FILTER_ERROR, (long)input);
 | 
			
		||||
            case -1073477888:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_PERSISTING, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_PERSISTING, (long)input);
 | 
			
		||||
            case -1073477887:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_NOITEMLIST, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_NOITEMLIST, (long)input);
 | 
			
		||||
            case -1073477886:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_VERSION_MISMATCH, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_VERSION_MISMATCH, (long)input);
 | 
			
		||||
            case -1073477885:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_VERSION_MISMATCH, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_VERSION_MISMATCH, (long)input);
 | 
			
		||||
            case -1073477884:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_ITEM_PATH, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_ITEM_PATH, (long)input);
 | 
			
		||||
            case -1073477883:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_ITEM_NAME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_ITEM_NAME, (long)input);
 | 
			
		||||
            case -1073477882:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_ITEM_PATH, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_ITEM_PATH, (long)input);
 | 
			
		||||
            case -1073477881:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_ITEM_NAME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_ITEM_NAME, (long)input);
 | 
			
		||||
            case -1073477880:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_NAME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_NAME, (long)input);
 | 
			
		||||
            case -1073477879:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_DUPLICATE_NAME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_DUPLICATE_NAME, (long)input);
 | 
			
		||||
            case -1073477878:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_BROWSE_PATH, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_BROWSE_PATH, (long)input);
 | 
			
		||||
            case -1073477877:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_SERVER_URL, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_SERVER_URL, (long)input);
 | 
			
		||||
            case -1073477876:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_SERVER_TYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_SERVER_TYPE, (long)input);
 | 
			
		||||
            case -1073477875:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNSUPPORTED_SERVER_TYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNSUPPORTED_SERVER_TYPE, (long)input);
 | 
			
		||||
            case -1073477874:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_CONNECTIONS_EXIST, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_CONNECTIONS_EXIST, (long)input);
 | 
			
		||||
            case -1073477873:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TOO_MANY_CONNECTIONS, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TOO_MANY_CONNECTIONS, (long)input);
 | 
			
		||||
            case -1073477872:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_OVERRIDE_BADTYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_OVERRIDE_BADTYPE, (long)input);
 | 
			
		||||
            case -1073477871:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_OVERRIDE_RANGE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_OVERRIDE_RANGE, (long)input);
 | 
			
		||||
            case -1073477870:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SUBSTITUTE_BADTYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SUBSTITUTE_BADTYPE, (long)input);
 | 
			
		||||
            case -1073477869:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SUBSTITUTE_RANGE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SUBSTITUTE_RANGE, (long)input);
 | 
			
		||||
            case -1073477868:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_TARGET_ITEM, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_TARGET_ITEM, (long)input);
 | 
			
		||||
            case -1073477867:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_TARGET_ITEM, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_TARGET_ITEM, (long)input);
 | 
			
		||||
            case -1073477866:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_ALREADY_CONNECTED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_ALREADY_CONNECTED, (long)input);
 | 
			
		||||
            case -1073477865:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_SERVER_NAME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_SERVER_NAME, (long)input);
 | 
			
		||||
            case -1073477864:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_SOURCE_ITEM, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_UNKNOWN_SOURCE_ITEM, (long)input);
 | 
			
		||||
            case -1073477863:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_SOURCE_ITEM, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_SOURCE_ITEM, (long)input);
 | 
			
		||||
            case -1073477862:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_QUEUE_SIZE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_QUEUE_SIZE, (long)input);
 | 
			
		||||
            case -1073477861:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_DEADBAND, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_DEADBAND, (long)input);
 | 
			
		||||
            case -1073477860:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_CONFIG_FILE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_INVALID_CONFIG_FILE, (long)input);
 | 
			
		||||
            case -1073477859:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_PERSIST_FAILED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_PERSIST_FAILED, (long)input);
 | 
			
		||||
            case -1073477858:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_FAULT, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_FAULT, (long)input);
 | 
			
		||||
            case -1073477857:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_NO_ACCESSS, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_NO_ACCESSS, (long)input);
 | 
			
		||||
            case -1073477856:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_SERVER_FAULT, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_SERVER_FAULT, (long)input);
 | 
			
		||||
            case -1073477855:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_SERVER_NO_ACCESSS, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_SERVER_NO_ACCESSS, (long)input);
 | 
			
		||||
            case -1073477854:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SUBSCRIPTION_FAULT, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SUBSCRIPTION_FAULT, (long)input);
 | 
			
		||||
            case -1073477853:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BADRIGHTS, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BADRIGHTS, (long)input);
 | 
			
		||||
            case -1073477852:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BAD_QUALITY, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BAD_QUALITY, (long)input);
 | 
			
		||||
            case -1073477851:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BADTYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BADTYPE, (long)input);
 | 
			
		||||
            case -1073477850:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_ITEM_RANGE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_ITEM_RANGE, (long)input);
 | 
			
		||||
            case -1073477849:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_SERVER_NOT_CONNECTED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_SERVER_NOT_CONNECTED, (long)input);
 | 
			
		||||
            case -1073477848:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_SERVER_TIMEOUT, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_SOURCE_SERVER_TIMEOUT, (long)input);
 | 
			
		||||
            case -1073477847:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_ITEM_DISCONNECTED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_ITEM_DISCONNECTED, (long)input);
 | 
			
		||||
            case -1073477846:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_NO_WRITES_ATTEMPTED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_NO_WRITES_ATTEMPTED, (long)input);
 | 
			
		||||
            case -1073477845:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_ITEM_BADTYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_ITEM_BADTYPE, (long)input);
 | 
			
		||||
            case -1073477844:
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_ITEM_RANGE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.E_TARGET_ITEM_RANGE, (long)input);
 | 
			
		||||
            case -1073475583:
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_MAXEXCEEDED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_MAXEXCEEDED, (long)input);
 | 
			
		||||
            case -1073475580:
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_INVALIDAGGREGATE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_INVALIDAGGREGATE, (long)input);
 | 
			
		||||
            case -1073475576:
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_UNKNOWNATTRID, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_UNKNOWNATTRID, (long)input);
 | 
			
		||||
            case -1073475575:
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_NOT_AVAIL, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_NOT_AVAIL, (long)input);
 | 
			
		||||
            case -1073475574:
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_INVALIDDATATYPE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_INVALIDDATATYPE, (long)input);
 | 
			
		||||
            case -1073475573:
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_DATAEXISTS, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_DATAEXISTS, (long)input);
 | 
			
		||||
            case -1073475572:
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_INVALIDATTRID, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_INVALIDATTRID, (long)input);
 | 
			
		||||
            case -1073475571:
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_NODATAEXISTS, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.E_NODATAEXISTS, (long)input);
 | 
			
		||||
            case 0:
 | 
			
		||||
                return new ResultID(ResultID.S_OK, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.S_OK, (long)input);
 | 
			
		||||
            case 262157:
 | 
			
		||||
                return new ResultID(ResultID.Da.S_UNSUPPORTEDRATE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.S_UNSUPPORTEDRATE, (long)input);
 | 
			
		||||
            case 262158:
 | 
			
		||||
                return new ResultID(ResultID.Da.S_CLAMP, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.S_CLAMP, (long)input);
 | 
			
		||||
            case 262656:
 | 
			
		||||
                return new ResultID(ResultID.Ae.S_ALREADYACKED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Ae.S_ALREADYACKED, (long)input);
 | 
			
		||||
            case 262657:
 | 
			
		||||
                return new ResultID(ResultID.Ae.S_INVALIDBUFFERTIME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Ae.S_INVALIDBUFFERTIME, (long)input);
 | 
			
		||||
            case 262658:
 | 
			
		||||
                return new ResultID(ResultID.Ae.S_INVALIDMAXSIZE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Ae.S_INVALIDMAXSIZE, (long)input);
 | 
			
		||||
            case 262659:
 | 
			
		||||
                return new ResultID(ResultID.Ae.S_INVALIDKEEPALIVETIME, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Ae.S_INVALIDKEEPALIVETIME, (long)input);
 | 
			
		||||
            case 263172:
 | 
			
		||||
                return new ResultID(ResultID.Da.S_DATAQUEUEOVERFLOW, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Da.S_DATAQUEUEOVERFLOW, (long)input);
 | 
			
		||||
            case 263179:
 | 
			
		||||
                return new ResultID(ResultID.Cpx.S_FILTER_NO_DATA, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Cpx.S_FILTER_NO_DATA, (long)input);
 | 
			
		||||
            case 264064:
 | 
			
		||||
                return new ResultID(ResultID.Dx.S_TARGET_SUBSTITUTED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.S_TARGET_SUBSTITUTED, (long)input);
 | 
			
		||||
            case 264065:
 | 
			
		||||
                return new ResultID(ResultID.Dx.S_TARGET_OVERRIDEN, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.S_TARGET_OVERRIDEN, (long)input);
 | 
			
		||||
            case 264066:
 | 
			
		||||
                return new ResultID(ResultID.Dx.S_CLAMP, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Dx.S_CLAMP, (long)input);
 | 
			
		||||
            case 1074008066:
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_NODATA, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_NODATA, (long)input);
 | 
			
		||||
            case 1074008067:
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_MOREDATA, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_MOREDATA, (long)input);
 | 
			
		||||
            case 1074008069:
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_CURRENTVALUE, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_CURRENTVALUE, (long)input);
 | 
			
		||||
            case 1074008070:
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_EXTRADATA, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_EXTRADATA, (long)input);
 | 
			
		||||
            case 1074008078:
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_INSERTED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_INSERTED, (long)input);
 | 
			
		||||
            case 1074008079:
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_REPLACED, input);
 | 
			
		||||
 | 
			
		||||
                return new ResultID(ResultID.Hda.S_REPLACED, (long)input);
 | 
			
		||||
            default:
 | 
			
		||||
                if ((input & 2147418112) == 65536)
 | 
			
		||||
                    return new ResultID(ResultID.E_NETWORK_ERROR, input);
 | 
			
		||||
                return input >= 0 ? new ResultID(ResultID.S_FALSE, input) : new ResultID(ResultID.E_FAIL, input);
 | 
			
		||||
                    return new ResultID(ResultID.E_NETWORK_ERROR, (long)input);
 | 
			
		||||
                return input >= 0 ? new ResultID(ResultID.S_FALSE, (long)input) : new ResultID(ResultID.E_FAIL, (long)input);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static int GetResultID(ResultID input)
 | 
			
		||||
    {
 | 
			
		||||
        if (input.Name?.Namespace == "http://opcfoundation.org/DataAccess/")
 | 
			
		||||
        if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/DataAccess/")
 | 
			
		||||
        {
 | 
			
		||||
            if (input == ResultID.S_OK)
 | 
			
		||||
                return 0;
 | 
			
		||||
@@ -568,7 +470,7 @@ public static class Interop
 | 
			
		||||
            if (input == ResultID.Da.S_DATAQUEUEOVERFLOW)
 | 
			
		||||
                return 263172;
 | 
			
		||||
        }
 | 
			
		||||
        else if (input.Name?.Namespace == "http://opcfoundation.org/ComplexData/")
 | 
			
		||||
        else if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/ComplexData/")
 | 
			
		||||
        {
 | 
			
		||||
            if (input == ResultID.Cpx.E_TYPE_CHANGED)
 | 
			
		||||
                return -1073478649;
 | 
			
		||||
@@ -581,7 +483,7 @@ public static class Interop
 | 
			
		||||
            if (input == ResultID.Cpx.S_FILTER_NO_DATA)
 | 
			
		||||
                return 263179;
 | 
			
		||||
        }
 | 
			
		||||
        else if (input.Name?.Namespace == "http://opcfoundation.org/HistoricalDataAccess/")
 | 
			
		||||
        else if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/HistoricalDataAccess/")
 | 
			
		||||
        {
 | 
			
		||||
            if (input == ResultID.Hda.E_MAXEXCEEDED)
 | 
			
		||||
                return -1073475583;
 | 
			
		||||
@@ -612,7 +514,7 @@ public static class Interop
 | 
			
		||||
            if (input == ResultID.Hda.S_REPLACED)
 | 
			
		||||
                return 1074008079;
 | 
			
		||||
        }
 | 
			
		||||
        if (input.Name?.Namespace == "http://opcfoundation.org/DataExchange/")
 | 
			
		||||
        if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/DataExchange/")
 | 
			
		||||
        {
 | 
			
		||||
            if (input == ResultID.Dx.E_PERSISTING)
 | 
			
		||||
                return -1073477888;
 | 
			
		||||
@@ -716,7 +618,7 @@ public static class Interop
 | 
			
		||||
 | 
			
		||||
    internal static VarEnum GetType(System.Type input)
 | 
			
		||||
    {
 | 
			
		||||
        if (input == null)
 | 
			
		||||
        if (input == (System.Type)null)
 | 
			
		||||
            return VarEnum.VT_EMPTY;
 | 
			
		||||
        if (input == typeof(sbyte))
 | 
			
		||||
            return VarEnum.VT_I1;
 | 
			
		||||
@@ -784,129 +686,94 @@ public static class Interop
 | 
			
		||||
            return VarEnum.VT_I2;
 | 
			
		||||
        return input == typeof(accessRights) || input == typeof(euType) ? VarEnum.VT_I4 : VarEnum.VT_EMPTY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static System.Type GetType(VarEnum input)
 | 
			
		||||
    {
 | 
			
		||||
        switch (input)
 | 
			
		||||
        {
 | 
			
		||||
            case VarEnum.VT_EMPTY:
 | 
			
		||||
                return null;
 | 
			
		||||
 | 
			
		||||
                return (System.Type)null;
 | 
			
		||||
            case VarEnum.VT_I2:
 | 
			
		||||
                return typeof(short);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_I4:
 | 
			
		||||
                return typeof(int);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_R4:
 | 
			
		||||
                return typeof(float);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_R8:
 | 
			
		||||
                return typeof(double);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_CY:
 | 
			
		||||
                return typeof(Decimal);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_DATE:
 | 
			
		||||
                return typeof(DateTime);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_BSTR:
 | 
			
		||||
                return typeof(string);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_BOOL:
 | 
			
		||||
                return typeof(bool);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_I1:
 | 
			
		||||
                return typeof(sbyte);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_UI1:
 | 
			
		||||
                return typeof(byte);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_UI2:
 | 
			
		||||
                return typeof(ushort);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_UI4:
 | 
			
		||||
                return typeof(uint);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_I8:
 | 
			
		||||
                return typeof(long);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_UI8:
 | 
			
		||||
                return typeof(ulong);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_I2 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(short[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_I4 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(int[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_R4 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(float[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_R8 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(double[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_CY | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(Decimal[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_DATE | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(DateTime[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_BSTR | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(string[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_BOOL | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(bool[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_VARIANT | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(object[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_I1 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(sbyte[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_UI1 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(byte[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_UI2 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(ushort[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_UI4 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(uint[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_I8 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(long[]);
 | 
			
		||||
 | 
			
		||||
            case VarEnum.VT_UI8 | VarEnum.VT_ARRAY:
 | 
			
		||||
                return typeof(ulong[]);
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return Type.ILLEGAL_TYPE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static object MarshalPropertyValue(PropertyID propertyID, object input)
 | 
			
		||||
    {
 | 
			
		||||
        if (input == null)
 | 
			
		||||
            return null;
 | 
			
		||||
            return (object)null;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (propertyID == Property.DATATYPE)
 | 
			
		||||
                return (short)Interop.GetType((System.Type)input);
 | 
			
		||||
                return (object)(short)Interop.GetType((System.Type)input);
 | 
			
		||||
            if (propertyID == Property.ACCESSRIGHTS)
 | 
			
		||||
            {
 | 
			
		||||
                switch ((accessRights)input)
 | 
			
		||||
                {
 | 
			
		||||
                    case accessRights.readable:
 | 
			
		||||
                        return 1;
 | 
			
		||||
 | 
			
		||||
                        return (object)1;
 | 
			
		||||
                    case accessRights.writable:
 | 
			
		||||
                        return 2;
 | 
			
		||||
 | 
			
		||||
                        return (object)2;
 | 
			
		||||
                    case accessRights.readWritable:
 | 
			
		||||
                        return 3;
 | 
			
		||||
 | 
			
		||||
                        return (object)3;
 | 
			
		||||
                    default:
 | 
			
		||||
                        return null;
 | 
			
		||||
                        return (object)null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (propertyID == Property.EUTYPE)
 | 
			
		||||
@@ -914,28 +781,25 @@ public static class Interop
 | 
			
		||||
                switch ((euType)input)
 | 
			
		||||
                {
 | 
			
		||||
                    case euType.noEnum:
 | 
			
		||||
                        return OPCEUTYPE.OPC_NOENUM;
 | 
			
		||||
 | 
			
		||||
                        return (object)OPCEUTYPE.OPC_NOENUM;
 | 
			
		||||
                    case euType.analog:
 | 
			
		||||
                        return OPCEUTYPE.OPC_ANALOG;
 | 
			
		||||
 | 
			
		||||
                        return (object)OPCEUTYPE.OPC_ANALOG;
 | 
			
		||||
                    case euType.enumerated:
 | 
			
		||||
                        return OPCEUTYPE.OPC_ENUMERATED;
 | 
			
		||||
 | 
			
		||||
                        return (object)OPCEUTYPE.OPC_ENUMERATED;
 | 
			
		||||
                    default:
 | 
			
		||||
                        return null;
 | 
			
		||||
                        return (object)null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (propertyID == Property.QUALITY)
 | 
			
		||||
                    return ((Quality)input).GetCode();
 | 
			
		||||
                    return (object)((Quality)input).GetCode();
 | 
			
		||||
                if (propertyID == Property.TIMESTAMP)
 | 
			
		||||
                {
 | 
			
		||||
                    if (input.GetType() == typeof(DateTime))
 | 
			
		||||
                    {
 | 
			
		||||
                        DateTime dateTime = (DateTime)input;
 | 
			
		||||
                        return dateTime != DateTime.MinValue ? dateTime.ToLocalTime() : (object)dateTime;
 | 
			
		||||
                        return dateTime != DateTime.MinValue ? (object)dateTime.ToLocalTime() : (object)dateTime;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -949,26 +813,23 @@ public static class Interop
 | 
			
		||||
    internal static object UnmarshalPropertyValue(PropertyID propertyID, object input)
 | 
			
		||||
    {
 | 
			
		||||
        if (input == null)
 | 
			
		||||
            return null;
 | 
			
		||||
            return (object)null;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (propertyID == Property.DATATYPE)
 | 
			
		||||
                return Interop.GetType((VarEnum)System.Convert.ToUInt16(input));
 | 
			
		||||
                return (object)Interop.GetType((VarEnum)System.Convert.ToUInt16(input));
 | 
			
		||||
            if (propertyID == Property.ACCESSRIGHTS)
 | 
			
		||||
            {
 | 
			
		||||
                switch (System.Convert.ToInt32(input))
 | 
			
		||||
                {
 | 
			
		||||
                    case 1:
 | 
			
		||||
                        return accessRights.readable;
 | 
			
		||||
 | 
			
		||||
                        return (object)accessRights.readable;
 | 
			
		||||
                    case 2:
 | 
			
		||||
                        return accessRights.writable;
 | 
			
		||||
 | 
			
		||||
                        return (object)accessRights.writable;
 | 
			
		||||
                    case 3:
 | 
			
		||||
                        return accessRights.readWritable;
 | 
			
		||||
 | 
			
		||||
                        return (object)accessRights.readWritable;
 | 
			
		||||
                    default:
 | 
			
		||||
                        return null;
 | 
			
		||||
                        return (object)null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if (propertyID == Property.EUTYPE)
 | 
			
		||||
@@ -976,28 +837,25 @@ public static class Interop
 | 
			
		||||
                switch ((OPCEUTYPE)input)
 | 
			
		||||
                {
 | 
			
		||||
                    case OPCEUTYPE.OPC_NOENUM:
 | 
			
		||||
                        return euType.noEnum;
 | 
			
		||||
 | 
			
		||||
                        return (object)euType.noEnum;
 | 
			
		||||
                    case OPCEUTYPE.OPC_ANALOG:
 | 
			
		||||
                        return euType.analog;
 | 
			
		||||
 | 
			
		||||
                        return (object)euType.analog;
 | 
			
		||||
                    case OPCEUTYPE.OPC_ENUMERATED:
 | 
			
		||||
                        return euType.enumerated;
 | 
			
		||||
 | 
			
		||||
                        return (object)euType.enumerated;
 | 
			
		||||
                    default:
 | 
			
		||||
                        return null;
 | 
			
		||||
                        return (object)null;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (propertyID == Property.QUALITY)
 | 
			
		||||
                    return new Quality(System.Convert.ToInt16(input));
 | 
			
		||||
                    return (object)new Quality(System.Convert.ToInt16(input));
 | 
			
		||||
                if (propertyID == Property.TIMESTAMP)
 | 
			
		||||
                {
 | 
			
		||||
                    if (input.GetType() == typeof(DateTime))
 | 
			
		||||
                    {
 | 
			
		||||
                        DateTime dateTime = (DateTime)input;
 | 
			
		||||
                        return dateTime != DateTime.MinValue ? dateTime.ToLocalTime() : (object)dateTime;
 | 
			
		||||
                        return dateTime != DateTime.MinValue ? (object)dateTime.ToLocalTime() : (object)dateTime;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -1007,4 +865,4 @@ public static class Interop
 | 
			
		||||
        }
 | 
			
		||||
        return input;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +1,23 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://thingsgateway.cn/
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Runtime.Serialization;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.OpcDa.Rcw;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
 | 
			
		||||
#pragma warning disable CS8605 // 取消装箱可能为 null 的值。
 | 
			
		||||
 | 
			
		||||
public enum accessRights
 | 
			
		||||
{
 | 
			
		||||
@@ -126,7 +128,7 @@ public struct PropertyID : ISerializable
 | 
			
		||||
        return a.Equals(b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object? target)
 | 
			
		||||
    public override bool Equals(object target)
 | 
			
		||||
    {
 | 
			
		||||
        if (target != null && target.GetType() == typeof(PropertyID))
 | 
			
		||||
        {
 | 
			
		||||
@@ -188,10 +190,10 @@ public struct PropertyID : ISerializable
 | 
			
		||||
            return $"{Code}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return string.Empty;
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private sealed class Names
 | 
			
		||||
    private class Names
 | 
			
		||||
    {
 | 
			
		||||
        internal const string CODE = "CO";
 | 
			
		||||
        internal const string NAME = "NA";
 | 
			
		||||
@@ -208,7 +210,6 @@ public struct Quality
 | 
			
		||||
    private limitBits m_limitBits;
 | 
			
		||||
    private qualityBits m_qualityBits;
 | 
			
		||||
    private byte m_vendorBits;
 | 
			
		||||
 | 
			
		||||
    public Quality(qualityBits quality)
 | 
			
		||||
    {
 | 
			
		||||
        m_qualityBits = quality;
 | 
			
		||||
@@ -246,7 +247,6 @@ public struct Quality
 | 
			
		||||
            m_qualityBits = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public byte VendorBits
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
@@ -269,7 +269,7 @@ public struct Quality
 | 
			
		||||
        return a.Equals(b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object? target)
 | 
			
		||||
    public override bool Equals(object target)
 | 
			
		||||
    {
 | 
			
		||||
        if (target == null || target.GetType() != typeof(Quality))
 | 
			
		||||
        {
 | 
			
		||||
@@ -320,13 +320,12 @@ public struct Quality
 | 
			
		||||
        m_limitBits = (limitBits)(code & 3);
 | 
			
		||||
        m_vendorBits = (byte)((code & -253) >> 8);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        string text = QualityBits.ToString();
 | 
			
		||||
        if (LimitBits != 0)
 | 
			
		||||
        {
 | 
			
		||||
            text += $"[{LimitBits}]";
 | 
			
		||||
            text += $"[{LimitBits.ToString()}]";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (VendorBits != 0)
 | 
			
		||||
@@ -419,7 +418,7 @@ public struct ResultID : ISerializable
 | 
			
		||||
        return a.Equals(b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override bool Equals(object? target)
 | 
			
		||||
    public override bool Equals(object target)
 | 
			
		||||
    {
 | 
			
		||||
        if (target != null && target.GetType() == typeof(ResultID))
 | 
			
		||||
        {
 | 
			
		||||
@@ -499,7 +498,7 @@ public struct ResultID : ISerializable
 | 
			
		||||
        return string.Format("0x{0,0:X}", Code);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Ae
 | 
			
		||||
    public class Ae
 | 
			
		||||
    {
 | 
			
		||||
        public static readonly ResultID E_BUSY = new ResultID("E_BUSY", "http://opcfoundation.org/AlarmAndEvents/");
 | 
			
		||||
        public static readonly ResultID E_INVALIDBRANCHNAME = new ResultID("E_INVALIDBRANCHNAME", "http://opcfoundation.org/AlarmAndEvents/");
 | 
			
		||||
@@ -513,7 +512,7 @@ public struct ResultID : ISerializable
 | 
			
		||||
        public static readonly ResultID S_INVALIDMAXSIZE = new ResultID("S_INVALIDMAXSIZE", "http://opcfoundation.org/AlarmAndEvents/");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Cpx
 | 
			
		||||
    public class Cpx
 | 
			
		||||
    {
 | 
			
		||||
        public static readonly ResultID E_FILTER_DUPLICATE = new ResultID("E_FILTER_DUPLICATE", "http://opcfoundation.org/ComplexData/");
 | 
			
		||||
        public static readonly ResultID E_FILTER_ERROR = new ResultID("E_FILTER_ERROR", "http://opcfoundation.org/ComplexData/");
 | 
			
		||||
@@ -522,7 +521,7 @@ public struct ResultID : ISerializable
 | 
			
		||||
        public static readonly ResultID S_FILTER_NO_DATA = new ResultID("S_FILTER_NO_DATA", "http://opcfoundation.org/ComplexData/");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Da
 | 
			
		||||
    public class Da
 | 
			
		||||
    {
 | 
			
		||||
        public static readonly ResultID E_BADTYPE = new ResultID("E_BADTYPE", "http://opcfoundation.org/DataAccess/");
 | 
			
		||||
        public static readonly ResultID E_INVALID_FILTER = new ResultID("E_INVALID_FILTER", "http://opcfoundation.org/DataAccess/");
 | 
			
		||||
@@ -546,7 +545,7 @@ public struct ResultID : ISerializable
 | 
			
		||||
        public static readonly ResultID S_UNSUPPORTEDRATE = new ResultID("S_UNSUPPORTEDRATE", "http://opcfoundation.org/DataAccess/");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Dx
 | 
			
		||||
    public class Dx
 | 
			
		||||
    {
 | 
			
		||||
        public static readonly ResultID E_CONNECTIONS_EXIST = new ResultID("E_CONNECTIONS_EXIST", "http://opcfoundation.org/DataExchange/");
 | 
			
		||||
        public static readonly ResultID E_DUPLICATE_NAME = new ResultID("E_DUPLICATE_NAME", "http://opcfoundation.org/DataExchange/");
 | 
			
		||||
@@ -599,7 +598,7 @@ public struct ResultID : ISerializable
 | 
			
		||||
        public static readonly ResultID S_TARGET_SUBSTITUTED = new ResultID("S_TARGET_SUBSTITUTED", "http://opcfoundation.org/DataExchange/");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class Hda
 | 
			
		||||
    public class Hda
 | 
			
		||||
    {
 | 
			
		||||
        public static readonly ResultID E_DATAEXISTS = new ResultID("E_DATAEXISTS", "http://opcfoundation.org/HistoricalDataAccess/");
 | 
			
		||||
        public static readonly ResultID E_INVALIDAGGREGATE = new ResultID("E_INVALIDAGGREGATE", "http://opcfoundation.org/HistoricalDataAccess/");
 | 
			
		||||
@@ -619,7 +618,7 @@ public struct ResultID : ISerializable
 | 
			
		||||
        public static readonly ResultID W_NOFILTER = new ResultID("W_NOFILTER", "http://opcfoundation.org/HistoricalDataAccess/");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private sealed class Names
 | 
			
		||||
    private class Names
 | 
			
		||||
    {
 | 
			
		||||
        internal const string CODE = "CO";
 | 
			
		||||
        internal const string NAME = "NA";
 | 
			
		||||
@@ -627,7 +626,6 @@ public struct ResultID : ISerializable
 | 
			
		||||
        internal const string NAMESPACE = "NS";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[Serializable]
 | 
			
		||||
public class ItemProperty : ICloneable, IResult
 | 
			
		||||
{
 | 
			
		||||
@@ -639,7 +637,6 @@ public class ItemProperty : ICloneable, IResult
 | 
			
		||||
    private string m_itemPath;
 | 
			
		||||
    private ResultID m_resultID = ResultID.S_OK;
 | 
			
		||||
    private object m_value;
 | 
			
		||||
 | 
			
		||||
    public System.Type DataType
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
@@ -687,7 +684,6 @@ public class ItemProperty : ICloneable, IResult
 | 
			
		||||
            m_id = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string ItemName
 | 
			
		||||
    {
 | 
			
		||||
        get
 | 
			
		||||
@@ -744,8 +740,7 @@ public class ItemProperty : ICloneable, IResult
 | 
			
		||||
        return obj;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public static class Property
 | 
			
		||||
public class Property
 | 
			
		||||
{
 | 
			
		||||
    public static readonly PropertyID ACCESSRIGHTS = new PropertyID("accessRights", 5, "http://opcfoundation.org/DataAccess/");
 | 
			
		||||
    public static readonly PropertyID ALARM_AREA_LIST = new PropertyID("alarmAreaList", 302, "http://opcfoundation.org/DataAccess/");
 | 
			
		||||
@@ -793,7 +788,6 @@ public static class Property
 | 
			
		||||
    public static readonly PropertyID VALUE_PRECISION = new PropertyID("valuePrecision", 111, "http://opcfoundation.org/DataAccess/");
 | 
			
		||||
    public static readonly PropertyID WRITE_BEHAVIOR = new PropertyID("writeBehavior", 606, "http://opcfoundation.org/DataAccess/");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class Type
 | 
			
		||||
{
 | 
			
		||||
    public static System.Type ANY_TYPE = typeof(object);
 | 
			
		||||
@@ -828,7 +822,6 @@ public class Type
 | 
			
		||||
    public static System.Type UINT = typeof(uint);
 | 
			
		||||
    public static System.Type ULONG = typeof(ulong);
 | 
			
		||||
    public static System.Type USHORT = typeof(ushort);
 | 
			
		||||
 | 
			
		||||
    public static System.Type[] Enumerate()
 | 
			
		||||
    {
 | 
			
		||||
        ArrayList arrayList = new ArrayList();
 | 
			
		||||
@@ -841,7 +834,6 @@ public class Type
 | 
			
		||||
        return (System.Type[])arrayList.ToArray(typeof(System.Type));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[Serializable]
 | 
			
		||||
public class PropertyDescription
 | 
			
		||||
{
 | 
			
		||||
@@ -893,7 +885,6 @@ public class PropertyDescription
 | 
			
		||||
 | 
			
		||||
    private string m_name;
 | 
			
		||||
    private System.Type m_type;
 | 
			
		||||
 | 
			
		||||
    public PropertyDescription(PropertyID id, System.Type type, string name)
 | 
			
		||||
    {
 | 
			
		||||
        ID = id;
 | 
			
		||||
@@ -936,7 +927,6 @@ public class PropertyDescription
 | 
			
		||||
            m_type = value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static PropertyDescription[] Enumerate()
 | 
			
		||||
    {
 | 
			
		||||
        ArrayList arrayList = new ArrayList();
 | 
			
		||||
@@ -0,0 +1,49 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA;
 | 
			
		||||
 | 
			
		||||
internal static class CollectionExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 移除符合条件的元素
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <param name="this"></param>
 | 
			
		||||
    /// <param name="where"></param>
 | 
			
		||||
    internal static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var obj in @this.Where(where).ToList())
 | 
			
		||||
        {
 | 
			
		||||
            @this.Remove(obj);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 将项目列表分解为特定大小的块
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <param name="source"></param>
 | 
			
		||||
    /// <param name="chunksize"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static List<List<T>> ChunkTrivialBetter<T>(this IEnumerable<T> source, int chunksize)
 | 
			
		||||
    {
 | 
			
		||||
        var pos = 0;
 | 
			
		||||
        List<List<T>> n = new();
 | 
			
		||||
        while (source.Skip(pos).Any())
 | 
			
		||||
        {
 | 
			
		||||
            n.Add(source.Skip(pos).Take(chunksize).ToList());
 | 
			
		||||
            pos += chunksize;
 | 
			
		||||
        }
 | 
			
		||||
        return n;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,123 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
 | 
			
		||||
//  CSDN博客:https://blog.csdn.net/qq_40374647
 | 
			
		||||
//  哔哩哔哩视频:https://space.bilibili.com/94253567
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/RRQM_Home
 | 
			
		||||
//  Github源代码仓库:https://github.com/RRQM
 | 
			
		||||
//  API首页:http://rrqm_home.gitee.io/touchsocket/
 | 
			
		||||
//  交流QQ群:234762506
 | 
			
		||||
//  感谢您的下载和使用
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// DictionaryExtension
 | 
			
		||||
/// </summary>
 | 
			
		||||
internal static class DictionaryExtension
 | 
			
		||||
{
 | 
			
		||||
    #region 字典扩展
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 移除满足条件的项目。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="TKey"></typeparam>
 | 
			
		||||
    /// <typeparam name="TValue"></typeparam>
 | 
			
		||||
    /// <param name="pairs"></param>
 | 
			
		||||
    /// <param name="func"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static int RemoveWhen<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> pairs, Func<KeyValuePair<TKey, TValue>, bool> func)
 | 
			
		||||
    {
 | 
			
		||||
        var list = new List<TKey>();
 | 
			
		||||
        foreach (var item in pairs)
 | 
			
		||||
        {
 | 
			
		||||
            if (func?.Invoke(item) == true)
 | 
			
		||||
            {
 | 
			
		||||
                list.Add(item.Key);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var count = 0;
 | 
			
		||||
        foreach (var item in list)
 | 
			
		||||
        {
 | 
			
		||||
            if (pairs.TryRemove(item, out _))
 | 
			
		||||
            {
 | 
			
		||||
                count++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return count;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if NET45_OR_GREATER || NETSTANDARD2_0_OR_GREATER
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 尝试添加
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="Tkey"></typeparam>
 | 
			
		||||
    /// <typeparam name="TValue"></typeparam>
 | 
			
		||||
    /// <param name="dictionary"></param>
 | 
			
		||||
    /// <param name="tkey"></param>
 | 
			
		||||
    /// <param name="value"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static bool TryAdd<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
 | 
			
		||||
    {
 | 
			
		||||
        if (dictionary.ContainsKey(tkey))
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        dictionary.Add(tkey, value);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 尝试添加
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="Tkey"></typeparam>
 | 
			
		||||
    /// <typeparam name="TValue"></typeparam>
 | 
			
		||||
    /// <param name="dictionary"></param>
 | 
			
		||||
    /// <param name="tkey"></param>
 | 
			
		||||
    /// <param name="value"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static void AddOrUpdate<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
 | 
			
		||||
    {
 | 
			
		||||
        if (dictionary.ContainsKey(tkey))
 | 
			
		||||
        {
 | 
			
		||||
            dictionary[tkey] = value;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            dictionary.Add(tkey, value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取值。如果键不存在,则返回默认值。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="Tkey"></typeparam>
 | 
			
		||||
    /// <typeparam name="TValue"></typeparam>
 | 
			
		||||
    /// <param name="dictionary"></param>
 | 
			
		||||
    /// <param name="tkey"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey)
 | 
			
		||||
    {
 | 
			
		||||
        return dictionary.TryGetValue(tkey, out var value) ? value : default;
 | 
			
		||||
    }
 | 
			
		||||
    #endregion 字典扩展
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
#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.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,457 @@
 | 
			
		||||
#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.Runtime.InteropServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Timers;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Adapter.OPCDA.Da;
 | 
			
		||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
 | 
			
		||||
 | 
			
		||||
using Timer = System.Timers.Timer;
 | 
			
		||||
 | 
			
		||||
//部分非托管交互代码来自https://gitee.com/Zer0Day/opc-client与OPC基金会opcnet库,更改部分逻辑
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCDA;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 订阅变化项
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="values"></param>
 | 
			
		||||
public delegate void DataChangedEventHandler(List<ItemReadResult> values);
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// OPCDAClient
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OPCDAClient : IDisposable
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// LogAction
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly Action<byte, object, string, Exception> _logAction;
 | 
			
		||||
 | 
			
		||||
    private readonly object checkLock = new();
 | 
			
		||||
 | 
			
		||||
    private Timer checkTimer;
 | 
			
		||||
 | 
			
		||||
    private int IsExit = 1;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 当前保存的需订阅列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private Dictionary<string, List<OpcItem>> ItemDicts = new();
 | 
			
		||||
 | 
			
		||||
    private OpcServer m_server;
 | 
			
		||||
    private bool publicConnect;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public OPCDAClient(Action<byte, object, string, Exception> logAction)
 | 
			
		||||
    {
 | 
			
		||||
#if (NET6_0_OR_GREATER)
 | 
			
		||||
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotSupportedException("不支持非windows系统");
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        _logAction = logAction;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 数据变化事件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public event DataChangedEventHandler DataChangedHandler;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 是否连接成功
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool IsConnected => m_server?.IsConnected == true;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 当前配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public OPCNode OPCNode { get; private set; }
 | 
			
		||||
    private List<OpcGroup> Groups => m_server.OpcGroups;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加节点,需要在连接成功后执行
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="items">组名称/变量节点,注意每次添加的组名称不能相同</param>
 | 
			
		||||
    public void AddItems(Dictionary<string, List<OpcItem>> items)
 | 
			
		||||
    {
 | 
			
		||||
        if (IsExit == 1) throw new("对象已释放");
 | 
			
		||||
        foreach (var item in items)
 | 
			
		||||
        {
 | 
			
		||||
            if (IsExit == 1) throw new("对象已释放");
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var subscription = m_server.AddGroup(item.Key, true, OPCNode.UpdateRate, OPCNode.DeadBand);
 | 
			
		||||
                subscription.ActiveSubscribe = OPCNode.ActiveSubscribe;
 | 
			
		||||
                subscription.OnDataChanged += Subscription_OnDataChanged;
 | 
			
		||||
                subscription.OnReadCompleted += Subscription_OnDataChanged;
 | 
			
		||||
 | 
			
		||||
                var result = subscription.AddOpcItem(item.Value.ToArray());
 | 
			
		||||
                StringBuilder stringBuilder = new StringBuilder();
 | 
			
		||||
                if (result.Count > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var item1 in result)
 | 
			
		||||
                    {
 | 
			
		||||
                        stringBuilder.Append($"{item1.Item1.ItemID}:{item1.Item2}");
 | 
			
		||||
                    }
 | 
			
		||||
                    _logAction?.Invoke(3, this, $"添加变量失败:{stringBuilder}", null);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ItemDicts.AddOrUpdate(item.Key, item.Value.Where(a => !result.Select(b => b.Item1).Contains(a)).ToList());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                _logAction?.Invoke(3, this, $"添加组失败:{ex.Message}", ex);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 0; i < Groups?.Count; i++)
 | 
			
		||||
        {
 | 
			
		||||
            var group = Groups[i];
 | 
			
		||||
            if (group != null)
 | 
			
		||||
            {
 | 
			
		||||
                if (group.OpcItems.Count == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    ItemDicts.Remove(group.Name);
 | 
			
		||||
                    m_server.RemoveGroup(group);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置节点并保存,每次重连会自动添加节点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="items"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public Dictionary<string, List<OpcItem>> AddItemsWithSave(List<string> items)
 | 
			
		||||
    {
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        ItemDicts = items.ToList().ConvertAll(o => new OpcItem(o)).ChunkTrivialBetter(OPCNode.GroupSize).ToDictionary(a => "default" + (i++));
 | 
			
		||||
        return ItemDicts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 连接服务器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Connect()
 | 
			
		||||
    {
 | 
			
		||||
        publicConnect = true;
 | 
			
		||||
        Interlocked.CompareExchange(ref IsExit, 0, 1);
 | 
			
		||||
        PrivateConnect();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 断开连接
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void Disconnect()
 | 
			
		||||
    {
 | 
			
		||||
        Interlocked.CompareExchange(ref IsExit, 1, 0);
 | 
			
		||||
        PrivateDisconnect();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            PrivateDisconnect();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            _logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex);
 | 
			
		||||
        }
 | 
			
		||||
        Interlocked.CompareExchange(ref IsExit, 1, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 浏览节点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="itemId"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public List<BrowseElement> GetBrowseElements(string itemId = null)
 | 
			
		||||
    {
 | 
			
		||||
        return this.m_server?.Browse(itemId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取服务状态
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public ServerStatus GetServerStatus()
 | 
			
		||||
    {
 | 
			
		||||
        return this.m_server?.GetServerStatus();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 初始化设置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="node"></param>
 | 
			
		||||
    public void Init(OPCNode node)
 | 
			
		||||
    {
 | 
			
		||||
        if (node != null)
 | 
			
		||||
            OPCNode = node;
 | 
			
		||||
        checkTimer?.Stop();
 | 
			
		||||
        checkTimer?.Dispose();
 | 
			
		||||
        checkTimer = new Timer(OPCNode.CheckRate * 60 * 1000);
 | 
			
		||||
        checkTimer.Elapsed += CheckTimer_Elapsed;
 | 
			
		||||
        checkTimer.Start();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            m_server?.Dispose();
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            _logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex);
 | 
			
		||||
        }
 | 
			
		||||
        m_server = new OpcServer(OPCNode.OPCName, OPCNode.OPCIP);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 按OPC组读取组内变量,结果会在订阅事件中返回
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="groupName">组名称,值为null时读取全部组</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public void ReadItemsWithGroup(string groupName = null)
 | 
			
		||||
    {
 | 
			
		||||
        PrivateConnect();
 | 
			
		||||
        {
 | 
			
		||||
            var groups = groupName != null ? Groups.Where(a => a.Name == groupName) : Groups;
 | 
			
		||||
            foreach (var group in groups)
 | 
			
		||||
            {
 | 
			
		||||
                if (group.OpcItems.Count > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    group.ReadAsync();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 移除节点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="items"></param>
 | 
			
		||||
    public void RemoveItems(List<string> items)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var item in items)
 | 
			
		||||
        {
 | 
			
		||||
            if (IsExit == 1) return;
 | 
			
		||||
            var opcGroups = Groups.Where(it => it.OpcItems.Any(a => a.ItemID == item));
 | 
			
		||||
            foreach (var opcGroup in opcGroups)
 | 
			
		||||
            {
 | 
			
		||||
                var tag = opcGroup.OpcItems.Where(a => item == a.ItemID);
 | 
			
		||||
                var result = opcGroup.RemoveItem(tag.ToArray());
 | 
			
		||||
 | 
			
		||||
                if (opcGroup.OpcItems.Count == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    opcGroup.OnDataChanged -= Subscription_OnDataChanged;
 | 
			
		||||
                    ItemDicts.Remove(opcGroup.Name);
 | 
			
		||||
                    m_server.RemoveGroup(opcGroup);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ItemDicts[opcGroup.Name].RemoveWhere(a => tag.Contains(a) && !result.Select(b => b.Item1).Contains(a));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return OPCNode?.ToString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 批量写入值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public Dictionary<string, Tuple<bool, string>> WriteItem(Dictionary<string, object> writeInfos)
 | 
			
		||||
    {
 | 
			
		||||
        PrivateConnect();
 | 
			
		||||
        Dictionary<string, Tuple<bool, string>> results = new();
 | 
			
		||||
 | 
			
		||||
        var valueGroup = writeInfos.GroupBy(itemId =>
 | 
			
		||||
          {
 | 
			
		||||
              var group = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == itemId.Key));
 | 
			
		||||
              return group;
 | 
			
		||||
          }).ToList();
 | 
			
		||||
 | 
			
		||||
        foreach (var item1 in valueGroup)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                if (item1.Key == null)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (var item2 in item1)
 | 
			
		||||
                    {
 | 
			
		||||
                        results.AddOrUpdate(item2.Key, Tuple.Create(true, $"不存在该变量{item2.Key}"));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    List<int> serverHandles = new();
 | 
			
		||||
                    Dictionary<int, OpcItem> handleItems = new();
 | 
			
		||||
                    List<object> values = new();
 | 
			
		||||
                    foreach (var item2 in item1)
 | 
			
		||||
                    {
 | 
			
		||||
                        var opcItem = item1.Key.OpcItems.Where(it => it.ItemID == item2.Key).FirstOrDefault();
 | 
			
		||||
                        serverHandles.Add(opcItem.ServerHandle);
 | 
			
		||||
                        handleItems.AddOrUpdate(opcItem.ServerHandle, opcItem);
 | 
			
		||||
                        var rawWriteValue = item2.Value;
 | 
			
		||||
                        values.Add(rawWriteValue);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var result = item1.Key.Write(values.ToArray(), serverHandles.ToArray());
 | 
			
		||||
                    var data = item1.ToList();
 | 
			
		||||
                    foreach (var item2 in result)
 | 
			
		||||
                    {
 | 
			
		||||
                        results.AddOrUpdate(handleItems[item2.Item1].ItemID, Tuple.Create(true, $"错误代码{item2.Item2}"));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                foreach (var item2 in item1)
 | 
			
		||||
                {
 | 
			
		||||
                    results.AddOrUpdate(item2.Key, Tuple.Create(false, $"成功"));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                var keys = writeInfos.Keys.ToList();
 | 
			
		||||
                foreach (var item in keys)
 | 
			
		||||
                {
 | 
			
		||||
                    results.AddOrUpdate(item, Tuple.Create(true, ex.Message));
 | 
			
		||||
                }
 | 
			
		||||
                return results;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return results;
 | 
			
		||||
    }
 | 
			
		||||
    private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        lock (checkLock)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (IsExit == 0)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var status = m_server.GetServerStatus();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    if (IsExit == 0 && publicConnect)
 | 
			
		||||
                    {
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            PrivateConnect();
 | 
			
		||||
                            _logAction?.Invoke(1, this, $"重新链接成功", null);
 | 
			
		||||
                        }
 | 
			
		||||
                        catch (Exception ex)
 | 
			
		||||
                        {
 | 
			
		||||
                            _logAction?.Invoke(3, this, $"重新链接失败:{ex.Message}", ex);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var timeer = sender as Timer;
 | 
			
		||||
                timeer.Enabled = false;
 | 
			
		||||
                timeer.Stop();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void PrivateAddItems()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            AddItems(ItemDicts);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            _logAction?.Invoke(3, this, $"添加点位失败:{ex.Message}", ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private void PrivateConnect()
 | 
			
		||||
    {
 | 
			
		||||
        lock (this)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (m_server?.IsConnected == true)
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var status = m_server.GetServerStatus();
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        var status1 = m_server.GetServerStatus();
 | 
			
		||||
                    }
 | 
			
		||||
                    catch
 | 
			
		||||
                    {
 | 
			
		||||
 | 
			
		||||
                        Init(OPCNode);
 | 
			
		||||
                        m_server?.Connect();
 | 
			
		||||
                        _logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 连接成功", null);
 | 
			
		||||
                        PrivateAddItems();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                Init(OPCNode);
 | 
			
		||||
                m_server?.Connect();
 | 
			
		||||
                _logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 连接成功", null);
 | 
			
		||||
                PrivateAddItems();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void PrivateDisconnect()
 | 
			
		||||
    {
 | 
			
		||||
        lock (this)
 | 
			
		||||
        {
 | 
			
		||||
            if (IsConnected)
 | 
			
		||||
                _logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 断开连接", null);
 | 
			
		||||
            if (checkTimer != null)
 | 
			
		||||
            {
 | 
			
		||||
                checkTimer.Enabled = false;
 | 
			
		||||
                checkTimer.Stop();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                m_server?.Dispose();
 | 
			
		||||
                m_server = null;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                _logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    private void Subscription_OnDataChanged(List<ItemReadResult> values)
 | 
			
		||||
    {
 | 
			
		||||
        DataChangedHandler?.Invoke(values);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,64 @@
 | 
			
		||||
#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.Adapter.OPCDA;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// OPCDA连接配置项
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OPCNode
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 是否订阅
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("订阅")]
 | 
			
		||||
    public bool ActiveSubscribe { get; set; } = true;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 内部检测重连间隔/min
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("重连间隔/min")]
 | 
			
		||||
    public int CheckRate { get; set; } = 30;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 死区
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("死区")]
 | 
			
		||||
    public float DeadBand { get; set; } = 0;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分组大小
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("分组大小")]
 | 
			
		||||
    public int GroupSize { get; set; } = 500;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OPCIP
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("OPCIP")]
 | 
			
		||||
    public string OPCIP { get; set; } = "localhost";
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OPCNAME
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("OPCNAME")]
 | 
			
		||||
    public string OPCName { get; set; } = "Kepware.KEPServerEX.V6";
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 订阅间隔
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("订阅间隔")]
 | 
			
		||||
    public int UpdateRate { get; set; } = 1000;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return $"{(string.IsNullOrEmpty(OPCIP) ? "localhost" : OPCIP)}:{OPCName}";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,4 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -0,0 +1,17 @@
 | 
			
		||||
#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.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
@@ -0,0 +1,83 @@
 | 
			
		||||
#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.Adapter.OPCUA;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// OPCUAClient配置项
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OPCNode
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OPCUrl
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("OPCUrl")]
 | 
			
		||||
    public string OPCUrl { get; set; } = "opc.tcp://127.0.0.1:49320";
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("登录账号")]
 | 
			
		||||
    public string UserName { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("登录密码")]
 | 
			
		||||
    public string Password { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查域
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("检查域")]
 | 
			
		||||
    public bool CheckDomain { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新间隔
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("更新间隔")]
 | 
			
		||||
    public int UpdateRate { get; set; } = 1000;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 是否订阅
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("是否订阅")]
 | 
			
		||||
    public bool ActiveSubscribe { get; set; } = true;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分组大小
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("分组大小")]
 | 
			
		||||
    public int GroupSize { get; set; } = 500;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 死区
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("死区")]
 | 
			
		||||
    public double DeadBand { get; set; } = 0;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// KeepAliveInterval/ms
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("KeepAliveInterval/ms")]
 | 
			
		||||
    public int KeepAliveInterval { get; set; } = 3000;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 安全策略
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("安全策略")]
 | 
			
		||||
    public bool IsUseSecurity { get; set; } = false;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 加载服务端数据类型
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("加载服务端数据类型")]
 | 
			
		||||
    public bool LoadType { get; set; } = true;
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return OPCUrl;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
#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 Opc.Ua;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCUA;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// OPC UA的状态更新消息
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpcUaStatusEventArgs
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志等级,<br></br>
 | 
			
		||||
    /// 更为详细的步骤型日志输出 Trace = 0,<br></br>
 | 
			
		||||
    /// 调试信息日志Debug = 1,<br></br>
 | 
			
		||||
    /// 消息类日志输出 Info = 2,<br></br>
 | 
			
		||||
    /// 警告类日志输出 Warning = 3,<br></br>
 | 
			
		||||
    /// 错误类日志输出 Error = 4,<br></br>
 | 
			
		||||
    /// 不可控中断类日输出Critical = 5,
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int LogLevel { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime Time { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 文本
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Text { get; set; }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 读取属性过程中用于描述的
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OPCNodeAttribute
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 属性的名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 操作结果状态描述
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public StatusCode StatusCode { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 属性的类型描述
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Type { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 属性的值,如果读取错误,返回文本描述
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public object Value { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,14 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
 | 
			
		||||
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
 | 
			
		||||
		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.4.372.56" />
 | 
			
		||||
		<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.4.372.56" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCUA;
 | 
			
		||||
 | 
			
		||||
internal static class CollectionExtensions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 移除符合条件的元素
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <param name="this"></param>
 | 
			
		||||
    /// <param name="where"></param>
 | 
			
		||||
    internal static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var obj in @this.Where(where).ToList())
 | 
			
		||||
        {
 | 
			
		||||
            @this.Remove(obj);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 异步Select
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <typeparam name="TResult"></typeparam>
 | 
			
		||||
    /// <param name="source"></param>
 | 
			
		||||
    /// <param name="selector"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static Task<TResult[]> SelectAsync<T, TResult>(this IEnumerable<T> source, Func<T, Task<TResult>> selector)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.WhenAll(source.Select(selector));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user