Compare commits
	
		
			2203 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					e045de5acb | ||
| 
						 | 
					d3bef31aa6 | ||
| 
						 | 
					347d4d6e5d | ||
| 
						 | 
					b006a066e1 | ||
| 
						 | 
					e331bc5d3c | ||
| 
						 | 
					2e81806231 | ||
| 
						 | 
					37b48cf221 | ||
| 
						 | 
					5997487434 | ||
| 
						 | 
					4ce843182f | ||
| 
						 | 
					a4aa000cf0 | ||
| 
						 | 
					01aa6ca066 | ||
| 
						 | 
					10a0280c4a | ||
| 
						 | 
					3b8b00c783 | ||
| 
						 | 
					232ee5d5d4 | ||
| 
						 | 
					605830edce | ||
| 
						 | 
					ca86441e05 | ||
| 
						 | 
					b30b876c5f | ||
| 
						 | 
					f95590b4cb | ||
| 
						 | 
					9921130406 | ||
| 
						 | 
					5c47589ca1 | ||
| 
						 | 
					048abfae2e | ||
| 
						 | 
					8e35c16edf | ||
| 
						 | 
					143b751213 | ||
| 
						 | 
					2cafe745b9 | ||
| 
						 | 
					210ac2c122 | ||
| 
						 | 
					4707ce6d58 | ||
| 
						 | 
					788a8b670d | ||
| 
						 | 
					5e4f0057e4 | ||
| 
						 | 
					2960c13ef1 | ||
| 
						 | 
					f11b7f7ab4 | ||
| 
						 | 
					9bb9cd7419 | ||
| 
						 | 
					ba008ef8ba | ||
| 
						 | 
					28b533decf | ||
| 
						 | 
					9ee638c2f1 | ||
| 
						 | 
					d90fdbaf35 | ||
| 
						 | 
					b55e3db736 | ||
| 
						 | 
					dbee8496cb | ||
| 
						 | 
					044e78bea9 | ||
| 
						 | 
					fe79128d90 | ||
| 
						 | 
					34120da008 | ||
| 
						 | 
					4c62bb0b21 | ||
| 
						 | 
					46b16279c7 | ||
| 
						 | 
					0779efc5dd | ||
| 
						 | 
					8acdb780e8 | ||
| 
						 | 
					2e310b919e | ||
| 
						 | 
					4155c07269 | ||
| 
						 | 
					32fa833736 | ||
| 
						 | 
					2258f08555 | ||
| 
						 | 
					d90b32f165 | ||
| 
						 | 
					1492377322 | ||
| 
						 | 
					32eefbf545 | ||
| 
						 | 
					a825ca5f6f | ||
| 
						 | 
					e270b0c4f6 | ||
| 
						 | 
					6ae44ccf58 | ||
| 
						 | 
					1aa0df6339 | ||
| 
						 | 
					62c3693dbe | ||
| 
						 | 
					e4e503c97b | ||
| 
						 | 
					5ec1ee7627 | ||
| 
						 | 
					79789388fc | ||
| 
						 | 
					2c4194ee18 | ||
| 
						 | 
					1b2be585af | ||
| 
						 | 
					83736647e7 | ||
| 
						 | 
					b06405717d | ||
| 
						 | 
					298a1f2ed4 | ||
| 
						 | 
					74a47a1983 | ||
| 
						 | 
					a48a42abe4 | ||
| 
						 | 
					feb1d0a3c5 | ||
| 
						 | 
					92bca824e6 | ||
| 
						 | 
					025c699517 | ||
| 
						 | 
					53f8fbe4b1 | ||
| 
						 | 
					77bfabc41d | ||
| 
						 | 
					6427ee6ee0 | ||
| 
						 | 
					4c95997d62 | ||
| 
						 | 
					1d82cea40d | ||
| 
						 | 
					e78799424c | ||
| 
						 | 
					1000c8d38f | ||
| 
						 | 
					b2589fc634 | ||
| 
						 | 
					c80e57a4e8 | ||
| 
						 | 
					6510c3e289 | ||
| 
						 | 
					920e407d05 | ||
| 
						 | 
					7314c8901d | ||
| 
						 | 
					4e9f02b48c | ||
| 
						 | 
					9ae7602cb4 | ||
| 
						 | 
					aa8aa36aef | ||
| 
						 | 
					0174f7c6f2 | ||
| 
						 | 
					df9e7d6ff1 | ||
| 
						 | 
					b40ca920d3 | ||
| 
						 | 
					5a4b0a0e93 | ||
| 
						 | 
					a879edd68b | ||
| 
						 | 
					62e0a6ee9d | ||
| 
						 | 
					765e5564d4 | ||
| 
						 | 
					10eecac19b | ||
| 
						 | 
					59241b8faa | ||
| 
						 | 
					52b3097f04 | ||
| 
						 | 
					d922296b70 | ||
| 
						 | 
					aec91da28b | ||
| 
						 | 
					013ff394be | ||
| 
						 | 
					081e07473d | ||
| 
						 | 
					d33d900592 | ||
| 
						 | 
					29365c4ef9 | ||
| 
						 | 
					17a6189089 | ||
| 
						 | 
					003b8a3763 | ||
| 
						 | 
					1c7f8b5cab | ||
| 
						 | 
					b7ff9ffca2 | ||
| 
						 | 
					9bba9bda76 | ||
| 
						 | 
					ec2fcc75d3 | ||
| 
						 | 
					57a4038577 | ||
| 
						 | 
					b78a76e60f | ||
| 
						 | 
					28bd751d44 | ||
| 
						 | 
					dcba7b9810 | ||
| 
						 | 
					7f1a741ce6 | ||
| 
						 | 
					ca2b17d433 | ||
| 
						 | 
					af589eac10 | ||
| 
						 | 
					573670f1f5 | ||
| 
						 | 
					f3ec85a03d | ||
| 
						 | 
					c94308454f | ||
| 
						 | 
					c9c2b2b69d | ||
| 
						 | 
					40574b776f | ||
| 
						 | 
					f426c1533d | ||
| 
						 | 
					4a3b5163f1 | ||
| 
						 | 
					3a0946d357 | ||
| 
						 | 
					9dd21c644c | ||
| 
						 | 
					6713190a23 | ||
| 
						 | 
					9f24533425 | ||
| 
						 | 
					ed1d9374eb | ||
| 
						 | 
					f14d27129e | ||
| 
						 | 
					5b04f02fbe | ||
| 
						 | 
					227080e332 | ||
| 
						 | 
					1e87482a49 | ||
| 
						 | 
					054d31c3ea | ||
| 
						 | 
					3a068a7b03 | ||
| 
						 | 
					b12e923c99 | ||
| 
						 | 
					ab33eed8d3 | ||
| 
						 | 
					d930a9a8eb | ||
| 
						 | 
					af381fce12 | ||
| 
						 | 
					b64ac0539e | ||
| 
						 | 
					541c60b363 | ||
| 
						 | 
					824e95f7cb | ||
| 
						 | 
					38f7850196 | ||
| 
						 | 
					bef9de88e2 | ||
| 
						 | 
					48cd5e7c7f | ||
| 
						 | 
					3b44fda51c | ||
| 
						 | 
					dbfc9a5bb4 | ||
| 
						 | 
					1b758aa41a | ||
| 
						 | 
					43bdc70899 | ||
| 
						 | 
					fadda000a6 | ||
| 
						 | 
					45a8c91a5a | ||
| 
						 | 
					8e938f18be | ||
| 
						 | 
					ab1b364c54 | ||
| 
						 | 
					5ec65b2fb0 | ||
| 
						 | 
					926eced724 | ||
| 
						 | 
					f7f8802272 | ||
| 
						 | 
					c6910dff02 | ||
| 
						 | 
					ad299d0dbb | ||
| 
						 | 
					8b124d1050 | ||
| 
						 | 
					ff41080dbd | ||
| 
						 | 
					0e28606e3d | ||
| 
						 | 
					6a025ceee5 | ||
| 
						 | 
					6b2e53d6dc | ||
| 
						 | 
					b989aa5561 | ||
| 
						 | 
					f5b0b7ebd2 | ||
| 
						 | 
					16881ae076 | ||
| 
						 | 
					af04112656 | ||
| 
						 | 
					a2863112dc | ||
| 
						 | 
					f531e4dfc5 | ||
| 
						 | 
					8db9b32ba7 | ||
| 
						 | 
					dd5691cbef | ||
| 
						 | 
					de48b32af3 | ||
| 
						 | 
					600b5042a1 | ||
| 
						 | 
					aac77029da | ||
| 
						 | 
					e50205f557 | ||
| 
						 | 
					e227411d1f | ||
| 
						 | 
					2de0ed793f | ||
| 
						 | 
					cb0276f273 | ||
| 
						 | 
					562b3f17c9 | ||
| 
						 | 
					0f78f81c1c | ||
| 
						 | 
					6594937d0a | ||
| 
						 | 
					64bc6084be | ||
| 
						 | 
					20434d5bd2 | ||
| 
						 | 
					9ecc9380e6 | ||
| 
						 | 
					a57c55080b | ||
| 
						 | 
					b8ca06c6be | ||
| 
						 | 
					5aff6461a1 | ||
| 
						 | 
					6cf53fefec | ||
| 
						 | 
					45132f3503 | ||
| 
						 | 
					f1e78a0e8a | ||
| 
						 | 
					0bf28ec275 | ||
| 
						 | 
					41f8412c97 | ||
| 
						 | 
					c535974362 | ||
| 
						 | 
					1860c5f215 | ||
| 
						 | 
					6d778b2d39 | ||
| 
						 | 
					f48b99c259 | ||
| 
						 | 
					3c73b93051 | ||
| 
						 | 
					98f3f2d519 | ||
| 
						 | 
					b76b4e8d68 | ||
| 
						 | 
					07285a7c61 | ||
| 
						 | 
					03c0dfef37 | ||
| 
						 | 
					6ef6929c35 | ||
| 
						 | 
					e3c0c173f0 | ||
| 
						 | 
					7d64e058d4 | ||
| 
						 | 
					e97ee9b64b | ||
| 
						 | 
					6a03e39eeb | ||
| 
						 | 
					525ec740b5 | ||
| 
						 | 
					b790cf5f4e | ||
| 
						 | 
					d1248811fd | ||
| 
						 | 
					022d016e8e | ||
| 
						 | 
					f73245e650 | ||
| 
						 | 
					484461fa05 | ||
| 
						 | 
					7e0b7aff2a | ||
| 
						 | 
					6c450dcb09 | ||
| 
						 | 
					227f44283f | ||
| 
						 | 
					74f6e79625 | ||
| 
						 | 
					cec43e2ce8 | ||
| 
						 | 
					7553b258bb | ||
| 
						 | 
					8bdbdc117e | ||
| 
						 | 
					0e206be296 | ||
| 
						 | 
					00b7353433 | ||
| 
						 | 
					44e7a83593 | ||
| 
						 | 
					dd68d555d4 | ||
| 
						 | 
					0456296103 | ||
| 
						 | 
					a1b66277ff | ||
| 
						 | 
					50758b79bc | ||
| 
						 | 
					06a1f902ad | ||
| 
						 | 
					58f8b23b7c | ||
| 
						 | 
					9e7c348b15 | ||
| 
						 | 
					5f5ff8b43b | ||
| 
						 | 
					f626b4e5fc | ||
| 
						 | 
					bc23200e66 | ||
| 
						 | 
					95ab59fd5a | ||
| 
						 | 
					0bbee003b0 | ||
| 
						 | 
					d20cb7a928 | ||
| 
						 | 
					952c134aba | ||
| 
						 | 
					5533d980e3 | ||
| 
						 | 
					aa0213818f | ||
| 
						 | 
					af88ffd57c | ||
| 
						 | 
					87447b1c2a | ||
| 
						 | 
					384a12880b | ||
| 
						 | 
					791f91f377 | ||
| 
						 | 
					4f8b405cc3 | ||
| 
						 | 
					e1b7af201a | ||
| 
						 | 
					a04b7817a9 | ||
| 
						 | 
					21bf83aec7 | ||
| 
						 | 
					d060d5179d | ||
| 
						 | 
					e73dd58e46 | ||
| 
						 | 
					a34e4a6942 | ||
| 
						 | 
					a7ee6d78b5 | ||
| 
						 | 
					0a6651ac11 | ||
| 
						 | 
					fa25053c13 | ||
| 
						 | 
					61dd4edeb5 | ||
| 
						 | 
					dbaeff302b | ||
| 
						 | 
					6cbe7ebcf2 | ||
| 
						 | 
					2826e08c8c | ||
| 
						 | 
					e30e337efa | ||
| 
						 | 
					39788e0357 | ||
| 
						 | 
					c47363df71 | ||
| 
						 | 
					4199453038 | ||
| 
						 | 
					db991081c8 | ||
| 
						 | 
					24f946a727 | ||
| 
						 | 
					772dae047a | ||
| 
						 | 
					020f440fdd | ||
| 
						 | 
					c88fc5ec09 | ||
| 
						 | 
					475849fbef | ||
| 
						 | 
					1d63228fb2 | ||
| 
						 | 
					c918c0c010 | ||
| 
						 | 
					1028179983 | ||
| 
						 | 
					686f61ff90 | ||
| 
						 | 
					ae43fadca3 | ||
| 
						 | 
					1bf05780b3 | ||
| 
						 | 
					3e30daa1d3 | ||
| 
						 | 
					54ef1233b1 | ||
| 
						 | 
					4413633de7 | ||
| 
						 | 
					c76e20d146 | ||
| 
						 | 
					36d53b7ce4 | ||
| 
						 | 
					0b0bce2761 | ||
| 
						 | 
					d47a9be03c | ||
| 
						 | 
					bc32d7ae7a | ||
| 
						 | 
					e6747a2c2c | ||
| 
						 | 
					3fb48cb64e | ||
| 
						 | 
					381db1cf53 | ||
| 
						 | 
					d3939aeb9d | ||
| 
						 | 
					5956f90557 | ||
| 
						 | 
					7f54ecfe5d | ||
| 
						 | 
					e7bda99c8b | ||
| 
						 | 
					ea7ab5a2bf | ||
| 
						 | 
					ee566d199a | ||
| 
						 | 
					56eca454b1 | ||
| 
						 | 
					d057974c3e | ||
| 
						 | 
					9514e7fdea | ||
| 
						 | 
					e546113873 | ||
| 
						 | 
					60f1139355 | ||
| 
						 | 
					1b18b59054 | ||
| 
						 | 
					afec0187e9 | ||
| 
						 | 
					d0d7e1ccf8 | ||
| 
						 | 
					d72376b4b7 | ||
| 
						 | 
					3be60cb305 | ||
| 
						 | 
					75b1f351a1 | ||
| 
						 | 
					07185f8018 | ||
| 
						 | 
					2f081b62a7 | ||
| 
						 | 
					bbce2a11e1 | ||
| 
						 | 
					96ab609157 | ||
| 
						 | 
					d961d56a35 | ||
| 
						 | 
					82a5335a92 | ||
| 
						 | 
					729a23949c | ||
| 
						 | 
					a1b0419d7a | ||
| 
						 | 
					7a23015b9d | ||
| 
						 | 
					9cbe0dbae1 | ||
| 
						 | 
					4f139b7d03 | ||
| 
						 | 
					1e925e2560 | ||
| 
						 | 
					a42e1e3af3 | ||
| 
						 | 
					799caec7ee | ||
| 
						 | 
					dd7d30498f | ||
| 
						 | 
					e9b1720e98 | ||
| 
						 | 
					5f41371bdb | ||
| 
						 | 
					5226bfae20 | ||
| 
						 | 
					d1eab168b7 | ||
| 
						 | 
					8760518c10 | ||
| 
						 | 
					6719a352c2 | ||
| 
						 | 
					8449b6c6b8 | ||
| 
						 | 
					0550eaf465 | ||
| 
						 | 
					bdc9eb44ec | ||
| 
						 | 
					37b99cb1b0 | ||
| 
						 | 
					492381e4fc | ||
| 
						 | 
					27d65dd799 | ||
| 
						 | 
					434724310c | ||
| 
						 | 
					e5ad5a8521 | ||
| 
						 | 
					3942819fed | ||
| 
						 | 
					1cc5f538aa | ||
| 
						 | 
					b0d8534ea4 | ||
| 
						 | 
					5b2bc38670 | ||
| 
						 | 
					b36aa3557c | ||
| 
						 | 
					a1a525e6e3 | ||
| 
						 | 
					14453cc437 | ||
| 
						 | 
					db760ba5d8 | ||
| 
						 | 
					4ae692008f | ||
| 
						 | 
					f37449f9ac | ||
| 
						 | 
					8b7348b3d4 | ||
| 
						 | 
					709cbb7b2e | ||
| 
						 | 
					187f1a4ce2 | ||
| 
						 | 
					0f2ebd3a54 | ||
| 
						 | 
					86b63c2792 | ||
| 
						 | 
					cec2e71b41 | ||
| 
						 | 
					9380f3e1ec | ||
| 
						 | 
					76c2934e12 | ||
| 
						 | 
					9714c35d4b | ||
| 
						 | 
					0d9beb713a | ||
| 
						 | 
					c5f8d0e0b7 | ||
| 
						 | 
					5e8f4b075d | ||
| 
						 | 
					4b53fca3db | ||
| 
						 | 
					5d9c030a09 | ||
| 
						 | 
					bacbb9a28e | ||
| 
						 | 
					7c287e0c03 | ||
| 
						 | 
					10f675137d | ||
| 
						 | 
					6bdb5328e3 | ||
| 
						 | 
					14f529ac4e | ||
| 
						 | 
					ffe96d9ef1 | ||
| 
						 | 
					335f264caa | ||
| 
						 | 
					b73e86ea9f | ||
| 
						 | 
					2bc2c4c57b | ||
| 
						 | 
					6f87c2f03a | ||
| 
						 | 
					75e677af88 | ||
| 
						 | 
					9ffe355396 | ||
| 
						 | 
					60b0fb23f8 | ||
| 
						 | 
					1c05ffc6ed | ||
| 
						 | 
					8cd4e43c68 | ||
| 
						 | 
					8486d28310 | ||
| 
						 | 
					e8aadda185 | ||
| 
						 | 
					fcfd0428ab | ||
| 
						 | 
					ddcc087800 | ||
| 
						 | 
					d574de54b4 | ||
| 
						 | 
					1682ac03ac | ||
| 
						 | 
					447dbece1c | ||
| 
						 | 
					dcca901b17 | ||
| 
						 | 
					a32c779074 | ||
| 
						 | 
					187c0f9829 | ||
| 
						 | 
					6c970c88ab | ||
| 
						 | 
					28a4520c77 | ||
| 
						 | 
					02d0dcdff7 | ||
| 
						 | 
					427d4e31c9 | ||
| 
						 | 
					c60f7aac44 | ||
| 
						 | 
					42a7758a34 | ||
| 
						 | 
					e0bade4670 | ||
| 
						 | 
					6af83152de | ||
| 
						 | 
					091a30045d | ||
| 
						 | 
					b2c8ae0c33 | ||
| 
						 | 
					8b6e0982c6 | ||
| 
						 | 
					caa3ebd600 | ||
| 
						 | 
					d248e047b3 | ||
| 
						 | 
					25a95a1de0 | ||
| 
						 | 
					d506b2c692 | ||
| 
						 | 
					24c672c2b9 | ||
| 
						 | 
					c407e0e83e | ||
| 
						 | 
					b1346698d6 | ||
| 
						 | 
					5c01129567 | ||
| 
						 | 
					cd7ce53bd0 | ||
| 
						 | 
					4c95c20061 | ||
| 
						 | 
					5339db5058 | ||
| 
						 | 
					623e932f8c | ||
| 
						 | 
					43659de4f4 | ||
| 
						 | 
					2a661eda42 | ||
| 
						 | 
					c85a93a17b | ||
| 
						 | 
					8ab36cb3e1 | ||
| 
						 | 
					72820d0708 | ||
| 
						 | 
					95b551d4ba | ||
| 
						 | 
					68af9a0695 | ||
| 
						 | 
					6953c3989b | ||
| 
						 | 
					cd4cb385d5 | ||
| 
						 | 
					f4f6d595fc | ||
| 
						 | 
					d2356f00e9 | ||
| 
						 | 
					e408136196 | ||
| 
						 | 
					c355968add | ||
| 
						 | 
					783f878947 | ||
| 
						 | 
					4faf41e482 | ||
| 
						 | 
					d8964b6ed2 | ||
| 
						 | 
					b5b999941c | ||
| 
						 | 
					deeba8c4de | ||
| 
						 | 
					9e48729eb0 | ||
| 
						 | 
					936e374c05 | ||
| 
						 | 
					0f3524b114 | ||
| 
						 | 
					d62de3d72c | ||
| 
						 | 
					957a80da4b | ||
| 
						 | 
					1dccd70f7c | ||
| 
						 | 
					006a5d4e61 | ||
| 
						 | 
					ce7cdcf54e | ||
| 
						 | 
					c1ebb4a79b | ||
| 
						 | 
					41964ae1de | ||
| 
						 | 
					66cf34adf7 | ||
| 
						 | 
					9e9e0009cf | ||
| 
						 | 
					d5cd9660fc | ||
| 
						 | 
					12a7803653 | ||
| 
						 | 
					8f7bef6a20 | ||
| 
						 | 
					a0715b830c | ||
| 
						 | 
					f87ce10dc4 | ||
| 
						 | 
					ee4936b8c9 | ||
| 
						 | 
					3cb2592660 | ||
| 
						 | 
					574c901b40 | ||
| 
						 | 
					7590ba4ad5 | ||
| 
						 | 
					39ce18f8bb | ||
| 
						 | 
					777a4f9d3f | ||
| 
						 | 
					b91a938787 | ||
| 
						 | 
					c903abdb1b | ||
| 
						 | 
					3279bc0580 | ||
| 
						 | 
					bd32c9f49e | ||
| 
						 | 
					5507088e3d | ||
| 
						 | 
					37ad48ae4a | ||
| 
						 | 
					120c6e6d87 | ||
| 
						 | 
					866e907b7e | ||
| 
						 | 
					2bd8c2cb10 | ||
| 
						 | 
					c6c9919178 | ||
| 
						 | 
					575bf9d1e0 | ||
| 
						 | 
					82a56e0285 | ||
| 
						 | 
					25d5f7c132 | ||
| 
						 | 
					a3e9ecf30f | ||
| 
						 | 
					ec5ff0a07f | ||
| 
						 | 
					be836d30c5 | ||
| 
						 | 
					42b1529a5f | ||
| 
						 | 
					47708c4807 | ||
| 
						 | 
					d2a51e004c | ||
| 
						 | 
					ab14230101 | ||
| 
						 | 
					4bcf8c1f78 | ||
| 
						 | 
					44d00e9da3 | ||
| 
						 | 
					d7e6a4493c | ||
| 
						 | 
					53e89d8c54 | ||
| 
						 | 
					4497c13634 | ||
| 
						 | 
					4a35fade46 | ||
| 
						 | 
					6cba0601fd | ||
| 
						 | 
					ed4332ea78 | ||
| 
						 | 
					aba069cec5 | ||
| 
						 | 
					8a82ac0a11 | ||
| 
						 | 
					1cd1456d75 | ||
| 
						 | 
					b791a3eb10 | ||
| 
						 | 
					3b22a8b170 | ||
| 
						 | 
					419e8214ca | ||
| 
						 | 
					b9f8571f0f | ||
| 
						 | 
					c6d9a9d7f8 | ||
| 
						 | 
					310aba6ccb | ||
| 
						 | 
					9bd89ac4f6 | ||
| 
						 | 
					ea6a51dca9 | ||
| 
						 | 
					e701bcc50c | ||
| 
						 | 
					a1abf06e75 | ||
| 
						 | 
					76ace394b0 | ||
| 
						 | 
					4dac462f8f | ||
| 
						 | 
					039672b1e7 | ||
| 
						 | 
					1b26ecbbf5 | ||
| 
						 | 
					f4a7e96943 | ||
| 
						 | 
					a45bc2954f | ||
| 
						 | 
					62f32467b7 | ||
| 
						 | 
					600a1bf201 | ||
| 
						 | 
					7e196e7aa6 | ||
| 
						 | 
					9ad3507b66 | ||
| 
						 | 
					add1bdfcf6 | ||
| 
						 | 
					7ea8a7c079 | ||
| 
						 | 
					28e31f5165 | ||
| 
						 | 
					10a6975c5d | ||
| 
						 | 
					b34ea87660 | ||
| 
						 | 
					86ed69c50b | ||
| 
						 | 
					02e824154c | ||
| 
						 | 
					dc973c8491 | ||
| 
						 | 
					0a28e3a8d3 | ||
| 
						 | 
					7a48c260e1 | ||
| 
						 | 
					c55c49a3a2 | ||
| 
						 | 
					0fc9b06d12 | ||
| 
						 | 
					d71ad5a6bf | ||
| 
						 | 
					f0b3028306 | ||
| 
						 | 
					752992c527 | ||
| 
						 | 
					43c4476396 | ||
| 
						 | 
					f8172bed56 | ||
| 
						 | 
					c1f71b4cfc | ||
| 
						 | 
					4799da15e7 | ||
| 
						 | 
					4936e47c7a | ||
| 
						 | 
					cb965373aa | ||
| 
						 | 
					654a91184a | ||
| 
						 | 
					57997f7f4b | ||
| 
						 | 
					ca2f5be3d1 | ||
| 
						 | 
					1b091073f1 | ||
| 
						 | 
					85d2d3c442 | ||
| 
						 | 
					d77dfd63cf | ||
| 
						 | 
					db2bf52fca | ||
| 
						 | 
					2c62d97440 | ||
| 
						 | 
					53b2e64214 | ||
| 
						 | 
					ccf82a3ee5 | ||
| 
						 | 
					9cb6f35e60 | ||
| 
						 | 
					da2e33a040 | ||
| 
						 | 
					f2d260bfa4 | ||
| 
						 | 
					783c7244f9 | ||
| 
						 | 
					c9287e0e63 | ||
| 
						 | 
					69b84d115c | ||
| 
						 | 
					1745b898b1 | ||
| 
						 | 
					5ba4c85249 | ||
| 
						 | 
					03c87bb46d | ||
| 
						 | 
					dff5a76e9e | ||
| 
						 | 
					50386f4ca4 | ||
| 
						 | 
					de6ba2ec80 | ||
| 
						 | 
					ce126015d9 | ||
| 
						 | 
					f2337c3d43 | ||
| 
						 | 
					41b59d322a | ||
| 
						 | 
					dfea7c111e | ||
| 
						 | 
					3d5d672297 | ||
| 
						 | 
					e4dcc6f342 | ||
| 
						 | 
					9847249611 | ||
| 
						 | 
					19742a54ec | ||
| 
						 | 
					5af9ae7a80 | ||
| 
						 | 
					78962a4a75 | ||
| 
						 | 
					9a35d826aa | ||
| 
						 | 
					c9cdc89d35 | ||
| 
						 | 
					33b75703ba | ||
| 
						 | 
					ca55aa3a24 | ||
| 
						 | 
					95307c3902 | ||
| 
						 | 
					89c8eae7e2 | ||
| 
						 | 
					91614772dc | ||
| 
						 | 
					cd497ab4dc | ||
| 
						 | 
					f81895905e | ||
| 
						 | 
					d82fafaa7a | ||
| 
						 | 
					0ea01acc46 | ||
| 
						 | 
					df55792a68 | ||
| 
						 | 
					6a19c45269 | ||
| 
						 | 
					23ee345441 | ||
| 
						 | 
					0e8ff253f3 | ||
| 
						 | 
					89bddb0bc4 | ||
| 
						 | 
					77bbca9bb3 | ||
| 
						 | 
					1acd4d8d58 | ||
| 
						 | 
					4db72a712d | ||
| 
						 | 
					da2b7ba08e | ||
| 
						 | 
					f8b49411bf | ||
| 
						 | 
					a04b0d99b0 | ||
| 
						 | 
					9e9735f617 | ||
| 
						 | 
					f906b36f01 | ||
| 
						 | 
					a9d1f4f854 | ||
| 
						 | 
					d017ae905f | ||
| 
						 | 
					bede280507 | ||
| 
						 | 
					ea130047bc | ||
| 
						 | 
					d77d8de63e | ||
| 
						 | 
					f5413d9202 | ||
| 
						 | 
					559fc8f216 | ||
| 
						 | 
					34b2df911d | ||
| 
						 | 
					3d5a8a8cbd | ||
| 
						 | 
					2b07097e14 | ||
| 
						 | 
					372e943d8b | ||
| 
						 | 
					95afcedc4f | ||
| 
						 | 
					c1dc8f017c | ||
| 
						 | 
					dbeddca263 | ||
| 
						 | 
					e65d133dec | ||
| 
						 | 
					b5e1dea1c2 | ||
| 
						 | 
					3f19469939 | ||
| 
						 | 
					87874b037a | ||
| 
						 | 
					adda6841d1 | ||
| 
						 | 
					a8092b921b | ||
| 
						 | 
					930e4d69d1 | ||
| 
						 | 
					d9219df45b | ||
| 
						 | 
					a6d4ec28db | ||
| 
						 | 
					b29e13f777 | ||
| 
						 | 
					9363b78ff6 | ||
| 
						 | 
					eba411fec7 | ||
| 
						 | 
					9afe99e134 | ||
| 
						 | 
					6678856178 | ||
| 
						 | 
					a1fea38c2c | ||
| 
						 | 
					2d3265e66d | ||
| 
						 | 
					f6d4400db9 | ||
| 
						 | 
					6d892a9266 | ||
| 
						 | 
					356fa08da5 | ||
| 
						 | 
					2225f8c890 | ||
| 
						 | 
					d1a89739f9 | ||
| 
						 | 
					6ec6896763 | ||
| 
						 | 
					9fb718d610 | ||
| 
						 | 
					c913b89a47 | ||
| 
						 | 
					58b45693b7 | ||
| 
						 | 
					29d3ece9b1 | ||
| 
						 | 
					7e5db75230 | ||
| 
						 | 
					283c0b9845 | ||
| 
						 | 
					071a0f1651 | ||
| 
						 | 
					1a116557a4 | ||
| 
						 | 
					1468297626 | ||
| 
						 | 
					87e94fdf52 | ||
| 
						 | 
					64e15c33c8 | ||
| 
						 | 
					7170f435d1 | ||
| 
						 | 
					9942be0493 | ||
| 
						 | 
					eaad36277f | ||
| 
						 | 
					37cc498568 | ||
| 
						 | 
					8a3191340a | ||
| 
						 | 
					0f4e998f0a | ||
| 
						 | 
					0c93b649ba | ||
| 
						 | 
					947d269877 | ||
| 
						 | 
					b4b9453948 | ||
| 
						 | 
					b99a4cd095 | ||
| 
						 | 
					a363d5aa46 | ||
| 
						 | 
					ae442dabb4 | ||
| 
						 | 
					1477c88397 | ||
| 
						 | 
					4172462938 | ||
| 
						 | 
					27341ba4f8 | ||
| 
						 | 
					7443076094 | ||
| 
						 | 
					a764bb4b1d | ||
| 
						 | 
					57130e1af7 | ||
| 
						 | 
					3f8a354c99 | ||
| 
						 | 
					0b10d92929 | ||
| 
						 | 
					1a1c55e661 | ||
| 
						 | 
					87768f9e8b | ||
| 
						 | 
					d079f50364 | ||
| 
						 | 
					72a89835c5 | ||
| 
						 | 
					39d686810b | ||
| 
						 | 
					d936626001 | ||
| 
						 | 
					cdce1c5ea6 | ||
| 
						 | 
					d59530a54f | ||
| 
						 | 
					fe480811fb | ||
| 
						 | 
					2e3e0d34c3 | ||
| 
						 | 
					1e2984d0d9 | ||
| 
						 | 
					3e9fe693c7 | ||
| 
						 | 
					63161838d8 | ||
| 
						 | 
					7c92951ae9 | ||
| 
						 | 
					59a25854ef | ||
| 
						 | 
					3ca5768974 | ||
| 
						 | 
					9a2dce43dd | ||
| 
						 | 
					b7fce4317b | ||
| 
						 | 
					97d9514b00 | ||
| 
						 | 
					58f88708fa | ||
| 
						 | 
					d79c874aa5 | ||
| 
						 | 
					58917c1ff8 | ||
| 
						 | 
					13b171a62d | ||
| 
						 | 
					f0a601922a | ||
| 
						 | 
					d90090b35e | ||
| 
						 | 
					094d1142e7 | ||
| 
						 | 
					eb09d1c925 | ||
| 
						 | 
					bd5d75361e | ||
| 
						 | 
					5d7c0c635b | ||
| 
						 | 
					606f538bf3 | ||
| 
						 | 
					574c0eea27 | ||
| 
						 | 
					4730cbc543 | ||
| 
						 | 
					5c63359550 | ||
| 
						 | 
					ec3607f779 | ||
| 
						 | 
					7efd0f8098 | ||
| 
						 | 
					2a7c30a21a | ||
| 
						 | 
					2639c3ee3f | ||
| 
						 | 
					547e0153cb | ||
| 
						 | 
					423664fcfa | ||
| 
						 | 
					c986c230ef | ||
| 
						 | 
					7c6001f76e | ||
| 
						 | 
					d3620946ac | ||
| 
						 | 
					479643221c | ||
| 
						 | 
					c9ef8bcf6a | ||
| 
						 | 
					d655a7c2f6 | ||
| 
						 | 
					306861d3b4 | ||
| 
						 | 
					7abab41eed | ||
| 
						 | 
					acc2691e33 | ||
| 
						 | 
					21a65956b0 | ||
| 
						 | 
					9738275ad1 | ||
| 
						 | 
					7265034703 | ||
| 
						 | 
					9bcc45591d | ||
| 
						 | 
					38d657ba0e | ||
| 
						 | 
					6a0d16c5bb | ||
| 
						 | 
					490d4abb56 | ||
| 
						 | 
					a7d0f7fc99 | ||
| 
						 | 
					0d2d3ff528 | ||
| 
						 | 
					526169f0aa | ||
| 
						 | 
					42940ff0e3 | ||
| 
						 | 
					e9b943363d | ||
| 
						 | 
					01e98ff10c | ||
| 
						 | 
					d38981a17d | ||
| 
						 | 
					7612e91691 | ||
| 
						 | 
					76b0f20033 | ||
| 
						 | 
					b78c0f8a29 | ||
| 
						 | 
					ea93c7c8fc | ||
| 
						 | 
					8ac1952974 | ||
| 
						 | 
					b4514a5bbf | ||
| 
						 | 
					568053b06d | ||
| 
						 | 
					bb180bfc39 | ||
| 
						 | 
					5ee635caed | ||
| 
						 | 
					5dfd7c8597 | ||
| 
						 | 
					fe61b75d7e | ||
| 
						 | 
					e7bf565418 | ||
| 
						 | 
					12071f6251 | ||
| 
						 | 
					72ed2e3ed0 | ||
| 
						 | 
					d7c1f134fe | ||
| 
						 | 
					5072d9f7ad | ||
| 
						 | 
					5d42a1247a | ||
| 
						 | 
					ce64be22bd | ||
| 
						 | 
					36f9386e8d | ||
| 
						 | 
					e9dc963805 | ||
| 
						 | 
					8854322997 | ||
| 
						 | 
					6018f1e8ca | ||
| 
						 | 
					68f8ebb543 | ||
| 
						 | 
					301f0218fd | ||
| 
						 | 
					a9c4a56893 | ||
| 
						 | 
					3ea3d72f11 | ||
| 
						 | 
					33da5bf4a7 | ||
| 
						 | 
					e3de01948a | ||
| 
						 | 
					3cd92aaf54 | ||
| 
						 | 
					ab4e1996dd | ||
| 
						 | 
					b9ef082abf | ||
| 
						 | 
					dac7186da3 | ||
| 
						 | 
					1fd6cc2b5e | ||
| 
						 | 
					0b7da9790a | ||
| 
						 | 
					a9e0e0ead6 | ||
| 
						 | 
					079754f1a9 | ||
| 
						 | 
					c45b5b1e86 | ||
| 
						 | 
					2115cc685c | ||
| 
						 | 
					b99e56fc19 | ||
| 
						 | 
					74c057eb84 | ||
| 
						 | 
					e8ececc0dc | ||
| 
						 | 
					ad120b7736 | ||
| 
						 | 
					61c0711e79 | ||
| 
						 | 
					81c3f6fce0 | ||
| 
						 | 
					0a4416e633 | ||
| 
						 | 
					6bb712854d | ||
| 
						 | 
					46959e2407 | ||
| 
						 | 
					debbb049b0 | ||
| 
						 | 
					375377fef9 | ||
| 
						 | 
					d5758dd6a3 | ||
| 
						 | 
					d5deffea58 | ||
| 
						 | 
					fadb43d9a1 | ||
| 
						 | 
					044b641df0 | ||
| 
						 | 
					fbbacdb2ff | ||
| 
						 | 
					4bfd7acd96 | ||
| 
						 | 
					ff03225cf4 | ||
| 
						 | 
					8e820c0e47 | ||
| 
						 | 
					d718314324 | ||
| 
						 | 
					1c653ec1b1 | ||
| 
						 | 
					fe16ddadce | ||
| 
						 | 
					7a978054cc | ||
| 
						 | 
					8ed3b5a747 | ||
| 
						 | 
					6885150800 | ||
| 
						 | 
					35d33a1c7d | ||
| 
						 | 
					93a16bbd3c | ||
| 
						 | 
					d3cf6a4985 | ||
| 
						 | 
					8ef7200c32 | ||
| 
						 | 
					e43da5ba86 | ||
| 
						 | 
					10428b4340 | ||
| 
						 | 
					43b7c05cd7 | ||
| 
						 | 
					057f64b04c | ||
| 
						 | 
					0971d783f3 | ||
| 
						 | 
					54aabad3e8 | ||
| 
						 | 
					606fbb5fec | ||
| 
						 | 
					5093c27ba5 | ||
| 
						 | 
					1d3661c946 | ||
| 
						 | 
					304f7a068d | ||
| 
						 | 
					7ea91be3fc | ||
| 
						 | 
					d15464b1ae | ||
| 
						 | 
					150435f24e | ||
| 
						 | 
					e49507cd14 | ||
| 
						 | 
					ad3cc3ebb6 | ||
| 
						 | 
					ecaf53836d | ||
| 
						 | 
					8fc36e7b24 | ||
| 
						 | 
					1dbbac2aa3 | ||
| 
						 | 
					4e5775fde4 | ||
| 
						 | 
					9d81c7c366 | ||
| 
						 | 
					2df0441f0f | ||
| 
						 | 
					4216002ce5 | ||
| 
						 | 
					1af6f4e151 | ||
| 
						 | 
					05f284bc11 | ||
| 
						 | 
					40c19b3ad4 | ||
| 
						 | 
					d1d972cca0 | ||
| 
						 | 
					2a969921d6 | ||
| 
						 | 
					4b3e1f5187 | ||
| 
						 | 
					561963aa26 | ||
| 
						 | 
					6c2c192836 | ||
| 
						 | 
					f0730f2295 | ||
| 
						 | 
					9df91d2b63 | ||
| 
						 | 
					09da1941fd | ||
| 
						 | 
					613c8e4c28 | ||
| 
						 | 
					7b407fd34d | ||
| 
						 | 
					fb7ff7a803 | ||
| 
						 | 
					0c1f0e0522 | ||
| 
						 | 
					b5d771e9aa | ||
| 
						 | 
					2f64255895 | ||
| 
						 | 
					5b629a5c40 | ||
| 
						 | 
					b749cf52c3 | ||
| 
						 | 
					b9a36a9223 | ||
| 
						 | 
					32debf6e6b | ||
| 
						 | 
					06f6ae01c9 | ||
| 
						 | 
					db4d8ad802 | ||
| 
						 | 
					9c82de9c9e | ||
| 
						 | 
					8329835528 | ||
| 
						 | 
					8e624aa31b | ||
| 
						 | 
					a2e8741517 | ||
| 
						 | 
					3b7c107e8f | ||
| 
						 | 
					558bc3f971 | ||
| 
						 | 
					90a326f380 | ||
| 
						 | 
					867dc1ae35 | ||
| 
						 | 
					3f555464f7 | ||
| 
						 | 
					c0c0782258 | ||
| 
						 | 
					377f57bcb6 | ||
| 
						 | 
					a51253f1e5 | ||
| 
						 | 
					e674e94da6 | ||
| 
						 | 
					93a415f6d3 | ||
| 
						 | 
					9364b97692 | ||
| 
						 | 
					030915c9ea | ||
| 
						 | 
					10882c309a | ||
| 
						 | 
					8232fde160 | ||
| 
						 | 
					45c3474d4d | ||
| 
						 | 
					c5a9e85343 | ||
| 
						 | 
					8c7ef59583 | ||
| 
						 | 
					36404b5c92 | ||
| 
						 | 
					9949a117be | ||
| 
						 | 
					a3896db4f5 | ||
| 
						 | 
					f668062cab | ||
| 
						 | 
					bac0721078 | ||
| 
						 | 
					e29d7244d8 | ||
| 
						 | 
					5a34b91a7a | ||
| 
						 | 
					eaac8b1428 | ||
| 
						 | 
					6c6275dc88 | ||
| 
						 | 
					bd350ae679 | ||
| 
						 | 
					cadee28c68 | ||
| 
						 | 
					3534828a99 | ||
| 
						 | 
					e1c5777e25 | ||
| 
						 | 
					63553d4dd3 | ||
| 
						 | 
					6033d779fc | ||
| 
						 | 
					97db27e61f | ||
| 
						 | 
					8ed861c05f | ||
| 
						 | 
					4d416657bf | ||
| 
						 | 
					ede0399275 | ||
| 
						 | 
					1a28ada04c | ||
| 
						 | 
					8ac771cde4 | ||
| 
						 | 
					c6afbbfc73 | ||
| 
						 | 
					5ba71e8109 | ||
| 
						 | 
					a0d2922aca | ||
| 
						 | 
					446610b9df | ||
| 
						 | 
					4905eea503 | ||
| 
						 | 
					519fc128f6 | ||
| 
						 | 
					cc10955483 | ||
| 
						 | 
					e0bd47bb87 | ||
| 
						 | 
					fb0c3c4a06 | ||
| 
						 | 
					ceef1fdaf2 | ||
| 
						 | 
					e1ac287ed1 | ||
| 
						 | 
					29cd3a5945 | ||
| 
						 | 
					cab082166b | ||
| 
						 | 
					711dfd4da9 | ||
| 
						 | 
					643948331d | ||
| 
						 | 
					119030f1a5 | ||
| 
						 | 
					3efee3dc52 | ||
| 
						 | 
					70716d5121 | ||
| 
						 | 
					a7d11ead62 | ||
| 
						 | 
					017830e433 | ||
| 
						 | 
					bceb175c75 | ||
| 
						 | 
					98277c6b71 | ||
| 
						 | 
					b227fbe523 | ||
| 
						 | 
					ac6659a293 | ||
| 
						 | 
					30a24ced1d | ||
| 
						 | 
					3d1f4ae2ed | ||
| 
						 | 
					e86aff049c | ||
| 
						 | 
					8ec0ba7c9e | ||
| 
						 | 
					c2898f0b2e | ||
| 
						 | 
					0747374378 | ||
| 
						 | 
					a0c2fea833 | ||
| 
						 | 
					811c1a445f | ||
| 
						 | 
					72d3041e7e | ||
| 
						 | 
					503180e7ec | ||
| 
						 | 
					8ad548cad7 | ||
| 
						 | 
					85d9acf605 | ||
| 
						 | 
					fc93c5ffcb | ||
| 
						 | 
					d6b0a9314c | ||
| 
						 | 
					83bc07c3ff | ||
| 
						 | 
					6ca680cd41 | ||
| 
						 | 
					2b533dcb2b | ||
| 
						 | 
					13a5a09f98 | ||
| 
						 | 
					75ff7ebdbe | ||
| 
						 | 
					6d8f3a1fc8 | ||
| 
						 | 
					a11b411912 | ||
| 
						 | 
					2dba817c69 | ||
| 
						 | 
					d4df9a545f | ||
| 
						 | 
					cae21391e5 | ||
| 
						 | 
					573b22d2c2 | ||
| 
						 | 
					14d4112a9d | ||
| 
						 | 
					28aa9db9c5 | ||
| 
						 | 
					d50c349e00 | ||
| 
						 | 
					905362c59c | ||
| 
						 | 
					97e7d44310 | ||
| 
						 | 
					bb71a1ef23 | ||
| 
						 | 
					de2771d008 | ||
| 
						 | 
					02de34b65a | ||
| 
						 | 
					c6e14889f2 | ||
| 
						 | 
					f843732790 | ||
| 
						 | 
					80e7ff0fc1 | ||
| 
						 | 
					2964db6bdd | ||
| 
						 | 
					5fbd94ec74 | ||
| 
						 | 
					1d8d31d4f0 | ||
| 
						 | 
					707c3eed99 | ||
| 
						 | 
					e33a81ad36 | ||
| 
						 | 
					0f7d216379 | ||
| 
						 | 
					f0052e97a6 | ||
| 
						 | 
					5c6076d48d | ||
| 
						 | 
					883bd31249 | ||
| 
						 | 
					d7b5fde3c7 | ||
| 
						 | 
					f4273efef0 | ||
| 
						 | 
					471dc966d9 | ||
| 
						 | 
					50368654a4 | ||
| 
						 | 
					b1e1cc04ce | ||
| 
						 | 
					554f67e295 | ||
| 
						 | 
					278bd09a6c | ||
| 
						 | 
					300d083038 | ||
| 
						 | 
					34664f0025 | ||
| 
						 | 
					7aaa803813 | ||
| 
						 | 
					b075b3ec1c | ||
| 
						 | 
					0f3f4b0f5b | ||
| 
						 | 
					fd5ed78442 | ||
| 
						 | 
					d516e7dcb4 | ||
| 
						 | 
					6a1f1ebf9d | ||
| 
						 | 
					6ba5c02dd6 | ||
| 
						 | 
					c9b8f90859 | ||
| 
						 | 
					70a0bf6d5c | ||
| 
						 | 
					4caf8d6fab | ||
| 
						 | 
					aa90593158 | ||
| 
						 | 
					9da5531483 | ||
| 
						 | 
					9483459ba7 | ||
| 
						 | 
					ae57883770 | ||
| 
						 | 
					8cb5a89d10 | ||
| 
						 | 
					1a35fb0d4d | ||
| 
						 | 
					1a2483c68c | ||
| 
						 | 
					6c806238b2 | ||
| 
						 | 
					ea6e07a38b | ||
| 
						 | 
					ec6b07b275 | ||
| 
						 | 
					e85a03a1ca | ||
| 
						 | 
					5d66b7677f | ||
| 
						 | 
					3932834172 | ||
| 
						 | 
					d2e8294a1a | ||
| 
						 | 
					b06aa66ad8 | ||
| 
						 | 
					c45b48d949 | ||
| 
						 | 
					f779af1f65 | ||
| 
						 | 
					78d039cd5f | ||
| 
						 | 
					fdbd5418c7 | ||
| 
						 | 
					5df42466fd | ||
| 
						 | 
					ff2bed87f2 | ||
| 
						 | 
					765e7d0b1e | ||
| 
						 | 
					71ab6d42c8 | ||
| 
						 | 
					2ea2a662ef | ||
| 
						 | 
					c2d45349a4 | ||
| 
						 | 
					1b5cf410b7 | ||
| 
						 | 
					3e79e2bed0 | ||
| 
						 | 
					2a1f933702 | ||
| 
						 | 
					251d3b487f | ||
| 
						 | 
					e55ec48c7d | ||
| 
						 | 
					7ce2195adb | ||
| 
						 | 
					537b79f1c2 | ||
| 
						 | 
					e4c4d22f56 | ||
| 
						 | 
					c4d244398c | ||
| 
						 | 
					47260cdaf0 | ||
| 
						 | 
					7ef13058c7 | ||
| 
						 | 
					8066cb1804 | ||
| 
						 | 
					c5af6062fa | ||
| 
						 | 
					908a2eba20 | ||
| 
						 | 
					6f43957703 | ||
| 
						 | 
					897ea28cf4 | ||
| 
						 | 
					d056ddbff1 | ||
| 
						 | 
					f157350059 | ||
| 
						 | 
					7e3d99bda3 | ||
| 
						 | 
					c7c53e1d94 | ||
| 
						 | 
					b912ee3f6f | ||
| 
						 | 
					06c05ad8e5 | ||
| 
						 | 
					7f411400d5 | ||
| 
						 | 
					f24f234869 | ||
| 
						 | 
					4e384e7811 | ||
| 
						 | 
					5143448163 | ||
| 
						 | 
					8e5e9f7cbc | ||
| 
						 | 
					5ea7fd01bf | ||
| 
						 | 
					8f3f6bc774 | ||
| 
						 | 
					7f1524b42f | ||
| 
						 | 
					4b7db4689d | ||
| 
						 | 
					92831ecb43 | ||
| 
						 | 
					d486f3f306 | ||
| 
						 | 
					bfb7468f62 | ||
| 
						 | 
					2e417d41db | ||
| 
						 | 
					b17f68cbfb | ||
| 
						 | 
					8a22a523d6 | ||
| 
						 | 
					97fe752a4e | ||
| 
						 | 
					cc4ea122a2 | ||
| 
						 | 
					db995d29e5 | ||
| 
						 | 
					c22dde8008 | ||
| 
						 | 
					462d888894 | ||
| 
						 | 
					9311b914ad | ||
| 
						 | 
					c5e0488210 | ||
| 
						 | 
					f548c27179 | ||
| 
						 | 
					1f5f405b52 | ||
| 
						 | 
					cd923ab838 | ||
| 
						 | 
					aacdff92d2 | ||
| 
						 | 
					32160d388a | ||
| 
						 | 
					72e31929eb | ||
| 
						 | 
					29014bb40e | ||
| 
						 | 
					e9d0a65536 | ||
| 
						 | 
					f7a3b68834 | ||
| 
						 | 
					dcc9d3b588 | ||
| 
						 | 
					b8f6f65943 | ||
| 
						 | 
					3dacdda21a | ||
| 
						 | 
					49b766120f | ||
| 
						 | 
					2d7e1553f8 | ||
| 
						 | 
					d5d41213b0 | ||
| 
						 | 
					328df00d2e | ||
| 
						 | 
					e4a5bf8234 | ||
| 
						 | 
					d6ee0f216c | ||
| 
						 | 
					0246ef6b6a | ||
| 
						 | 
					7fd40518fd | ||
| 
						 | 
					95eb53edf6 | ||
| 
						 | 
					4cdf2962cd | ||
| 
						 | 
					5bf8ccd1fd | ||
| 
						 | 
					dd9fbbcfa7 | ||
| 
						 | 
					72d8fd5be0 | ||
| 
						 | 
					b577229ed3 | ||
| 
						 | 
					4ae870b799 | ||
| 
						 | 
					0590e74b30 | ||
| 
						 | 
					b9956ba800 | ||
| 
						 | 
					fef6259d09 | ||
| 
						 | 
					e24f19e88b | ||
| 
						 | 
					cc6de1e71c | ||
| 
						 | 
					b0240a1c95 | ||
| 
						 | 
					8acdec333b | ||
| 
						 | 
					eafcbb81f9 | ||
| 
						 | 
					4b083463c8 | ||
| 
						 | 
					78e834e04f | ||
| 
						 | 
					2ed1893620 | ||
| 
						 | 
					35f188a7dd | ||
| 
						 | 
					07f577a9dd | ||
| 
						 | 
					25c72b50c0 | ||
| 
						 | 
					e07346ebea | ||
| 
						 | 
					699b466291 | ||
| 
						 | 
					f0f24ff96f | ||
| 
						 | 
					1113997c71 | ||
| 
						 | 
					487fb6e5c9 | ||
| 
						 | 
					62908a323c | ||
| 
						 | 
					021131271a | ||
| 
						 | 
					c20fa4dbd2 | ||
| 
						 | 
					b1826678da | ||
| 
						 | 
					1630338f4e | ||
| 
						 | 
					78af3d979c | ||
| 
						 | 
					8346dfb1f5 | ||
| 
						 | 
					5ecb4e4fe4 | ||
| 
						 | 
					ec9ff23b23 | ||
| 
						 | 
					149e9931e7 | ||
| 
						 | 
					58b62094bd | ||
| 
						 | 
					d357074ad4 | ||
| 
						 | 
					d9bacc129b | ||
| 
						 | 
					28d869b099 | ||
| 
						 | 
					d04e87718a | ||
| 
						 | 
					8d81bf60c9 | ||
| 
						 | 
					4fe9094ff7 | ||
| 
						 | 
					4cfde45755 | ||
| 
						 | 
					c28844dbeb | ||
| 
						 | 
					837ea7c8d6 | ||
| 
						 | 
					6c85ac8827 | ||
| 
						 | 
					c8eb8bf35d | ||
| 
						 | 
					0f6cbddd57 | ||
| 
						 | 
					530b4755bf | ||
| 
						 | 
					679395b3fe | ||
| 
						 | 
					9293442836 | ||
| 
						 | 
					4841b412f3 | ||
| 
						 | 
					f03575ad89 | ||
| 
						 | 
					e0e5428ebd | ||
| 
						 | 
					015f7edae2 | ||
| 
						 | 
					d5414f5d91 | ||
| 
						 | 
					4bc3512d42 | ||
| 
						 | 
					4c305994ca | ||
| 
						 | 
					788e2cba04 | ||
| 
						 | 
					b79b79970e | ||
| 
						 | 
					8b0f4de516 | ||
| 
						 | 
					a17000ad6d | ||
| 
						 | 
					c9f63c58df | ||
| 
						 | 
					c311b1c706 | ||
| 
						 | 
					072451afae | ||
| 
						 | 
					7349793463 | ||
| 
						 | 
					c0144eab86 | ||
| 
						 | 
					ba79b25944 | ||
| 
						 | 
					3fb34b12a0 | ||
| 
						 | 
					b6c17e76c2 | ||
| 
						 | 
					1201f20d79 | ||
| 
						 | 
					98ae8e692e | ||
| 
						 | 
					8342dbff4e | ||
| 
						 | 
					a22aa0f9a7 | ||
| 
						 | 
					b49d221ec1 | ||
| 
						 | 
					f1be315147 | ||
| 
						 | 
					8aaac2a3d4 | ||
| 
						 | 
					bd07d30e47 | ||
| 
						 | 
					54e103f00f | ||
| 
						 | 
					5404d8f7ab | ||
| 
						 | 
					1364e02c67 | ||
| 
						 | 
					b4818e2f9a | ||
| 
						 | 
					734cf57d4a | ||
| 
						 | 
					9236610ec1 | ||
| 
						 | 
					c33828a5d9 | ||
| 
						 | 
					faadb03e46 | ||
| 
						 | 
					3a0c4c51d2 | ||
| 
						 | 
					6c28c2b91e | ||
| 
						 | 
					5226df6194 | ||
| 
						 | 
					1ad23a3cbb | ||
| 
						 | 
					c4149ca304 | ||
| 
						 | 
					68b02fe950 | ||
| 
						 | 
					d9408523a5 | ||
| 
						 | 
					40ad0f063a | ||
| 
						 | 
					40d85e8926 | ||
| 
						 | 
					e655f719e6 | ||
| 
						 | 
					ede0ea02c5 | ||
| 
						 | 
					e579575b21 | ||
| 
						 | 
					06ffcc9fdf | ||
| 
						 | 
					774d03e510 | ||
| 
						 | 
					adcac9dfe6 | ||
| 
						 | 
					399e14c70c | ||
| 
						 | 
					ec8b51033b | ||
| 
						 | 
					ca5d167b6a | ||
| 
						 | 
					fcd1b001d1 | ||
| 
						 | 
					1cabbefe04 | ||
| 
						 | 
					ecefe22c95 | ||
| 
						 | 
					64db77b9a2 | ||
| 
						 | 
					19d7360ad5 | ||
| 
						 | 
					2f014cd827 | ||
| 
						 | 
					7fb84205d9 | ||
| 
						 | 
					47e1127c5f | ||
| 
						 | 
					e036b59306 | ||
| 
						 | 
					f4904c3b53 | ||
| 
						 | 
					3002a63ba5 | ||
| 
						 | 
					b69717e6c3 | ||
| 
						 | 
					d07962953f | ||
| 
						 | 
					857cf0d21e | ||
| 
						 | 
					5f08c2615d | ||
| 
						 | 
					e87428ef33 | ||
| 
						 | 
					03fd54fe70 | ||
| 
						 | 
					086c2c8253 | ||
| 
						 | 
					05c19a32ea | ||
| 
						 | 
					99d174906a | ||
| 
						 | 
					82e30a326a | ||
| 
						 | 
					2963a9cdca | ||
| 
						 | 
					288da75b2b | ||
| 
						 | 
					0943a496dd | ||
| 
						 | 
					e5dd7cc2fa | ||
| 
						 | 
					358836ef9f | ||
| 
						 | 
					aab4fac6c5 | ||
| 
						 | 
					525540b603 | ||
| 
						 | 
					b30eeb4694 | ||
| 
						 | 
					3faf0aa2fc | ||
| 
						 | 
					fd728dec5d | ||
| 
						 | 
					08b14b72d4 | ||
| 
						 | 
					de2e005abf | ||
| 
						 | 
					0fc75239a6 | ||
| 
						 | 
					390fe30a0d | ||
| 
						 | 
					633f49fcd2 | ||
| 
						 | 
					30c0ba93b9 | ||
| 
						 | 
					e935fb9621 | ||
| 
						 | 
					5ce8bb1d08 | ||
| 
						 | 
					1acd12980a | ||
| 
						 | 
					683235dd8a | ||
| 
						 | 
					65fe183ad4 | ||
| 
						 | 
					f39d5d355c | ||
| 
						 | 
					eccc8e0ff0 | ||
| 
						 | 
					dd4d8e775c | ||
| 
						 | 
					7a7f857b2f | ||
| 
						 | 
					10882b7d93 | ||
| 
						 | 
					e669b81005 | ||
| 
						 | 
					28a81d9539 | ||
| 
						 | 
					6bf5e4a6b8 | ||
| 
						 | 
					a51eee93f4 | ||
| 
						 | 
					bece2555c2 | ||
| 
						 | 
					d98d405009 | ||
| 
						 | 
					8d0881632c | ||
| 
						 | 
					e8b81da897 | ||
| 
						 | 
					acf4fbf750 | ||
| 
						 | 
					b4f1921d33 | ||
| 
						 | 
					94b4816f53 | ||
| 
						 | 
					b6ddafde3e | ||
| 
						 | 
					b01036818f | ||
| 
						 | 
					ad7da1a0c3 | ||
| 
						 | 
					1e0818d9d9 | ||
| 
						 | 
					a4686f01c3 | ||
| 
						 | 
					84d76f9aab | ||
| 
						 | 
					b90bf5eb86 | ||
| 
						 | 
					fe258f3fe5 | ||
| 
						 | 
					d339494594 | ||
| 
						 | 
					adfaf13055 | ||
| 
						 | 
					8abfeb5923 | ||
| 
						 | 
					c1a027a771 | ||
| 
						 | 
					5f3094d79b | ||
| 
						 | 
					925b81aca8 | ||
| 
						 | 
					c0c8437966 | ||
| 
						 | 
					1d27b2fc4a | ||
| 
						 | 
					5da43e7808 | ||
| 
						 | 
					5edbb558ae | ||
| 
						 | 
					7347cc1df2 | ||
| 
						 | 
					20cac11b2a | ||
| 
						 | 
					75c35c4ff8 | ||
| 
						 | 
					9d573512d0 | ||
| 
						 | 
					7921365853 | ||
| 
						 | 
					43263fd3b9 | ||
| 
						 | 
					59042a5ead | ||
| 
						 | 
					91b14de807 | ||
| 
						 | 
					81fab2be08 | ||
| 
						 | 
					165b742782 | ||
| 
						 | 
					76fef9c807 | ||
| 
						 | 
					e69ea0b9dc | ||
| 
						 | 
					98d3183f2b | ||
| 
						 | 
					a29390a951 | ||
| 
						 | 
					6291ce8617 | ||
| 
						 | 
					c76b1b50a0 | ||
| 
						 | 
					cc45e2aec0 | ||
| 
						 | 
					17efebb8e8 | ||
| 
						 | 
					5c94c733ee | ||
| 
						 | 
					156b89dd9c | ||
| 
						 | 
					34ba9f67e7 | ||
| 
						 | 
					5ddaa6b872 | ||
| 
						 | 
					9043fa7f56 | ||
| 
						 | 
					4c8e487dc9 | ||
| 
						 | 
					d3b87179aa | ||
| 
						 | 
					2166de8331 | ||
| 
						 | 
					f0bc3f001f | ||
| 
						 | 
					50448e7085 | ||
| 
						 | 
					cd1d42353e | ||
| 
						 | 
					5e588bf737 | ||
| 
						 | 
					7ff777d178 | ||
| 
						 | 
					861621189a | ||
| 
						 | 
					dcc00e08fd | ||
| 
						 | 
					a1b8a47d4b | ||
| 
						 | 
					1fd6b5e239 | ||
| 
						 | 
					f91e45bf44 | ||
| 
						 | 
					0675a45592 | ||
| 
						 | 
					0c7c6ae451 | ||
| 
						 | 
					cf089e8c4c | ||
| 
						 | 
					90928ac679 | ||
| 
						 | 
					3bbcf71784 | ||
| 
						 | 
					222734775d | ||
| 
						 | 
					f23ee7a6e0 | ||
| 
						 | 
					5b075aa6d5 | ||
| 
						 | 
					80cd6b693e | ||
| 
						 | 
					04721a12b1 | ||
| 
						 | 
					64e22c0e46 | ||
| 
						 | 
					d5a70c5b08 | ||
| 
						 | 
					eaac7b6bcf | ||
| 
						 | 
					b062a491cd | ||
| 
						 | 
					1e868517bb | ||
| 
						 | 
					7b2a93a2d7 | ||
| 
						 | 
					f57f0447c6 | ||
| 
						 | 
					7126ff881e | ||
| 
						 | 
					e28da4b165 | ||
| 
						 | 
					92d9b91f7c | ||
| 
						 | 
					149c4a30c0 | ||
| 
						 | 
					84e62062ec | ||
| 
						 | 
					dc1fb74850 | ||
| 
						 | 
					00c6010789 | ||
| 
						 | 
					5d35c058e0 | ||
| 
						 | 
					1522a521f6 | ||
| 
						 | 
					6e11b885f9 | ||
| 
						 | 
					442ae6e0e8 | ||
| 
						 | 
					6b49e83464 | ||
| 
						 | 
					f1ecf13fe1 | ||
| 
						 | 
					83d1c8582b | ||
| 
						 | 
					179f6cd454 | ||
| 
						 | 
					91b1474ff0 | ||
| 
						 | 
					15aabc88a1 | ||
| 
						 | 
					afc0d3017d | ||
| 
						 | 
					9e0b1dc8aa | ||
| 
						 | 
					55b482fd26 | ||
| 
						 | 
					4f9c9a6566 | ||
| 
						 | 
					4725120ee9 | ||
| 
						 | 
					a288f50fbb | ||
| 
						 | 
					09cf2560a4 | ||
| 
						 | 
					16353de7b1 | ||
| 
						 | 
					eb5834cb5c | ||
| 
						 | 
					3ef5736aed | ||
| 
						 | 
					d2e7b77d16 | ||
| 
						 | 
					8f794bce75 | ||
| 
						 | 
					c33196a50a | ||
| 
						 | 
					6e8b5b431f | ||
| 
						 | 
					8ca3ecf17f | ||
| 
						 | 
					91f7db59ea | ||
| 
						 | 
					a0b4501352 | ||
| 
						 | 
					345047820a | ||
| 
						 | 
					ac3525a953 | ||
| 
						 | 
					c8c3f5b134 | ||
| 
						 | 
					51319c0718 | ||
| 
						 | 
					43399b8b47 | ||
| 
						 | 
					a6596042b7 | ||
| 
						 | 
					23ae85fc9c | ||
| 
						 | 
					2da54862f1 | ||
| 
						 | 
					f272fb0559 | ||
| 
						 | 
					bd04e33586 | ||
| 
						 | 
					b09b9752ca | ||
| 
						 | 
					a810a48158 | ||
| 
						 | 
					b4f5792aa8 | ||
| 
						 | 
					fdf0330b4f | ||
| 
						 | 
					ca73743082 | ||
| 
						 | 
					df0cde2cfd | ||
| 
						 | 
					5a8421e807 | ||
| 
						 | 
					025ac95d81 | ||
| 
						 | 
					71b5824fdc | ||
| 
						 | 
					1a907c0be7 | ||
| 
						 | 
					1635cabd32 | ||
| 
						 | 
					27946a5b0f | ||
| 
						 | 
					e141822fd0 | ||
| 
						 | 
					6326828a02 | ||
| 
						 | 
					96b5362491 | ||
| 
						 | 
					8ab91a51bc | ||
| 
						 | 
					35e7e7af18 | ||
| 
						 | 
					0e248a7302 | ||
| 
						 | 
					d5ce4d6600 | ||
| 
						 | 
					2365bb5e87 | ||
| 
						 | 
					2230216a7e | ||
| 
						 | 
					54f896d25f | ||
| 
						 | 
					e47d239caf | ||
| 
						 | 
					ac259958d7 | ||
| 
						 | 
					0505ad88a6 | ||
| 
						 | 
					b323de3383 | ||
| 
						 | 
					c0e023324b | ||
| 
						 | 
					495806d5e3 | ||
| 
						 | 
					3a9f745a2d | ||
| 
						 | 
					7b15d114a3 | ||
| 
						 | 
					6ac0f72169 | ||
| 
						 | 
					ff68030231 | ||
| 
						 | 
					d34361b6dd | ||
| 
						 | 
					56c6ebe523 | ||
| 
						 | 
					2c6d2e0eb4 | ||
| 
						 | 
					8e64196a8f | ||
| 
						 | 
					926d5faf21 | ||
| 
						 | 
					18bad2af25 | ||
| 
						 | 
					6be4dc4aac | ||
| 
						 | 
					6398a59dda | ||
| 
						 | 
					48158b1db6 | ||
| 
						 | 
					47fe519e54 | ||
| 
						 | 
					7e06f19bbc | ||
| 
						 | 
					0b66c1f0d6 | ||
| 
						 | 
					ff2742ffd8 | ||
| 
						 | 
					70c2992a5f | ||
| 
						 | 
					4ac4ee6874 | ||
| 
						 | 
					865164a404 | ||
| 
						 | 
					f9c8069ab2 | ||
| 
						 | 
					2fbf5140e5 | ||
| 
						 | 
					0126f8144a | ||
| 
						 | 
					71383d7482 | ||
| 
						 | 
					4d3bfc5f42 | ||
| 
						 | 
					c173d217de | ||
| 
						 | 
					d2da9a039f | ||
| 
						 | 
					c915edd458 | ||
| 
						 | 
					504948452e | ||
| 
						 | 
					3daa6c7a54 | ||
| 
						 | 
					10cd1580a5 | ||
| 
						 | 
					5b0876e106 | ||
| 
						 | 
					cfcdeaff01 | ||
| 
						 | 
					7c34f233fd | ||
| 
						 | 
					686146a7d9 | ||
| 
						 | 
					44732d5977 | ||
| 
						 | 
					05c3794cf7 | ||
| 
						 | 
					f0431a504a | ||
| 
						 | 
					eccec85ea6 | ||
| 
						 | 
					16ff538804 | ||
| 
						 | 
					f81e07eb6f | ||
| 
						 | 
					1cbd41ad87 | ||
| 
						 | 
					d8567b8427 | ||
| 
						 | 
					afe6a1923b | ||
| 
						 | 
					0f60f4754f | ||
| 
						 | 
					7e988d0401 | ||
| 
						 | 
					d85813c229 | ||
| 
						 | 
					12eb56809d | ||
| 
						 | 
					d792172779 | ||
| 
						 | 
					011562ae73 | ||
| 
						 | 
					71c4e36933 | ||
| 
						 | 
					6261982f6c | ||
| 
						 | 
					4ac01e0ed0 | ||
| 
						 | 
					2dfe07f13e | ||
| 
						 | 
					7f4e4017ce | ||
| 
						 | 
					635970f37b | ||
| 
						 | 
					106e740220 | ||
| 
						 | 
					3e6420ba6f | ||
| 
						 | 
					79be8010d5 | ||
| 
						 | 
					0b6e679f3b | ||
| 
						 | 
					97c43ad2b9 | ||
| 
						 | 
					cbf978745c | ||
| 
						 | 
					63dceada90 | ||
| 
						 | 
					366aade8b4 | ||
| 
						 | 
					aeb505e3d9 | ||
| 
						 | 
					f9482e4773 | ||
| 
						 | 
					57dfc22ff7 | ||
| 
						 | 
					adc489b6ed | ||
| 
						 | 
					0623684315 | ||
| 
						 | 
					68afccd859 | ||
| 
						 | 
					99eef8fb28 | ||
| 
						 | 
					47e7685d39 | ||
| 
						 | 
					b6ea596ade | ||
| 
						 | 
					44d60b469b | ||
| 
						 | 
					51087408df | ||
| 
						 | 
					96226d9e6e | ||
| 
						 | 
					28f0f62424 | ||
| 
						 | 
					2e772a8cd4 | ||
| 
						 | 
					3fd192f0cf | ||
| 
						 | 
					d16ae81961 | ||
| 
						 | 
					211a8e093e | ||
| 
						 | 
					84010a51d6 | ||
| 
						 | 
					e9700ea19b | ||
| 
						 | 
					e81ec68072 | ||
| 
						 | 
					6096e97708 | ||
| 
						 | 
					6f302b66b4 | ||
| 
						 | 
					b6e41890f7 | ||
| 
						 | 
					be5332a048 | ||
| 
						 | 
					4d72aeb0cd | ||
| 
						 | 
					c7c3c35100 | ||
| 
						 | 
					2f0df3c552 | ||
| 
						 | 
					e6effcd921 | ||
| 
						 | 
					cd462b9523 | ||
| 
						 | 
					7e2e17d38b | ||
| 
						 | 
					665d616a06 | ||
| 
						 | 
					ced90f5bb9 | ||
| 
						 | 
					ca0ddb6cb4 | ||
| 
						 | 
					3d733813e1 | ||
| 
						 | 
					45ddc0b154 | ||
| 
						 | 
					87294497f3 | ||
| 
						 | 
					5f45f9c0d0 | ||
| 
						 | 
					7f78c06793 | ||
| 
						 | 
					8ea5b7eebc | ||
| 
						 | 
					e49cd511af | ||
| 
						 | 
					15c88646df | ||
| 
						 | 
					f47919543d | ||
| 
						 | 
					a95291d9cd | ||
| 
						 | 
					033deb3d29 | ||
| 
						 | 
					923994c1f4 | ||
| 
						 | 
					3d45b839b4 | ||
| 
						 | 
					55d1efa212 | ||
| 
						 | 
					21d794a0e5 | ||
| 
						 | 
					614022a78c | ||
| 
						 | 
					c00688e23a | ||
| 
						 | 
					5c69917d19 | ||
| 
						 | 
					6a5eb75b6c | ||
| 
						 | 
					727c55eaa2 | ||
| 
						 | 
					9b03173ec5 | ||
| 
						 | 
					ab95855d6d | ||
| 
						 | 
					bb84594c6b | ||
| 
						 | 
					97392c76b1 | ||
| 
						 | 
					53aec2b306 | ||
| 
						 | 
					a0a381dc63 | ||
| 
						 | 
					4fa95edeec | ||
| 
						 | 
					b944f1d70e | ||
| 
						 | 
					0f1a5d0085 | ||
| 
						 | 
					e7e10222e7 | ||
| 
						 | 
					87c4fda588 | ||
| 
						 | 
					0ff820de6f | ||
| 
						 | 
					0dcf55e6d9 | ||
| 
						 | 
					a69842c910 | ||
| 
						 | 
					318f635e4d | ||
| 
						 | 
					abf0d72316 | ||
| 
						 | 
					5cd89c8844 | ||
| 
						 | 
					501c1af38c | ||
| 
						 | 
					8526e130d9 | ||
| 
						 | 
					76fd08eade | ||
| 
						 | 
					bfe48ae9d2 | ||
| 
						 | 
					8547cdba7c | ||
| 
						 | 
					00d0cd631d | ||
| 
						 | 
					04460d13cd | ||
| 
						 | 
					a17a15e5d7 | ||
| 
						 | 
					bfe2465658 | ||
| 
						 | 
					4568987785 | ||
| 
						 | 
					c0e5a1419d | ||
| 
						 | 
					23ac7ab748 | ||
| 
						 | 
					5622dc74a8 | ||
| 
						 | 
					528986f4f0 | ||
| 
						 | 
					e6b8ff3f91 | ||
| 
						 | 
					3fc3c5296f | ||
| 
						 | 
					f4f2a14a31 | ||
| 
						 | 
					fa3f464cb6 | ||
| 
						 | 
					bed20538ae | ||
| 
						 | 
					3da461c6e6 | ||
| 
						 | 
					9c73e8452a | ||
| 
						 | 
					750dab69d3 | ||
| 
						 | 
					702d0da47c | ||
| 
						 | 
					3a955b1e53 | ||
| 
						 | 
					d408bd93e8 | ||
| 
						 | 
					63fd9eb1de | ||
| 
						 | 
					4718ab6ddf | ||
| 
						 | 
					affd4b3a26 | ||
| 
						 | 
					6a660dbcda | ||
| 
						 | 
					5ff3af8b48 | ||
| 
						 | 
					9d49070f93 | ||
| 
						 | 
					5b3994a1dd | ||
| 
						 | 
					bb1e62c580 | ||
| 
						 | 
					29cc776908 | ||
| 
						 | 
					d2ef60b2ec | ||
| 
						 | 
					41ea496d41 | ||
| 
						 | 
					c69bb38929 | ||
| 
						 | 
					23b3f8494e | ||
| 
						 | 
					0ff94eec1b | ||
| 
						 | 
					f67a638565 | ||
| 
						 | 
					778cf71c61 | ||
| 
						 | 
					e2da083b48 | ||
| 
						 | 
					dcc48c0d4b | ||
| 
						 | 
					2446b4a591 | ||
| 
						 | 
					a973d633c8 | ||
| 
						 | 
					c3b11e6e0f | ||
| 
						 | 
					44bec2d99e | ||
| 
						 | 
					0d59d0a3f6 | ||
| 
						 | 
					ddc8372b09 | ||
| 
						 | 
					ea7325f4e0 | ||
| 
						 | 
					6048f95415 | ||
| 
						 | 
					5682705b8d | ||
| 
						 | 
					6c3b82607d | ||
| 
						 | 
					486c9f4ebf | ||
| 
						 | 
					7c8adebf2d | ||
| 
						 | 
					ce98bb0dc1 | ||
| 
						 | 
					60b49a4296 | ||
| 
						 | 
					5d0a2ffae0 | ||
| 
						 | 
					bbcf7c722a | ||
| 
						 | 
					66574eec6d | ||
| 
						 | 
					305df3a42c | ||
| 
						 | 
					323c6f4270 | ||
| 
						 | 
					c056b2b14c | ||
| 
						 | 
					02ebb07fd8 | ||
| 
						 | 
					59db0890c3 | ||
| 
						 | 
					3ea0d9ed3a | ||
| 
						 | 
					0371d11dd5 | ||
| 
						 | 
					12f1ebaee9 | ||
| 
						 | 
					5202cc22d1 | ||
| 
						 | 
					07a34bdcbf | ||
| 
						 | 
					27806da4be | ||
| 
						 | 
					a15dd1a3ce | ||
| 
						 | 
					6cc7451c7c | ||
| 
						 | 
					652aeee782 | ||
| 
						 | 
					8e1e887b8f | ||
| 
						 | 
					6601a2de64 | ||
| 
						 | 
					75a42ffc32 | ||
| 
						 | 
					8a80a52f5f | ||
| 
						 | 
					b373a434ba | ||
| 
						 | 
					cd78fdafe6 | ||
| 
						 | 
					7a1b4c6db7 | ||
| 
						 | 
					f2c7664033 | ||
| 
						 | 
					12b295f067 | ||
| 
						 | 
					3ee46da40f | ||
| 
						 | 
					beadaa7212 | ||
| 
						 | 
					16ef09426f | ||
| 
						 | 
					b8c1f1f5a9 | ||
| 
						 | 
					e027b5cbd6 | ||
| 
						 | 
					bf1bd73ad1 | ||
| 
						 | 
					86fc95119a | ||
| 
						 | 
					b3a76ea17b | ||
| 
						 | 
					bababd9d53 | ||
| 
						 | 
					8cd5180531 | ||
| 
						 | 
					f1ccbade8c | ||
| 
						 | 
					a66f4b0417 | ||
| 
						 | 
					ff495b2261 | ||
| 
						 | 
					90e5824212 | ||
| 
						 | 
					3d64021062 | ||
| 
						 | 
					fb8ed7428d | ||
| 
						 | 
					bdc273c10a | ||
| 
						 | 
					2d96cffdc7 | ||
| 
						 | 
					4356fccbcd | ||
| 
						 | 
					a169fd4ce7 | ||
| 
						 | 
					dcd418139e | ||
| 
						 | 
					0cac2062d3 | ||
| 
						 | 
					854c5d4ade | ||
| 
						 | 
					0f96c6ec84 | ||
| 
						 | 
					b219bd66c1 | ||
| 
						 | 
					cb52f2c0b3 | ||
| 
						 | 
					d3273e03ef | ||
| 
						 | 
					afbfd963f3 | ||
| 
						 | 
					2cd101c5f3 | ||
| 
						 | 
					b10bc24dee | ||
| 
						 | 
					51a23df861 | ||
| 
						 | 
					03a0a9dad9 | ||
| 
						 | 
					362c0affbe | ||
| 
						 | 
					d5b523479f | ||
| 
						 | 
					7719b8f6d7 | ||
| 
						 | 
					3656c0d524 | ||
| 
						 | 
					8b9e0dd6ea | ||
| 
						 | 
					b3921b1037 | ||
| 
						 | 
					d7d96e5dbf | ||
| 
						 | 
					3934395b7b | ||
| 
						 | 
					04fbe9a529 | ||
| 
						 | 
					badd6dd9a2 | ||
| 
						 | 
					059082456d | ||
| 
						 | 
					405b68e22f | ||
| 
						 | 
					c90bf6692c | ||
| 
						 | 
					b5ea5d0cc5 | ||
| 
						 | 
					243617a1e1 | ||
| 
						 | 
					9e703edd59 | ||
| 
						 | 
					d6c2cf2810 | ||
| 
						 | 
					ae7811acfa | ||
| 
						 | 
					e55731c099 | ||
| 
						 | 
					9df5c74da4 | ||
| 
						 | 
					8b75f9f785 | ||
| 
						 | 
					bf12428cf4 | ||
| 
						 | 
					b551df978b | ||
| 
						 | 
					c91d85d2f0 | ||
| 
						 | 
					965237fa1f | ||
| 
						 | 
					24cd9afc06 | ||
| 
						 | 
					602fdefebd | ||
| 
						 | 
					7e7818aa17 | ||
| 
						 | 
					27a6023a6a | ||
| 
						 | 
					3861879900 | ||
| 
						 | 
					807eeb8351 | ||
| 
						 | 
					045fa53d58 | ||
| 
						 | 
					69d405ece7 | ||
| 
						 | 
					d3dc4d0c5b | ||
| 
						 | 
					7beba1188e | ||
| 
						 | 
					9779ebe12c | ||
| 
						 | 
					f72cfaa093 | ||
| 
						 | 
					cf8ccafb4a | ||
| 
						 | 
					7fe281b0b8 | ||
| 
						 | 
					5f70e9574f | ||
| 
						 | 
					4be51923ba | ||
| 
						 | 
					a72c78f4a4 | ||
| 
						 | 
					b4d32a6de1 | ||
| 
						 | 
					8a6b2d5daa | ||
| 
						 | 
					bebadeef14 | ||
| 
						 | 
					3551be67f0 | ||
| 
						 | 
					1103950b25 | ||
| 
						 | 
					9d06e01cec | ||
| 
						 | 
					24be946b7e | ||
| 
						 | 
					f56d2fc60f | ||
| 
						 | 
					9f8356f409 | ||
| 
						 | 
					8e6345d938 | ||
| 
						 | 
					0eac78bd1c | ||
| 
						 | 
					21db2502d0 | ||
| 
						 | 
					38c3492123 | ||
| 
						 | 
					6376bff476 | ||
| 
						 | 
					38495a3ebc | ||
| 
						 | 
					cf00897be2 | ||
| 
						 | 
					da640f24ec | ||
| 
						 | 
					ed6a777c42 | ||
| 
						 | 
					81af4485d3 | ||
| 
						 | 
					94d8f0237d | ||
| 
						 | 
					c5e579dd38 | ||
| 
						 | 
					7865e76ee2 | ||
| 
						 | 
					4d37212cc0 | ||
| 
						 | 
					9ca43f0bb4 | ||
| 
						 | 
					fe8d262175 | ||
| 
						 | 
					493d6e7165 | ||
| 
						 | 
					17b630bcd8 | ||
| 
						 | 
					018c5ad3b4 | ||
| 
						 | 
					a14b4da977 | ||
| 
						 | 
					01b4597cc7 | ||
| 
						 | 
					0b083ccc1c | ||
| 
						 | 
					ca22cb0eae | ||
| 
						 | 
					bcbb8e5f8c | ||
| 
						 | 
					bea286cdd4 | ||
| 
						 | 
					892d2ee8d2 | ||
| 
						 | 
					7440608c14 | ||
| 
						 | 
					6246f0295b | ||
| 
						 | 
					53c39d9b43 | ||
| 
						 | 
					9a50b60031 | ||
| 
						 | 
					fd74527320 | ||
| 
						 | 
					f91fb522ac | ||
| 
						 | 
					7019c02b18 | ||
| 
						 | 
					94f8422b9b | ||
| 
						 | 
					b25c01567a | ||
| 
						 | 
					b9329885de | ||
| 
						 | 
					ac88c4cff9 | ||
| 
						 | 
					9674f2d238 | ||
| 
						 | 
					38c9ecb6f1 | ||
| 
						 | 
					be9e9cdd5a | ||
| 
						 | 
					afdaf07446 | ||
| 
						 | 
					5caaf10225 | ||
| 
						 | 
					daffc3a776 | ||
| 
						 | 
					34c9748ce5 | ||
| 
						 | 
					f17acbf4d1 | ||
| 
						 | 
					229f56ce2f | ||
| 
						 | 
					0847bedc51 | ||
| 
						 | 
					9e4546c305 | ||
| 
						 | 
					97b01bf26a | ||
| 
						 | 
					e7011628d0 | ||
| 
						 | 
					044ffdecbd | ||
| 
						 | 
					1a06a3543b | ||
| 
						 | 
					5fedc0bf1e | ||
| 
						 | 
					7a881f867e | ||
| 
						 | 
					dfe73a6cfb | ||
| 
						 | 
					45d0fb27ad | ||
| 
						 | 
					31890af61d | ||
| 
						 | 
					a54b3ecc15 | ||
| 
						 | 
					c37cd1bd51 | ||
| 
						 | 
					0e3ee89cbf | ||
| 
						 | 
					a049176c14 | ||
| 
						 | 
					5ef2b0089e | ||
| 
						 | 
					1cc4899593 | ||
| 
						 | 
					26d1390be9 | ||
| 
						 | 
					dc65584fb3 | ||
| 
						 | 
					b3d5a708d5 | ||
| 
						 | 
					904a5d296b | ||
| 
						 | 
					cc82e6a47a | ||
| 
						 | 
					a9d02ec854 | ||
| 
						 | 
					6b5a2d3767 | ||
| 
						 | 
					10d9060b94 | ||
| 
						 | 
					8441839c01 | ||
| 
						 | 
					b669f5838f | ||
| 
						 | 
					1a942f9ce1 | ||
| 
						 | 
					ae840758f7 | ||
| 
						 | 
					082c08e5f9 | ||
| 
						 | 
					74a43557ab | ||
| 
						 | 
					184271789d | ||
| 
						 | 
					fa61b0cad8 | ||
| 
						 | 
					7b197a90d1 | ||
| 
						 | 
					62384af2f6 | ||
| 
						 | 
					b466202a1a | ||
| 
						 | 
					7ab38c18d8 | ||
| 
						 | 
					39841ee43e | ||
| 
						 | 
					e419800d98 | ||
| 
						 | 
					63c99ab69a | ||
| 
						 | 
					e2a8fd2279 | ||
| 
						 | 
					bbde520471 | ||
| 
						 | 
					45fb12f98e | ||
| 
						 | 
					4be3dcce50 | ||
| 
						 | 
					d12a41c769 | ||
| 
						 | 
					ed9a58c9ed | ||
| 
						 | 
					4389cea5a1 | ||
| 
						 | 
					d24854920b | ||
| 
						 | 
					9902443bee | ||
| 
						 | 
					8040b2ef16 | ||
| 
						 | 
					4533680b10 | ||
| 
						 | 
					3b28175135 | ||
| 
						 | 
					8e22812265 | ||
| 
						 | 
					409bc91b9e | ||
| 
						 | 
					61fe543a2a | ||
| 
						 | 
					8a949a7e64 | ||
| 
						 | 
					b0dc9fb97a | ||
| 
						 | 
					83114d1002 | ||
| 
						 | 
					94a25a903f | ||
| 
						 | 
					4f402f9e55 | ||
| 
						 | 
					27bb2e3dcc | ||
| 
						 | 
					66cc52f6ec | ||
| 
						 | 
					ed1367b116 | ||
| 
						 | 
					5c055352e4 | ||
| 
						 | 
					8dcff5ada1 | ||
| 
						 | 
					4b8b23d7d5 | ||
| 
						 | 
					e576f6aed3 | ||
| 
						 | 
					97ab04da91 | ||
| 
						 | 
					c6361bb36e | ||
| 
						 | 
					b0b3dc225d | ||
| 
						 | 
					9cbaba192a | ||
| 
						 | 
					f511598e13 | ||
| 
						 | 
					5ffff3b7aa | ||
| 
						 | 
					24c4fa1c4f | ||
| 
						 | 
					7f312c1273 | ||
| 
						 | 
					ef5c226c81 | ||
| 
						 | 
					3f029cf799 | ||
| 
						 | 
					5bf64a8bec | ||
| 
						 | 
					89f9c537f8 | ||
| 
						 | 
					e8d7f57818 | ||
| 
						 | 
					9b42fa78e7 | ||
| 
						 | 
					96d98e8f39 | ||
| 
						 | 
					1f3a281e78 | ||
| 
						 | 
					dab4b66ee0 | ||
| 
						 | 
					64dd192ddb | ||
| 
						 | 
					b0a5c383d0 | ||
| 
						 | 
					4836cc99eb | ||
| 
						 | 
					ba33873354 | ||
| 
						 | 
					12c838cab0 | ||
| 
						 | 
					a83a1c83a7 | ||
| 
						 | 
					2234f47170 | ||
| 
						 | 
					4bae1b17fc | ||
| 
						 | 
					6a7a0e0fa6 | ||
| 
						 | 
					bff987c55f | ||
| 
						 | 
					e467006913 | ||
| 
						 | 
					1d646d4a6d | ||
| 
						 | 
					70bd4936a1 | ||
| 
						 | 
					5ae7add6d2 | ||
| 
						 | 
					02b206ce7c | ||
| 
						 | 
					2ea626a567 | ||
| 
						 | 
					653909ec11 | ||
| 
						 | 
					1d7c148eaf | ||
| 
						 | 
					94a7e130ca | ||
| 
						 | 
					609abacdf0 | ||
| 
						 | 
					033ff2e49a | ||
| 
						 | 
					00a9bf0641 | ||
| 
						 | 
					100f322456 | ||
| 
						 | 
					2251e08d30 | ||
| 
						 | 
					14e9beef53 | ||
| 
						 | 
					c7cce23d54 | ||
| 
						 | 
					7becf32352 | ||
| 
						 | 
					741eda9f4e | ||
| 
						 | 
					473f51481c | ||
| 
						 | 
					684f9bd11b | ||
| 
						 | 
					b4b7f0c360 | ||
| 
						 | 
					cf42d02628 | ||
| 
						 | 
					cc9a9eab7b | ||
| 
						 | 
					784d6abc9b | ||
| 
						 | 
					0eafc07184 | ||
| 
						 | 
					7e9499bd2e | ||
| 
						 | 
					d01a52aa95 | ||
| 
						 | 
					6bcd492980 | ||
| 
						 | 
					96b15da04b | ||
| 
						 | 
					e0f1801693 | ||
| 
						 | 
					d195ad85c5 | ||
| 
						 | 
					773f7b6c1b | ||
| 
						 | 
					40f29fe399 | ||
| 
						 | 
					b19eb117c1 | ||
| 
						 | 
					f930ba5eff | ||
| 
						 | 
					fbbda6c230 | ||
| 
						 | 
					47db4968e5 | ||
| 
						 | 
					1aff2e518c | ||
| 
						 | 
					24134ca49e | ||
| 
						 | 
					aadbe05a5e | ||
| 
						 | 
					18eddf4700 | ||
| 
						 | 
					0869ba7e70 | ||
| 
						 | 
					cba5db1c53 | ||
| 
						 | 
					0aa5430e23 | ||
| 
						 | 
					88a91828cb | ||
| 
						 | 
					27af89f5fe | ||
| 
						 | 
					6f6d483bfe | ||
| 
						 | 
					915072c191 | ||
| 
						 | 
					76077b92ae | ||
| 
						 | 
					f0d1a527a5 | ||
| 
						 | 
					fdb2a96d1c | ||
| 
						 | 
					849e3ac187 | ||
| 
						 | 
					5c4cf21c59 | ||
| 
						 | 
					40ee3e5d9b | ||
| 
						 | 
					77a7cc034e | ||
| 
						 | 
					82b7881765 | ||
| 
						 | 
					3515775267 | ||
| 
						 | 
					10e12aa1c1 | ||
| 
						 | 
					7fb4e62aa3 | ||
| 
						 | 
					2605eaa614 | ||
| 
						 | 
					8e97b4820d | ||
| 
						 | 
					89af4cdb68 | ||
| 
						 | 
					6099c1a25a | ||
| 
						 | 
					fd23758aea | ||
| 
						 | 
					8ad0b89db1 | ||
| 
						 | 
					e7dffa3ff4 | ||
| 
						 | 
					2772b5d2e2 | ||
| 
						 | 
					df6d9b5f70 | ||
| 
						 | 
					c9622d6d57 | ||
| 
						 | 
					1f0b7c3c7e | ||
| 
						 | 
					c12245037e | ||
| 
						 | 
					fe6d2a50a5 | ||
| 
						 | 
					7be84950f8 | ||
| 
						 | 
					ba212da222 | ||
| 
						 | 
					122b833256 | ||
| 
						 | 
					7c73479041 | ||
| 
						 | 
					151f2e99ae | ||
| 
						 | 
					da367e813f | ||
| 
						 | 
					9143dfe336 | ||
| 
						 | 
					29914d6a72 | ||
| 
						 | 
					0dbef72a97 | ||
| 
						 | 
					3bb118bfe1 | ||
| 
						 | 
					85ce3be077 | ||
| 
						 | 
					064c2bf0a8 | ||
| 
						 | 
					c96d06ccc5 | ||
| 
						 | 
					a658dbb753 | ||
| 
						 | 
					bf5d5b629e | ||
| 
						 | 
					241d576519 | ||
| 
						 | 
					7807e9bdd0 | ||
| 
						 | 
					fcbf15fed6 | ||
| 
						 | 
					cd90a11209 | ||
| 
						 | 
					ff65707ea2 | ||
| 
						 | 
					b16026070c | ||
| 
						 | 
					1b5fbf86d8 | ||
| 
						 | 
					5eb1be8101 | ||
| 
						 | 
					6a863cd26a | ||
| 
						 | 
					87ebb7b6c7 | ||
| 
						 | 
					1233a74307 | ||
| 
						 | 
					221890acfb | ||
| 
						 | 
					687c7e766e | ||
| 
						 | 
					ba22c407d3 | ||
| 
						 | 
					a8b9ed56b5 | ||
| 
						 | 
					3004869e19 | ||
| 
						 | 
					1145853fe1 | ||
| 
						 | 
					f1617a25b1 | ||
| 
						 | 
					932be558d9 | ||
| 
						 | 
					ee3a37d3b9 | ||
| 
						 | 
					5ac412a582 | ||
| 
						 | 
					1625290bc3 | ||
| 
						 | 
					1ec169ad49 | ||
| 
						 | 
					53be552e44 | ||
| 
						 | 
					d3a75d46b9 | ||
| 
						 | 
					256512c961 | ||
| 
						 | 
					5c12ac5bcc | ||
| 
						 | 
					02a2bcb113 | ||
| 
						 | 
					1f0a01c725 | ||
| 
						 | 
					6ea164ede1 | ||
| 
						 | 
					65bae85ecc | ||
| 
						 | 
					2fd7dcf4d0 | ||
| 
						 | 
					aa3bbbe038 | ||
| 
						 | 
					6cb09a6f95 | ||
| 
						 | 
					45868f05d3 | ||
| 
						 | 
					aac7401e20 | ||
| 
						 | 
					c2fc290edd | ||
| 
						 | 
					330357fc36 | ||
| 
						 | 
					f4a3d6a64e | ||
| 
						 | 
					2e0963ec81 | ||
| 
						 | 
					897cb6e62a | ||
| 
						 | 
					80868bd48e | ||
| 
						 | 
					b3df78c56f | ||
| 
						 | 
					783a4259e3 | ||
| 
						 | 
					fcf19b8dc8 | ||
| 
						 | 
					1f9f89817d | ||
| 
						 | 
					7b94da7d85 | ||
| 
						 | 
					164406f6c2 | ||
| 
						 | 
					90ef2adc6b | ||
| 
						 | 
					d65f10f88b | ||
| 
						 | 
					dca0ece9e0 | ||
| 
						 | 
					baabc155c8 | ||
| 
						 | 
					7eb94412d6 | ||
| 
						 | 
					0fc8b24f85 | ||
| 
						 | 
					88f6ef5b96 | ||
| 
						 | 
					7442483419 | ||
| 
						 | 
					9c61933c04 | ||
| 
						 | 
					2b36a99720 | ||
| 
						 | 
					c2cfc42ba4 | ||
| 
						 | 
					4f32704e08 | ||
| 
						 | 
					fccf43685f | ||
| 
						 | 
					39135d81ad | ||
| 
						 | 
					ff4b10681e | ||
| 
						 | 
					10b7908fc2 | ||
| 
						 | 
					31790da8c6 | ||
| 
						 | 
					4621201c47 | ||
| 
						 | 
					692ac31dbf | ||
| 
						 | 
					ed1d163d55 | ||
| 
						 | 
					e931c9040c | ||
| 
						 | 
					f23cb48ea4 | ||
| 
						 | 
					c3b2b6b07b | ||
| 
						 | 
					f89bf590ba | ||
| 
						 | 
					fea17dc00b | ||
| 
						 | 
					dbb1069920 | ||
| 
						 | 
					afc4ccfaa9 | ||
| 
						 | 
					38d55a1c07 | ||
| 
						 | 
					5fca29f103 | ||
| 
						 | 
					78f34b2ca4 | ||
| 
						 | 
					8e8028e809 | ||
| 
						 | 
					8dc70687f8 | ||
| 
						 | 
					fa22f9ee64 | ||
| 
						 | 
					b33c0d3f81 | ||
| 
						 | 
					339009add4 | ||
| 
						 | 
					798c823c4b | ||
| 
						 | 
					14ad86f2a3 | ||
| 
						 | 
					a3a714dc17 | ||
| 
						 | 
					e8d8b0d41d | ||
| 
						 | 
					17daad8f89 | ||
| 
						 | 
					e178263b3b | ||
| 
						 | 
					4febbc261e | ||
| 
						 | 
					ef3a6942fd | ||
| 
						 | 
					f8ac2be62b | ||
| 
						 | 
					25447c34e5 | ||
| 
						 | 
					37cdf8fd48 | ||
| 
						 | 
					ac8e7dc959 | ||
| 
						 | 
					8293382840 | ||
| 
						 | 
					d85a3e7743 | ||
| 
						 | 
					f3f5ffb5c8 | ||
| 
						 | 
					99e6702c62 | ||
| 
						 | 
					e143c25078 | ||
| 
						 | 
					3fb67972be | ||
| 
						 | 
					61c04d4e09 | ||
| 
						 | 
					b22f728958 | ||
| 
						 | 
					112be7e383 | ||
| 
						 | 
					6a7f83fed5 | ||
| 
						 | 
					4b9698a735 | ||
| 
						 | 
					ec3d203a6d | ||
| 
						 | 
					e77b8f5475 | ||
| 
						 | 
					da8bbd321f | ||
| 
						 | 
					98fd011def | ||
| 
						 | 
					9dccbc5316 | ||
| 
						 | 
					03fa26daf9 | ||
| 
						 | 
					fbc41e3895 | ||
| 
						 | 
					f1a6d0c02c | ||
| 
						 | 
					67f6bb7155 | ||
| 
						 | 
					86282f596c | ||
| 
						 | 
					e0f24c795c | ||
| 
						 | 
					7d44ed860c | ||
| 
						 | 
					091094a24c | ||
| 
						 | 
					ba18ee518c | ||
| 
						 | 
					513a031691 | ||
| 
						 | 
					cc79de1106 | ||
| 
						 | 
					bd90e8efb2 | ||
| 
						 | 
					4bd88ff11d | ||
| 
						 | 
					d27ee61292 | ||
| 
						 | 
					0c20b3345f | ||
| 
						 | 
					0f3c8c7193 | ||
| 
						 | 
					16761ec605 | ||
| 
						 | 
					1069440cda | ||
| 
						 | 
					60f5702f17 | ||
| 
						 | 
					ee70133e47 | ||
| 
						 | 
					06a9fdeb2e | ||
| 
						 | 
					f14bd62004 | ||
| 
						 | 
					67cbaf22b7 | ||
| 
						 | 
					60b166dba2 | ||
| 
						 | 
					66e1647ede | ||
| 
						 | 
					a1a35c00a5 | ||
| 
						 | 
					b02e3361c4 | ||
| 
						 | 
					2b9ceaa25a | ||
| 
						 | 
					109b5a9755 | ||
| 
						 | 
					cc4df86c10 | ||
| 
						 | 
					e93532c395 | ||
| 
						 | 
					6ff688326a | ||
| 
						 | 
					eda5d2872f | ||
| 
						 | 
					dc88394f5f | ||
| 
						 | 
					ec85c9a2c6 | ||
| 
						 | 
					1341638556 | ||
| 
						 | 
					4875dfee11 | ||
| 
						 | 
					d834ba8bd4 | ||
| 
						 | 
					6191067771 | ||
| 
						 | 
					6c3a571163 | ||
| 
						 | 
					5efb07e10e | ||
| 
						 | 
					b25ec18ce6 | ||
| 
						 | 
					f3cd281241 | ||
| 
						 | 
					58b93cbf4c | ||
| 
						 | 
					57fc0349ff | ||
| 
						 | 
					d0a2cea772 | ||
| 
						 | 
					58d5801fb5 | ||
| 
						 | 
					e5f2e59798 | ||
| 
						 | 
					1166921057 | ||
| 
						 | 
					aeb49576f2 | ||
| 
						 | 
					7ef1fecef8 | ||
| 
						 | 
					fd630373b5 | ||
| 
						 | 
					d365d8f170 | ||
| 
						 | 
					dfa5e1172f | ||
| 
						 | 
					daf195898a | ||
| 
						 | 
					c61c0a39cf | ||
| 
						 | 
					6137e6baa5 | ||
| 
						 | 
					fd16fd9ffe | ||
| 
						 | 
					2b8bd5f2cc | ||
| 
						 | 
					d312c2e9e7 | ||
| 
						 | 
					578b0d2268 | ||
| 
						 | 
					ffb41b0109 | ||
| 
						 | 
					c63fb5d796 | ||
| 
						 | 
					8caee732e8 | ||
| 
						 | 
					df5381adce | ||
| 
						 | 
					f34836b7fa | ||
| 
						 | 
					44b459883a | ||
| 
						 | 
					14a8592ae3 | ||
| 
						 | 
					787d0dce4a | ||
| 
						 | 
					8be05ff93d | ||
| 
						 | 
					3014af565c | ||
| 
						 | 
					315252bdc4 | ||
| 
						 | 
					c36c3b4607 | ||
| 
						 | 
					7d5e939bab | ||
| 
						 | 
					f75c9d1eed | ||
| 
						 | 
					38cb9855ea | ||
| 
						 | 
					ed8b56d624 | ||
| 
						 | 
					bca48b13ae | ||
| 
						 | 
					29426edb05 | ||
| 
						 | 
					33a2dc687f | ||
| 
						 | 
					e2398a21b2 | ||
| 
						 | 
					f19530276e | ||
| 
						 | 
					2b9b92a78c | ||
| 
						 | 
					99c55dac10 | ||
| 
						 | 
					25667e46f9 | ||
| 
						 | 
					2f4e8f2399 | ||
| 
						 | 
					9169183769 | ||
| 
						 | 
					c420f50831 | ||
| 
						 | 
					c6c9279ef4 | ||
| 
						 | 
					c125e2991d | ||
| 
						 | 
					5348b19d6a | ||
| 
						 | 
					afd426daac | ||
| 
						 | 
					202c511cfa | ||
| 
						 | 
					651eb295a4 | ||
| 
						 | 
					d798aaed33 | ||
| 
						 | 
					63ef347cc9 | ||
| 
						 | 
					3139b2d5a0 | ||
| 
						 | 
					2c80bbb244 | ||
| 
						 | 
					af06755ada | ||
| 
						 | 
					97d6dbaa6c | ||
| 
						 | 
					74cf82a1c7 | ||
| 
						 | 
					74634889ab | ||
| 
						 | 
					dbe30fcd77 | ||
| 
						 | 
					24a7f3a320 | ||
| 
						 | 
					d3650f1145 | ||
| 
						 | 
					4801db9050 | ||
| 
						 | 
					6445281658 | ||
| 
						 | 
					87719f5938 | ||
| 
						 | 
					5ba1ec433b | ||
| 
						 | 
					7c5b382458 | ||
| 
						 | 
					8960426128 | ||
| 
						 | 
					e1d47d5a92 | ||
| 
						 | 
					9407c272aa | ||
| 
						 | 
					bccbd7b400 | ||
| 
						 | 
					a24dc010ec | ||
| 
						 | 
					c5e5d50fb8 | ||
| 
						 | 
					be1c520320 | ||
| 
						 | 
					3d18d0f893 | ||
| 
						 | 
					c3351a38a6 | ||
| 
						 | 
					0b86340a8d | ||
| 
						 | 
					829371b032 | ||
| 
						 | 
					b342207bc7 | ||
| 
						 | 
					58f4fdced3 | ||
| 
						 | 
					253844cbcf | ||
| 
						 | 
					e2e2b9ffb4 | ||
| 
						 | 
					2568042f5f | ||
| 
						 | 
					722edf4b9a | ||
| 
						 | 
					e406d364e4 | ||
| 
						 | 
					31c37f41b2 | ||
| 
						 | 
					6cd879bbc5 | ||
| 
						 | 
					b7974050fe | ||
| 
						 | 
					e9c9d0816e | ||
| 
						 | 
					a437692e1a | ||
| 
						 | 
					2a14d2f3c8 | ||
| 
						 | 
					559e2d1889 | ||
| 
						 | 
					a1aa919f80 | ||
| 
						 | 
					cbccb27a5a | ||
| 
						 | 
					bbad36d576 | ||
| 
						 | 
					df6c9d55b5 | ||
| 
						 | 
					36a1d9c364 | ||
| 
						 | 
					1f3681d5ac | ||
| 
						 | 
					0159f8e53f | ||
| 
						 | 
					0432be64fc | ||
| 
						 | 
					481e062961 | ||
| 
						 | 
					72b8abdeb6 | ||
| 
						 | 
					7ec3ee41d1 | ||
| 
						 | 
					b765fa4769 | ||
| 
						 | 
					06685b162e | ||
| 
						 | 
					7bcad7c424 | ||
| 
						 | 
					6e054b3cc6 | ||
| 
						 | 
					e643f6b0f8 | ||
| 
						 | 
					e44c0f85c2 | ||
| 
						 | 
					ea66e968eb | ||
| 
						 | 
					f7e73d804e | ||
| 
						 | 
					2ff4df56a1 | ||
| 
						 | 
					346c45fe0c | ||
| 
						 | 
					f98b42a36e | ||
| 
						 | 
					f77a0a266c | ||
| 
						 | 
					0be672788f | ||
| 
						 | 
					e2e3d11d42 | ||
| 
						 | 
					bb127bb567 | ||
| 
						 | 
					8eb4f89db8 | ||
| 
						 | 
					4a87ea3e70 | ||
| 
						 | 
					61115fce99 | ||
| 
						 | 
					4f31120394 | ||
| 
						 | 
					b085cd65ce | ||
| 
						 | 
					f76ec0721a | ||
| 
						 | 
					ef3944fbbf | ||
| 
						 | 
					b158fbc0d8 | ||
| 
						 | 
					68f2d66e97 | ||
| 
						 | 
					0cdd1735bd | ||
| 
						 | 
					70f5ead20b | ||
| 
						 | 
					574e5616f8 | ||
| 
						 | 
					94fa810590 | ||
| 
						 | 
					37f69da701 | ||
| 
						 | 
					f2f8448ade | ||
| 
						 | 
					b357d3fff2 | ||
| 
						 | 
					b417194905 | ||
| 
						 | 
					d8741da20a | ||
| 
						 | 
					c469be9a62 | ||
| 
						 | 
					ff4eb339ef | ||
| 
						 | 
					78489383c0 | ||
| 
						 | 
					f67c1b415f | ||
| 
						 | 
					570c16d921 | ||
| 
						 | 
					c8bc60568a | ||
| 
						 | 
					73de3ba856 | ||
| 
						 | 
					6a69be8537 | ||
| 
						 | 
					3ee381d505 | ||
| 
						 | 
					7ce744e2e4 | ||
| 
						 | 
					b81d21c991 | ||
| 
						 | 
					e9fe0992c6 | ||
| 
						 | 
					d5ef9018fa | ||
| 
						 | 
					c2737a7c51 | ||
| 
						 | 
					4b6d8733c6 | ||
| 
						 | 
					f22a3e0955 | ||
| 
						 | 
					4b8144a2f7 | ||
| 
						 | 
					abac52e23c | ||
| 
						 | 
					9aa089313e | ||
| 
						 | 
					b18f2c481a | ||
| 
						 | 
					ec83b9f77b | ||
| 
						 | 
					fad5495e02 | ||
| 
						 | 
					70dec1171e | ||
| 
						 | 
					0673a6fce3 | ||
| 
						 | 
					8f525b1407 | ||
| 
						 | 
					e83a991a4b | ||
| 
						 | 
					c000432a52 | ||
| 
						 | 
					c06d2d6927 | ||
| 
						 | 
					aa29653a8f | ||
| 
						 | 
					3f7e4ad486 | ||
| 
						 | 
					0eab546b2f | ||
| 
						 | 
					8830d216d1 | ||
| 
						 | 
					9fa1e3d449 | ||
| 
						 | 
					fe5dce7159 | ||
| 
						 | 
					952fa31548 | ||
| 
						 | 
					cc058ccc61 | ||
| 
						 | 
					bc26ed9701 | ||
| 
						 | 
					26135fc1a0 | ||
| 
						 | 
					0bd14672ff | ||
| 
						 | 
					7c7150cde8 | ||
| 
						 | 
					b08282b0c1 | ||
| 
						 | 
					3bf6e1befc | ||
| 
						 | 
					52692371ac | ||
| 
						 | 
					159ac1d8bb | ||
| 
						 | 
					7f1ffdbc79 | ||
| 
						 | 
					5fe64931dc | ||
| 
						 | 
					ee67855b48 | ||
| 
						 | 
					594a97f43f | ||
| 
						 | 
					7fb435a8b4 | ||
| 
						 | 
					01e74d6116 | ||
| 
						 | 
					b321d75b39 | ||
| 
						 | 
					1e47a45723 | ||
| 
						 | 
					88c609e5ef | ||
| 
						 | 
					775d1a424e | 
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -362,5 +362,9 @@ MigrationBackup/
 | 
			
		||||
# Fody - auto-generated XML schema
 | 
			
		||||
FodyWeavers.xsd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/framework/*Pro*
 | 
			
		||||
/src/*Pro*/
 | 
			
		||||
/src/*Pro*
 | 
			
		||||
/src/*pro*
 | 
			
		||||
/src/*pro*/
 | 
			
		||||
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
 | 
			
		||||
/src/.idea/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										202
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								LICENSE
									
									
									
									
									
										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 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
									
									
									
									
									
								
							
							
						
						
									
										202
									
								
								LICENSE.txt
									
									
									
									
									
								
							@@ -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 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,38 +1,101 @@
 | 
			
		||||
 | 
			
		||||
# ThingsGateway
 | 
			
		||||
 | 
			
		||||
## 介绍
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 **NetCore** 跨平台边缘采集网关(工业设备采集)
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
 **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
A cross-platform, high-performance edge data collection gateway based on net9.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[Documentation](https://thingsgateway.cn/).
 | 
			
		||||
 | 
			
		||||
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 文档
 | 
			
		||||
## Demo
 | 
			
		||||
 | 
			
		||||
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
 | 
			
		||||
 | 
			
		||||
[Demo](https://demo.thingsgateway.cn/)
 | 
			
		||||
 | 
			
		||||
## 协议
 | 
			
		||||
 | 
			
		||||
Account: **SuperAdmin**
 | 
			
		||||
 | 
			
		||||
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。
 | 
			
		||||
 | 
			
		||||
Password: **111111**
 | 
			
		||||
 | 
			
		||||
## 演示
 | 
			
		||||
 | 
			
		||||
**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
 | 
			
		||||
 | 
			
		||||
[ThingsGateway演示地址](http://120.24.62.140:5000/)
 | 
			
		||||
## Docker
 | 
			
		||||
 | 
			
		||||
账户	:  **superAdmin**	
 | 
			
		||||
```shell
 | 
			
		||||
 | 
			
		||||
密码 : **111111**
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
 | 
			
		||||
 | 
			
		||||
## 赞助
 | 
			
		||||
 | 
			
		||||
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
 | 
			
		||||
 | 
			
		||||
## 社区
 | 
			
		||||
 | 
			
		||||
QQ群:605534569
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								README.zh-CN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								README.zh-CN.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
# 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/>
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
[*.cs]
 | 
			
		||||
 | 
			
		||||
# CA1848: 使用 LoggerMessage 委托
 | 
			
		||||
dotnet_diagnostic.CA1848.severity = none
 | 
			
		||||
 | 
			
		||||
# CA2254: 模板应为静态表达式
 | 
			
		||||
dotnet_diagnostic.CA2254.severity = suggestion
 | 
			
		||||
 | 
			
		||||
[*.cs]
 | 
			
		||||
#### 命名样式 ####
 | 
			
		||||
 | 
			
		||||
# 命名规则
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
 | 
			
		||||
 | 
			
		||||
# 符号规范
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_kinds = interface
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.interface.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
 | 
			
		||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.types.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
 | 
			
		||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.non_field_members.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
# 命名样式
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.begins_with_i.required_prefix = I
 | 
			
		||||
dotnet_naming_style.begins_with_i.required_suffix = 
 | 
			
		||||
dotnet_naming_style.begins_with_i.word_separator = 
 | 
			
		||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.pascal_case.required_prefix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.required_suffix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.word_separator = 
 | 
			
		||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.pascal_case.required_prefix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.required_suffix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.word_separator = 
 | 
			
		||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
[*.vb]
 | 
			
		||||
#### 命名样式 ####
 | 
			
		||||
 | 
			
		||||
# 命名规则
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
 | 
			
		||||
 | 
			
		||||
# 符号规范
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_kinds = interface
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.interface.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
 | 
			
		||||
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.类型.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
 | 
			
		||||
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.非字段成员.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
# 命名样式
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.以_i_开始.required_prefix = I
 | 
			
		||||
dotnet_naming_style.以_i_开始.required_suffix = 
 | 
			
		||||
dotnet_naming_style.以_i_开始.word_separator = 
 | 
			
		||||
dotnet_naming_style.以_i_开始.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_prefix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_suffix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.word_separator = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_prefix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_suffix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.word_separator = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
 | 
			
		||||
							
								
								
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
								
							@@ -1,63 +0,0 @@
 | 
			
		||||
###############################################################################
 | 
			
		||||
# Set default behavior to automatically normalize line endings.
 | 
			
		||||
###############################################################################
 | 
			
		||||
* text=auto
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# Set default behavior for command prompt diff.
 | 
			
		||||
#
 | 
			
		||||
# This is need for earlier builds of msysgit that does not have it on by
 | 
			
		||||
# default for csharp files.
 | 
			
		||||
# Note: This is only used by command line
 | 
			
		||||
###############################################################################
 | 
			
		||||
#*.cs     diff=csharp
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# Set the merge driver for project and solution files
 | 
			
		||||
#
 | 
			
		||||
# Merging from the command prompt will add diff markers to the files if there
 | 
			
		||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
 | 
			
		||||
# the diff markers are never inserted). Diff markers may cause the following 
 | 
			
		||||
# file extensions to fail to load in VS. An alternative would be to treat
 | 
			
		||||
# these files as binary and thus will always conflict and require user
 | 
			
		||||
# intervention with every merge. To do so, just uncomment the entries below
 | 
			
		||||
###############################################################################
 | 
			
		||||
#*.sln       merge=binary
 | 
			
		||||
#*.csproj    merge=binary
 | 
			
		||||
#*.vbproj    merge=binary
 | 
			
		||||
#*.vcxproj   merge=binary
 | 
			
		||||
#*.vcproj    merge=binary
 | 
			
		||||
#*.dbproj    merge=binary
 | 
			
		||||
#*.fsproj    merge=binary
 | 
			
		||||
#*.lsproj    merge=binary
 | 
			
		||||
#*.wixproj   merge=binary
 | 
			
		||||
#*.modelproj merge=binary
 | 
			
		||||
#*.sqlproj   merge=binary
 | 
			
		||||
#*.wwaproj   merge=binary
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# behavior for image files
 | 
			
		||||
#
 | 
			
		||||
# image files are treated as binary by default.
 | 
			
		||||
###############################################################################
 | 
			
		||||
#*.jpg   binary
 | 
			
		||||
#*.png   binary
 | 
			
		||||
#*.gif   binary
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# diff behavior for common document formats
 | 
			
		||||
# 
 | 
			
		||||
# Convert binary document formats to text before diffing them. This feature
 | 
			
		||||
# is only available from the command line. Turn it on by uncommenting the 
 | 
			
		||||
# entries below.
 | 
			
		||||
###############################################################################
 | 
			
		||||
#*.doc   diff=astextplain
 | 
			
		||||
#*.DOC   diff=astextplain
 | 
			
		||||
#*.docx  diff=astextplain
 | 
			
		||||
#*.DOCX  diff=astextplain
 | 
			
		||||
#*.dot   diff=astextplain
 | 
			
		||||
#*.DOT   diff=astextplain
 | 
			
		||||
#*.pdf   diff=astextplain
 | 
			
		||||
#*.PDF   diff=astextplain
 | 
			
		||||
#*.rtf   diff=astextplain
 | 
			
		||||
#*.RTF   diff=astextplain
 | 
			
		||||
							
								
								
									
										364
									
								
								framework/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										364
									
								
								framework/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,364 +0,0 @@
 | 
			
		||||
## Ignore Visual Studio temporary files, build results, and
 | 
			
		||||
## files generated by popular Visual Studio add-ons.
 | 
			
		||||
##
 | 
			
		||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
 | 
			
		||||
 | 
			
		||||
# User-specific files
 | 
			
		||||
*.rsuser
 | 
			
		||||
*.suo
 | 
			
		||||
*.user
 | 
			
		||||
*.userosscache
 | 
			
		||||
*.sln.docstates
 | 
			
		||||
 | 
			
		||||
# User-specific files (MonoDevelop/Xamarin Studio)
 | 
			
		||||
*.userprefs
 | 
			
		||||
 | 
			
		||||
# Mono auto generated files
 | 
			
		||||
mono_crash.*
 | 
			
		||||
 | 
			
		||||
# Build results
 | 
			
		||||
[Dd]ebug/
 | 
			
		||||
[Dd]ebugPublic/
 | 
			
		||||
[Rr]elease/
 | 
			
		||||
[Rr]eleases/
 | 
			
		||||
x64/
 | 
			
		||||
x86/
 | 
			
		||||
[Ww][Ii][Nn]32/
 | 
			
		||||
[Aa][Rr][Mm]/
 | 
			
		||||
[Aa][Rr][Mm]64/
 | 
			
		||||
bld/
 | 
			
		||||
[Bb]in/
 | 
			
		||||
[Oo]bj/
 | 
			
		||||
[Oo]ut/
 | 
			
		||||
[Ll]og/
 | 
			
		||||
[Ll]ogs/
 | 
			
		||||
 | 
			
		||||
# Visual Studio 2015/2017 cache/options directory
 | 
			
		||||
.vs/
 | 
			
		||||
# Uncomment if you have tasks that create the project's static files in wwwroot
 | 
			
		||||
#wwwroot/
 | 
			
		||||
 | 
			
		||||
# Visual Studio 2017 auto generated files
 | 
			
		||||
Generated\ Files/
 | 
			
		||||
 | 
			
		||||
# MSTest test Results
 | 
			
		||||
[Tt]est[Rr]esult*/
 | 
			
		||||
[Bb]uild[Ll]og.*
 | 
			
		||||
 | 
			
		||||
# NUnit
 | 
			
		||||
*.VisualState.xml
 | 
			
		||||
TestResult.xml
 | 
			
		||||
nunit-*.xml
 | 
			
		||||
 | 
			
		||||
# Build Results of an ATL Project
 | 
			
		||||
[Dd]ebugPS/
 | 
			
		||||
[Rr]eleasePS/
 | 
			
		||||
dlldata.c
 | 
			
		||||
 | 
			
		||||
# Benchmark Results
 | 
			
		||||
BenchmarkDotNet.Artifacts/
 | 
			
		||||
 | 
			
		||||
# .NET Core
 | 
			
		||||
project.lock.json
 | 
			
		||||
project.fragment.lock.json
 | 
			
		||||
artifacts/
 | 
			
		||||
 | 
			
		||||
# ASP.NET Scaffolding
 | 
			
		||||
ScaffoldingReadMe.txt
 | 
			
		||||
 | 
			
		||||
# StyleCop
 | 
			
		||||
StyleCopReport.xml
 | 
			
		||||
 | 
			
		||||
# Files built by Visual Studio
 | 
			
		||||
*_i.c
 | 
			
		||||
*_p.c
 | 
			
		||||
*_h.h
 | 
			
		||||
*.ilk
 | 
			
		||||
*.meta
 | 
			
		||||
*.obj
 | 
			
		||||
*.iobj
 | 
			
		||||
*.pch
 | 
			
		||||
*.pdb
 | 
			
		||||
*.ipdb
 | 
			
		||||
*.pgc
 | 
			
		||||
*.pgd
 | 
			
		||||
*.rsp
 | 
			
		||||
*.sbr
 | 
			
		||||
*.tlb
 | 
			
		||||
*.tli
 | 
			
		||||
*.tlh
 | 
			
		||||
*.tmp
 | 
			
		||||
*.tmp_proj
 | 
			
		||||
*_wpftmp.csproj
 | 
			
		||||
*.log
 | 
			
		||||
*.vspscc
 | 
			
		||||
*.vssscc
 | 
			
		||||
.builds
 | 
			
		||||
*.pidb
 | 
			
		||||
*.svclog
 | 
			
		||||
*.scc
 | 
			
		||||
 | 
			
		||||
# Chutzpah Test files
 | 
			
		||||
_Chutzpah*
 | 
			
		||||
 | 
			
		||||
# Visual C++ cache files
 | 
			
		||||
ipch/
 | 
			
		||||
*.aps
 | 
			
		||||
*.ncb
 | 
			
		||||
*.opendb
 | 
			
		||||
*.opensdf
 | 
			
		||||
*.sdf
 | 
			
		||||
*.cachefile
 | 
			
		||||
*.VC.db
 | 
			
		||||
*.VC.VC.opendb
 | 
			
		||||
 | 
			
		||||
# Visual Studio profiler
 | 
			
		||||
*.psess
 | 
			
		||||
*.vsp
 | 
			
		||||
*.vspx
 | 
			
		||||
*.sap
 | 
			
		||||
 | 
			
		||||
# Visual Studio Trace Files
 | 
			
		||||
*.e2e
 | 
			
		||||
 | 
			
		||||
# TFS 2012 Local Workspace
 | 
			
		||||
$tf/
 | 
			
		||||
 | 
			
		||||
# Guidance Automation Toolkit
 | 
			
		||||
*.gpState
 | 
			
		||||
 | 
			
		||||
# ReSharper is a .NET coding add-in
 | 
			
		||||
_ReSharper*/
 | 
			
		||||
*.[Rr]e[Ss]harper
 | 
			
		||||
*.DotSettings.user
 | 
			
		||||
 | 
			
		||||
# TeamCity is a build add-in
 | 
			
		||||
_TeamCity*
 | 
			
		||||
 | 
			
		||||
# DotCover is a Code Coverage Tool
 | 
			
		||||
*.dotCover
 | 
			
		||||
 | 
			
		||||
# AxoCover is a Code Coverage Tool
 | 
			
		||||
.axoCover/*
 | 
			
		||||
!.axoCover/settings.json
 | 
			
		||||
 | 
			
		||||
# Coverlet is a free, cross platform Code Coverage Tool
 | 
			
		||||
coverage*.json
 | 
			
		||||
coverage*.xml
 | 
			
		||||
coverage*.info
 | 
			
		||||
 | 
			
		||||
# Visual Studio code coverage results
 | 
			
		||||
*.coverage
 | 
			
		||||
*.coveragexml
 | 
			
		||||
 | 
			
		||||
# NCrunch
 | 
			
		||||
_NCrunch_*
 | 
			
		||||
.*crunch*.local.xml
 | 
			
		||||
nCrunchTemp_*
 | 
			
		||||
 | 
			
		||||
# MightyMoose
 | 
			
		||||
*.mm.*
 | 
			
		||||
AutoTest.Net/
 | 
			
		||||
 | 
			
		||||
# Web workbench (sass)
 | 
			
		||||
.sass-cache/
 | 
			
		||||
 | 
			
		||||
# Installshield output folder
 | 
			
		||||
[Ee]xpress/
 | 
			
		||||
 | 
			
		||||
# DocProject is a documentation generator add-in
 | 
			
		||||
DocProject/buildhelp/
 | 
			
		||||
DocProject/Help/*.HxT
 | 
			
		||||
DocProject/Help/*.HxC
 | 
			
		||||
DocProject/Help/*.hhc
 | 
			
		||||
DocProject/Help/*.hhk
 | 
			
		||||
DocProject/Help/*.hhp
 | 
			
		||||
DocProject/Help/Html2
 | 
			
		||||
DocProject/Help/html
 | 
			
		||||
 | 
			
		||||
# Click-Once directory
 | 
			
		||||
publish/
 | 
			
		||||
 | 
			
		||||
# Publish Web Output
 | 
			
		||||
*.[Pp]ublish.xml
 | 
			
		||||
*.azurePubxml
 | 
			
		||||
# Note: Comment the next line if you want to checkin your web deploy settings,
 | 
			
		||||
# but database connection strings (with potential passwords) will be unencrypted
 | 
			
		||||
*.pubxml
 | 
			
		||||
*.publishproj
 | 
			
		||||
 | 
			
		||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
 | 
			
		||||
# checkin your Azure Web App publish settings, but sensitive information contained
 | 
			
		||||
# in these scripts will be unencrypted
 | 
			
		||||
PublishScripts/
 | 
			
		||||
 | 
			
		||||
# NuGet Packages
 | 
			
		||||
*.nupkg
 | 
			
		||||
# NuGet Symbol Packages
 | 
			
		||||
*.snupkg
 | 
			
		||||
# The packages folder can be ignored because of Package Restore
 | 
			
		||||
**/[Pp]ackages/*
 | 
			
		||||
# except build/, which is used as an MSBuild target.
 | 
			
		||||
!**/[Pp]ackages/build/
 | 
			
		||||
# Uncomment if necessary however generally it will be regenerated when needed
 | 
			
		||||
#!**/[Pp]ackages/repositories.config
 | 
			
		||||
# NuGet v3's project.json files produces more ignorable files
 | 
			
		||||
*.nuget.props
 | 
			
		||||
*.nuget.targets
 | 
			
		||||
 | 
			
		||||
# Microsoft Azure Build Output
 | 
			
		||||
csx/
 | 
			
		||||
*.build.csdef
 | 
			
		||||
 | 
			
		||||
# Microsoft Azure Emulator
 | 
			
		||||
ecf/
 | 
			
		||||
rcf/
 | 
			
		||||
 | 
			
		||||
# Windows Store app package directories and files
 | 
			
		||||
AppPackages/
 | 
			
		||||
BundleArtifacts/
 | 
			
		||||
Package.StoreAssociation.xml
 | 
			
		||||
_pkginfo.txt
 | 
			
		||||
*.appx
 | 
			
		||||
*.appxbundle
 | 
			
		||||
*.appxupload
 | 
			
		||||
 | 
			
		||||
# Visual Studio cache files
 | 
			
		||||
# files ending in .cache can be ignored
 | 
			
		||||
*.[Cc]ache
 | 
			
		||||
# but keep track of directories ending in .cache
 | 
			
		||||
!?*.[Cc]ache/
 | 
			
		||||
 | 
			
		||||
# Others
 | 
			
		||||
ClientBin/
 | 
			
		||||
~$*
 | 
			
		||||
*~
 | 
			
		||||
*.dbmdl
 | 
			
		||||
*.dbproj.schemaview
 | 
			
		||||
*.jfm
 | 
			
		||||
*.pfx
 | 
			
		||||
*.publishsettings
 | 
			
		||||
orleans.codegen.cs
 | 
			
		||||
 | 
			
		||||
# Including strong name files can present a security risk
 | 
			
		||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
 | 
			
		||||
#*.snk
 | 
			
		||||
 | 
			
		||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
 | 
			
		||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
 | 
			
		||||
#bower_components/
 | 
			
		||||
 | 
			
		||||
# RIA/Silverlight projects
 | 
			
		||||
Generated_Code/
 | 
			
		||||
 | 
			
		||||
# Backup & report files from converting an old project file
 | 
			
		||||
# to a newer Visual Studio version. Backup files are not needed,
 | 
			
		||||
# because we have git ;-)
 | 
			
		||||
_UpgradeReport_Files/
 | 
			
		||||
Backup*/
 | 
			
		||||
UpgradeLog*.XML
 | 
			
		||||
UpgradeLog*.htm
 | 
			
		||||
ServiceFabricBackup/
 | 
			
		||||
*.rptproj.bak
 | 
			
		||||
 | 
			
		||||
# SQL Server files
 | 
			
		||||
*.mdf
 | 
			
		||||
*.ldf
 | 
			
		||||
*.ndf
 | 
			
		||||
 | 
			
		||||
# Business Intelligence projects
 | 
			
		||||
*.rdl.data
 | 
			
		||||
*.bim.layout
 | 
			
		||||
*.bim_*.settings
 | 
			
		||||
*.rptproj.rsuser
 | 
			
		||||
*- [Bb]ackup.rdl
 | 
			
		||||
*- [Bb]ackup ([0-9]).rdl
 | 
			
		||||
*- [Bb]ackup ([0-9][0-9]).rdl
 | 
			
		||||
 | 
			
		||||
# Microsoft Fakes
 | 
			
		||||
FakesAssemblies/
 | 
			
		||||
 | 
			
		||||
# GhostDoc plugin setting file
 | 
			
		||||
*.GhostDoc.xml
 | 
			
		||||
 | 
			
		||||
# Node.js Tools for Visual Studio
 | 
			
		||||
.ntvs_analysis.dat
 | 
			
		||||
node_modules/
 | 
			
		||||
 | 
			
		||||
# Visual Studio 6 build log
 | 
			
		||||
*.plg
 | 
			
		||||
 | 
			
		||||
# Visual Studio 6 workspace options file
 | 
			
		||||
*.opt
 | 
			
		||||
 | 
			
		||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
 | 
			
		||||
*.vbw
 | 
			
		||||
 | 
			
		||||
# Visual Studio LightSwitch build output
 | 
			
		||||
**/*.HTMLClient/GeneratedArtifacts
 | 
			
		||||
**/*.DesktopClient/GeneratedArtifacts
 | 
			
		||||
**/*.DesktopClient/ModelManifest.xml
 | 
			
		||||
**/*.Server/GeneratedArtifacts
 | 
			
		||||
**/*.Server/ModelManifest.xml
 | 
			
		||||
_Pvt_Extensions
 | 
			
		||||
 | 
			
		||||
# Paket dependency manager
 | 
			
		||||
.paket/paket.exe
 | 
			
		||||
paket-files/
 | 
			
		||||
 | 
			
		||||
# FAKE - F# Make
 | 
			
		||||
.fake/
 | 
			
		||||
 | 
			
		||||
# CodeRush personal settings
 | 
			
		||||
.cr/personal
 | 
			
		||||
 | 
			
		||||
# Python Tools for Visual Studio (PTVS)
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.pyc
 | 
			
		||||
 | 
			
		||||
# Cake - Uncomment if you are using it
 | 
			
		||||
# tools/**
 | 
			
		||||
# !tools/packages.config
 | 
			
		||||
 | 
			
		||||
# Tabs Studio
 | 
			
		||||
*.tss
 | 
			
		||||
 | 
			
		||||
# Telerik's JustMock configuration file
 | 
			
		||||
*.jmconfig
 | 
			
		||||
 | 
			
		||||
# BizTalk build output
 | 
			
		||||
*.btp.cs
 | 
			
		||||
*.btm.cs
 | 
			
		||||
*.odx.cs
 | 
			
		||||
*.xsd.cs
 | 
			
		||||
 | 
			
		||||
# OpenCover UI analysis results
 | 
			
		||||
OpenCover/
 | 
			
		||||
 | 
			
		||||
# Azure Stream Analytics local run output
 | 
			
		||||
ASALocalRun/
 | 
			
		||||
 | 
			
		||||
# MSBuild Binary and Structured Log
 | 
			
		||||
*.binlog
 | 
			
		||||
 | 
			
		||||
# NVidia Nsight GPU debugger configuration file
 | 
			
		||||
*.nvuser
 | 
			
		||||
 | 
			
		||||
# MFractors (Xamarin productivity tool) working folder
 | 
			
		||||
.mfractor/
 | 
			
		||||
 | 
			
		||||
# Local History for Visual Studio
 | 
			
		||||
.localhistory/
 | 
			
		||||
 | 
			
		||||
# BeatPulse healthcheck temp database
 | 
			
		||||
healthchecksdb
 | 
			
		||||
 | 
			
		||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
 | 
			
		||||
MigrationBackup/
 | 
			
		||||
 | 
			
		||||
# Ionide (cross platform F# VS Code tools) working folder
 | 
			
		||||
.ionide/
 | 
			
		||||
 | 
			
		||||
# Fody - auto-generated XML schema
 | 
			
		||||
FodyWeavers.xsd
 | 
			
		||||
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
<Project>
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<Version>3.0.0.23</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>
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
 | 
			
		||||
global using ThingsGateway.Components;
 | 
			
		||||
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using 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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
<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>
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.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>
 | 
			
		||||
 | 
			
		||||
@@ -1,148 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Components;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,205 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -1,236 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using BlazorComponent;
 | 
			
		||||
 | 
			
		||||
using 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@using BlazorComponent;
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web;
 | 
			
		||||
@using System.IO.Ports;
 | 
			
		||||
@using 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>
 | 
			
		||||
@@ -1,106 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@using BlazorComponent;
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web;
 | 
			
		||||
@using System.IO.Ports;
 | 
			
		||||
@using System.Collections.Concurrent;
 | 
			
		||||
@using ThingsGateway.Foundation.Core;
 | 
			
		||||
@using 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>
 | 
			
		||||
@@ -1,116 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@using BlazorComponent;
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web;
 | 
			
		||||
@using System.IO.Ports;
 | 
			
		||||
@using System.Collections.Concurrent;
 | 
			
		||||
@using ThingsGateway.Foundation.Core;
 | 
			
		||||
@using 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>
 | 
			
		||||
@@ -1,108 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@using BlazorComponent;
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web;
 | 
			
		||||
@using System.IO.Ports;
 | 
			
		||||
@using System.Collections.Concurrent;
 | 
			
		||||
@using ThingsGateway.Foundation.Core;
 | 
			
		||||
@using 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>
 | 
			
		||||
@@ -1,113 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections.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>();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.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;
 | 
			
		||||
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@page "/"
 | 
			
		||||
@layout BaseLayout
 | 
			
		||||
@inject NavigationManager NavigationManager
 | 
			
		||||
@namespace ThingsGateway.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);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@page "/index"
 | 
			
		||||
 | 
			
		||||
<div class="ml-2">
 | 
			
		||||
    <div class="my-6 ">
 | 
			
		||||
        <MLabel Class="text-h3" Color="primary">ThingsGateway</MLabel>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <strong class="text--lighten-1 text-h5 my-1">文档</strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="my-2 ml-4">
 | 
			
		||||
        <PCopyableText>
 | 
			
		||||
            https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
        </PCopyableText>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div>
 | 
			
		||||
        <strong class="text--lighten-1 text-h5 my-1">协议</strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="my-2 ml-4">
 | 
			
		||||
        <PCopyableText Text="https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh">
 | 
			
		||||
            Apache-2.0开源协议
 | 
			
		||||
        </PCopyableText>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
        <strong class="text--lighten-1 text-h5 my-1">赞助</strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="my-2 ml-4">
 | 
			
		||||
        <PCopyableText Text="https://diego2098.gitee.io/thingsgateway-docs/docs/donate">
 | 
			
		||||
            ThingsGateway赞助途径
 | 
			
		||||
        </PCopyableText>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
        <strong class="text--lighten-1 text-h5 my-1">社区</strong>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="my-2 ml-4">
 | 
			
		||||
        <PCopyableText Text="605534569">
 | 
			
		||||
            QQ群:605534569
 | 
			
		||||
        </PCopyableText>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,77 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@namespace ThingsGateway.Foundation.Demo
 | 
			
		||||
@using 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";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,232 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
 | 
			
		||||
//  <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
 | 
			
		||||
//  Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
 | 
			
		||||
//  GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQȺ<51><C8BA>605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Demo;
 | 
			
		||||
 | 
			
		||||
public partial class MainLayout
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    private List<NavItem> Navs { get; set; } = new();
 | 
			
		||||
    private List<PageTabItem> pageTabItems { get; set; } = new();
 | 
			
		||||
    protected override void OnInitialized()
 | 
			
		||||
    {
 | 
			
		||||
        var dataString =
 | 
			
		||||
"""
 | 
			
		||||
[
 | 
			
		||||
  {
 | 
			
		||||
    "Href": "/index",
 | 
			
		||||
    "Title": "<22><>ҳ"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Modbus",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusRtu",
 | 
			
		||||
        "Title": "ModbusRtu"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusTcp",
 | 
			
		||||
        "Title": "ModbusTcp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusRtuOverTcp",
 | 
			
		||||
        "Title": "ModbusRtuOverTcp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusRtuOverUdp",
 | 
			
		||||
        "Title": "ModbusRtuOverUdp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusUdp",
 | 
			
		||||
        "Title": "ModbusUdp"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusTcpDtu",
 | 
			
		||||
        "Title": "ModbusTcpDtu"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusTcpServer",
 | 
			
		||||
        "Title": "ModbusTcpServer"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/ModbusSerialServer",
 | 
			
		||||
        "Title": "ModbusSerialServer"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "Siemens",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_1500",
 | 
			
		||||
        "Title": "S7_1500"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_1200",
 | 
			
		||||
        "Title": "S7_1200"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_200",
 | 
			
		||||
        "Title": "S7_200"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_200SMART",
 | 
			
		||||
        "Title": "S7_200SMART"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_300",
 | 
			
		||||
        "Title": "S7_400"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/S7_400",
 | 
			
		||||
        "Title": "S7_400"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "DLT645",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/DLT645_2007",
 | 
			
		||||
        "Title": "DLT645_2007"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/DLT645_2007OverTcp",
 | 
			
		||||
        "Title": "DLT645_2007OverTcp"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "OPCDA",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/OPCDAClient",
 | 
			
		||||
        "Title": "OPCDAClient"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "Title": "OPCUA",
 | 
			
		||||
    "Children": [
 | 
			
		||||
      {
 | 
			
		||||
        "Href": "/OPCUAClient",
 | 
			
		||||
        "Title": "OPCUAClient"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,152 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
 | 
			
		||||
		<DefineConstants>Pro</DefineConstants>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
	<ItemGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.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\PrivateLogger.cs" Link="Pages\Mqtt\PrivateLogger.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClient.cs" Link="Pages\Mqtt\MqttRpcClient.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClientExtensions.cs" Link="Pages\Mqtt\MqttRpcClientExtensions.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcTopicPair.cs" Link="Pages\Mqtt\MqttRpcTopicPair.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor" Link="Pages\DLT645\DLT645_2007DebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor" />
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor" Link="Pages\Modbus\ModbusRtuDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor" Link="Pages\Modbus\ModbusTcpDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusUdpDebugPage.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor" Link="Pages\Modbus\ModbusUdpDebugPage.razor" />
 | 
			
		||||
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor.cs" Link="Pages\OPCDA\OPCDAClientDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor.cs" Link="Pages\OPCDA\OPCDAClientPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor.cs" Link="Pages\OPCDA\OPCDAImportVariable.razor.cs" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor" Link="Pages\OPCDA\OPCDAClientDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor" Link="Pages\OPCDA\OPCDAClientPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor" Link="Pages\OPCDA\OPCDAImportVariable.razor" />
 | 
			
		||||
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor" Link="Pages\OPCUA\OPCUAClientDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor" Link="Pages\OPCUA\OPCUAClientPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor" Link="Pages\OPCUA\OPCUAImportVariable.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor.cs" Link="Pages\OPCUA\OPCUAClientDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor.cs" Link="Pages\OPCUA\OPCUAClientPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor.cs" Link="Pages\OPCUA\OPCUAImportVariable.razor.cs" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor" Link="Pages\Siemens\S7_1200DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor" Link="Pages\Siemens\S7_1500DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor" Link="Pages\Siemens\S7_200DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor" Link="Pages\Siemens\S7_200SMARTDebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor" Link="Pages\Siemens\S7_300DebugPage.razor" />
 | 
			
		||||
		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor" Link="Pages\Siemens\S7_400DebugPage.razor" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor.cs" Link="Pages\Siemens\S7_1200DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor.cs" Link="Pages\Siemens\S7_1500DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor.cs" Link="Pages\Siemens\S7_200DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor.cs" Link="Pages\Siemens\S7_200SMARTDebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor.cs" Link="Pages\Siemens\S7_300DebugPage.razor.cs" />
 | 
			
		||||
		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor.cs" Link="Pages\Siemens\S7_400DebugPage.razor.cs" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	<ItemGroup >
 | 
			
		||||
		<!--<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>
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
 | 
			
		||||
@using System.Net.Http
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Forms
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Routing
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web
 | 
			
		||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
 | 
			
		||||
@using Microsoft.JSInterop
 | 
			
		||||
@using BlazorComponent
 | 
			
		||||
@using Masa.Blazor
 | 
			
		||||
@using Masa.Blazor.Presets
 | 
			
		||||
@using 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;
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
 | 
			
		||||
    <title>ThingsGateway.Foundation.Demo</title>
 | 
			
		||||
    <base href="/" />
 | 
			
		||||
 | 
			
		||||
    <link rel="icon" href="favicon.ico" type="image/x-icon">
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    <link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet" />
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/css/materialdesign/v7.1.96/css/materialdesignicons.min.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/css/material/icons.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/css/fontawesome/v6.4.0/css/all.min.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/style/custom.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/prism/prism-material-dark-for-masa.css" rel="stylesheet">
 | 
			
		||||
    <link href="_content/ThingsGateway.Components/prism/prism-line-highlight.min.css" rel="stylesheet">
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <div id="app"></div>
 | 
			
		||||
 | 
			
		||||
    <div id="blazor-error-ui">
 | 
			
		||||
        <span>
 | 
			
		||||
            <environment include="Staging,Production">
 | 
			
		||||
                An error has occurred. This application may no longer respond until reloaded.
 | 
			
		||||
            </environment>
 | 
			
		||||
            <environment include="Development">
 | 
			
		||||
                An unhandled exception has occurred. See browser dev tools for details.
 | 
			
		||||
            </environment>
 | 
			
		||||
        </span>
 | 
			
		||||
        <a href="" class="reload">Reload</a>
 | 
			
		||||
        <a class="dismiss">🗙</a>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <script src="_framework/blazor.webview.js" autostart="true"></script>
 | 
			
		||||
    <script src="_content/ThingsGateway.Components/prism/prism.min.js"></script>
 | 
			
		||||
    <script src="_content/BlazorComponent/js/blazor-component.js"></script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
<Project>
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<Version>3.0.0.23</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>
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,480 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// DLT645_2007
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class DLT645_2007 : ReadWriteDevicesSerialSessionBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// DLT645_2007
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="serialSession"></param>
 | 
			
		||||
    public DLT645_2007(SerialSession serialSession) : base(serialSession)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 增加FE FE FE FE的报文头部
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("前导符报文头")]
 | 
			
		||||
    public bool EnableFEHead { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写入需操作员代码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("操作员代码")]
 | 
			
		||||
    public string OperCode { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写入密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("写入密码")]
 | 
			
		||||
    public string Password { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通讯地址BCD码,一般应该是12个字符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("通讯地址")]
 | 
			
		||||
    public string Station { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        var str = """
 | 
			
		||||
            查看附带文档或者相关资料,下面列举一下常见的数据标识地址 
 | 
			
		||||
            
 | 
			
		||||
            地址                       说明                    
 | 
			
		||||
            -----------------------------------------
 | 
			
		||||
            02010100    A相电压
 | 
			
		||||
            02020100    A相电流
 | 
			
		||||
            02030000    瞬时总有功功率
 | 
			
		||||
            00000000    (当前)组合有功总电能
 | 
			
		||||
            00010000    (当前)正向有功总电能
 | 
			
		||||
            
 | 
			
		||||
            """;
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + str;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
    {
 | 
			
		||||
        var dataHandleAdapter = new DLT645_2007DataHandleAdapter
 | 
			
		||||
        {
 | 
			
		||||
            EnableFEHead = EnableFEHead
 | 
			
		||||
        };
 | 
			
		||||
        SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, string value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            Password ??= string.Empty;
 | 
			
		||||
            OperCode ??= string.Empty;
 | 
			
		||||
            if (Password.Length < 8)
 | 
			
		||||
                Password = Password.PadLeft(8, '0');
 | 
			
		||||
            if (OperCode.Length < 8)
 | 
			
		||||
                OperCode = OperCode.PadLeft(8, '0');
 | 
			
		||||
            var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
 | 
			
		||||
            string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            Password ??= string.Empty;
 | 
			
		||||
            OperCode ??= string.Empty;
 | 
			
		||||
            if (Password.Length < 8)
 | 
			
		||||
                Password = Password.PadLeft(8, '0');
 | 
			
		||||
            if (OperCode.Length < 8)
 | 
			
		||||
                OperCode = OperCode.PadLeft(8, '0');
 | 
			
		||||
            var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
 | 
			
		||||
            string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #region 其他方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 广播校时
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="dateTime"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}";
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().ToArray(), "999999999999".ByHexStringToBytes());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                SerialSession.Send(commandResult.Content);
 | 
			
		||||
                return OperResult.CreateSuccessResult();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 冻结
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="dateTime"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}";
 | 
			
		||||
            if (Station.IsNullOrEmpty()) Station = string.Empty;
 | 
			
		||||
            if (Station.Length < 12) Station = Station.PadLeft(12, '0');
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读取通信地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    var buffer = result1.Content.SelectMiddle(0, 6).BytesAdd(-0x33);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString());
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult<string>(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<string>(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 修改波特率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="baudRate"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            byte baudRateByte;
 | 
			
		||||
            switch (baudRate)
 | 
			
		||||
            {
 | 
			
		||||
                case 600: baudRateByte = 0x02; break;
 | 
			
		||||
                case 1200: baudRateByte = 0x04; break;
 | 
			
		||||
                case 2400: baudRateByte = 0x08; break;
 | 
			
		||||
                case 4800: baudRateByte = 0x10; break;
 | 
			
		||||
                case 9600: baudRateByte = 0x20; break;
 | 
			
		||||
                case 19200: baudRateByte = 0x40; break;
 | 
			
		||||
                default: return new OperResult<string>($"不支持此波特率:{baudRate}");
 | 
			
		||||
            }
 | 
			
		||||
            if (Station.IsNullOrEmpty()) Station = string.Empty;
 | 
			
		||||
            if (Station.Length < 12) Station = Station.PadLeft(12, '0');
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新通信地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="station"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 修改密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="level"></param>
 | 
			
		||||
    /// <param name="oldPassword"></param>
 | 
			
		||||
    /// <param name="newPassword"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
 | 
			
		||||
            if (Station.IsNullOrEmpty()) Station = string.Empty;
 | 
			
		||||
            if (Station.Length < 12) Station = Station.PadLeft(12, '0');
 | 
			
		||||
            string str = $"04000C{level + 1:D2}";
 | 
			
		||||
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword,
 | 
			
		||||
                str.ByHexStringToBytes().Reverse().ToArray()
 | 
			
		||||
                .SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray())
 | 
			
		||||
                .SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray())
 | 
			
		||||
                , Station.ByHexStringToBytes().Reverse().ToArray());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,109 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using 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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,117 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,191 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// DLT645_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;
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,480 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.DLT645;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// DLT645_2007
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// DLT645_2007
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="tcpClient"></param>
 | 
			
		||||
    public DLT645_2007OverTcp(TcpClient tcpClient) : base(tcpClient)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 增加FE FE FE FE的报文头部
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("前导符报文头")]
 | 
			
		||||
    public bool EnableFEHead { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写入需操作员代码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("操作员代码")]
 | 
			
		||||
    public string OperCode { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写入密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("写入密码")]
 | 
			
		||||
    public string Password { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通讯地址BCD码,一般应该是12个字符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("通讯地址")]
 | 
			
		||||
    public string Station { get; set; }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        var str = """
 | 
			
		||||
            查看附带文档或者相关资料,下面列举一下常见的数据标识地址 
 | 
			
		||||
            
 | 
			
		||||
            地址                       说明                    
 | 
			
		||||
            -----------------------------------------
 | 
			
		||||
            02010100    A相电压
 | 
			
		||||
            02020100    A相电流
 | 
			
		||||
            02030000    瞬时总有功功率
 | 
			
		||||
            00000000    (当前)组合有功总电能
 | 
			
		||||
            00010000    (当前)正向有功总电能
 | 
			
		||||
            
 | 
			
		||||
            """;
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + str;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
    {
 | 
			
		||||
        var dataHandleAdapter = new DLT645_2007DataHandleAdapter
 | 
			
		||||
        {
 | 
			
		||||
            EnableFEHead = EnableFEHead
 | 
			
		||||
        };
 | 
			
		||||
        TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, string value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            Password ??= string.Empty;
 | 
			
		||||
            OperCode ??= string.Empty;
 | 
			
		||||
            if (Password.Length < 8)
 | 
			
		||||
                Password = Password.PadLeft(8, '0');
 | 
			
		||||
            if (OperCode.Length < 8)
 | 
			
		||||
                OperCode = OperCode.PadLeft(8, '0');
 | 
			
		||||
            var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
 | 
			
		||||
            string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            Password ??= string.Empty;
 | 
			
		||||
            OperCode ??= string.Empty;
 | 
			
		||||
            if (Password.Length < 8)
 | 
			
		||||
                Password = Password.PadLeft(8, '0');
 | 
			
		||||
            if (OperCode.Length < 8)
 | 
			
		||||
                OperCode = OperCode.PadLeft(8, '0');
 | 
			
		||||
            var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
 | 
			
		||||
            string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                return (MessageBase)result.RequestInfo;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #region 其他方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 广播校时
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="dateTime"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            Connect(cancellationToken);
 | 
			
		||||
            string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}";
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().Reverse().ToArray(), "999999999999".ByHexStringToBytes());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                TcpClient.Send(commandResult.Content);
 | 
			
		||||
                return OperResult.CreateSuccessResult();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 冻结
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="dateTime"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}";
 | 
			
		||||
            if (Station.IsNullOrEmpty()) Station = string.Empty;
 | 
			
		||||
            if (Station.Length < 12) Station = Station.PadLeft(12, '0');
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().Reverse().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读取通信地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    var buffer = result1.Content.SelectMiddle(0, 6).BytesAdd(-0x33);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString());
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult<string>(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<string>(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 修改波特率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="baudRate"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            byte baudRateByte;
 | 
			
		||||
            switch (baudRate)
 | 
			
		||||
            {
 | 
			
		||||
                case 600: baudRateByte = 0x02; break;
 | 
			
		||||
                case 1200: baudRateByte = 0x04; break;
 | 
			
		||||
                case 2400: baudRateByte = 0x08; break;
 | 
			
		||||
                case 4800: baudRateByte = 0x10; break;
 | 
			
		||||
                case 9600: baudRateByte = 0x20; break;
 | 
			
		||||
                case 19200: baudRateByte = 0x40; break;
 | 
			
		||||
                default: return new OperResult<string>($"不支持此波特率:{baudRate}");
 | 
			
		||||
            }
 | 
			
		||||
            if (Station.IsNullOrEmpty()) Station = string.Empty;
 | 
			
		||||
            if (Station.Length < 12) Station = Station.PadLeft(12, '0');
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新通信地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="station"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 修改密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="level"></param>
 | 
			
		||||
    /// <param name="oldPassword"></param>
 | 
			
		||||
    /// <param name="newPassword"></param>
 | 
			
		||||
    /// <param name="cancellationToken"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            await ConnectAsync(cancellationToken);
 | 
			
		||||
 | 
			
		||||
            if (Station.IsNullOrEmpty()) Station = string.Empty;
 | 
			
		||||
            if (Station.Length < 12) Station = Station.PadLeft(12, '0');
 | 
			
		||||
            string str = $"04000C{level:D2}";
 | 
			
		||||
 | 
			
		||||
            var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword,
 | 
			
		||||
                str.ByHexStringToBytes().Reverse().ToArray()
 | 
			
		||||
                .SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray())
 | 
			
		||||
                .SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray())
 | 
			
		||||
                , Station.ByHexStringToBytes().Reverse().ToArray());
 | 
			
		||||
            if (commandResult.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                var result1 = ((MessageBase)result.RequestInfo);
 | 
			
		||||
                if (result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult(result1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(commandResult);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<string>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Collections.Generic;
 | 
			
		||||
global using System.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;
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,21 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Collections.Generic;
 | 
			
		||||
global using System.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;
 | 
			
		||||
@@ -1,175 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.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",
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,334 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,191 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,85 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using 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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,185 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,184 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,452 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Bool;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SerialSession, Task<OperResult>> WriteData;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 继电器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 开关输入
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 输入寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 保持寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusSerialServer(SerialSession serialSession) : base(serialSession)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 多站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool MulStation { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 默认站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var item in ModbusServer01ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer02ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer03ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer04ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        ModbusServer01ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer02ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer03ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer04ByteBlocks.Clear();
 | 
			
		||||
        base.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EasyLock easyLock = new();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult<byte[]>("地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
            int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    byte[] bytes0 = new byte[len];
 | 
			
		||||
                    ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer01ByteBlock.Read(bytes0);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes0);
 | 
			
		||||
                case 2:
 | 
			
		||||
                    byte[] bytes1 = new byte[len];
 | 
			
		||||
                    ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer02ByteBlock.Read(bytes1);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes1);
 | 
			
		||||
                case 3:
 | 
			
		||||
 | 
			
		||||
                    byte[] bytes3 = new byte[len];
 | 
			
		||||
                    ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer03ByteBlock.Read(bytes3);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes3);
 | 
			
		||||
                case 4:
 | 
			
		||||
                    byte[] bytes4 = new byte[len];
 | 
			
		||||
                    ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer04ByteBlock.Read(bytes4);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes4);
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult<byte[]>("功能码错误");
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Read(address, length));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusSerialServerDataHandleAdapter dataHandleAdapter = new();
 | 
			
		||||
        dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
 | 
			
		||||
        SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(ex);
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult("地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 3:
 | 
			
		||||
                    ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer03ByteBlock.Write(value);
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
                case 4:
 | 
			
		||||
                    ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer04ByteBlock.Write(value);
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult("功能码错误");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return (new OperResult(ex));
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return (new OperResult("地址错误"));
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                    return (OperResult.CreateSuccessResult());
 | 
			
		||||
                case 2:
 | 
			
		||||
                    ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                    return (OperResult.CreateSuccessResult());
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult("功能码错误");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override async Task Received(SerialSession client, ReceivedDataEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var requestInfo = e.RequestInfo;
 | 
			
		||||
            //接收外部报文
 | 
			
		||||
            if (requestInfo is ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
            {
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
                if (!modbusServerMessage.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
 | 
			
		||||
                {
 | 
			
		||||
                    var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
 | 
			
		||||
                    if (data.IsSuccess)
 | 
			
		||||
                    {
 | 
			
		||||
                        var coreData = data.Content;
 | 
			
		||||
                        if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                        {
 | 
			
		||||
                            coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
 | 
			
		||||
                        }
 | 
			
		||||
                        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
 | 
			
		||||
                        SerialSession.Send(sendData);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        WriteError(SerialSession, modbusServerMessage);//返回错误码
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else//写入
 | 
			
		||||
                {
 | 
			
		||||
                    var coreData = modbusServerMessage.Content;
 | 
			
		||||
                    if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入继电器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
                            if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            //写入内存区
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入寄存器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                            if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(SerialSession, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(SerialSession, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Logger.LogError(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        //返回错误码
 | 
			
		||||
        static void WriteError(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
        {
 | 
			
		||||
            var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2)
 | 
			
		||||
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
 | 
			
		||||
            sendData[1] = (byte)(sendData[1] + 128);
 | 
			
		||||
            SerialSession.Send(sendData);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void WriteSuccess03(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
 | 
			
		||||
    {
 | 
			
		||||
        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 6);
 | 
			
		||||
        SerialSession.Send(sendData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Init(ModbusAddress mAddress)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,141 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,40 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,184 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.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;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,249 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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();//如果本插件无法处理当前数据,请将数据转至下一个插件。
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,470 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Bool;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Modbus;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ModbusTcpServer : ReadWriteDevicesTcpServerBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<OperResult>> WriteData;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 继电器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 开关输入
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 输入寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 保持寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ModbusTcpServer(TcpService tcpService) : base(tcpService)
 | 
			
		||||
    {
 | 
			
		||||
        ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
        RegisterByteLength = 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 多站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("多站点")]
 | 
			
		||||
    public bool MulStation { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 默认站点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("默认站点")]
 | 
			
		||||
    public byte Station { get; set; } = 1;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var item in ModbusServer01ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer02ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer03ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        foreach (var item in ModbusServer04ByteBlocks)
 | 
			
		||||
        {
 | 
			
		||||
            item.Value.SafeDispose();
 | 
			
		||||
        }
 | 
			
		||||
        ModbusServer01ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer02ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer03ByteBlocks.Clear();
 | 
			
		||||
        ModbusServer04ByteBlocks.Clear();
 | 
			
		||||
        base.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override string GetAddressDescription()
 | 
			
		||||
    {
 | 
			
		||||
        return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
 | 
			
		||||
    {
 | 
			
		||||
        return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EasyLock easyLock = new();
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult<byte[]>("地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
            int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    byte[] bytes0 = new byte[len];
 | 
			
		||||
                    ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer01ByteBlock.Read(bytes0);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes0);
 | 
			
		||||
                case 2:
 | 
			
		||||
                    byte[] bytes1 = new byte[len];
 | 
			
		||||
                    ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer02ByteBlock.Read(bytes1);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes1);
 | 
			
		||||
                case 3:
 | 
			
		||||
 | 
			
		||||
                    byte[] bytes3 = new byte[len];
 | 
			
		||||
                    ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer03ByteBlock.Read(bytes3);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes3);
 | 
			
		||||
                case 4:
 | 
			
		||||
                    byte[] bytes4 = new byte[len];
 | 
			
		||||
                    ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer04ByteBlock.Read(bytes4);
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes4);
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult<byte[]>("功能码错误");
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Read(address, length));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override void SetDataAdapter(object socketClient)
 | 
			
		||||
    {
 | 
			
		||||
        if (socketClient is SocketClient client)
 | 
			
		||||
        {
 | 
			
		||||
            ModbusTcpServerDataHandleAdapter dataHandleAdapter = new();
 | 
			
		||||
            dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
 | 
			
		||||
            client.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var item in TcpService.GetClients())
 | 
			
		||||
            {
 | 
			
		||||
                ModbusTcpDataHandleAdapter dataHandleAdapter = new()
 | 
			
		||||
                {
 | 
			
		||||
                    CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
 | 
			
		||||
                };
 | 
			
		||||
                item.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult(ex);
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult("地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
            }
 | 
			
		||||
            var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 3:
 | 
			
		||||
                    ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer03ByteBlock.Write(value);
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
                case 4:
 | 
			
		||||
                    ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
 | 
			
		||||
                    ModbusServer04ByteBlock.Write(value);
 | 
			
		||||
                    return OperResult.CreateSuccessResult();
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult("功能码错误");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            easyLock.Wait();
 | 
			
		||||
            ModbusAddress mAddress;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                mAddress = ModbusAddress.ParseFrom(address, Station);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return (new OperResult(ex));
 | 
			
		||||
            }
 | 
			
		||||
            if (MulStation)
 | 
			
		||||
            {
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                if (Station != mAddress.Station)
 | 
			
		||||
                {
 | 
			
		||||
                    return (new OperResult("地址错误"));
 | 
			
		||||
                }
 | 
			
		||||
                Init(mAddress);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
 | 
			
		||||
            var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
 | 
			
		||||
            switch (mAddress.ReadFunction)
 | 
			
		||||
            {
 | 
			
		||||
                case 1:
 | 
			
		||||
                    ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                    return (OperResult.CreateSuccessResult());
 | 
			
		||||
                case 2:
 | 
			
		||||
                    ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
 | 
			
		||||
                    ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
 | 
			
		||||
                    return (OperResult.CreateSuccessResult());
 | 
			
		||||
            }
 | 
			
		||||
            return new OperResult("功能码错误");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        finally
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            easyLock.Release();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
    {
 | 
			
		||||
        return Task.FromResult(Write(address, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override async Task Received(SocketClient client, ReceivedDataEventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var requestInfo = e.RequestInfo;
 | 
			
		||||
            //接收外部报文
 | 
			
		||||
            if (requestInfo is ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
            {
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
                if (!modbusServerMessage.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    return;//无法解析直接返回
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
 | 
			
		||||
                {
 | 
			
		||||
                    var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
 | 
			
		||||
                    if (data.IsSuccess)
 | 
			
		||||
                    {
 | 
			
		||||
                        var coreData = data.Content;
 | 
			
		||||
                        if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                        {
 | 
			
		||||
                            coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
 | 
			
		||||
                        }
 | 
			
		||||
                        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
 | 
			
		||||
                        sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
                        client.Send(sendData);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        WriteError(client, modbusServerMessage);//返回错误码
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else//写入
 | 
			
		||||
                {
 | 
			
		||||
                    var coreData = modbusServerMessage.Content;
 | 
			
		||||
                    if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入继电器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
 | 
			
		||||
                            if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            //写入内存区
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        //写入寄存器
 | 
			
		||||
                        if (WriteData != null)
 | 
			
		||||
                        {
 | 
			
		||||
 | 
			
		||||
                            if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                                if (result.IsSuccess)
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    WriteError(client, modbusServerMessage);
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
 | 
			
		||||
                            if (result.IsSuccess)
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteSuccess03(client, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                WriteError(client, modbusServerMessage);
 | 
			
		||||
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            Logger.LogError(ex, ToString());
 | 
			
		||||
        }
 | 
			
		||||
        //返回错误码
 | 
			
		||||
        static void WriteError(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
        {
 | 
			
		||||
            var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
 | 
			
		||||
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
 | 
			
		||||
            sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
            sendData[7] = (byte)(sendData[7] + 128);
 | 
			
		||||
            client.Send(sendData);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void WriteSuccess03(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
 | 
			
		||||
    {
 | 
			
		||||
        var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12);
 | 
			
		||||
        sendData[5] = (byte)(sendData.Length - 6);
 | 
			
		||||
        client.Send(sendData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Init(ModbusAddress mAddress)
 | 
			
		||||
    {
 | 
			
		||||
        ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
        ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,132 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,187 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,56 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,169 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,611 +0,0 @@
 | 
			
		||||
<?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,57 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.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,49 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,123 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
 | 
			
		||||
//  CSDN博客:https://blog.csdn.net/qq_40374647
 | 
			
		||||
//  哔哩哔哩视频:https://space.bilibili.com/94253567
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/RRQM_Home
 | 
			
		||||
//  Github源代码仓库:https://github.com/RRQM
 | 
			
		||||
//  API首页:http://rrqm_home.gitee.io/touchsocket/
 | 
			
		||||
//  交流QQ群:234762506
 | 
			
		||||
//  感谢您的下载和使用
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
using System.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 字典扩展
 | 
			
		||||
}
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Collections.Generic;
 | 
			
		||||
global using System.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
 | 
			
		||||
@@ -1,457 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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}";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,17 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Collections.Generic;
 | 
			
		||||
global using System.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
@@ -1,83 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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
											
										
									
								
							@@ -1,69 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using 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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
<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>
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,123 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
 | 
			
		||||
//  CSDN博客:https://blog.csdn.net/qq_40374647
 | 
			
		||||
//  哔哩哔哩视频:https://space.bilibili.com/94253567
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/RRQM_Home
 | 
			
		||||
//  Github源代码仓库:https://github.com/RRQM
 | 
			
		||||
//  API首页:http://rrqm_home.gitee.io/touchsocket/
 | 
			
		||||
//  交流QQ群:234762506
 | 
			
		||||
//  感谢您的下载和使用
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.OPCUA;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// DictionaryExtension
 | 
			
		||||
/// </summary>
 | 
			
		||||
internal static class DictionaryExtension
 | 
			
		||||
{
 | 
			
		||||
    #region 字典扩展
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 移除满足条件的项目。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="TKey"></typeparam>
 | 
			
		||||
    /// <typeparam name="TValue"></typeparam>
 | 
			
		||||
    /// <param name="pairs"></param>
 | 
			
		||||
    /// <param name="func"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static int RemoveWhen<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> pairs, Func<KeyValuePair<TKey, TValue>, bool> func)
 | 
			
		||||
    {
 | 
			
		||||
        var list = new List<TKey>();
 | 
			
		||||
        foreach (var item in pairs)
 | 
			
		||||
        {
 | 
			
		||||
            if (func?.Invoke(item) == true)
 | 
			
		||||
            {
 | 
			
		||||
                list.Add(item.Key);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var count = 0;
 | 
			
		||||
        foreach (var item in list)
 | 
			
		||||
        {
 | 
			
		||||
            if (pairs.TryRemove(item, out _))
 | 
			
		||||
            {
 | 
			
		||||
                count++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return count;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if NET45_OR_GREATER || NETSTANDARD2_0_OR_GREATER
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 尝试添加
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="Tkey"></typeparam>
 | 
			
		||||
    /// <typeparam name="TValue"></typeparam>
 | 
			
		||||
    /// <param name="dictionary"></param>
 | 
			
		||||
    /// <param name="tkey"></param>
 | 
			
		||||
    /// <param name="value"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static bool TryAdd<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
 | 
			
		||||
    {
 | 
			
		||||
        if (dictionary.ContainsKey(tkey))
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        dictionary.Add(tkey, value);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 尝试添加
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="Tkey"></typeparam>
 | 
			
		||||
    /// <typeparam name="TValue"></typeparam>
 | 
			
		||||
    /// <param name="dictionary"></param>
 | 
			
		||||
    /// <param name="tkey"></param>
 | 
			
		||||
    /// <param name="value"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static void AddOrUpdate<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
 | 
			
		||||
    {
 | 
			
		||||
        if (dictionary.ContainsKey(tkey))
 | 
			
		||||
        {
 | 
			
		||||
            dictionary[tkey] = value;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            dictionary.Add(tkey, value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取值。如果键不存在,则返回默认值。
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="Tkey"></typeparam>
 | 
			
		||||
    /// <typeparam name="TValue"></typeparam>
 | 
			
		||||
    /// <param name="dictionary"></param>
 | 
			
		||||
    /// <param name="tkey"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    internal static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey)
 | 
			
		||||
    {
 | 
			
		||||
        return dictionary.TryGetValue(tkey, out var value) ? value : default;
 | 
			
		||||
    }
 | 
			
		||||
    #endregion 字典扩展
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,20 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Collections.Generic;
 | 
			
		||||
global using System.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
global using ThingsGateway.Foundation.Core;
 | 
			
		||||
global using ThingsGateway.Foundation.Sockets;
 | 
			
		||||
@@ -1,293 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 区域
 | 
			
		||||
/// </summary>
 | 
			
		||||
public enum S7Area : byte
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    PE = 0x81,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    PA = 0x82,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    MK = 0x83,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    DB = 0x84,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    CT = 0x1C,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    TM = 0x1D,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    AI = 0X06,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    AQ = 0x07,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 西门子PLC地址数据信息
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SiemensAddress : DeviceAddressBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// bit位偏移
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte BitCode { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 数据块代码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public byte DataCode { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// DB块数据信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public ushort DbBlock { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// IsWString,默认是true,如果不是WString,需要填写W=false;
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool IsWString { get; set; } = true;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取起始地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"></param>
 | 
			
		||||
    /// <param name="isCounterOrTimer"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static int GetAddressStart(string address, bool isCounterOrTimer = false)
 | 
			
		||||
    {
 | 
			
		||||
        if (address.IndexOf('.') < 0)
 | 
			
		||||
        {
 | 
			
		||||
            return isCounterOrTimer ? Convert.ToInt32(address) : Convert.ToInt32(address) * 8;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        string[] strArray = address.Split('.');
 | 
			
		||||
        return Convert.ToInt32(strArray[0]) * 8;
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取bit
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static byte GetBitCode(string address)
 | 
			
		||||
    {
 | 
			
		||||
        if (address.IndexOf('.') < 0)
 | 
			
		||||
        {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        string[] strArray = address.Split('.');
 | 
			
		||||
        return Convert.ToByte(strArray[1]);
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 解析地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static SiemensAddress ParseFrom(string address, int len)
 | 
			
		||||
    {
 | 
			
		||||
        SiemensAddress s7AddressData = ParseFrom(address);
 | 
			
		||||
        s7AddressData.Length = len;
 | 
			
		||||
        return s7AddressData;
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 解析地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static SiemensAddress ParseFrom(string address)
 | 
			
		||||
    {
 | 
			
		||||
        SiemensAddress s7AddressData = new();
 | 
			
		||||
        address = address.ToUpper();
 | 
			
		||||
        string[] strArr = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
        for (int index = 0; index < strArr.Length; ++index)
 | 
			
		||||
        {
 | 
			
		||||
            if (strArr[index].StartsWith("W="))
 | 
			
		||||
            {
 | 
			
		||||
                s7AddressData.IsWString = strArr[index].Substring(2).ToBoolean(true);
 | 
			
		||||
            }
 | 
			
		||||
            else if (!strArr[index].Contains("="))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                s7AddressData.DbBlock = 0;
 | 
			
		||||
 | 
			
		||||
                if (strArr[index].StartsWith("AI"))
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.AI;
 | 
			
		||||
                    if (strArr[index].StartsWith("AIX") || strArr[index].StartsWith("AIB") || strArr[index].StartsWith("AIW") || strArr[index].StartsWith("AID"))
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(3)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(3));
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(2)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(2));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArr[index].StartsWith("AQ"))
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.AQ;
 | 
			
		||||
                    if (strArr[index].StartsWith("AQX") || strArr[index].StartsWith("AQB") || strArr[index].StartsWith("AQW") || strArr[index].StartsWith("AQD"))
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(3)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(3));
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(2)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(2));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArr[index][0] == 'I')
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.PE;
 | 
			
		||||
                    if (strArr[index].StartsWith("IX") || strArr[index].StartsWith("IB") || strArr[index].StartsWith("IW") || strArr[index].StartsWith("ID"))
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(2)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(2));
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(1)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(1));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArr[index][0] == 'Q')
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.PA;
 | 
			
		||||
                    if (strArr[index].StartsWith("QX") || strArr[index].StartsWith("QB") || strArr[index].StartsWith("QW") || strArr[index].StartsWith("QD"))
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(2)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(2));
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(1)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(1));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArr[index][0] == 'M')
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.MK;
 | 
			
		||||
                    if (strArr[index].StartsWith("MX") || strArr[index].StartsWith("MB") || strArr[index].StartsWith("MW") || strArr[index].StartsWith("MD"))
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(2)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(2));
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(1)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(1));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArr[index][0] == 'D' || strArr[index].Substring(0, 2) == "DB")
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.DB;
 | 
			
		||||
                    string[] strArray = strArr[index].Split('.');
 | 
			
		||||
                    s7AddressData.DbBlock = strArray[index][1] != 'B' ? Convert.ToUInt16(strArray[0].Substring(1)) : Convert.ToUInt16(strArray[0].Substring(2));
 | 
			
		||||
                    string address1 = strArr[index].Substring(strArr[index].IndexOf('.') + 1);
 | 
			
		||||
                    if (address1.StartsWith("DBX") || address1.StartsWith("DBB") || address1.StartsWith("DBW") || address1.StartsWith("DBD"))
 | 
			
		||||
                    {
 | 
			
		||||
                        address1 = address1.Substring(3);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    s7AddressData.Address = GetAddressStart(address1).ToString();
 | 
			
		||||
                    s7AddressData.BitCode = GetBitCode(address1);
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArr[index][0] == 'T')
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.TM;
 | 
			
		||||
                    s7AddressData.Address = GetAddressStart(strArr[index].Substring(1), true).ToString();
 | 
			
		||||
                    s7AddressData.BitCode = GetBitCode(strArr[index].Substring(1));
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArr[index][0] == 'C')
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.CT;
 | 
			
		||||
                    s7AddressData.Address = GetAddressStart(strArr[index].Substring(1), true).ToString();
 | 
			
		||||
                    s7AddressData.BitCode = GetBitCode(strArr[index].Substring(1));
 | 
			
		||||
                }
 | 
			
		||||
                else if (strArr[index][0] == 'V')
 | 
			
		||||
                {
 | 
			
		||||
                    s7AddressData.DataCode = (byte)S7Area.DB;
 | 
			
		||||
                    s7AddressData.DbBlock = 1;
 | 
			
		||||
                    if (strArr[index].StartsWith("VB") || strArr[index].StartsWith("VW") || strArr[index].StartsWith("VD") || strArr[index].StartsWith("VX"))
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(2)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(2));
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        s7AddressData.Address = GetAddressStart(strArr[index].Substring(1)).ToString();
 | 
			
		||||
                        s7AddressData.BitCode = GetBitCode(strArr[index].Substring(1));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return s7AddressData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Length
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Length { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc cref="DeviceAddressBase.Address"/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int AddressStart => Address.ToInt();
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        if (DataCode == (byte)S7Area.TM)
 | 
			
		||||
        {
 | 
			
		||||
            return $"T{Address.ToString()}{(IsWString ? ";W=true;" : ";W=false;")}";
 | 
			
		||||
        }
 | 
			
		||||
        if (DataCode == (byte)S7Area.CT)
 | 
			
		||||
        {
 | 
			
		||||
            return $"C{Address.ToString()}{(IsWString ? ";W=true;" : ";W=false;")}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (DataCode == (byte)S7Area.AI)
 | 
			
		||||
        {
 | 
			
		||||
            return $"AI{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (DataCode == (byte)S7Area.AQ)
 | 
			
		||||
        {
 | 
			
		||||
            return $"AQ{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (DataCode == (byte)S7Area.PE)
 | 
			
		||||
        {
 | 
			
		||||
            return $"I{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (DataCode == (byte)S7Area.PA)
 | 
			
		||||
        {
 | 
			
		||||
            return $"Q{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (DataCode == (byte)S7Area.MK)
 | 
			
		||||
        {
 | 
			
		||||
            return $"M{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DataCode == (byte)S7Area.DB ? $"DB{DbBlock.ToString()}.{GetStringAddress(AddressStart)}{(IsWString ? ";W=true;" : ";W=false;")}" : Address.ToString() + (IsWString ? ";W=true;" : ";W=false;");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static string GetStringAddress(int addressStart)
 | 
			
		||||
    {
 | 
			
		||||
        return addressStart % 8 == 0 ? (addressStart / 8).ToString() : $"{addressStart / 8}.{addressStart % 8}";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,164 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// https://github.com/S7NetPlus/s7netplus/blob/develop/S7.Net/Types/DateTime.cs
 | 
			
		||||
/// Contains the methods to convert between <see cref="T:System.DateTime"/> and S7 representation of datetime values.
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class DateTime
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The maximum <see cref="T:System.DateTime"/> value supported by the specification.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static readonly System.DateTime SpecMaximumDateTime = new(2089, 12, 31, 23, 59, 59, 999);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The minimum <see cref="T:System.DateTime"/> value supported by the specification.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static readonly System.DateTime SpecMinimumDateTime = new(1990, 1, 1);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Parses a <see cref="T:System.DateTime"/> value from bytes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="bytes">Input bytes read from PLC.</param>
 | 
			
		||||
    /// <returns>A <see cref="T:System.DateTime"/> object representing the value read from PLC.</returns>
 | 
			
		||||
    /// <exception cref="ArgumentOutOfRangeException">Thrown when the length of
 | 
			
		||||
    ///   <paramref name="bytes"/> is not 8 or any value in <paramref name="bytes"/>
 | 
			
		||||
    ///   is outside the valid range of values.</exception>
 | 
			
		||||
    public static System.DateTime FromByteArray(byte[] bytes)
 | 
			
		||||
    {
 | 
			
		||||
        return FromByteArrayImpl(bytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Parses an array of <see cref="T:System.DateTime"/> values from bytes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="bytes">Input bytes read from PLC.</param>
 | 
			
		||||
    /// <returns>An array of <see cref="T:System.DateTime"/> objects representing the values read from PLC.</returns>
 | 
			
		||||
    /// <exception cref="ArgumentOutOfRangeException">Thrown when the length of
 | 
			
		||||
    ///   <paramref name="bytes"/> is not a multiple of 8 or any value in
 | 
			
		||||
    ///   <paramref name="bytes"/> is outside the valid range of values.</exception>
 | 
			
		||||
    public static System.DateTime[] ToArray(byte[] bytes)
 | 
			
		||||
    {
 | 
			
		||||
        if (bytes.Length % 8 != 0)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length,
 | 
			
		||||
                $"Parsing an array of DateTime requires a multiple of 8 bytes of input data, input data is '{bytes.Length}' long.");
 | 
			
		||||
 | 
			
		||||
        var cnt = bytes.Length / 8;
 | 
			
		||||
        var result = new System.DateTime[bytes.Length / 8];
 | 
			
		||||
 | 
			
		||||
        for (var i = 0; i < cnt; i++)
 | 
			
		||||
            result[i] = FromByteArrayImpl(new ArraySegment<byte>(bytes, i * 8, 8));
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts a <see cref="T:System.DateTime"/> value to a byte array.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="dateTime">The DateTime value to convert.</param>
 | 
			
		||||
    /// <returns>A byte array containing the S7 date time representation of <paramref name="dateTime"/>.</returns>
 | 
			
		||||
    /// <exception cref="ArgumentOutOfRangeException">Thrown when the value of
 | 
			
		||||
    ///   <paramref name="dateTime"/> is before <see cref="P:SpecMinimumDateTime"/>
 | 
			
		||||
    ///   or after <see cref="P:SpecMaximumDateTime"/>.</exception>
 | 
			
		||||
    public static byte[] ToByteArray(System.DateTime dateTime)
 | 
			
		||||
    {
 | 
			
		||||
        byte EncodeBcd(int value)
 | 
			
		||||
        {
 | 
			
		||||
            return (byte)((value / 10 << 4) | value % 10);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (dateTime < SpecMinimumDateTime)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime,
 | 
			
		||||
                $"Date time '{dateTime}' is before the minimum '{SpecMinimumDateTime}' supported in S7 date time representation.");
 | 
			
		||||
 | 
			
		||||
        if (dateTime > SpecMaximumDateTime)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime,
 | 
			
		||||
                $"Date time '{dateTime}' is after the maximum '{SpecMaximumDateTime}' supported in S7 date time representation.");
 | 
			
		||||
 | 
			
		||||
        byte MapYear(int year) => (byte)(year < 2000 ? year - 1900 : year - 2000);
 | 
			
		||||
 | 
			
		||||
        int DayOfWeekToInt(DayOfWeek dayOfWeek) => (int)dayOfWeek + 1;
 | 
			
		||||
 | 
			
		||||
        return new[]
 | 
			
		||||
        {
 | 
			
		||||
            EncodeBcd(MapYear(dateTime.Year)),
 | 
			
		||||
            EncodeBcd(dateTime.Month),
 | 
			
		||||
            EncodeBcd(dateTime.Day),
 | 
			
		||||
            EncodeBcd(dateTime.Hour),
 | 
			
		||||
            EncodeBcd(dateTime.Minute),
 | 
			
		||||
            EncodeBcd(dateTime.Second),
 | 
			
		||||
            EncodeBcd(dateTime.Millisecond / 10),
 | 
			
		||||
            (byte) (dateTime.Millisecond % 10 << 4 | DayOfWeekToInt(dateTime.DayOfWeek))
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Converts an array of <see cref="T:System.DateTime"/> values to a byte array.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="dateTimes">The DateTime values to convert.</param>
 | 
			
		||||
    /// <returns>A byte array containing the S7 date time representations of <paramref name="dateTimes"/>.</returns>
 | 
			
		||||
    /// <exception cref="ArgumentOutOfRangeException">Thrown when any value of
 | 
			
		||||
    ///   <paramref name="dateTimes"/> is before <see cref="P:SpecMinimumDateTime"/>
 | 
			
		||||
    ///   or after <see cref="P:SpecMaximumDateTime"/>.</exception>
 | 
			
		||||
    public static byte[] ToByteArray(System.DateTime[] dateTimes)
 | 
			
		||||
    {
 | 
			
		||||
        var bytes = new List<byte>(dateTimes.Length * 8);
 | 
			
		||||
        foreach (var dateTime in dateTimes) bytes.AddRange(ToByteArray(dateTime));
 | 
			
		||||
 | 
			
		||||
        return bytes.ToArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static System.DateTime FromByteArrayImpl(IList<byte> bytes)
 | 
			
		||||
    {
 | 
			
		||||
        if (bytes.Count != 8)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Count,
 | 
			
		||||
                $"Parsing a DateTime requires exactly 8 bytes of input data, input data is {bytes.Count} bytes long.");
 | 
			
		||||
 | 
			
		||||
        int DecodeBcd(byte input) => 10 * (input >> 4) + (input & 0b00001111);
 | 
			
		||||
 | 
			
		||||
        int ByteToYear(byte bcdYear)
 | 
			
		||||
        {
 | 
			
		||||
            var input = DecodeBcd(bcdYear);
 | 
			
		||||
            if (input < 90) return input + 2000;
 | 
			
		||||
            if (input < 100) return input + 1900;
 | 
			
		||||
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(bcdYear), bcdYear,
 | 
			
		||||
                $"Value '{input}' is higher than the maximum '99' of S7 date and time representation.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int AssertRangeInclusive(int input, byte min, byte max, string field)
 | 
			
		||||
        {
 | 
			
		||||
            if (input < min)
 | 
			
		||||
                throw new ArgumentOutOfRangeException(nameof(input), input,
 | 
			
		||||
                    $"Value '{input}' is lower than the minimum '{min}' allowed for {field}.");
 | 
			
		||||
            if (input > max)
 | 
			
		||||
                throw new ArgumentOutOfRangeException(nameof(input), input,
 | 
			
		||||
                    $"Value '{input}' is higher than the maximum '{max}' allowed for {field}.");
 | 
			
		||||
 | 
			
		||||
            return input;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var year = ByteToYear(bytes[0]);
 | 
			
		||||
        var month = AssertRangeInclusive(DecodeBcd(bytes[1]), 1, 12, "month");
 | 
			
		||||
        var day = AssertRangeInclusive(DecodeBcd(bytes[2]), 1, 31, "day of month");
 | 
			
		||||
        var hour = AssertRangeInclusive(DecodeBcd(bytes[3]), 0, 23, "hour");
 | 
			
		||||
        var minute = AssertRangeInclusive(DecodeBcd(bytes[4]), 0, 59, "minute");
 | 
			
		||||
        var second = AssertRangeInclusive(DecodeBcd(bytes[5]), 0, 59, "second");
 | 
			
		||||
        var hsec = AssertRangeInclusive(DecodeBcd(bytes[6]), 0, 99, "first two millisecond digits");
 | 
			
		||||
        var msec = AssertRangeInclusive(bytes[7] >> 4, 0, 9, "third millisecond digit");
 | 
			
		||||
        _ = AssertRangeInclusive(bytes[7] & 0b00001111, 1, 7, "day of week");
 | 
			
		||||
 | 
			
		||||
        return new System.DateTime(year, month, day, hour, minute, second, hsec * 10 + msec);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,233 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
 | 
			
		||||
internal static class PackHelper
 | 
			
		||||
{
 | 
			
		||||
    public static List<T> LoadSourceRead<T, T2>(SiemensS7PLC siemensS7Net, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
 | 
			
		||||
    {
 | 
			
		||||
        var byteConverter = siemensS7Net.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 = siemensS7Net.GetBitOffset(item.VariableAddress);
 | 
			
		||||
        }
 | 
			
		||||
        //按读取间隔分组
 | 
			
		||||
        var tags = deviceVariables.GroupBy(it => it.IntervalTime);
 | 
			
		||||
        foreach (var item in tags)
 | 
			
		||||
        {
 | 
			
		||||
            Dictionary<SiemensAddress, T2> map = item.ToDictionary(it =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    var s7Address = SiemensAddress.ParseFrom(it.VariableAddress);
 | 
			
		||||
                    var lastLen = it.DataTypeEnum.GetByteLength();
 | 
			
		||||
                    if (lastLen <= 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        switch (it.DataTypeEnum)
 | 
			
		||||
                        {
 | 
			
		||||
                            case DataTypeEnum.String:
 | 
			
		||||
                                if (it.ThingsGatewayBitConverter.Length == null)
 | 
			
		||||
                                {
 | 
			
		||||
                                    throw new("数据类型为字符串时,必须指定字符串长度,才能进行打包");
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    if (siemensS7Net.CurrentPlc == SiemensEnum.S200Smart)
 | 
			
		||||
                                    {
 | 
			
		||||
                                        if (s7Address.IsWString)
 | 
			
		||||
                                        {
 | 
			
		||||
                                            //字符串在S200Smart中,第一个字节不属于实际内容
 | 
			
		||||
                                            it.Index += 1;
 | 
			
		||||
                                            lastLen = it.ThingsGatewayBitConverter.Length.Value + 1;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        else
 | 
			
		||||
                                        {
 | 
			
		||||
                                            lastLen = it.ThingsGatewayBitConverter.Length.Value;
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                    else
 | 
			
		||||
                                    {
 | 
			
		||||
                                        if (s7Address.IsWString)
 | 
			
		||||
                                        {
 | 
			
		||||
                                            //字符串在S7中,前两个字节不属于实际内容
 | 
			
		||||
                                            it.Index += 2;
 | 
			
		||||
                                            lastLen = it.ThingsGatewayBitConverter.Length.Value + 2;
 | 
			
		||||
                                        }
 | 
			
		||||
                                        else
 | 
			
		||||
                                        {
 | 
			
		||||
                                            lastLen = it.ThingsGatewayBitConverter.Length.Value;
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                                break;
 | 
			
		||||
                            default:
 | 
			
		||||
                                lastLen = 1;
 | 
			
		||||
                                break;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if ((s7Address.DataCode == (byte)S7WordLength.Counter || s7Address.DataCode == (byte)S7WordLength.Timer) && lastLen == 1)
 | 
			
		||||
                    {
 | 
			
		||||
                        lastLen = 2;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (it.ThingsGatewayBitConverter.Length != null && it.DataTypeEnum != DataTypeEnum.String)
 | 
			
		||||
                    {
 | 
			
		||||
                        lastLen *= it.ThingsGatewayBitConverter.Length.Value;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    //这里把每个变量的应读取长度都写入变量地址实体中
 | 
			
		||||
                    s7Address.Length = lastLen;
 | 
			
		||||
                    return s7Address;
 | 
			
		||||
                }
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    siemensS7Net.Logger.LogWarning(ex, "解析地址错误");
 | 
			
		||||
                }
 | 
			
		||||
                return null;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            //获取变量的地址
 | 
			
		||||
            var modbusAddressList = map.Keys.Where(a => a != null).ToList();
 | 
			
		||||
 | 
			
		||||
            //获取S7数据代码
 | 
			
		||||
            var functionCodes = modbusAddressList.Select(t => t.DataCode).Distinct();
 | 
			
		||||
            foreach (var functionCode in functionCodes)
 | 
			
		||||
            {
 | 
			
		||||
                //相同数据代码的变量集合
 | 
			
		||||
                var modbusAddressSameFunList = modbusAddressList
 | 
			
		||||
                    .Where(t => t.DataCode == functionCode);
 | 
			
		||||
                //相同数据代码的变量集合中的不同DB块
 | 
			
		||||
                var stationNumbers = modbusAddressSameFunList
 | 
			
		||||
                    .Select(t => t.DbBlock).Distinct();
 | 
			
		||||
                foreach (var stationNumber in stationNumbers)
 | 
			
		||||
                {
 | 
			
		||||
                    var addressList = modbusAddressSameFunList.Where(t => t.DbBlock == stationNumber)
 | 
			
		||||
                        .ToDictionary(t => t, t => map[t]);
 | 
			
		||||
                    //循环对数据代码,站号都一样的变量进行分配连读包
 | 
			
		||||
                    var tempResult = LoadSourceRead<T, T2>(addressList, functionCode, item.Key, siemensS7Net);
 | 
			
		||||
                    //添加到总连读包
 | 
			
		||||
                    result.AddRange(tempResult);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static List<T> LoadSourceRead<T, T2>(Dictionary<SiemensAddress, T2> addressList, int functionCode, int intervalTime, SiemensS7PLC siemensS7Net) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        List<T> sourceReads = new();
 | 
			
		||||
        //实际地址与长度排序
 | 
			
		||||
        var addresss = addressList.Keys.OrderBy(it =>
 | 
			
		||||
        {
 | 
			
		||||
            int address = 0;
 | 
			
		||||
            if (it.DataCode == (byte)S7WordLength.Counter || it.DataCode == (byte)S7WordLength.Timer)
 | 
			
		||||
            {
 | 
			
		||||
                address = it.AddressStart * 2;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                address = it.AddressStart / 8;
 | 
			
		||||
            }
 | 
			
		||||
            return address + it.Length;
 | 
			
		||||
        }).ToList();
 | 
			
		||||
        var minAddress = addresss.First().AddressStart;
 | 
			
		||||
        var maxAddress = addresss.Last().AddressStart;
 | 
			
		||||
        while (maxAddress >= minAddress)
 | 
			
		||||
        {
 | 
			
		||||
            //这里直接避免末位变量长度超限的情况,pdu长度-8
 | 
			
		||||
            int readLength = siemensS7Net.PDULength == 0 ? 200 : siemensS7Net.PDULength - 8;
 | 
			
		||||
            List<SiemensAddress> tempAddress = new();
 | 
			
		||||
            if (functionCode == (byte)S7WordLength.Counter || functionCode == (byte)S7WordLength.Timer)
 | 
			
		||||
            {
 | 
			
		||||
                tempAddress = addresss.Where(t => t.AddressStart >= minAddress && ((t.AddressStart) + t.Length) <= ((minAddress) + readLength)).ToList();
 | 
			
		||||
                while ((tempAddress.Last().AddressStart * 2) + tempAddress.Last().Length - (tempAddress.First().AddressStart * 2) > readLength)
 | 
			
		||||
                {
 | 
			
		||||
                    tempAddress.Remove(tempAddress.Last());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                tempAddress = addresss.Where(t => t.AddressStart >= minAddress && ((t.AddressStart) + t.Length) <= ((minAddress) + readLength)).ToList();
 | 
			
		||||
                while ((tempAddress.Last().AddressStart / 8) + tempAddress.Last().Length - (tempAddress.First().AddressStart / 8) > readLength)
 | 
			
		||||
                {
 | 
			
		||||
                    tempAddress.Remove(tempAddress.Last());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //读取寄存器长度
 | 
			
		||||
            int lastAddress = 0;
 | 
			
		||||
            int firstAddress = 0;
 | 
			
		||||
            if (functionCode == (byte)S7WordLength.Counter || functionCode == (byte)S7WordLength.Timer)
 | 
			
		||||
            {
 | 
			
		||||
                lastAddress = tempAddress.Last().AddressStart * 2;
 | 
			
		||||
                firstAddress = tempAddress.First().AddressStart * 2;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                lastAddress = tempAddress.Last().AddressStart / 8;
 | 
			
		||||
                firstAddress = tempAddress.First().AddressStart / 8;
 | 
			
		||||
            }
 | 
			
		||||
            var sourceLen = lastAddress + tempAddress.Last().Length - firstAddress;
 | 
			
		||||
            T sourceRead = new()
 | 
			
		||||
            {
 | 
			
		||||
                TimerTick = new(intervalTime),
 | 
			
		||||
                VariableAddress = tempAddress.OrderBy(it => it.AddressStart).First().ToString(),
 | 
			
		||||
                Length = sourceLen
 | 
			
		||||
            };
 | 
			
		||||
            foreach (var item in tempAddress)
 | 
			
		||||
            {
 | 
			
		||||
                var readNode = addressList[item];
 | 
			
		||||
                if (functionCode == (byte)S7WordLength.Counter || functionCode == (byte)S7WordLength.Timer)
 | 
			
		||||
                {
 | 
			
		||||
                    if (readNode.DataTypeEnum == DataTypeEnum.Boolean)
 | 
			
		||||
                    {
 | 
			
		||||
                        readNode.Index = (((item.AddressStart * 2) - (tempAddress.First().AddressStart * 2)) * 8) + readNode.Index;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        readNode.Index = (item.AddressStart * 2) - (tempAddress.First().AddressStart * 2) + readNode.Index;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    if (readNode.DataTypeEnum == DataTypeEnum.Boolean)
 | 
			
		||||
                    {
 | 
			
		||||
                        readNode.Index = (((item.AddressStart / 8) - (tempAddress.First().AddressStart / 8)) * 8) + readNode.Index;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        readNode.Index = (item.AddressStart / 8) - (tempAddress.First().AddressStart / 8) + readNode.Index;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                sourceRead.DeviceVariableRunTimes.Add(readNode);
 | 
			
		||||
                addresss.Remove(item);
 | 
			
		||||
            }
 | 
			
		||||
            sourceReads.Add(sourceRead);
 | 
			
		||||
            if (addresss.Count > 0)
 | 
			
		||||
                minAddress = addresss.First().AddressStart;
 | 
			
		||||
            else
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        return sourceReads;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,383 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
 | 
			
		||||
internal partial class SiemensHelper
 | 
			
		||||
{
 | 
			
		||||
    #region 验证
 | 
			
		||||
    //internal static OperResult<byte[]> AnalysisReadBit(byte[] content)
 | 
			
		||||
    //{
 | 
			
		||||
    //    int length = 1;
 | 
			
		||||
    //    if (content.Length < 21 || content[20] != 1)
 | 
			
		||||
    //        return new OperResult<byte[]>("数据块长度校验失败");
 | 
			
		||||
    //    byte[] numArray = new byte[length];
 | 
			
		||||
    //    if (content[21] == byte.MaxValue && content[22] == 3)//Bit:3;Byte:4;Counter或者Timer:9
 | 
			
		||||
    //    {
 | 
			
		||||
    //        numArray[0] = content[25];//+4
 | 
			
		||||
    //    }
 | 
			
		||||
    //    else
 | 
			
		||||
    //    {
 | 
			
		||||
    //        return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21]));
 | 
			
		||||
    //    }
 | 
			
		||||
    //    return OperResult.CreateSuccessResult<byte[]>(numArray);
 | 
			
		||||
    //}
 | 
			
		||||
 | 
			
		||||
    internal static OperResult<byte[]> AnalysisReadByte(byte[] sends, byte[] content)
 | 
			
		||||
    {
 | 
			
		||||
        int length = 0;
 | 
			
		||||
        int itemLen = (sends.Length - 19) / 12;
 | 
			
		||||
 | 
			
		||||
        for (int index = 0; index < itemLen; index++)
 | 
			
		||||
        {
 | 
			
		||||
            if (sends[22 + (index * 12)] >= (byte)S7WordLength.Word)
 | 
			
		||||
            {
 | 
			
		||||
                length += ((sends[23 + (index * 12)] * 256) + sends[24 + (index * 12)]) * 2;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                length += (sends[23 + (index * 12)] * 256) + sends[24 + (index * 12)];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (content.Length < 21 || content[20] != itemLen)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>("数据块长度校验失败");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        byte[] dataArray = new byte[length];
 | 
			
		||||
        int index1 = 0;
 | 
			
		||||
        int dataIndex = 0;
 | 
			
		||||
        for (int index2 = 21; index2 < content.Length; index2++)
 | 
			
		||||
        {
 | 
			
		||||
            if (index2 + 1 < content.Length)
 | 
			
		||||
            {
 | 
			
		||||
                int s7len;
 | 
			
		||||
                if (sends[22 + (index1 * 12)] >= (byte)S7WordLength.Word)
 | 
			
		||||
                {
 | 
			
		||||
                    s7len = ((sends[23 + (index1 * 12)] * 256) + sends[24 + (index1 * 12)]) * 2;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    s7len = (sends[23 + (index1 * 12)] * 256) + sends[24 + (index1 * 12)];
 | 
			
		||||
                }
 | 
			
		||||
                if (content[index2] == byte.MaxValue && content[index2 + 1] == 4)//Bit:3;Byte:4;Counter或者Timer:9
 | 
			
		||||
                {
 | 
			
		||||
                    Array.Copy(content, index2 + 4, dataArray, dataIndex, s7len);
 | 
			
		||||
                    index2 += s7len + 3;
 | 
			
		||||
                    dataIndex += s7len;
 | 
			
		||||
                    index1++;
 | 
			
		||||
                }
 | 
			
		||||
                else if (content[index2] == byte.MaxValue && content[index2 + 1] == 9)//Counter或者Timer:9
 | 
			
		||||
                {
 | 
			
		||||
                    int num = (content[index2 + 2] * 256) + content[index2 + 3];
 | 
			
		||||
                    if (num % 3 == 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        for (int index3 = 0; index3 < num / 3; index3++)
 | 
			
		||||
                        {
 | 
			
		||||
                            Array.Copy(content, index2 + 5 + (3 * index3), dataArray, dataIndex, 2);
 | 
			
		||||
                            dataIndex += 2;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        for (int index4 = 0; index4 < num / 5; index4++)
 | 
			
		||||
                        {
 | 
			
		||||
                            Array.Copy(content, index2 + 7 + (5 * index4), dataArray, dataIndex, 2);
 | 
			
		||||
                            dataIndex += 2;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    index2 += num + 4;
 | 
			
		||||
                    index1++;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new OperResult<byte[]>((int)content[index2] + GetCpuError(content[index2]));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return OperResult.CreateSuccessResult(dataArray);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static OperResult<byte[]> AnalysisWrite(byte[] content)
 | 
			
		||||
    {
 | 
			
		||||
        if (content.Length < 22)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>() { Message = "未知错误" };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        byte err = content[21];
 | 
			
		||||
        if (err != byte.MaxValue)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21]));
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return OperResult.CreateSuccessResult(content);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    private static string GetCpuError(ushort Error)
 | 
			
		||||
    {
 | 
			
		||||
        return Error switch
 | 
			
		||||
        {
 | 
			
		||||
            0x05 => "地址超限",
 | 
			
		||||
            0x06 => "返回长度无效",
 | 
			
		||||
            0x07 => "数据大小不匹配",
 | 
			
		||||
            0x0a or 0xd209 => "数据块不存在",
 | 
			
		||||
            0x8500 => "超出PDU大小",
 | 
			
		||||
            0xdc01 => "无效的值",
 | 
			
		||||
            0x8104 => "功能不可用",
 | 
			
		||||
            0xd241 => "需要密码",
 | 
			
		||||
            0xd602 => "无效密码",
 | 
			
		||||
            0xd604 or 0xd605 => "没有设置密码或已清除",
 | 
			
		||||
            _ => "未知错误",
 | 
			
		||||
        };
 | 
			
		||||
        ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 获取报文
 | 
			
		||||
    internal static OperResult<byte[]> GetReadCommand(SiemensAddress[] siemensAddress)
 | 
			
		||||
    {
 | 
			
		||||
        if (siemensAddress == null)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<byte[]>("地址为null");
 | 
			
		||||
        }
 | 
			
		||||
        int len = siemensAddress.Length <= 19 ? siemensAddress.Length : throw new Exception("读取数量大于19!");
 | 
			
		||||
        int telegramLen = len * 12 + 19;
 | 
			
		||||
        int parameterLen = len * 12 + 2;
 | 
			
		||||
 | 
			
		||||
        byte[] numArray = new byte[telegramLen];
 | 
			
		||||
 | 
			
		||||
        Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
 | 
			
		||||
        numArray[2] = (byte)(telegramLen / 256);
 | 
			
		||||
        numArray[3] = (byte)(telegramLen % 256);
 | 
			
		||||
        numArray[13] = (byte)(parameterLen / 256);
 | 
			
		||||
        numArray[14] = (byte)(parameterLen % 256);
 | 
			
		||||
        numArray[18] = (byte)len;
 | 
			
		||||
 | 
			
		||||
        for (int index = 0; index < len; index++)
 | 
			
		||||
        {
 | 
			
		||||
            Array.Copy(S7_MULRD_ITEM, 0, numArray, 19 + (index * 12), S7_MULRD_ITEM.Length);
 | 
			
		||||
            if (siemensAddress[index].DataCode == (byte)S7WordLength.Counter || siemensAddress[index].DataCode == (byte)S7WordLength.Timer)
 | 
			
		||||
            {
 | 
			
		||||
                numArray[22 + (index * 12)] = siemensAddress[index].DataCode;
 | 
			
		||||
                numArray[23 + (index * 12)] = (byte)(siemensAddress[index].Length / 256);
 | 
			
		||||
                numArray[24 + (index * 12)] = (byte)(siemensAddress[index].Length % 256);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                numArray[22 + (index * 12)] = (byte)S7WordLength.Byte;
 | 
			
		||||
                numArray[23 + (index * 12)] = (byte)(siemensAddress[index].Length / 256);
 | 
			
		||||
                numArray[24 + (index * 12)] = (byte)(siemensAddress[index].Length % 256);
 | 
			
		||||
            }
 | 
			
		||||
            numArray[25 + (index * 12)] = (byte)(siemensAddress[index].DbBlock / 256U);
 | 
			
		||||
            numArray[26 + (index * 12)] = (byte)(siemensAddress[index].DbBlock % 256U);
 | 
			
		||||
            numArray[27 + (index * 12)] = siemensAddress[index].DataCode;
 | 
			
		||||
            numArray[28 + (index * 12)] = (byte)(siemensAddress[index].AddressStart / 256 / 256 % 256);
 | 
			
		||||
            numArray[29 + (index * 12)] = (byte)(siemensAddress[index].AddressStart / 256 % 256);
 | 
			
		||||
            numArray[30 + (index * 12)] = (byte)(siemensAddress[index].AddressStart % 256);
 | 
			
		||||
        }
 | 
			
		||||
        return OperResult.CreateSuccessResult(numArray);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static OperResult<byte[]> GetWriteBitCommand(SiemensAddress address, bool data)
 | 
			
		||||
    {
 | 
			
		||||
        int len = 1;
 | 
			
		||||
        int telegramLen = 16 + 19 + len;//最后的1是写入值的byte数量
 | 
			
		||||
        int parameterLen = 12 + 2;
 | 
			
		||||
 | 
			
		||||
        byte[] numArray = new byte[telegramLen];
 | 
			
		||||
 | 
			
		||||
        Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
 | 
			
		||||
        numArray[2] = (byte)(telegramLen / 256);
 | 
			
		||||
        numArray[3] = (byte)(telegramLen % 256);
 | 
			
		||||
        numArray[13] = (byte)(parameterLen / 256);
 | 
			
		||||
        numArray[14] = (byte)(parameterLen % 256);
 | 
			
		||||
        numArray[15] = (byte)((4 + len) / 256);
 | 
			
		||||
        numArray[16] = (byte)((4 + len) % 256);
 | 
			
		||||
        numArray[17] = 5;
 | 
			
		||||
        numArray[18] = (byte)1;
 | 
			
		||||
        //写入Item与读取大致相同
 | 
			
		||||
        numArray[19] = (byte)18;
 | 
			
		||||
        numArray[20] = (byte)10;
 | 
			
		||||
        numArray[21] = (byte)16;
 | 
			
		||||
        numArray[22] = (byte)S7WordLength.Bit;
 | 
			
		||||
        numArray[23] = (byte)(len / 256);
 | 
			
		||||
        numArray[24] = (byte)(len % 256);
 | 
			
		||||
        numArray[25] = (byte)(address.DbBlock / 256U);
 | 
			
		||||
        numArray[26] = (byte)(address.DbBlock % 256U);
 | 
			
		||||
        numArray[27] = (byte)address.DataCode;
 | 
			
		||||
        numArray[28] = (byte)((address.AddressStart + address.BitCode) / 256 / 256);
 | 
			
		||||
        numArray[29] = (byte)((address.AddressStart + address.BitCode) / 256);
 | 
			
		||||
        numArray[30] = (byte)((address.AddressStart + address.BitCode) % 256);
 | 
			
		||||
        //后面跟的是写入的数据信息
 | 
			
		||||
        numArray[31] = 0;
 | 
			
		||||
        numArray[32] = 3;//Bit:3;Byte:4;Counter或者Timer:9
 | 
			
		||||
        numArray[33] = (byte)(len / 256);
 | 
			
		||||
        numArray[34] = (byte)(len % 256);
 | 
			
		||||
        numArray[35] = (byte)(data ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
        return OperResult.CreateSuccessResult(numArray);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static OperResult<byte[]> GetWriteByteCommand(SiemensAddress address, byte[] data)
 | 
			
		||||
    {
 | 
			
		||||
        int len = data.Length;
 | 
			
		||||
        int telegramLen = 16 + 19 + len;//最后的1是写入值的byte数量
 | 
			
		||||
        int parameterLen = 12 + 2;
 | 
			
		||||
 | 
			
		||||
        byte[] numArray = new byte[telegramLen];
 | 
			
		||||
 | 
			
		||||
        Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
 | 
			
		||||
        numArray[2] = (byte)(telegramLen / 256);
 | 
			
		||||
        numArray[3] = (byte)(telegramLen % 256);
 | 
			
		||||
        numArray[13] = (byte)(parameterLen / 256);
 | 
			
		||||
        numArray[14] = (byte)(parameterLen % 256);
 | 
			
		||||
        numArray[15] = (byte)((4 + len) / 256);
 | 
			
		||||
        numArray[16] = (byte)((4 + len) % 256);
 | 
			
		||||
        numArray[17] = 5;
 | 
			
		||||
        numArray[18] = (byte)1;
 | 
			
		||||
        //写入Item与读取大致相同
 | 
			
		||||
        numArray[19] = (byte)18;
 | 
			
		||||
        numArray[20] = (byte)10;
 | 
			
		||||
        numArray[21] = (byte)16;
 | 
			
		||||
        numArray[22] = (byte)S7WordLength.Byte;
 | 
			
		||||
        numArray[23] = (byte)(len / 256);
 | 
			
		||||
        numArray[24] = (byte)(len % 256);
 | 
			
		||||
 | 
			
		||||
        numArray[25] = (byte)(address.DbBlock / 256U);
 | 
			
		||||
        numArray[26] = (byte)(address.DbBlock % 256U);
 | 
			
		||||
        numArray[27] = (byte)address.DataCode;
 | 
			
		||||
        numArray[28] = (byte)(address.AddressStart / 256 / 256);
 | 
			
		||||
        numArray[29] = (byte)(address.AddressStart / 256);
 | 
			
		||||
        numArray[30] = (byte)(address.AddressStart % 256);
 | 
			
		||||
        //后面跟的是写入的数据信息
 | 
			
		||||
        numArray[31] = 0;
 | 
			
		||||
        numArray[32] = 4;//Bit:3;Byte:4;Counter或者Timer:9
 | 
			
		||||
        numArray[33] = (byte)(len * 8 / 256);
 | 
			
		||||
        numArray[34] = (byte)(len * 8 % 256);
 | 
			
		||||
        data.CopyTo(numArray, 35);
 | 
			
		||||
 | 
			
		||||
        return OperResult.CreateSuccessResult(numArray);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
    #region 字符串
 | 
			
		||||
 | 
			
		||||
    internal static async Task<OperResult<string>> ReadStringAsync(SiemensS7PLC plc, string address, Encoding encoding, CancellationToken cancellationToken)
 | 
			
		||||
    {
 | 
			
		||||
        //先读取一次获取长度,再读取实际值
 | 
			
		||||
        if (plc.CurrentPlc != SiemensEnum.S200Smart)
 | 
			
		||||
        {
 | 
			
		||||
            var result1 = await plc.ReadAsync(address, 2, cancellationToken);
 | 
			
		||||
            if (!result1.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                return new(result1);
 | 
			
		||||
            }
 | 
			
		||||
            if (result1.Content[0] == (byte)0 || result1.Content[0] == byte.MaxValue)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<string>("在PLC中不是字符串类型");
 | 
			
		||||
            }
 | 
			
		||||
            var result2 = await plc.ReadAsync(address, 2 + result1.Content[1], cancellationToken);
 | 
			
		||||
            if (!result2.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                return new(result2);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 2, result2.Content.Length - 2));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var result1 = await plc.ReadAsync(address, 1, cancellationToken);
 | 
			
		||||
            if (!result1.IsSuccess)
 | 
			
		||||
                return new(result1);
 | 
			
		||||
            var result2 = await plc.ReadAsync(address, 1 + result1.Content[0], cancellationToken);
 | 
			
		||||
            if (!result2.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                return new(result2);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 1, result2.Content.Length - 1));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal static async Task<OperResult> WriteAsync(SiemensS7PLC plc, string address, string value, Encoding encoding)
 | 
			
		||||
    {
 | 
			
		||||
        value ??= string.Empty;
 | 
			
		||||
        byte[] inBytes = encoding.GetBytes(value);
 | 
			
		||||
        //if (encoding == Encoding.Unicode)
 | 
			
		||||
        //    inBytes = inBytes.BytesReverseByWord();
 | 
			
		||||
        if (plc.CurrentPlc != SiemensEnum.S200Smart)
 | 
			
		||||
        {
 | 
			
		||||
            OperResult<byte[]> result = await plc.ReadAsync(address, 2);
 | 
			
		||||
            if (!result.IsSuccess) return result;
 | 
			
		||||
            if (result.Content[0] == byte.MaxValue) return new OperResult<string>("在PLC中不是字符串类型");
 | 
			
		||||
            if (result.Content[0] == 0) result.Content[0] = 254;
 | 
			
		||||
            if (value.Length > result.Content[0]) return new OperResult<string>("写入值长度超限");
 | 
			
		||||
            return await plc.WriteAsync(
 | 
			
		||||
                address,
 | 
			
		||||
                GenericExtensions.SpliceArray(new byte[2] { result.Content[0], (byte)value.Length },
 | 
			
		||||
                inBytes
 | 
			
		||||
                ));
 | 
			
		||||
        }
 | 
			
		||||
        return await plc.WriteAsync(address, GenericExtensions.SpliceArray<byte>(new byte[1]
 | 
			
		||||
        {
 | 
			
		||||
    (byte) value.Length
 | 
			
		||||
        }, inBytes));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public enum S7WordLength : byte
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    Bit = 0x01,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    Byte = 0x02,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    Char = 0x03,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    Word = 0x04,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    Int = 0x05,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    DWord = 0x06,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    DInt = 0x07,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    Real = 0x08,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    Counter = 0x1C,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    Timer = 0x1D,
 | 
			
		||||
}
 | 
			
		||||
@@ -1,153 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
 | 
			
		||||
internal partial class SiemensHelper
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// S7连读写请求头(包含ISO头和COTP头) 
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    static readonly byte[] S7_MULRW_HEADER = {
 | 
			
		||||
            0x03,0x00,
 | 
			
		||||
            0x00,0x1f,       // 报文长度(item.len*12+19,注意:根据传入读取item数量更改)
 | 
			
		||||
		0x02,0xf0, 0x80, //COTP信息
 | 
			
		||||
		0x32,            // S7协议ID
 | 
			
		||||
		0x01,            // 类型,请求命令
 | 
			
		||||
		0x00,0x00,       // 冗余识别
 | 
			
		||||
		0x00,0x01,       // 序列号
 | 
			
		||||
		0x00,0x0e,       // parameter长度(item.len*12+2,注意:根据传入读取item数量更改)
 | 
			
		||||
		0x00,0x00,       // Data Length+4 ,写入时填写,读取时为0  
 | 
			
		||||
		0x04,            //  4 Read Var, 5 Write Var  ,注意更改
 | 
			
		||||
		0x01,            // Item数量(item.len,注意:根据传入读取item数量更改)
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
    // S7变量多读Item
 | 
			
		||||
    static readonly byte[] S7_MULRD_ITEM = {
 | 
			
		||||
            0x12,            // Var 规范.
 | 
			
		||||
		0x0a,            // 剩余的字节长度
 | 
			
		||||
		0x10,            // Syntax ID 
 | 
			
		||||
		(byte)S7WordLength.Byte,  // 相关的数据长度代码(注意:根据传入的变量更改)                
 | 
			
		||||
		0x00,0x01,       // 数据长度						(注意:根据传入的变量更改)      
 | 
			
		||||
		0x00,0x00,       // DB编号							(注意:根据传入的变量更改)
 | 
			
		||||
		0x84,            // 数据块类型					    (注意:根据传入的变量更改)          
 | 
			
		||||
		0x00,0x00,0x00   // 数据块偏移量			    (注意:根据传入的变量更改)              
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // ISO连接请求报文(也包含ISO头和COTP头)
 | 
			
		||||
    internal static byte[] ISO_CR = {
 | 
			
		||||
		// TPKT (RFC1006 Header)
 | 
			
		||||
		0x03, // RFC 1006 ID (3) 
 | 
			
		||||
		0x00, // 保留 0
 | 
			
		||||
		0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
 | 
			
		||||
		0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
 | 
			
		||||
		// COTP (ISO 8073 Header)
 | 
			
		||||
		0x11, // PDU Size Length
 | 
			
		||||
		0xE0, // CR -连接请求ID
 | 
			
		||||
		0x00, 0x00, // Dst Reference
 | 
			
		||||
		0x00, 0x01, // Src Reference
 | 
			
		||||
		0x00, // Class + Options Flags
 | 
			
		||||
		//对于S7200/Smart ,下面参数也需要重写
 | 
			
		||||
		0xC0, // PDU Max Length ID
 | 
			
		||||
		0x01, 0x0A, // PDU Max Length 
 | 
			
		||||
		0xC1, // Src TSAP Identifier
 | 
			
		||||
		0x02, // Src TSAP Length (2 bytes)
 | 
			
		||||
		0x01, 0x00, // Src TSAP  (需重写)
 | 
			
		||||
		0xC2, // Dst TSAP Identifier
 | 
			
		||||
		0x02, // Dst TSAP Length (2 bytes)
 | 
			
		||||
		0x01, 0x02  // Dst TSAP  (需重写)
 | 
			
		||||
	};
 | 
			
		||||
    // ISO连接请求报文(也包含ISO头和COTP头)
 | 
			
		||||
    internal static byte[] ISO_CR200 = {
 | 
			
		||||
		// TPKT (RFC1006 Header)
 | 
			
		||||
		0x03, // RFC 1006 ID (3) 
 | 
			
		||||
		0x00, // 保留 0
 | 
			
		||||
		0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
 | 
			
		||||
		0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
 | 
			
		||||
		// COTP (ISO 8073 Header)
 | 
			
		||||
		0x11, // PDU Size Length
 | 
			
		||||
		0xE0, // CR -连接请求ID
 | 
			
		||||
		0x00, 0x00, // Dst Reference
 | 
			
		||||
		0x00, 0x01, // Src Reference
 | 
			
		||||
		0x00, // Class + Options Flags
 | 
			
		||||
 | 
			
		||||
		//对于S7200/Smart
 | 
			
		||||
		0xC1,0x02,
 | 
			
		||||
            0x4D,0x57, //LOCALTASP
 | 
			
		||||
		0xC2,
 | 
			
		||||
            0x02,
 | 
			
		||||
            0x4D,0x57, //DESTTASP
 | 
			
		||||
		0xC0,
 | 
			
		||||
            0x01,0x09
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
    // ISO连接请求报文(也包含ISO头和COTP头)
 | 
			
		||||
    internal static byte[] ISO_CR200SMART = {
 | 
			
		||||
		// TPKT (RFC1006 Header)
 | 
			
		||||
		0x03, // RFC 1006 ID (3) 
 | 
			
		||||
		0x00, // 保留 0
 | 
			
		||||
		0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
 | 
			
		||||
		0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
 | 
			
		||||
		// COTP (ISO 8073 Header)
 | 
			
		||||
		0x11, // PDU Size Length
 | 
			
		||||
		0xE0, // CR -连接请求ID
 | 
			
		||||
		0x00, 0x00, // Dst Reference
 | 
			
		||||
		0x00, 0x01, // Src Reference
 | 
			
		||||
		0x00, // Class + Options Flags
 | 
			
		||||
 | 
			
		||||
		//对于S7200/Smart
 | 
			
		||||
		0xC1,0x02,
 | 
			
		||||
            0x10,0x00,//LOCALTASP
 | 
			
		||||
		0xC2,
 | 
			
		||||
            0x02,
 | 
			
		||||
            0x03,0x00,//DESTTASP
 | 
			
		||||
		0xC0,
 | 
			
		||||
            0x01,0x0A
 | 
			
		||||
        };
 | 
			
		||||
    // PDU获取报文(也包含ISO头和COTP头)
 | 
			
		||||
    internal static byte[] S7_PN = {
 | 
			
		||||
            0x03, 0x00, 0x00, 0x19,
 | 
			
		||||
            0x02, 0xf0, 0x80, // TPKT + COTP
 | 
			
		||||
		0x32, 0x01, 0x00, 0x00,
 | 
			
		||||
 | 
			
		||||
		//这里对于S7200/Smart需要重写
 | 
			
		||||
            0x04, 0x00, 0x00, 0x08,
 | 
			
		||||
 | 
			
		||||
            0x00, 0x00, 0xf0, 0x00,
 | 
			
		||||
            0x00, 0x01, 0x00, 0x01,
 | 
			
		||||
            0x01,0xE0        // PDU Length Requested  这里默认480字节,对于S7200/Smart 960字节
 | 
			
		||||
	};
 | 
			
		||||
    // PDU获取报文(也包含ISO头和COTP头)
 | 
			
		||||
    internal static byte[] S7200_PN = {
 | 
			
		||||
            0x03, 0x00, 0x00, 0x19,
 | 
			
		||||
            0x02, 0xf0, 0x80, // TPKT + COTP
 | 
			
		||||
		0x32, 0x01, 0x00, 0x00,
 | 
			
		||||
 | 
			
		||||
            0x00,0x00,0x00,0x08,
 | 
			
		||||
 | 
			
		||||
            0x00, 0x00, 0xf0, 0x00,
 | 
			
		||||
            0x00, 0x01, 0x00, 0x01,
 | 
			
		||||
            0x01,0xE0        // PDU Length Requested  这里默认960字节
 | 
			
		||||
	};
 | 
			
		||||
    internal static byte[] S7200SMART_PN = {
 | 
			
		||||
            0x03, 0x00, 0x00, 0x19,
 | 
			
		||||
            0x02, 0xf0, 0x80, // TPKT + COTP
 | 
			
		||||
		0x32, 0x01, 0x00, 0x00,
 | 
			
		||||
 | 
			
		||||
            0xCC,0xC1,0x00,0x08,
 | 
			
		||||
 | 
			
		||||
            0x00, 0x00, 0xf0, 0x00,
 | 
			
		||||
            0x00, 0x01, 0x00, 0x01,
 | 
			
		||||
            0x01,0xE0        // PDU Length Requested  这里默认960字节
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
public class SiemensMessage : MessageBase, IMessage
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override int HeadBytesLength => 4;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override bool CheckHeadBytes(byte[] heads)
 | 
			
		||||
    {
 | 
			
		||||
        HeadBytes = heads;
 | 
			
		||||
        byte[] headBytes = HeadBytes;
 | 
			
		||||
        if (headBytes == null || headBytes.Length < 4)
 | 
			
		||||
            BodyLength = 0;
 | 
			
		||||
        int length = (HeadBytes[2] * 256) + HeadBytes[3] - 4;
 | 
			
		||||
        if (length < 0)
 | 
			
		||||
            length = 0;
 | 
			
		||||
        BodyLength = length;
 | 
			
		||||
        return HeadBytes != null && HeadBytes[0] == 3 && HeadBytes[1] == 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public enum SiemensEnum
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    S200,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    S200Smart,
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    S300,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    S400,
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    S1200,
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    S1500,
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,511 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 相关命令含义源自网络资料/Shrap7/s7netplus
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public partial class SiemensS7PLC : ReadWriteDevicesTcpClientBase
 | 
			
		||||
    {
 | 
			
		||||
        private readonly SiemensEnum _currentPlc = SiemensEnum.S1200;
 | 
			
		||||
        private readonly byte[] ISO_CR;
 | 
			
		||||
        private readonly byte[] S7_PN;
 | 
			
		||||
        private int pdu_length = 100;
 | 
			
		||||
        private byte plc_rack = 0;
 | 
			
		||||
        private byte plc_slot = 0;
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 传入PLC类型,程序内会改变相应PLC类型的S7协议LocalTSAP, RemoteTSAP等
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="tcpClient"></param>
 | 
			
		||||
        /// <param name="siemensPLCEnum"></param>
 | 
			
		||||
        public SiemensS7PLC(TcpClient tcpClient, SiemensEnum siemensPLCEnum) : base(tcpClient)
 | 
			
		||||
        {
 | 
			
		||||
            _currentPlc = siemensPLCEnum;
 | 
			
		||||
            RegisterByteLength = 1;
 | 
			
		||||
            ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
 | 
			
		||||
            ISO_CR = new byte[22];
 | 
			
		||||
            S7_PN = new byte[25];
 | 
			
		||||
            Array.Copy(SiemensHelper.ISO_CR, ISO_CR, ISO_CR.Length);
 | 
			
		||||
            Array.Copy(SiemensHelper.S7_PN, S7_PN, S7_PN.Length);
 | 
			
		||||
            switch (siemensPLCEnum)
 | 
			
		||||
            {
 | 
			
		||||
                case SiemensEnum.S1200:
 | 
			
		||||
                    ISO_CR[21] = 0x00;
 | 
			
		||||
                    break;
 | 
			
		||||
                case SiemensEnum.S300:
 | 
			
		||||
                    ISO_CR[21] = 0x02;
 | 
			
		||||
                    break;
 | 
			
		||||
                case SiemensEnum.S400:
 | 
			
		||||
                    ISO_CR[21] = 0x03;
 | 
			
		||||
                    ISO_CR[17] = 0x00;
 | 
			
		||||
                    break;
 | 
			
		||||
                case SiemensEnum.S1500:
 | 
			
		||||
                    ISO_CR[21] = 0x00;
 | 
			
		||||
                    break;
 | 
			
		||||
                case SiemensEnum.S200Smart:
 | 
			
		||||
                    ISO_CR = SiemensHelper.ISO_CR200SMART;
 | 
			
		||||
                    S7_PN = SiemensHelper.S7200SMART_PN;
 | 
			
		||||
                    break;
 | 
			
		||||
                case SiemensEnum.S200:
 | 
			
		||||
                    ISO_CR = SiemensHelper.ISO_CR200;
 | 
			
		||||
                    S7_PN = SiemensHelper.S7200_PN;
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 当前PLC类型
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public SiemensEnum CurrentPlc => _currentPlc;
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override string GetAddressDescription()
 | 
			
		||||
        {
 | 
			
		||||
            StringBuilder stringBuilder = new();
 | 
			
		||||
            stringBuilder.AppendLine("S7协议寄存器地址格式");
 | 
			
		||||
            stringBuilder.AppendLine("Txxxxx	Timer寄存器,例如T100/T100.1");
 | 
			
		||||
            stringBuilder.AppendLine("Cxxxxx,Counter寄存器,例如C100/C100.1");
 | 
			
		||||
            stringBuilder.AppendLine("AIxxxxx,AI寄存器,例如AI100/AI100.1");
 | 
			
		||||
            stringBuilder.AppendLine("AQxxxxx,AQ寄存器,例如AQ100/AQ100.1");
 | 
			
		||||
            stringBuilder.AppendLine("Ixxxxx,I寄存器,例如I100/I100.1");
 | 
			
		||||
            stringBuilder.AppendLine("Qxxxxx,Q寄存器,例如Q100/Q100.1");
 | 
			
		||||
            stringBuilder.AppendLine("Mxxxxx,M寄存器,例如M100/M100.1");
 | 
			
		||||
            stringBuilder.AppendLine("DBxxxxx,DB寄存器,例如DB100.1/DB100.1.1");
 | 
			
		||||
            stringBuilder.AppendLine("");
 | 
			
		||||
 | 
			
		||||
            return base.GetAddressDescription() + Environment.NewLine + stringBuilder.ToString();
 | 
			
		||||
        }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="address"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public override int GetBitOffset(string address)
 | 
			
		||||
        {
 | 
			
		||||
            if (address.IndexOf('.') > 0)
 | 
			
		||||
            {
 | 
			
		||||
                string[] addressSplits = address.SplitDot();
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    int bitIndex = 0;
 | 
			
		||||
                    if ((addressSplits.Length == 2 && !address.ToUpper().Contains("DB")) || (addressSplits.Length >= 3 && address.ToUpper().Contains("DB")))
 | 
			
		||||
                        bitIndex = Convert.ToInt32(addressSplits.Last());
 | 
			
		||||
                    return bitIndex;
 | 
			
		||||
                }
 | 
			
		||||
                catch
 | 
			
		||||
                {
 | 
			
		||||
                    return 0;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override bool IsBitReverse(string address)
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #region 设置
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 远程TSAP,需重新连接
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int DestTSAP
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return
 | 
			
		||||
                    _currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart ?
 | 
			
		||||
                    (ISO_CR[17] * 256) + ISO_CR[18] :
 | 
			
		||||
                    (ISO_CR[20] * 256) + ISO_CR[21];
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
 | 
			
		||||
                {
 | 
			
		||||
                    ISO_CR[17] = BitConverter.GetBytes(value)[1];
 | 
			
		||||
                    ISO_CR[18] = BitConverter.GetBytes(value)[0];
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ISO_CR[20] = BitConverter.GetBytes(value)[1];
 | 
			
		||||
                    ISO_CR[21] = BitConverter.GetBytes(value)[0];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 本地TSAP,需重新连接
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int LocalTSAP
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return
 | 
			
		||||
                    _currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart ?
 | 
			
		||||
                    (ISO_CR[13] * 256) + ISO_CR[14] :
 | 
			
		||||
                    (ISO_CR[16] * 256) + ISO_CR[17];
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
 | 
			
		||||
                {
 | 
			
		||||
                    ISO_CR[13] = BitConverter.GetBytes(value)[1];
 | 
			
		||||
                    ISO_CR[14] = BitConverter.GetBytes(value)[0];
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    ISO_CR[16] = BitConverter.GetBytes(value)[1];
 | 
			
		||||
                    ISO_CR[17] = BitConverter.GetBytes(value)[0];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// PDULength
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int PDULength => pdu_length;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 机架号,需重新连接
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public byte Rack
 | 
			
		||||
        {
 | 
			
		||||
            get => plc_rack;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                plc_rack = value;
 | 
			
		||||
                if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
 | 
			
		||||
                {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                ISO_CR[21] = (byte)((plc_rack * 0x20) + plc_slot);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 槽号,需重新连接
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public byte Slot
 | 
			
		||||
        {
 | 
			
		||||
            get => plc_slot;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                plc_slot = value;
 | 
			
		||||
                if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
 | 
			
		||||
                {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                ISO_CR[21] = (byte)((plc_rack * 0x20) + plc_slot);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        /// <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 = GetReadByteCommand(address, length);
 | 
			
		||||
                if (commandResult.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    List<byte> bytes = new();
 | 
			
		||||
                    foreach (var item in commandResult.Content)
 | 
			
		||||
                    {
 | 
			
		||||
                        var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
 | 
			
		||||
                        if (((MessageBase)result.RequestInfo).IsSuccess)
 | 
			
		||||
                            bytes.AddRange(((MessageBase)result.RequestInfo).Content);
 | 
			
		||||
                        else
 | 
			
		||||
                            return new(((MessageBase)result.RequestInfo));
 | 
			
		||||
                    }
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes.ToArray());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new(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 = GetReadByteCommand(address, length);
 | 
			
		||||
                if (commandResult.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    List<byte> bytes = new();
 | 
			
		||||
                    foreach (var item in commandResult.Content)
 | 
			
		||||
                    {
 | 
			
		||||
                        var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
 | 
			
		||||
                        if (((MessageBase)result.RequestInfo).IsSuccess)
 | 
			
		||||
                            bytes.AddRange(((MessageBase)result.RequestInfo).Content);
 | 
			
		||||
                        else
 | 
			
		||||
                            return new(((MessageBase)result.RequestInfo));
 | 
			
		||||
                    }
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes.ToArray());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new(commandResult);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<byte[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override void SetDataAdapter(object socketClient = null)
 | 
			
		||||
        {
 | 
			
		||||
            SiemensS7PLCDataHandleAdapter dataHandleAdapter = new();
 | 
			
		||||
            TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                Connect(cancellationToken);
 | 
			
		||||
                var commandResult = GetWriteByteCommand(address, value);
 | 
			
		||||
                if (commandResult.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    List<ResponsedData> bytes = new();
 | 
			
		||||
                    foreach (var item in commandResult.Content)
 | 
			
		||||
                    {
 | 
			
		||||
                        ResponsedData result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
 | 
			
		||||
                        if (!((MessageBase)result.RequestInfo).IsSuccess)
 | 
			
		||||
                            return new(((MessageBase)result.RequestInfo));
 | 
			
		||||
                        bytes.Add(result);
 | 
			
		||||
                    }
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes.ToArray());
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new(commandResult);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<bool[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            if (value.Length > 1)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult("不支持多写");
 | 
			
		||||
            }
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                Connect(cancellationToken);
 | 
			
		||||
 | 
			
		||||
                var commandResult = GetWriteBitCommand(address, value[0]);
 | 
			
		||||
                if (commandResult.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                    return (MessageBase)result.RequestInfo;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new(commandResult);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<bool[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, ThingsGatewayBitConverter);
 | 
			
		||||
            return SiemensHelper.WriteAsync(this, address, value, transformParameter.Encoding);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await ConnectAsync(cancellationToken);
 | 
			
		||||
                var commandResult = GetWriteByteCommand(address, value);
 | 
			
		||||
                if (commandResult.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    List<ResponsedData> bytes = new();
 | 
			
		||||
                    foreach (var item in commandResult.Content)
 | 
			
		||||
                    {
 | 
			
		||||
                        ResponsedData result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
 | 
			
		||||
                        if (!((MessageBase)result.RequestInfo).IsSuccess)
 | 
			
		||||
                            return new(((MessageBase)result.RequestInfo));
 | 
			
		||||
                        bytes.Add(result);
 | 
			
		||||
                    }
 | 
			
		||||
                    return OperResult.CreateSuccessResult(bytes.ToArray());
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new(commandResult);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<bool[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
 | 
			
		||||
        {
 | 
			
		||||
            if (value.Length > 1)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult("不支持多写");
 | 
			
		||||
            }
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                await ConnectAsync(cancellationToken);
 | 
			
		||||
 | 
			
		||||
                var commandResult = GetWriteBitCommand(address, value[0]);
 | 
			
		||||
                if (commandResult.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
 | 
			
		||||
                    return (MessageBase)result.RequestInfo;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return new(commandResult);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return new OperResult<bool[]>(ex);
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        #region 其他方法
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 读取日期
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public async Task<OperResult<System.DateTime>> ReadDateAsync(string address, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            return (await this.ReadAsync(address, 2, cancellationToken)).
 | 
			
		||||
                 Then(m => OperResult.CreateSuccessResult(ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime.AddDays(
 | 
			
		||||
                     ThingsGatewayBitConverter.ToUInt16(m, 0)))
 | 
			
		||||
                 );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 读取时间
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public async Task<OperResult<System.DateTime>> ReadDateTimeAsync(string address, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            return ByteTransformUtil.GetResultFromBytes(await ReadAsync(address, 8, cancellationToken), ThingsGateway.Foundation.Adapter.Siemens.DateTime.FromByteArray);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 读取变长字符串
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public async Task<OperResult<string>> ReadStringAsync(string address, Encoding encoding, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            return await SiemensHelper.ReadStringAsync(this, address, encoding, cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 写入日期
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public async Task<OperResult> WriteDateAsync(string address, System.DateTime dateTime, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            return await base.WriteAsync(address, Convert.ToUInt16((dateTime - ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime).TotalDays), cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 写入时间
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public async Task<OperResult> WriteDateTimeAsync(string address, System.DateTime dateTime, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            return await WriteAsync(address, ThingsGateway.Foundation.Adapter.Siemens.DateTime.ToByteArray(dateTime), cancellationToken);
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override async Task Connected(ITcpClient client, ConnectedEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                NormalDataHandlingAdapter dataHandleAdapter = new();
 | 
			
		||||
                TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
 | 
			
		||||
                var result1 = await SendThenResponseAsync(ISO_CR);
 | 
			
		||||
                if (!result1.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    Logger?.Warning($"{client.IP} : {client.Port}:ISO_TP握手失败-{result1.Message}");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                var result2 = await SendThenResponseAsync(S7_PN);
 | 
			
		||||
                if (!result2.IsSuccess)
 | 
			
		||||
                {
 | 
			
		||||
                    Logger?.Warning($"{client.IP} : {client.Port}:PDU初始化失败-{result2.Message}");
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                pdu_length = ThingsGatewayBitConverter.ToUInt16(result2.Content.SelectLast(2), 0);
 | 
			
		||||
                pdu_length = pdu_length < 200 ? 200 : pdu_length;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                Logger.Exception(ex);
 | 
			
		||||
            }
 | 
			
		||||
            finally
 | 
			
		||||
            {
 | 
			
		||||
                SetDataAdapter();
 | 
			
		||||
            }
 | 
			
		||||
            await base.Connected(client, e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// SiemensS7PLCDataHandleAdapter
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SiemensS7PLCDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<SiemensMessage>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override byte[] PackCommand(byte[] command)
 | 
			
		||||
    {
 | 
			
		||||
        return command;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    protected override SiemensMessage GetInstance()
 | 
			
		||||
    {
 | 
			
		||||
        return new SiemensMessage();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    protected override FilterResult UnpackResponse(SiemensMessage request, byte[] send, byte[] body, byte[] response)
 | 
			
		||||
    {
 | 
			
		||||
        var result = new OperResult<byte[]>();
 | 
			
		||||
        if (response[2] * 256 + response[3] == 7)
 | 
			
		||||
        {
 | 
			
		||||
            result = new OperResult<byte[]>() { Content = response };
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            //以请求方为准,分开返回类型校验
 | 
			
		||||
            switch (send[17])
 | 
			
		||||
            {
 | 
			
		||||
                case 0x04:
 | 
			
		||||
                    result = SiemensHelper.AnalysisReadByte(send, response);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 0x05:
 | 
			
		||||
                    result = SiemensHelper.AnalysisWrite(response);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        request.ErrorCode = result.ErrorCode;
 | 
			
		||||
        request.Message = result.Message;
 | 
			
		||||
        request.Content = result.Content;
 | 
			
		||||
        return FilterResult.Success;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,129 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.Generic;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Adapter.Siemens;
 | 
			
		||||
 | 
			
		||||
public partial class SiemensS7PLC : ReadWriteDevicesTcpClientBase
 | 
			
		||||
{
 | 
			
		||||
    private static OperResult<byte[]> GetWriteBitCommand(string address, bool data)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var result = SiemensAddress.ParseFrom(address);
 | 
			
		||||
            return SiemensHelper.GetWriteBitCommand(result, data);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private OperResult<List<byte[]>> GetReadByteCommand(string address, int length)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var from = SiemensAddress.ParseFrom(address, length);
 | 
			
		||||
            ushort num1 = 0;
 | 
			
		||||
            var listBytes = new List<byte[]>();
 | 
			
		||||
            while (num1 < length)
 | 
			
		||||
            {
 | 
			
		||||
                //pdu长度,重复生成报文,直至全部生成
 | 
			
		||||
                ushort num2 = (ushort)Math.Min(length - num1, pdu_length);
 | 
			
		||||
                from.Length = num2;
 | 
			
		||||
                var result = GetReadByteCommand(new SiemensAddress[1] { from });
 | 
			
		||||
                if (!result.IsSuccess) return new(result);
 | 
			
		||||
                listBytes.AddRange(result.Content);
 | 
			
		||||
                num1 += num2;
 | 
			
		||||
                if (from.DataCode == (byte)S7WordLength.Timer || from.DataCode == (byte)S7WordLength.Counter)
 | 
			
		||||
                {
 | 
			
		||||
                    from.Address += num2 / 2;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    from.Address += num2 * 8;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return OperResult.CreateSuccessResult(listBytes);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new OperResult<List<byte[]>>(ex);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OperResult<List<byte[]>> GetReadByteCommand(SiemensAddress[] siemensAddress)
 | 
			
		||||
    {
 | 
			
		||||
        if (siemensAddress.Length <= 19)
 | 
			
		||||
        {
 | 
			
		||||
            return ByteTransformUtil.GetResultFromBytes(SiemensHelper.GetReadCommand(siemensAddress), m => new List<byte[]>() { m });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        List<byte[]> byteList = new();
 | 
			
		||||
        List<SiemensAddress[]> s7AddressDataArrayList = siemensAddress.ArraySplitByLength(19);
 | 
			
		||||
        for (int index = 0; index < s7AddressDataArrayList.Count; ++index)
 | 
			
		||||
        {
 | 
			
		||||
            var result = GetReadByteCommand(s7AddressDataArrayList[index]);
 | 
			
		||||
            if (!result.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
            byteList.AddRange(result.Content);
 | 
			
		||||
        }
 | 
			
		||||
        return OperResult.CreateSuccessResult(byteList);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private OperResult<List<byte[]>> GetWriteByteCommand(string address, byte[] value)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var s_Address = SiemensAddress.ParseFrom(address);
 | 
			
		||||
 | 
			
		||||
            return GetWriteByteCommand(s_Address, value);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            return new(ex);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// DefalutConverter
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static ThingsGatewayBitConverter DefalutConverter = new(BitConverter.IsLittleEndian ? EndianType.Little : EndianType.Big);
 | 
			
		||||
    private OperResult<List<byte[]>> GetWriteByteCommand(SiemensAddress address, byte[] value)
 | 
			
		||||
    {
 | 
			
		||||
        int length1 = value.Length;
 | 
			
		||||
        ushort index = 0;
 | 
			
		||||
        List<byte[]> bytes = new();
 | 
			
		||||
        while (index < length1)
 | 
			
		||||
        {
 | 
			
		||||
            //pdu长度,重复生成报文,直至全部生成
 | 
			
		||||
            ushort length2 = (ushort)Math.Min(length1 - index, pdu_length);
 | 
			
		||||
            byte[] data = DefalutConverter.ToByte(value, index, length2);
 | 
			
		||||
            OperResult<byte[]> result1 = SiemensHelper.GetWriteByteCommand(address, data);
 | 
			
		||||
            if (!result1.IsSuccess)
 | 
			
		||||
            {
 | 
			
		||||
                return new(result1);
 | 
			
		||||
            }
 | 
			
		||||
            bytes.Add(result1.Content);
 | 
			
		||||
            index += length2;
 | 
			
		||||
            address.Address += length2 * 8;
 | 
			
		||||
        }
 | 
			
		||||
        return OperResult.CreateSuccessResult(bytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 设备地址数据的信息,对每个协议都建立其变量地址的表示类
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class DeviceAddressBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 起始地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Address { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 实体类转换为字符串
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
    {
 | 
			
		||||
        return Address;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Foundation.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 打包读取变量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IDeviceVariableRunTime
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 执行间隔
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    int IntervalTime { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 变量地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    string VariableAddress { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Index
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    int Index { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 赋值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    OperResult SetValue(object value, DateTime dateTime = default, bool isOnline = true);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// IThingsGatewayBitConverter
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 数据类型
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    DataTypeEnum DataTypeEnum { get; set; }
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user