Compare commits
	
		
			711 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | d1cff037c9 | ||
|   | fc88a2fafa | ||
|   | 45fcceb056 | ||
|   | 7043477038 | ||
|   | 7dd685cf54 | ||
|   | 5f5e4969c0 | ||
|   | 8a53fd19e9 | ||
|   | baf4714c36 | ||
|   | 7ba9ac7a5b | ||
|   | 85b8f26e8e | ||
|   | 594a0f1410 | ||
|   | d317d757d7 | ||
|   | fdf0ba6318 | ||
|   | 15bf7de5fa | ||
|   | d3402b058e | ||
|   | e7dfdd4031 | ||
|   | b2dd7b6364 | ||
|   | 9bd6d9abbf | ||
|   | cd14428fea | ||
|   | 19d9f03c2b | ||
|   | 0d57e72bbf | ||
|   | 329516a61b | ||
|   | d566869589 | ||
|   | 9cb8d8e6c7 | ||
|   | 9de3c57e5d | ||
|   | f32ff92b0b | ||
|   | 88d71e271e | ||
|   | fd9c14612a | ||
|   | e26e5a160f | ||
|   | b836bfed22 | ||
|   | a4b598c6d0 | ||
|   | c9ab755839 | ||
|   | 9920edba53 | ||
|   | 12bd7280d1 | ||
|   | d30ea7f63b | ||
|   | ebd3390db6 | ||
|   | 9a374a9ebc | ||
|   | b1bc22cb08 | ||
|   | 4930d53890 | ||
|   | c31327b5bc | ||
|   | 3f2aa1f1e1 | ||
|   | 6e78c00a96 | ||
|   | c27dde085e | ||
|   | d26cc308c0 | ||
|   | fb1efdf290 | ||
|   | 3c99f2a472 | ||
|   | affe9a44e0 | ||
|   | 43730fa519 | ||
|   | d39aa22b09 | ||
|   | e232a6b6ea | ||
|   | 71ebb36fe9 | ||
|   | 78a0b86327 | ||
|   | 2636c16a97 | ||
|   | fd77c0242d | ||
|   | e74819a900 | ||
|   | 9b7f696c9b | ||
|   | 0230d614e7 | ||
|   | 252d99ad78 | ||
|   | 1ffc200350 | ||
|   | 807d89b2b2 | ||
|   | 4013afa1f1 | ||
|   | a580927ceb | ||
|   | bf2cf52034 | ||
|   | 81bb8b7c31 | ||
|   | a825007fb5 | ||
|   | 988124d96a | ||
|   | f0de815296 | ||
|   | 0e2d58c887 | ||
|   | b155382626 | ||
|   | f362d740af | ||
|   | 4a85e31a4f | ||
|   | 302c270ad5 | ||
|   | 3c1517d0f3 | ||
|   | f9fb222044 | ||
|   | e8edc02ba3 | ||
|   | 95a44e3053 | ||
|   | 74a9fe9a87 | ||
|   | 4d03f9ea1a | ||
|   | 67c96ca991 | ||
|   | 88fb793c68 | ||
|   | d6d02d8cc5 | ||
|   | c5a3f8e2e3 | ||
|   | 27e8653a1a | ||
|   | 863beda82c | ||
|   | bac84c3ecd | ||
|   | 2fca2ad9f8 | ||
|   | dd75286fe0 | ||
|   | 7f91792cf1 | ||
|   | 0e0ccad311 | ||
|   | 0691f72e67 | ||
|   | 7e38a51720 | ||
|   | 34ca8243a3 | ||
|   | 112fea7632 | ||
|   | 378763e4ee | ||
|   | 517bd0394d | ||
|   | 70adb97fb5 | ||
|   | 623d44cabe | ||
|   | 0d479ca00b | ||
|   | 8bc49ef437 | ||
|   | f83fcec786 | ||
|   | 93690ce40d | ||
|   | f82c5f2f27 | ||
|   | a83c1c3899 | ||
|   | 91d6aed109 | ||
|   | db8f8fe51d | ||
|   | 4596004b17 | ||
|   | d5540906cb | ||
|   | 90796a979d | ||
|   | 2190a87772 | ||
|   | c5953b83f8 | ||
|   | 24bc60abf0 | ||
|   | 31eee6b009 | ||
|   | c5da565a8f | ||
|   | 947cd712e1 | ||
|   | edc208f96b | ||
|   | 1fb0296ee7 | ||
|   | 6488d3df87 | ||
|   | 56189d78e0 | ||
|   | bff18127b8 | ||
|   | 363206e0ba | ||
|   | fd3e378501 | ||
|   | 4ba2fe4c9d | ||
|   | 2c499626ad | ||
|   | 2b581a03c3 | ||
|   | 450c15210a | ||
|   | 65fed8cc93 | ||
|   | 4b64771ea2 | ||
|   | f39977a6ff | ||
|   | 933b535caa | ||
|   | 8abc5d2f20 | ||
|   | d8783cd994 | ||
|   | d5d087feb5 | ||
|   | 6ba3399df7 | ||
|   | 65124b3aa8 | ||
|   | 98597f4726 | ||
|   | e7981f0d8e | ||
|   | cf654427c3 | ||
|   | ff2f628282 | ||
|   | ae818ca265 | ||
|   | 0f2aed458e | ||
|   | d486c44ff6 | ||
|   | ca7b9980bf | ||
|   | 3c71e6a8e3 | ||
|   | 542442864c | ||
|   | 5edb64fa85 | ||
|   | 8dc1c898a3 | ||
|   | 1ed35726b0 | ||
|   | 27fae9ebaa | ||
|   | b103f25c94 | ||
|   | abff450274 | ||
|   | c260736a11 | ||
|   | 166ac2307a | ||
|   | b21a4e1a4d | ||
|   | f7dc943fa3 | ||
|   | bfbd2693ec | ||
|   | 819e71c993 | ||
|   | 9fd0b489a2 | ||
|   | f5fe9f8dae | ||
|   | f9ffc18145 | ||
|   | 08db5b983a | ||
|   | 5b3b4c8c50 | ||
|   | 73f914ffc4 | ||
|   | d6bdd73ed6 | ||
|   | 7370ee7349 | ||
|   | 4574596bac | ||
|   | 4d16855e36 | ||
|   | 13a0d4d282 | ||
|   | b9cd06b829 | ||
|   | 5b460e8fa2 | ||
|   | 41087edf17 | ||
|   | 2afcc38e38 | ||
|   | e59ccce25f | ||
|   | d7425890e8 | ||
|   | a989a837fb | ||
|   | db1221da50 | ||
|   | cf794569ed | ||
|   | 51e5bbab0d | ||
|   | 2c197ed2b2 | ||
|   | d8fc6665b3 | ||
|   | c671a79822 | ||
|   | 9d93ce4c41 | ||
|   | a6d99fe227 | ||
|   | 923b8bca31 | ||
|   | e2c30d1c88 | ||
|   | b6d9f2a04e | ||
|   | 57306ea664 | ||
|   | cd7f3fd02f | ||
|   | 0482e077a8 | ||
|   | 5f986a45ca | ||
|   | ca7b49c0d5 | ||
|   | 52dd555e6c | ||
|   | 579b1a59f9 | ||
|   | 5299c5c4be | ||
|   | f7756bccef | ||
|   | a6b874d160 | ||
|   | 3e5fb3ddcf | ||
|   | 5e6bcb12d3 | ||
|   | 14303f1429 | ||
|   | 96711ba022 | ||
|   | cbfc0fdbdc | ||
|   | 6e81886c0e | ||
|   | 2d976bc132 | ||
|   | 57f6a476af | ||
|   | 8491ed296e | ||
|   | cd1288afdc | ||
|   | ec6c830cb0 | ||
|   | 2f86ccc4bf | ||
|   | 8ca445aec0 | ||
|   | 1e1f27c8a5 | ||
|   | 2b84bde367 | ||
|   | b52e58551d | ||
|   | 9aceed00bf | ||
|   | 58814f7f74 | ||
|   | 6a70ef9f31 | ||
|   | 82cc4ca500 | ||
|   | 4567fa04ed | ||
|   | 8b98b5d818 | ||
|   | 176d0351af | ||
|   | d63dc3384b | ||
|   | 1ccd704e30 | ||
|   | f5d23dbe79 | ||
|   | 75bfe53ac3 | ||
|   | 3308f916dd | ||
|   | e7140279ca | ||
|   | 1034719f5e | ||
|   | 2c00043a7f | ||
|   | 65c695d9ce | ||
|   | 57253fe46a | ||
|   | 4e5c443440 | ||
|   | 0b3b73d8ec | ||
|   | 921eabc134 | ||
|   | 0faa428751 | ||
|   | f71a2fdd63 | ||
|   | 4eb9ed8aba | ||
|   | d7b549abb8 | ||
|   | 95d723c578 | ||
|   | 2fcd853e86 | ||
|   | 07eef7c812 | ||
|   | b01e0757fa | ||
|   | 32844a20c6 | ||
|   | 5b6532c601 | ||
|   | 2c5b4b4027 | ||
|   | 72d7ecf195 | ||
|   | 2cfa6b4306 | ||
|   | 6f6ffde0ab | ||
|   | 1694739a16 | ||
|   | 95d1e8bfca | ||
|   | 60dec08e3c | ||
|   | a99d71be93 | ||
|   | f1331b6a0c | ||
|   | 10d66b642b | ||
|   | cd2310e4a8 | ||
|   | 1b399cf6b0 | ||
|   | 877445bc0a | ||
|   | 9a5b345bde | ||
|   | fc9e8ea7b3 | ||
|   | 32be6fcfc1 | ||
|   | 49847236c2 | ||
|   | d8424443e6 | ||
|   | f3b571ec3f | ||
|   | 99318bb5d7 | ||
|   | 1aa154c9aa | ||
|   | c65d8a445b | ||
|   | 80f4f85570 | ||
|   | 5beee43a6b | ||
|   | 8d6ae203a0 | ||
|   | 4353479a5c | ||
|   | 34d7687f9e | ||
|   | b1dc3cf4af | ||
|   | 6a58b95933 | ||
|   | d3badfd02b | ||
|   | 0098be057b | ||
|   | 6f972aa515 | ||
|   | 7407ba6313 | ||
|   | 1c79de207b | ||
|   | 257c79db92 | ||
|   | 9d1934a308 | ||
|   | d70f959902 | ||
|   | e4d810222f | ||
|   | bc1af4ae07 | ||
|   | 6e688ef43f | ||
|   | f0fe1b23dc | ||
|   | aaf2006401 | ||
|   | b821e26935 | ||
|   | 7ae4287157 | ||
|   | c6fcc38a65 | ||
|   | ab2d5c8853 | ||
|   | 5e557ff0bc | ||
|   | 918ca449a1 | ||
|   | 8e73368008 | ||
|   | f3c1faf672 | ||
|   | d6df04dd6a | ||
|   | b1b9e51ab6 | ||
|   | e49d4770ac | ||
|   | 8fa1075511 | ||
|   | 9a70169b94 | ||
|   | fefb928237 | ||
|   | ad7e700d0d | ||
|   | 1699c69147 | ||
|   | 1695f7cece | ||
|   | 052c27f907 | ||
|   | dc46c32b30 | ||
|   | fa63349bb2 | ||
|   | ffe26448a6 | ||
|   | 4af51e8a84 | ||
|   | 1e453cf5a5 | ||
|   | 591282b87d | ||
|   | e87528d520 | ||
|   | d79eb0411d | ||
|   | ac1e0a4cf7 | ||
|   | 9525eab130 | ||
|   | 89b317496c | ||
|   | 13be91e78b | ||
|   | f68c1437f3 | ||
|   | 4c64c969bb | ||
|   | b4bf3b5138 | ||
|   | 083bc4b400 | ||
|   | e8683c5bcc | ||
|   | 80e0d1de91 | ||
|   | dbe841037e | ||
|   | bdd537c33c | ||
|   | c0c3846094 | ||
|   | 9e8710e7d2 | ||
|   | 475553fdf6 | ||
|   | 9d570f5b45 | ||
|   | af7fafd34f | ||
|   | d43130f4fc | ||
|   | 7500194620 | ||
|   | eb27c29144 | ||
|   | 43260b3e24 | ||
|   | f80713f0aa | ||
|   | 0c4bdc7ad1 | ||
|   | 811cff7bd0 | ||
|   | 30269aa75c | ||
|   | e345ef7083 | ||
|   | f559c9b8f7 | ||
|   | f4af0916b2 | ||
|   | f15f14f28d | ||
|   | 834f44f58d | ||
|   | b36f45dcf4 | ||
|   | 11ba21c9a8 | ||
|   | b045557ce1 | ||
|   | 0dd251a3f6 | ||
|   | 793acb1725 | ||
|   | 921243e8bd | ||
|   | bd9d7a90d9 | ||
|   | cc444a4cea | ||
|   | 38ca1fa168 | ||
|   | 7a552b87ec | ||
|   | 36923d3190 | ||
|   | a9d3017123 | ||
|   | 313acd4976 | ||
|   | a4c91bb268 | ||
|   | f9b566984b | ||
|   | 8dd261854d | ||
|   | 7351e62d87 | ||
|   | 0593ae720b | ||
|   | a0a7b08e08 | ||
|   | 9a3bc6b8b3 | ||
|   | 5acae17f71 | ||
|   | f1e5b76ef2 | ||
|   | 53c628fde9 | ||
|   | baca0a70c0 | ||
|   | 3e8d0af404 | ||
|   | cf9a91d9d5 | ||
|   | 02b9e282c6 | ||
|   | 9ce87f235f | ||
|   | e329bea1b2 | ||
|   | 8086e7b54d | ||
|   | f7a875606e | ||
|   | 196eaf85f4 | ||
|   | 876a55668e | ||
|   | 05bd21bdd5 | ||
|   | fb51a08cc6 | ||
|   | dd83d7f4d3 | ||
|   | 842a56f7ce | ||
|   | 9246a6e797 | ||
|   | 8ad693f717 | ||
|   | f4c2ee7cc4 | ||
|   | 6043441faa | ||
|   | 4a065c3710 | ||
|   | 0ef800bdd7 | ||
|   | 56eaa1910d | ||
|   | 201788e286 | ||
|   | 506e0f144f | ||
|   | 72f68bfdd9 | ||
|   | 2f9869b11d | ||
|   | 8ffcf6498c | ||
|   | d224ae1923 | ||
|   | fed2063a19 | ||
|   | db2810cdd7 | ||
|   | 4f1a6781ef | ||
|   | beffa5d5a4 | ||
|   | 7a20f1de07 | ||
|   | cd25cf726b | ||
|   | d6b1bc3842 | ||
|   | a4385fb9bb | ||
|   | 7045f2b8ea | ||
|   | 07ca1a4de8 | ||
|   | 24f289e692 | ||
|   | 01bcdaae2d | ||
|   | 55890008d1 | ||
|   | 5ab9b01879 | ||
|   | e4abb333b3 | ||
|   | 09f476c745 | ||
|   | 8806e68dce | ||
|   | 2ef1e25cd8 | ||
|   | 10e7f202aa | ||
|   | ccd7000c09 | ||
|   | 8ee7b798cf | ||
|   | 7733cf5bf0 | ||
|   | a05ce86dd7 | ||
|   | 91f51c32e8 | ||
|   | f910202bba | ||
|   | 6d77194a8f | ||
|   | 9deb89c15f | ||
|   | 4b62a092b4 | ||
|   | 81c8f626f9 | ||
|   | 3e846c42fb | ||
|   | 63ad7fd766 | ||
|   | 9ff1e9aa34 | ||
|   | 8d162b6f3d | ||
|   | 9844d10bef | ||
|   | b908fa8489 | ||
|   | 15a10643a7 | ||
|   | 299617aca1 | ||
|   | 45647d697a | ||
|   | 48f5105d38 | ||
|   | fe1c741d68 | ||
|   | fa42cc1f00 | ||
|   | 42cf5e7a81 | ||
|   | 47905e1aa1 | ||
|   | 9a8e907df3 | ||
|   | 106fe85582 | ||
|   | 4b3571bd57 | ||
|   | 96b537401a | ||
|   | 721c9eb057 | ||
|   | 51701bf6d6 | ||
|   | dbde68bd56 | ||
|   | ad2c9f585a | ||
|   | 562093c468 | ||
|   | b0295584a3 | ||
|   | 208c54de98 | ||
|   | 63e2d941a1 | ||
|   | 3956838e9c | ||
|   | abeee58bb0 | ||
|   | d5b1b49722 | ||
|   | 564ed03ff8 | ||
|   | 70db4c76b4 | ||
|   | d059f7975b | ||
|   | 4e74e6dc2d | ||
|   | b6deb96658 | ||
|   | 3839e966be | ||
|   | 3dd035849c | ||
|   | 3d6532b5d6 | ||
|   | bf7c175ee7 | ||
|   | f84af35ed6 | ||
|   | 99063b3eb1 | ||
|   | 3bec18f28d | ||
|   | 15de7a7894 | ||
|   | e20e04e677 | ||
|   | 5fc6ae2835 | ||
|   | 7d281b8c96 | ||
|   | 4880b801a7 | ||
|   | 74e354456a | ||
|   | af2e03aa36 | ||
|   | d8fa660ab6 | ||
|   | 1a62d48297 | ||
|   | 7ba01be13d | ||
|   | 1a83d64db7 | ||
|   | 5b53014c40 | ||
|   | 83685340af | ||
|   | 31e0cc4dec | ||
|   | 56b87fc1f5 | ||
|   | 6b956a2dd7 | ||
|   | 1937623d7d | ||
|   | 3b60b10945 | ||
|   | 7173acd350 | ||
|   | 6310d87338 | ||
|   | 49a1ed7c18 | ||
|   | d426e280d9 | ||
|   | 6154fb29f1 | ||
|   | 97d48ef9d6 | ||
|   | 88992625c4 | ||
|   | bc6eb44218 | ||
|   | cf9ccd799d | ||
|   | ffa0e4e771 | ||
|   | 60fa9c196c | ||
|   | df860d22fb | ||
|   | cb46ff326c | ||
|   | f277a853ef | ||
|   | 9ae34f67c3 | ||
|   | c9223218cc | ||
|   | c0dd645aba | ||
|   | 2e948eb5b6 | ||
|   | c3276889cf | ||
|   | a76ca8282d | ||
|   | 8ce6b8362f | ||
|   | 842fb12f05 | ||
|   | d63e1511af | ||
|   | 278783b8e0 | ||
|   | d24e3c922d | ||
|   | 1d02cd2283 | ||
|   | 8edeb82a87 | ||
|   | 146e9279de | ||
|   | 47105f50a9 | ||
|   | 16c9c80f37 | ||
|   | 8e7e4bc95a | ||
|   | 0aa3d2f930 | ||
|   | ce77755a1e | ||
|   | 0f31f20c87 | ||
|   | ee6da2aaa5 | ||
|   | a35f087cd9 | ||
|   | 6e029b44dd | ||
|   | 973c0cff34 | ||
|   | 2027eea6ac | ||
|   | 2f43692f33 | ||
|   | 6d24992f88 | ||
|   | b4388a58d6 | ||
|   | 158aa05fac | ||
|   | f2731bf55e | ||
|   | 7304e99fce | ||
|   | 02700b83eb | ||
|   | 676b25acf9 | ||
|   | 556359ea2d | ||
|   | b72923e0f5 | ||
|   | 115ac9f75e | ||
|   | 32e36f6708 | ||
|   | d949b7a4f9 | ||
|   | eae1171ff5 | ||
|   | 76a1b75a51 | ||
|   | 8882c0daea | ||
|   | 07ebc16d59 | ||
|   | 0ceb109964 | ||
|   | 118b0d0038 | ||
|   | 5e87067792 | ||
|   | c946a252e8 | ||
|   | f9ad2ba1dd | ||
|   | 0d0ecd33bd | ||
|   | e4b98fd05b | ||
|   | 95a5933303 | ||
|   | da3b55fa64 | ||
|   | fbbabfb90e | ||
|   | f13da6830d | ||
|   | f560a8e2f8 | ||
|   | 56f1139c2f | ||
|   | 773bdfc1e2 | ||
|   | f449666628 | ||
|   | 3f282de0ab | ||
|   | 440dd8d22f | ||
|   | dcff9de2f7 | ||
|   | a192866543 | ||
|   | 10081416de | ||
|   | e2bed618f9 | ||
|   | 03ab1f3823 | ||
|   | ac8aeb63d9 | ||
|   | 2e16d822fa | ||
|   | e407d873fa | ||
|   | fd712a1dbe | ||
|   | e9028b40ce | ||
|   | c9da3dee7c | ||
|   | c8c224e202 | ||
|   | f34559daaf | ||
|   | 9fefbf4c27 | ||
|   | 1af9fd73ea | ||
|   | 75ef394eff | ||
|   | ec6cc2c63e | ||
|   | 06bc2e192b | ||
|   | 78701ec7c1 | ||
|   | c925fab7e4 | ||
|   | 42fd72c164 | ||
|   | 7fd160e1a2 | ||
|   | 97a0d940eb | ||
|   | efaa099d81 | ||
|   | 47864a804b | ||
|   | 91136c0e43 | ||
|   | 28c3b1bd61 | ||
|   | 551352bc40 | ||
|   | e73c24c925 | ||
|   | 7ec4c286cc | ||
|   | 6705e2ec4b | ||
|   | 6f0373063b | ||
|   | f64eef60b5 | ||
|   | 89546bf86b | ||
|   | 793678feca | ||
|   | 923cc3019a | ||
|   | 10eb98a5f6 | ||
|   | bd9e89d8dd | ||
|   | 1926b4ce73 | ||
|   | 4ef3062d74 | ||
|   | abb6e0f60f | ||
|   | f204d8d84e | ||
|   | fa301656f1 | ||
|   | 7e1221028f | ||
|   | 41308cb2dd | ||
|   | 130600521c | ||
|   | cd57548a48 | ||
|   | efacc99f76 | ||
|   | f0d236e172 | ||
|   | a8118bd8c6 | ||
|   | 0e58f2ef53 | ||
|   | f4b22b3a0c | ||
|   | df5bd281c7 | ||
|   | a3f23837ce | ||
|   | 612d989b97 | ||
|   | 42c01ee9a2 | ||
|   | 14074db591 | ||
|   | 43dfdd7942 | ||
|   | f397b97ccf | ||
|   | 95f8716144 | ||
|   | 17ba472b2e | ||
|   | 42d82571ab | ||
|   | 9119a28141 | ||
|   | a32263d838 | ||
|   | 208ae2bb88 | ||
|   | 4d85462a85 | ||
|   | f601aa9ca0 | ||
|   | 8aee3ad455 | ||
|   | 6a2a1e9561 | ||
|   | 5f8786c9dc | ||
|   | 73f1d3eead | ||
|   | 2bf21bb3c3 | ||
|   | f80f0dbb11 | ||
|   | 37518c70c4 | ||
|   | e5951b5bef | ||
|   | ab320bd90b | ||
|   | 7bd36b5371 | ||
|   | b882b0f2bc | ||
|   | 38d7ae73cc | ||
|   | 4527c6ee5d | ||
|   | 85829e70c1 | ||
|   | 256c08d82a | ||
|   | c2ce03c047 | ||
|   | f2af19e198 | ||
|   | 930b7c092d | ||
|   | 00757c69c6 | ||
|   | 55f267d0fc | ||
|   | 6b96aff6e8 | ||
|   | 32b773a8fa | ||
|   | 03089adad6 | ||
|   | 4a1fe746ab | ||
|   | aa52c05d2c | ||
|   | 26407a43e7 | ||
|   | a02934bf19 | ||
|   | 09c65fba09 | ||
|   | 4305c727d0 | ||
|   | 188339897f | ||
|   | 4ecff9a707 | ||
|   | 355aed49c6 | ||
|   | 4717b6b0f0 | ||
|   | 45ebe9048d | ||
|   | b2170c49a3 | ||
|   | dc2f4d6115 | ||
|   | 1eb132440f | ||
|   | a464bbc37a | ||
|   | ed995697c2 | ||
|   | 163cd84c7b | ||
|   | 293d7cc292 | ||
|   | 5de1b4e74c | ||
|   | 7b474975da | ||
|   | beab51516b | ||
|   | fe8685a50c | ||
|   | f9af5d0885 | ||
|   | e8136a9720 | ||
|   | 531e5d4556 | ||
|   | e66255963a | ||
|   | 246aac8ee4 | ||
|   | 23cfeff685 | ||
|   | a5e7e0d126 | ||
|   | 5bebc30ba0 | ||
|   | 0e7057f5b9 | ||
|   | 7c6c365ba4 | ||
|   | 424c9bb0c5 | ||
|   | 9d0f26594c | ||
|   | 99c17de079 | ||
|   | b1e3dd0af6 | ||
|   | 261cb89530 | ||
|   | ff6773ba37 | ||
|   | bdfbbfcbbd | ||
|   | 0c4cd56758 | ||
|   | 4a36658321 | ||
|   | 7aae938685 | ||
|   | 3723401e7a | ||
|   | 70631366a9 | ||
|   | 0e40bbda3e | ||
|   | e9aa475398 | ||
|   | 8d2a811184 | ||
|   | dd7f5b6700 | ||
|   | a4f6277737 | ||
|   | c2bfaacbb7 | ||
|   | a17cbfa2d4 | ||
|   | fb9a101555 | ||
|   | e319cf0200 | ||
|   | 0a8395ef6a | ||
|   | 38df5e01be | ||
|   | ebd891a868 | ||
|   | 4ab2395cbe | ||
|   | 5f1f989fc9 | ||
|   | 44b709eee3 | ||
|   | d0d7726597 | ||
|   | 054c342aeb | ||
|   | c79c33baf7 | ||
|   | 23b00e35b2 | ||
|   | fe51079266 | ||
|   | 0791b0bbee | ||
|   | dbf04c8eeb | ||
|   | 6204256df8 | ||
|   | 93cc8c2327 | ||
|   | 68a2e5bbbc | ||
|   | 72792153f2 | ||
|   | 88b6ef1897 | 
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -362,8 +362,5 @@ MigrationBackup/ | |||||||
| # Fody - auto-generated XML schema | # Fody - auto-generated XML schema | ||||||
| FodyWeavers.xsd | FodyWeavers.xsd | ||||||
|  |  | ||||||
| /src/ThingsGateway.Web.Server/Default.db |  | ||||||
| /src/PluginPro*/ | /framework/*Pro* | ||||||
| /src/*Pro* |  | ||||||
| /src/TestResults*/ |  | ||||||
| /src/ThingsGateway.Web.Server/ThingsGateway.db |  | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								Image/1.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 135 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/2.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 125 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/3.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 217 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/4.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 119 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/5.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 189 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/6.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 151 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/7.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 187 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/8.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 164 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/9.png
									
									
									
									
									
								
							
							
						
						| Before Width: | Height: | Size: 149 KiB | 
							
								
								
									
										21
									
								
								LICENSE.txt
									
									
									
									
									
								
							
							
						
						| @@ -1,3 +1,4 @@ | |||||||
|  |  | ||||||
|                                  Apache License |                                  Apache License | ||||||
|                            Version 2.0, January 2004 |                            Version 2.0, January 2004 | ||||||
|                         http://www.apache.org/licenses/ |                         http://www.apache.org/licenses/ | ||||||
| @@ -186,16 +187,16 @@ | |||||||
|       same "printed page" as the copyright notice for easier |       same "printed page" as the copyright notice for easier | ||||||
|       identification within third-party archives. |       identification within third-party archives. | ||||||
|  |  | ||||||
|    Copyright [yyyy] [name of copyright owner] |    Copyright 2023-present Diego | ||||||
|  |  | ||||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|    you may not use this file except in compliance with the License. | you may not use this file except in compliance with the License. | ||||||
|    You may obtain a copy of the License at | You may obtain a copy of the License at | ||||||
|  |  | ||||||
|        http://www.apache.org/licenses/LICENSE-2.0 |     http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  | ||||||
|    Unless required by applicable law or agreed to in writing, software | Unless required by applicable law or agreed to in writing, software | ||||||
|    distributed under the License is distributed on an "AS IS" BASIS, | distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|    See the License for the specific language governing permissions and | See the License for the specific language governing permissions and | ||||||
|    limitations under the License. | limitations under the License. | ||||||
							
								
								
									
										82
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,78 +1,38 @@ | |||||||
| <div align="center"><h1 align="center">ThingsGateway</a></h1></div> |  | ||||||
| <div align="center"><h3 align="center">边缘采集网关</h3></div> |  | ||||||
|  |  | ||||||
| #### 介绍 | # ThingsGateway | ||||||
|  |  | ||||||
| 基于[ThingsBlazor](https://gitee.com/diego2098/ThingsBlazor)权限管理框架开发的跨平台边缘采集网关,支持南北端插件式开发, | ## 介绍 | ||||||
| 动态更新插件, |  | ||||||
| 并拥有较完善的北端Rpc权限管理。 |  **NetCore** 跨平台边缘采集网关(工业设备采集) | ||||||
|  |  | ||||||
|  |  **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | ||||||
|  |  | ||||||
|  |  **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin** | ||||||
|  |  | ||||||
|  |  | ||||||
| ####  功能亮点 | ## 文档 | ||||||
|  |  | ||||||
| - Blazor Server架构,开发部署更简单 | [ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。 | ||||||
| - 采集/上传配置完全支持Excel导入导出 |  | ||||||
| - 插件式驱动,方便驱动二次开发,并支持动态更新 |  | ||||||
| - Modbus、OPCDA、OPCUA采集插件(支持导入节点) |  | ||||||
| - Modbus Server、Mqtt Server(Client)、OPC UAServer插件,支持Rpc写入,支持WebApi |  | ||||||
| - 支持时序数据库存储 |  | ||||||
| - 实时/历史报警(Sql转储),支持布尔/高低限值 |  | ||||||
|  |  | ||||||
| #### nuget | ## 协议 | ||||||
|  |  | ||||||
| - Modbus库,支持ModbusTcp、ModbusRtuOverTcp、ModbusUdp、ModbusServer等 | [ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。 | ||||||
| ``` powershell |  | ||||||
|  dotnet add package ThingsGateway.Foundation.Adapter.Modbus |  | ||||||
| ``` |  | ||||||
| - OPCDA客户端库,支持X64,支持NetCore,支持检测重连 |  | ||||||
| ``` powershell |  | ||||||
|  dotnet add package ThingsGateway.Foundation.Adapter.OPCDA |  | ||||||
| ``` |  | ||||||
| - OPCUA客户端库 |  | ||||||
| ``` powershell |  | ||||||
|  dotnet add package ThingsGateway.Foundation.Adapter.OPCUA |  | ||||||
| ``` |  | ||||||
| ####  效果图 |  | ||||||
|  <table> |  | ||||||
|     <tr> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/1.png"/></td> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/2.png"/></td> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/3.png"/></td> |  | ||||||
|     </tr> |  | ||||||
|     <tr> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/4.png"/></td> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/5.png"/></td> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/6.png"/></td> |  | ||||||
|     </tr> |  | ||||||
|         <tr> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/7.png"/></td> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/8.png"/></td> |  | ||||||
|         <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/9.png"/></td> |  | ||||||
|     </tr> |  | ||||||
|  </table> |  | ||||||
|  |  | ||||||
|  | ## 演示 | ||||||
|  |  | ||||||
|  ####  文档 | [ThingsGateway演示地址](http://120.24.62.140:5000/) | ||||||
|  |  | ||||||
|  使用前请查看Gitee Pages [文档站点](https://diego2098.gitee.io/thingsgateway/) | 账户	:  **superAdmin**	 | ||||||
|  |  | ||||||
|  | 密码 : **111111** | ||||||
|  |  | ||||||
| #### 补充说明 | ## 赞助 | ||||||
| * 使用OPC相关插件时请遵循OPC基金会的授权规则 |  | ||||||
|  |  | ||||||
| #### 开源协议 | [ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate) | ||||||
|  |  | ||||||
| 请仔细阅读 [授权协议](https://diego2098.gitee.io/thingsgateway/docs/) | ## 社区 | ||||||
|  |  | ||||||
|  | QQ群:605534569 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ####  支持作者 |  | ||||||
|  如果对您有帮助,请点击右上角⭐Star关注,感谢支持开源! |  | ||||||
|  若希望捐赠项目,请跳转到文档站点查看捐赠码或使用Gitee捐赠功能 |  | ||||||
|   |  | ||||||
|  |  | ||||||
| ####  联系作者 |  | ||||||
|  * QQ群:605534569 |  | ||||||
|  * 邮箱:2248356998@qq.com |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										103
									
								
								framework/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,103 @@ | |||||||
|  | [*.cs] | ||||||
|  |  | ||||||
|  | # CA1848: 使用 LoggerMessage 委托 | ||||||
|  | dotnet_diagnostic.CA1848.severity = none | ||||||
|  |  | ||||||
|  | # CA2254: 模板应为静态表达式 | ||||||
|  | dotnet_diagnostic.CA2254.severity = suggestion | ||||||
|  |  | ||||||
|  | [*.cs] | ||||||
|  | #### 命名样式 #### | ||||||
|  |  | ||||||
|  | # 命名规则 | ||||||
|  |  | ||||||
|  | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion | ||||||
|  | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface | ||||||
|  | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i | ||||||
|  |  | ||||||
|  | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion | ||||||
|  | dotnet_naming_rule.types_should_be_pascal_case.symbols = types | ||||||
|  | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case | ||||||
|  |  | ||||||
|  | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion | ||||||
|  | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members | ||||||
|  | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case | ||||||
|  |  | ||||||
|  | # 符号规范 | ||||||
|  |  | ||||||
|  | dotnet_naming_symbols.interface.applicable_kinds = interface | ||||||
|  | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||||
|  | dotnet_naming_symbols.interface.required_modifiers =  | ||||||
|  |  | ||||||
|  | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum | ||||||
|  | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||||
|  | dotnet_naming_symbols.types.required_modifiers =  | ||||||
|  |  | ||||||
|  | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method | ||||||
|  | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected | ||||||
|  | dotnet_naming_symbols.non_field_members.required_modifiers =  | ||||||
|  |  | ||||||
|  | # 命名样式 | ||||||
|  |  | ||||||
|  | dotnet_naming_style.begins_with_i.required_prefix = I | ||||||
|  | dotnet_naming_style.begins_with_i.required_suffix =  | ||||||
|  | dotnet_naming_style.begins_with_i.word_separator =  | ||||||
|  | dotnet_naming_style.begins_with_i.capitalization = pascal_case | ||||||
|  |  | ||||||
|  | dotnet_naming_style.pascal_case.required_prefix =  | ||||||
|  | dotnet_naming_style.pascal_case.required_suffix =  | ||||||
|  | dotnet_naming_style.pascal_case.word_separator =  | ||||||
|  | dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||||
|  |  | ||||||
|  | dotnet_naming_style.pascal_case.required_prefix =  | ||||||
|  | dotnet_naming_style.pascal_case.required_suffix =  | ||||||
|  | dotnet_naming_style.pascal_case.word_separator =  | ||||||
|  | dotnet_naming_style.pascal_case.capitalization = pascal_case | ||||||
|  |  | ||||||
|  | [*.vb] | ||||||
|  | #### 命名样式 #### | ||||||
|  |  | ||||||
|  | # 命名规则 | ||||||
|  |  | ||||||
|  | dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion | ||||||
|  | dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface | ||||||
|  | dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始 | ||||||
|  |  | ||||||
|  | dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion | ||||||
|  | dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 | ||||||
|  | dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 | ||||||
|  |  | ||||||
|  | dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion | ||||||
|  | dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 | ||||||
|  | dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 | ||||||
|  |  | ||||||
|  | # 符号规范 | ||||||
|  |  | ||||||
|  | dotnet_naming_symbols.interface.applicable_kinds = interface | ||||||
|  | dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||||
|  | dotnet_naming_symbols.interface.required_modifiers =  | ||||||
|  |  | ||||||
|  | dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum | ||||||
|  | dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||||
|  | dotnet_naming_symbols.类型.required_modifiers =  | ||||||
|  |  | ||||||
|  | dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method | ||||||
|  | dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected | ||||||
|  | dotnet_naming_symbols.非字段成员.required_modifiers =  | ||||||
|  |  | ||||||
|  | # 命名样式 | ||||||
|  |  | ||||||
|  | dotnet_naming_style.以_i_开始.required_prefix = I | ||||||
|  | dotnet_naming_style.以_i_开始.required_suffix =  | ||||||
|  | dotnet_naming_style.以_i_开始.word_separator =  | ||||||
|  | dotnet_naming_style.以_i_开始.capitalization = pascal_case | ||||||
|  |  | ||||||
|  | dotnet_naming_style.帕斯卡拼写法.required_prefix =  | ||||||
|  | dotnet_naming_style.帕斯卡拼写法.required_suffix =  | ||||||
|  | dotnet_naming_style.帕斯卡拼写法.word_separator =  | ||||||
|  | dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case | ||||||
|  |  | ||||||
|  | dotnet_naming_style.帕斯卡拼写法.required_prefix =  | ||||||
|  | dotnet_naming_style.帕斯卡拼写法.required_suffix =  | ||||||
|  | dotnet_naming_style.帕斯卡拼写法.word_separator =  | ||||||
|  | dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case | ||||||
							
								
								
									
										63
									
								
								framework/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,63 @@ | |||||||
|  | ############################################################################### | ||||||
|  | # Set default behavior to automatically normalize line endings. | ||||||
|  | ############################################################################### | ||||||
|  | * text=auto | ||||||
|  |  | ||||||
|  | ############################################################################### | ||||||
|  | # Set default behavior for command prompt diff. | ||||||
|  | # | ||||||
|  | # This is need for earlier builds of msysgit that does not have it on by | ||||||
|  | # default for csharp files. | ||||||
|  | # Note: This is only used by command line | ||||||
|  | ############################################################################### | ||||||
|  | #*.cs     diff=csharp | ||||||
|  |  | ||||||
|  | ############################################################################### | ||||||
|  | # Set the merge driver for project and solution files | ||||||
|  | # | ||||||
|  | # Merging from the command prompt will add diff markers to the files if there | ||||||
|  | # are conflicts (Merging from VS is not affected by the settings below, in VS | ||||||
|  | # the diff markers are never inserted). Diff markers may cause the following  | ||||||
|  | # file extensions to fail to load in VS. An alternative would be to treat | ||||||
|  | # these files as binary and thus will always conflict and require user | ||||||
|  | # intervention with every merge. To do so, just uncomment the entries below | ||||||
|  | ############################################################################### | ||||||
|  | #*.sln       merge=binary | ||||||
|  | #*.csproj    merge=binary | ||||||
|  | #*.vbproj    merge=binary | ||||||
|  | #*.vcxproj   merge=binary | ||||||
|  | #*.vcproj    merge=binary | ||||||
|  | #*.dbproj    merge=binary | ||||||
|  | #*.fsproj    merge=binary | ||||||
|  | #*.lsproj    merge=binary | ||||||
|  | #*.wixproj   merge=binary | ||||||
|  | #*.modelproj merge=binary | ||||||
|  | #*.sqlproj   merge=binary | ||||||
|  | #*.wwaproj   merge=binary | ||||||
|  |  | ||||||
|  | ############################################################################### | ||||||
|  | # behavior for image files | ||||||
|  | # | ||||||
|  | # image files are treated as binary by default. | ||||||
|  | ############################################################################### | ||||||
|  | #*.jpg   binary | ||||||
|  | #*.png   binary | ||||||
|  | #*.gif   binary | ||||||
|  |  | ||||||
|  | ############################################################################### | ||||||
|  | # diff behavior for common document formats | ||||||
|  | #  | ||||||
|  | # Convert binary document formats to text before diffing them. This feature | ||||||
|  | # is only available from the command line. Turn it on by uncommenting the  | ||||||
|  | # entries below. | ||||||
|  | ############################################################################### | ||||||
|  | #*.doc   diff=astextplain | ||||||
|  | #*.DOC   diff=astextplain | ||||||
|  | #*.docx  diff=astextplain | ||||||
|  | #*.DOCX  diff=astextplain | ||||||
|  | #*.dot   diff=astextplain | ||||||
|  | #*.DOT   diff=astextplain | ||||||
|  | #*.pdf   diff=astextplain | ||||||
|  | #*.PDF   diff=astextplain | ||||||
|  | #*.rtf   diff=astextplain | ||||||
|  | #*.RTF   diff=astextplain | ||||||
							
								
								
									
										364
									
								
								framework/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,364 @@ | |||||||
|  | ## Ignore Visual Studio temporary files, build results, and | ||||||
|  | ## files generated by popular Visual Studio add-ons. | ||||||
|  | ## | ||||||
|  | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore | ||||||
|  |  | ||||||
|  | # User-specific files | ||||||
|  | *.rsuser | ||||||
|  | *.suo | ||||||
|  | *.user | ||||||
|  | *.userosscache | ||||||
|  | *.sln.docstates | ||||||
|  |  | ||||||
|  | # User-specific files (MonoDevelop/Xamarin Studio) | ||||||
|  | *.userprefs | ||||||
|  |  | ||||||
|  | # Mono auto generated files | ||||||
|  | mono_crash.* | ||||||
|  |  | ||||||
|  | # Build results | ||||||
|  | [Dd]ebug/ | ||||||
|  | [Dd]ebugPublic/ | ||||||
|  | [Rr]elease/ | ||||||
|  | [Rr]eleases/ | ||||||
|  | x64/ | ||||||
|  | x86/ | ||||||
|  | [Ww][Ii][Nn]32/ | ||||||
|  | [Aa][Rr][Mm]/ | ||||||
|  | [Aa][Rr][Mm]64/ | ||||||
|  | bld/ | ||||||
|  | [Bb]in/ | ||||||
|  | [Oo]bj/ | ||||||
|  | [Oo]ut/ | ||||||
|  | [Ll]og/ | ||||||
|  | [Ll]ogs/ | ||||||
|  |  | ||||||
|  | # Visual Studio 2015/2017 cache/options directory | ||||||
|  | .vs/ | ||||||
|  | # Uncomment if you have tasks that create the project's static files in wwwroot | ||||||
|  | #wwwroot/ | ||||||
|  |  | ||||||
|  | # Visual Studio 2017 auto generated files | ||||||
|  | Generated\ Files/ | ||||||
|  |  | ||||||
|  | # MSTest test Results | ||||||
|  | [Tt]est[Rr]esult*/ | ||||||
|  | [Bb]uild[Ll]og.* | ||||||
|  |  | ||||||
|  | # NUnit | ||||||
|  | *.VisualState.xml | ||||||
|  | TestResult.xml | ||||||
|  | nunit-*.xml | ||||||
|  |  | ||||||
|  | # Build Results of an ATL Project | ||||||
|  | [Dd]ebugPS/ | ||||||
|  | [Rr]eleasePS/ | ||||||
|  | dlldata.c | ||||||
|  |  | ||||||
|  | # Benchmark Results | ||||||
|  | BenchmarkDotNet.Artifacts/ | ||||||
|  |  | ||||||
|  | # .NET Core | ||||||
|  | project.lock.json | ||||||
|  | project.fragment.lock.json | ||||||
|  | artifacts/ | ||||||
|  |  | ||||||
|  | # ASP.NET Scaffolding | ||||||
|  | ScaffoldingReadMe.txt | ||||||
|  |  | ||||||
|  | # StyleCop | ||||||
|  | StyleCopReport.xml | ||||||
|  |  | ||||||
|  | # Files built by Visual Studio | ||||||
|  | *_i.c | ||||||
|  | *_p.c | ||||||
|  | *_h.h | ||||||
|  | *.ilk | ||||||
|  | *.meta | ||||||
|  | *.obj | ||||||
|  | *.iobj | ||||||
|  | *.pch | ||||||
|  | *.pdb | ||||||
|  | *.ipdb | ||||||
|  | *.pgc | ||||||
|  | *.pgd | ||||||
|  | *.rsp | ||||||
|  | *.sbr | ||||||
|  | *.tlb | ||||||
|  | *.tli | ||||||
|  | *.tlh | ||||||
|  | *.tmp | ||||||
|  | *.tmp_proj | ||||||
|  | *_wpftmp.csproj | ||||||
|  | *.log | ||||||
|  | *.vspscc | ||||||
|  | *.vssscc | ||||||
|  | .builds | ||||||
|  | *.pidb | ||||||
|  | *.svclog | ||||||
|  | *.scc | ||||||
|  |  | ||||||
|  | # Chutzpah Test files | ||||||
|  | _Chutzpah* | ||||||
|  |  | ||||||
|  | # Visual C++ cache files | ||||||
|  | ipch/ | ||||||
|  | *.aps | ||||||
|  | *.ncb | ||||||
|  | *.opendb | ||||||
|  | *.opensdf | ||||||
|  | *.sdf | ||||||
|  | *.cachefile | ||||||
|  | *.VC.db | ||||||
|  | *.VC.VC.opendb | ||||||
|  |  | ||||||
|  | # Visual Studio profiler | ||||||
|  | *.psess | ||||||
|  | *.vsp | ||||||
|  | *.vspx | ||||||
|  | *.sap | ||||||
|  |  | ||||||
|  | # Visual Studio Trace Files | ||||||
|  | *.e2e | ||||||
|  |  | ||||||
|  | # TFS 2012 Local Workspace | ||||||
|  | $tf/ | ||||||
|  |  | ||||||
|  | # Guidance Automation Toolkit | ||||||
|  | *.gpState | ||||||
|  |  | ||||||
|  | # ReSharper is a .NET coding add-in | ||||||
|  | _ReSharper*/ | ||||||
|  | *.[Rr]e[Ss]harper | ||||||
|  | *.DotSettings.user | ||||||
|  |  | ||||||
|  | # TeamCity is a build add-in | ||||||
|  | _TeamCity* | ||||||
|  |  | ||||||
|  | # DotCover is a Code Coverage Tool | ||||||
|  | *.dotCover | ||||||
|  |  | ||||||
|  | # AxoCover is a Code Coverage Tool | ||||||
|  | .axoCover/* | ||||||
|  | !.axoCover/settings.json | ||||||
|  |  | ||||||
|  | # Coverlet is a free, cross platform Code Coverage Tool | ||||||
|  | coverage*.json | ||||||
|  | coverage*.xml | ||||||
|  | coverage*.info | ||||||
|  |  | ||||||
|  | # Visual Studio code coverage results | ||||||
|  | *.coverage | ||||||
|  | *.coveragexml | ||||||
|  |  | ||||||
|  | # NCrunch | ||||||
|  | _NCrunch_* | ||||||
|  | .*crunch*.local.xml | ||||||
|  | nCrunchTemp_* | ||||||
|  |  | ||||||
|  | # MightyMoose | ||||||
|  | *.mm.* | ||||||
|  | AutoTest.Net/ | ||||||
|  |  | ||||||
|  | # Web workbench (sass) | ||||||
|  | .sass-cache/ | ||||||
|  |  | ||||||
|  | # Installshield output folder | ||||||
|  | [Ee]xpress/ | ||||||
|  |  | ||||||
|  | # DocProject is a documentation generator add-in | ||||||
|  | DocProject/buildhelp/ | ||||||
|  | DocProject/Help/*.HxT | ||||||
|  | DocProject/Help/*.HxC | ||||||
|  | DocProject/Help/*.hhc | ||||||
|  | DocProject/Help/*.hhk | ||||||
|  | DocProject/Help/*.hhp | ||||||
|  | DocProject/Help/Html2 | ||||||
|  | DocProject/Help/html | ||||||
|  |  | ||||||
|  | # Click-Once directory | ||||||
|  | publish/ | ||||||
|  |  | ||||||
|  | # Publish Web Output | ||||||
|  | *.[Pp]ublish.xml | ||||||
|  | *.azurePubxml | ||||||
|  | # Note: Comment the next line if you want to checkin your web deploy settings, | ||||||
|  | # but database connection strings (with potential passwords) will be unencrypted | ||||||
|  | *.pubxml | ||||||
|  | *.publishproj | ||||||
|  |  | ||||||
|  | # Microsoft Azure Web App publish settings. Comment the next line if you want to | ||||||
|  | # checkin your Azure Web App publish settings, but sensitive information contained | ||||||
|  | # in these scripts will be unencrypted | ||||||
|  | PublishScripts/ | ||||||
|  |  | ||||||
|  | # NuGet Packages | ||||||
|  | *.nupkg | ||||||
|  | # NuGet Symbol Packages | ||||||
|  | *.snupkg | ||||||
|  | # The packages folder can be ignored because of Package Restore | ||||||
|  | **/[Pp]ackages/* | ||||||
|  | # except build/, which is used as an MSBuild target. | ||||||
|  | !**/[Pp]ackages/build/ | ||||||
|  | # Uncomment if necessary however generally it will be regenerated when needed | ||||||
|  | #!**/[Pp]ackages/repositories.config | ||||||
|  | # NuGet v3's project.json files produces more ignorable files | ||||||
|  | *.nuget.props | ||||||
|  | *.nuget.targets | ||||||
|  |  | ||||||
|  | # Microsoft Azure Build Output | ||||||
|  | csx/ | ||||||
|  | *.build.csdef | ||||||
|  |  | ||||||
|  | # Microsoft Azure Emulator | ||||||
|  | ecf/ | ||||||
|  | rcf/ | ||||||
|  |  | ||||||
|  | # Windows Store app package directories and files | ||||||
|  | AppPackages/ | ||||||
|  | BundleArtifacts/ | ||||||
|  | Package.StoreAssociation.xml | ||||||
|  | _pkginfo.txt | ||||||
|  | *.appx | ||||||
|  | *.appxbundle | ||||||
|  | *.appxupload | ||||||
|  |  | ||||||
|  | # Visual Studio cache files | ||||||
|  | # files ending in .cache can be ignored | ||||||
|  | *.[Cc]ache | ||||||
|  | # but keep track of directories ending in .cache | ||||||
|  | !?*.[Cc]ache/ | ||||||
|  |  | ||||||
|  | # Others | ||||||
|  | ClientBin/ | ||||||
|  | ~$* | ||||||
|  | *~ | ||||||
|  | *.dbmdl | ||||||
|  | *.dbproj.schemaview | ||||||
|  | *.jfm | ||||||
|  | *.pfx | ||||||
|  | *.publishsettings | ||||||
|  | orleans.codegen.cs | ||||||
|  |  | ||||||
|  | # Including strong name files can present a security risk | ||||||
|  | # (https://github.com/github/gitignore/pull/2483#issue-259490424) | ||||||
|  | #*.snk | ||||||
|  |  | ||||||
|  | # Since there are multiple workflows, uncomment next line to ignore bower_components | ||||||
|  | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) | ||||||
|  | #bower_components/ | ||||||
|  |  | ||||||
|  | # RIA/Silverlight projects | ||||||
|  | Generated_Code/ | ||||||
|  |  | ||||||
|  | # Backup & report files from converting an old project file | ||||||
|  | # to a newer Visual Studio version. Backup files are not needed, | ||||||
|  | # because we have git ;-) | ||||||
|  | _UpgradeReport_Files/ | ||||||
|  | Backup*/ | ||||||
|  | UpgradeLog*.XML | ||||||
|  | UpgradeLog*.htm | ||||||
|  | ServiceFabricBackup/ | ||||||
|  | *.rptproj.bak | ||||||
|  |  | ||||||
|  | # SQL Server files | ||||||
|  | *.mdf | ||||||
|  | *.ldf | ||||||
|  | *.ndf | ||||||
|  |  | ||||||
|  | # Business Intelligence projects | ||||||
|  | *.rdl.data | ||||||
|  | *.bim.layout | ||||||
|  | *.bim_*.settings | ||||||
|  | *.rptproj.rsuser | ||||||
|  | *- [Bb]ackup.rdl | ||||||
|  | *- [Bb]ackup ([0-9]).rdl | ||||||
|  | *- [Bb]ackup ([0-9][0-9]).rdl | ||||||
|  |  | ||||||
|  | # Microsoft Fakes | ||||||
|  | FakesAssemblies/ | ||||||
|  |  | ||||||
|  | # GhostDoc plugin setting file | ||||||
|  | *.GhostDoc.xml | ||||||
|  |  | ||||||
|  | # Node.js Tools for Visual Studio | ||||||
|  | .ntvs_analysis.dat | ||||||
|  | node_modules/ | ||||||
|  |  | ||||||
|  | # Visual Studio 6 build log | ||||||
|  | *.plg | ||||||
|  |  | ||||||
|  | # Visual Studio 6 workspace options file | ||||||
|  | *.opt | ||||||
|  |  | ||||||
|  | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) | ||||||
|  | *.vbw | ||||||
|  |  | ||||||
|  | # Visual Studio LightSwitch build output | ||||||
|  | **/*.HTMLClient/GeneratedArtifacts | ||||||
|  | **/*.DesktopClient/GeneratedArtifacts | ||||||
|  | **/*.DesktopClient/ModelManifest.xml | ||||||
|  | **/*.Server/GeneratedArtifacts | ||||||
|  | **/*.Server/ModelManifest.xml | ||||||
|  | _Pvt_Extensions | ||||||
|  |  | ||||||
|  | # Paket dependency manager | ||||||
|  | .paket/paket.exe | ||||||
|  | paket-files/ | ||||||
|  |  | ||||||
|  | # FAKE - F# Make | ||||||
|  | .fake/ | ||||||
|  |  | ||||||
|  | # CodeRush personal settings | ||||||
|  | .cr/personal | ||||||
|  |  | ||||||
|  | # Python Tools for Visual Studio (PTVS) | ||||||
|  | __pycache__/ | ||||||
|  | *.pyc | ||||||
|  |  | ||||||
|  | # Cake - Uncomment if you are using it | ||||||
|  | # tools/** | ||||||
|  | # !tools/packages.config | ||||||
|  |  | ||||||
|  | # Tabs Studio | ||||||
|  | *.tss | ||||||
|  |  | ||||||
|  | # Telerik's JustMock configuration file | ||||||
|  | *.jmconfig | ||||||
|  |  | ||||||
|  | # BizTalk build output | ||||||
|  | *.btp.cs | ||||||
|  | *.btm.cs | ||||||
|  | *.odx.cs | ||||||
|  | *.xsd.cs | ||||||
|  |  | ||||||
|  | # OpenCover UI analysis results | ||||||
|  | OpenCover/ | ||||||
|  |  | ||||||
|  | # Azure Stream Analytics local run output | ||||||
|  | ASALocalRun/ | ||||||
|  |  | ||||||
|  | # MSBuild Binary and Structured Log | ||||||
|  | *.binlog | ||||||
|  |  | ||||||
|  | # NVidia Nsight GPU debugger configuration file | ||||||
|  | *.nvuser | ||||||
|  |  | ||||||
|  | # MFractors (Xamarin productivity tool) working folder | ||||||
|  | .mfractor/ | ||||||
|  |  | ||||||
|  | # Local History for Visual Studio | ||||||
|  | .localhistory/ | ||||||
|  |  | ||||||
|  | # BeatPulse healthcheck temp database | ||||||
|  | healthchecksdb | ||||||
|  |  | ||||||
|  | # Backup folder for Package Reference Convert tool in Visual Studio 2017 | ||||||
|  | MigrationBackup/ | ||||||
|  |  | ||||||
|  | # Ionide (cross platform F# VS Code tools) working folder | ||||||
|  | .ionide/ | ||||||
|  |  | ||||||
|  | # Fody - auto-generated XML schema | ||||||
|  | FodyWeavers.xsd | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								framework/Demo/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,15 @@ | |||||||
|  | <Project> | ||||||
|  | 	<PropertyGroup> | ||||||
|  | 		<Version>3.0.0.27</Version> | ||||||
|  | 		<LangVersion>latest</LangVersion> | ||||||
|  | 		<TargetFrameworks>net6.0;net7.0</TargetFrameworks> | ||||||
|  | 		<Authors>Diego</Authors> | ||||||
|  | 		<Product>ThingsGateway</Product> | ||||||
|  | 		<Copyright>© 2023-present Diego</Copyright> | ||||||
|  | 		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl> | ||||||
|  | 		<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages> | ||||||
|  | 		<GenerateDocumentationFile>False</GenerateDocumentationFile> | ||||||
|  | 	</PropertyGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | </Project> | ||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | global using System; | ||||||
|  |  | ||||||
|  | global using ThingsGateway.Components; | ||||||
|  |  | ||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using Photino.Blazor; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  | internal class Program | ||||||
|  | { | ||||||
|  |     [STAThread] | ||||||
|  |     private static void Main(string[] args) | ||||||
|  |     { | ||||||
|  |         System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory); | ||||||
|  |  | ||||||
|  |         var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args); | ||||||
|  |  | ||||||
|  |         appBuilder.RootComponents.Add<App>("#app"); | ||||||
|  |  | ||||||
|  |         appBuilder.Services.ThingsGatewayComponentsConfigureServices(); | ||||||
|  |         var app = appBuilder.Build(); | ||||||
|  |         app.MainWindow.SetTitle("ThingsGateway.Foundation.Demo"); | ||||||
|  |         app.MainWindow.SetIconFile("wwwroot/favicon.ico"); | ||||||
|  |         AppDomain.CurrentDomain.UnhandledException += (sender, error) => | ||||||
|  |         { | ||||||
|  |         }; | ||||||
|  |         app.Run(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,32 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||||
|  |  | ||||||
|  | 	<PropertyGroup> | ||||||
|  | 		<OutputType>WinExe</OutputType> | ||||||
|  | 		<ApplicationIcon>favicon.ico</ApplicationIcon> | ||||||
|  | 	</PropertyGroup> | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 	  <None Remove="favicon.ico" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 	  <Content Include="favicon.ico"> | ||||||
|  | 	    <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||||
|  | 	    <ExcludeFromSingleFile>true</ExcludeFromSingleFile> | ||||||
|  | 	    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||||
|  | 	  </Content> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<PackageReference Include="Photino.Blazor" Version="2.6.0" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 	  <ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | </Project> | ||||||
							
								
								
									
										
											BIN
										
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Photino/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										26
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Rcl/App.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  |  | ||||||
|  | <Router AppAssembly="@typeof(App).Assembly"> | ||||||
|  |     <Found Context="routeData"> | ||||||
|  |         <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> | ||||||
|  |         <FocusOnNavigate RouteData="@routeData" Selector="h1" /> | ||||||
|  |     </Found> | ||||||
|  |     <NotFound> | ||||||
|  |         <LayoutView Layout="@typeof(MainLayout)"> | ||||||
|  |             <p role="alert">Sorry, there's nothing at this address.</p> | ||||||
|  |         </LayoutView> | ||||||
|  |     </NotFound> | ||||||
|  | </Router> | ||||||
|  |  | ||||||
| @@ -0,0 +1,148 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Components; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Components; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  | using LogLevel = Microsoft.Extensions.Logging.LogLevel; | ||||||
|  | /// <summary> | ||||||
|  | /// 调试UI | ||||||
|  | /// </summary> | ||||||
|  | public abstract class DriverDebugUIBase : ComponentBase, IDisposable | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 日志缓存 | ||||||
|  |     /// </summary> | ||||||
|  |     public ConcurrentLinkedList<(LogLevel level, string message)> Messages = new(); | ||||||
|  |  | ||||||
|  |     private PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(1)); | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     ~DriverDebugUIBase() | ||||||
|  |     { | ||||||
|  |         this.SafeDispose(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 变量地址 | ||||||
|  |     /// </summary> | ||||||
|  |     public virtual string Address { get; set; } = "40001"; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 长度 | ||||||
|  |     /// </summary> | ||||||
|  |     public virtual int Length { get; set; } = 1; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 默认读写设备 | ||||||
|  |     /// </summary> | ||||||
|  |     public virtual IReadWrite Plc { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 写入值 | ||||||
|  |     /// </summary> | ||||||
|  |     public virtual string WriteValue { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 数据类型 | ||||||
|  |     /// </summary> | ||||||
|  |     protected virtual DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Int16; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     [Inject] | ||||||
|  |     public InitTimezone InitTimezone { get; set; } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public virtual void Dispose() | ||||||
|  |     { | ||||||
|  |         _periodicTimer?.Dispose(); | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public void LogOut(ThingsGateway.Foundation.Core.LogLevel logLevel, object source, string message, Exception exception) | ||||||
|  |     { | ||||||
|  |         Messages.Add(((LogLevel)logLevel, | ||||||
|  |             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {message} {exception}")); | ||||||
|  |         if (Messages.Count > 2500) | ||||||
|  |         { | ||||||
|  |             Messages.Clear(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public virtual async Task ReadAsync() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var data = await Plc.ReadAsync(Address, Length, DataTypeEnum); | ||||||
|  |             if (data.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 Messages.Add((LogLevel.Information, | ||||||
|  |             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 对应类型值:{Environment.NewLine}{data.Content.ToJsonString(true)} ")); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 Messages.Add((LogLevel.Warning, | ||||||
|  |             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}")); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Messages.Add((LogLevel.Error, | ||||||
|  |             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex}")); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public virtual async Task WriteAsync() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var data = await Plc.WriteAsync(Address, WriteValue, Length, DataTypeEnum); | ||||||
|  |             if (data.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 Messages.Add((LogLevel.Information, | ||||||
|  |             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}")); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 Messages.Add((LogLevel.Warning, | ||||||
|  |             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}")); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Messages.Add((LogLevel.Error, | ||||||
|  |             $"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex}")); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override void OnInitialized() | ||||||
|  |     { | ||||||
|  |         _ = RunTimerAsync(); | ||||||
|  |         base.OnInitialized(); | ||||||
|  |     } | ||||||
|  |     private async Task RunTimerAsync() | ||||||
|  |     { | ||||||
|  |         while (await _periodicTimer.WaitForNextTickAsync()) | ||||||
|  |         { | ||||||
|  |             await InvokeAsync(StateHasChanged); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,205 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @using BlazorComponent; | ||||||
|  | @using Microsoft.AspNetCore.Components.Web; | ||||||
|  | @using Microsoft.JSInterop; | ||||||
|  | @using ThingsGateway.Foundation.Core; | ||||||
|  | @using Masa.Blazor; | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  | @inherits DriverDebugUIBase | ||||||
|  |  | ||||||
|  | <MCard Elevation="1" Rounded="false" Class=" pa-2" Style="width:100%"> | ||||||
|  |     <MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters> | ||||||
|  |  | ||||||
|  |         <MCol Md="5"> | ||||||
|  |             <MTabs @bind-Value="tab" Class="ma-2"> | ||||||
|  |                 <MTab Value=1>   读写测试    </MTab> | ||||||
|  |                 <MTab Value=2>    特殊功能    </MTab> | ||||||
|  |                 <MTab Value=3>    代码示例    </MTab> | ||||||
|  |             </MTabs> | ||||||
|  |  | ||||||
|  |             <MTabsItems Value="tab"> | ||||||
|  |                 <MTabItem Value="1"> | ||||||
|  |                     @if (tab == 1) | ||||||
|  |                     { | ||||||
|  |                         @if (ReadWriteContent == null) | ||||||
|  |                         { | ||||||
|  |  | ||||||
|  |                             <div class="my-1 py-1"> | ||||||
|  |                                 <MTooltip Bottom Context="tip"> | ||||||
|  |                                     <ActivatorContent> | ||||||
|  |                                         <MTextarea Class="mx-1 my-1" Label="变量地址" @attributes="@tip.Attrs" Dense Outlined HideDetails="@("auto")" @bind-Value=@Address /> | ||||||
|  |                                     </ActivatorContent> | ||||||
|  |                                     <ChildContent> | ||||||
|  |                                         <span style="white-space: pre-wrap;">@Plc?.GetAddressDescription()</span> | ||||||
|  |                                     </ChildContent> | ||||||
|  |                                 </MTooltip> | ||||||
|  |                                 <MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters> | ||||||
|  |                                 <MTextField Class="mx-1 my-1" Style="max-width:200px" Label="读取长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@Length /> | ||||||
|  |                                     <MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="DataTypeEnum" Outlined Label="数据类型" | ||||||
|  |                                              Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar()) | ||||||
|  |                                              MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||||
|  |                                              ItemText=@((u) =>u.Description) | ||||||
|  |                                              ItemValue=@(u =>(DataTypeEnum)u.Value) | ||||||
|  |                                              HideDetails=@("auto") Height="30" | ||||||
|  |                                               Dense> | ||||||
|  |                                 </MSelect> | ||||||
|  |                             </MRow> | ||||||
|  |  | ||||||
|  |                             <MButton Class="mx-1 my-1" Color="primary" OnClick="ReadAsync"> | ||||||
|  |                                 读取 | ||||||
|  |                             </MButton> | ||||||
|  |                             <MTextarea Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@WriteValue /> | ||||||
|  |                                 <MButton Class="mx-1 my-1" Color="primary" OnClick="WriteAsync"> | ||||||
|  |                                     写入 | ||||||
|  |                                 </MButton> | ||||||
|  |  | ||||||
|  |                             </div> | ||||||
|  |  | ||||||
|  |                         } | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             @ReadWriteContent | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 </MTabItem> | ||||||
|  |  | ||||||
|  |                 <MTabItem Value="2"> | ||||||
|  |                     @if (tab == 2) | ||||||
|  |                     { | ||||||
|  |                         @if (ShowDefaultOtherContent) | ||||||
|  |                         { | ||||||
|  |                             <MSubheader> | ||||||
|  |                                 连读打包 | ||||||
|  |                             </MSubheader> | ||||||
|  |                             <MContainer> | ||||||
|  |  | ||||||
|  |                                 @foreach (var item in DeviceVariableRunTimes) | ||||||
|  |                                 { | ||||||
|  |                                     <MRow Dense Align="AlignTypes.Center"> | ||||||
|  |                                         <MTextField Class="ma-1" Outlined Style="min-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.VariableAddress)) Dense HideDetails="@("auto")" @bind-Value=@item.VariableAddress></MTextField> | ||||||
|  |                                         <MSelect Class="mx-1 my-1" Style="max-width:120px" @bind-Value="item.DataTypeEnum" Outlined Label=@(item.DescriptionWithOutSugar(x => x.DataTypeEnum)) | ||||||
|  |                                                  Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar()) | ||||||
|  |                                                  MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||||
|  |                                                  ItemText=@((u) =>u.Description) | ||||||
|  |                                                  ItemValue=@(u =>(DataTypeEnum)u.Value) | ||||||
|  |                                                  HideDetails=@("auto") Height="30" | ||||||
|  |                                               Dense> | ||||||
|  |                                     </MSelect> | ||||||
|  |  | ||||||
|  |                                     <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.IntervalTime)) Dense HideDetails="@("auto")" @bind-Value=@item.IntervalTime></MTextField> | ||||||
|  |  | ||||||
|  |                                         <MTextField Class="ma-1" Outlined Style="max-width:100px" Label=实时值 Readonly ClearIcon="" Dense HideDetails="@("auto")" Value=item.Value?.ToJsonString()></MTextField> | ||||||
|  |  | ||||||
|  |                                     </MRow> | ||||||
|  |                                 } | ||||||
|  |                                 <MRow Dense> | ||||||
|  |                                 <MTextField Class="ma-1" Outlined Style="max-width:100px" Label="打包长度" Dense HideDetails="@("auto")" @bind-Value=@MaxPack></MTextField> | ||||||
|  |                                     <MButton Class="ma-1" Color="primary" OnClick="MulReadAsync"> | ||||||
|  |                                         读取 | ||||||
|  |                                     </MButton> | ||||||
|  |                                 </MRow> | ||||||
|  |                             </MContainer> | ||||||
|  |  | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         @if (OtherContent != null) | ||||||
|  |                         { | ||||||
|  |                             <MSheet Style="height:100%;overflow-y:auto"> | ||||||
|  |                                 @OtherContent | ||||||
|  |                             </MSheet> | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |                 </MTabItem> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                 <MTabItem Value="3"> | ||||||
|  |                     @if (tab == 3) | ||||||
|  |                     { | ||||||
|  |                         @if (CodeContent != null) | ||||||
|  |                             @CodeContent | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |  | ||||||
|  |                             <MRow Align="AlignTypes.Center"> | ||||||
|  |                                 <MContainer> | ||||||
|  |  | ||||||
|  |                                     <MItemGroup @bind-Value="_selected" Class="shrink mr-6" Mandatory> | ||||||
|  |                                     @{ | ||||||
|  |  | ||||||
|  |                                             int index = 0; | ||||||
|  |  | ||||||
|  |                                         } | ||||||
|  |                                         <MRow> | ||||||
|  |  | ||||||
|  |                                             @foreach (var item in Sections) | ||||||
|  |                                             { | ||||||
|  |                                                 <MItem Value="@(index++)"> | ||||||
|  |                                                     <div> | ||||||
|  |                                                         <MButton IsActive="@context.Active" Icon OnClick="@context.Toggle"> | ||||||
|  |                                                             <MIcon>mdi-record</MIcon> | ||||||
|  |                                                         </MButton> | ||||||
|  |                                                     </div> | ||||||
|  |                                                 </MItem> | ||||||
|  |                                             } | ||||||
|  |                                         </MRow> | ||||||
|  |                                     </MItemGroup> | ||||||
|  |                                 </MContainer> | ||||||
|  |  | ||||||
|  |                                 <MCol> | ||||||
|  |                                     <MWindow Value="_selected" Vertical Class="elevation-1 grey lighten-5 rounded-b" Style=@($"height:450px;overflow:auto")> | ||||||
|  |                                         @{ | ||||||
|  |                                             int index = 0; | ||||||
|  |                                         } | ||||||
|  |                                         @foreach (var item in Sections) | ||||||
|  |                                         { | ||||||
|  |                                             <MWindowItem Value="@(index++)"> | ||||||
|  |                                                 <AppCode RoundedTop0 Code="@item.Code" Language="@item.Language" /> | ||||||
|  |                                             </MWindowItem> | ||||||
|  |                                         } | ||||||
|  |                                     </MWindow> | ||||||
|  |                                 </MCol> | ||||||
|  |                             </MRow> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                     } | ||||||
|  |                 </MTabItem> | ||||||
|  |             </MTabsItems> | ||||||
|  |  | ||||||
|  |         </MCol> | ||||||
|  |  | ||||||
|  |         <MCol Md="7"> | ||||||
|  |             <MCard Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4"> | ||||||
|  |                 <ConsoleTxt Messages="Messages" Height=500></ConsoleTxt> | ||||||
|  |             </MCard> | ||||||
|  |         </MCol> | ||||||
|  |  | ||||||
|  |     </MRow> | ||||||
|  |  | ||||||
|  | </MCard> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @code { | ||||||
|  |     StringNumber tab; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -0,0 +1,236 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using BlazorComponent; | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Components; | ||||||
|  |  | ||||||
|  | using System.Collections.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public partial class DriverDebugUIPage : DriverDebugUIBase | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// DeviceVariableRunTimes | ||||||
|  |     /// </summary> | ||||||
|  |     public List<DeviceVariableRunTime> DeviceVariableRunTimes; | ||||||
|  |     /// <summary> | ||||||
|  |     /// MaxPack | ||||||
|  |     /// </summary> | ||||||
|  |     public int MaxPack = 100; | ||||||
|  |     /// <summary> | ||||||
|  |     /// MulReadAsync | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task MulReadAsync() | ||||||
|  |     { | ||||||
|  |         var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack); | ||||||
|  |         foreach (var item in deviceVariableSourceReads) | ||||||
|  |         { | ||||||
|  |             var result = await Plc.ReadAsync(item.VariableAddress, item.Length); | ||||||
|  |             if (result.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     item.DeviceVariableRunTimes.PraseStructContent(Plc, result.Content); | ||||||
|  |                     Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Content.ToHexString(' '))); | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex)); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Message)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private StringNumber _selected = 0; | ||||||
|  |     /// <summary> | ||||||
|  |     /// Sections | ||||||
|  |     /// </summary> | ||||||
|  |     [Parameter] | ||||||
|  |     public List<(string Code, string Language)> Sections { get; set; } = new(); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// ShowDefaultOtherContent | ||||||
|  |     /// </summary> | ||||||
|  |     [Parameter] | ||||||
|  |     public bool ShowDefaultOtherContent { get; set; } = true; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override void OnInitialized() | ||||||
|  |     { | ||||||
|  |         DeviceVariableRunTimes = new() | ||||||
|  |             { | ||||||
|  |                                 new DeviceVariableRunTime() | ||||||
|  |                                 { | ||||||
|  |                                     DataTypeEnum=DataTypeEnum.Int16, | ||||||
|  |                                     VariableAddress="40001", | ||||||
|  |                                     IntervalTime=1000, | ||||||
|  |                                 }, | ||||||
|  |                                    new DeviceVariableRunTime() | ||||||
|  |                                 { | ||||||
|  |                                     DataTypeEnum=DataTypeEnum.Int16, | ||||||
|  |                                     VariableAddress="40011", | ||||||
|  |                                     IntervalTime=1000, | ||||||
|  |                                 }, | ||||||
|  |                                    new DeviceVariableRunTime() | ||||||
|  |                                 { | ||||||
|  |                                     DataTypeEnum=DataTypeEnum.Int16, | ||||||
|  |                                     VariableAddress="40031", | ||||||
|  |                                     IntervalTime=1000, | ||||||
|  |                                 }, | ||||||
|  |                                    new DeviceVariableRunTime() | ||||||
|  |                                 { | ||||||
|  |                                     DataTypeEnum=DataTypeEnum.Int16, | ||||||
|  |                                     VariableAddress="40101", | ||||||
|  |                                     IntervalTime=1000, | ||||||
|  |                                 }, | ||||||
|  |             }; | ||||||
|  |         Sections.Add(( | ||||||
|  | """ | ||||||
|  |                 /// <inheritdoc/> | ||||||
|  |                 public class DeviceVariableSourceRead : IDeviceVariableSourceRead<DeviceVariableRunTime> | ||||||
|  |                 { | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     public TimerTick TimerTick { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     public string VariableAddress { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     public int Length { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     public List<DeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<DeviceVariableRunTime>(); | ||||||
|  |                 } | ||||||
|  |                 /// <inheritdoc/> | ||||||
|  |                 public class DeviceVariableRunTime : IDeviceVariableRunTime | ||||||
|  |                 { | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     [Description("读取间隔")] | ||||||
|  |                     public int IntervalTime { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     [Description("变量地址")] | ||||||
|  |                     public string VariableAddress { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     public int Index { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     [Description("数据类型")] | ||||||
|  |                     public DataTypeEnum DataTypeEnum { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     [Description("实时值")] | ||||||
|  |                     public object Value { get; set; } | ||||||
|  |                     /// <inheritdoc/> | ||||||
|  |                     public OperResult SetValue(object value) | ||||||
|  |                     { | ||||||
|  |                         Value = value; | ||||||
|  |                         return OperResult.CreateSuccessResult(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 public List<DeviceVariableRunTime> DeviceVariableRunTimes; | ||||||
|  |                                  | ||||||
|  |                 private static async Task ModbusClientAsync(IReadWrite plc) | ||||||
|  |                 { | ||||||
|  |                 DeviceVariableRunTimes = new() | ||||||
|  |                 { | ||||||
|  |                                 new DeviceVariableRunTime() | ||||||
|  |                                 { | ||||||
|  |                                     DataTypeEnum=DataTypeEnum.Int16, | ||||||
|  |                                     VariableAddress="40001", | ||||||
|  |                                     IntervalTime=1000, | ||||||
|  |                                 }, | ||||||
|  |                                    new DeviceVariableRunTime() | ||||||
|  |                                 { | ||||||
|  |                                     DataTypeEnum=DataTypeEnum.Int16, | ||||||
|  |                                     VariableAddress="40011", | ||||||
|  |                                     IntervalTime=1000, | ||||||
|  |                                 }, | ||||||
|  |                                    new DeviceVariableRunTime() | ||||||
|  |                                 { | ||||||
|  |                                     DataTypeEnum=DataTypeEnum.Int16, | ||||||
|  |                                     VariableAddress="40031", | ||||||
|  |                                     IntervalTime=1000, | ||||||
|  |                                 }, | ||||||
|  |                                    new DeviceVariableRunTime() | ||||||
|  |                                 { | ||||||
|  |                                     DataTypeEnum=DataTypeEnum.Int16, | ||||||
|  |                                     VariableAddress="40101", | ||||||
|  |                                     IntervalTime=1000, | ||||||
|  |                                 }, | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |                     #region 连读 | ||||||
|  |                 var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack); | ||||||
|  |                 foreach (var item in deviceVariableSourceReads) | ||||||
|  |                 { | ||||||
|  |                     var result = await Plc.ReadAsync(item.VariableAddress, item.Length); | ||||||
|  |                     if (result.IsSuccess) | ||||||
|  |                     { | ||||||
|  |                         item.DeviceVariableRunTimes.PraseStructContent(result.Content); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                     #endregion | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  | """, "csharp")); | ||||||
|  |         base.OnInitialized(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 自定义模板 | ||||||
|  |     /// </summary> | ||||||
|  |     [Parameter] | ||||||
|  |     public RenderFragment ReadWriteContent { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 自定义模板 | ||||||
|  |     /// </summary> | ||||||
|  |     [Parameter] | ||||||
|  |     public RenderFragment OtherContent { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 自定义模板 | ||||||
|  |     /// </summary> | ||||||
|  |     [Parameter] | ||||||
|  |     public RenderFragment CodeContent { get; set; } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     ~DriverDebugUIPage() | ||||||
|  |     { | ||||||
|  |         this.SafeDispose(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     public override IReadWrite Plc { get; set; } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void Dispose() | ||||||
|  |     { | ||||||
|  |         Plc?.SafeDispose(); | ||||||
|  |         base.Dispose(); | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="firstRender"></param> | ||||||
|  |     protected override void OnAfterRender(bool firstRender) | ||||||
|  |     { | ||||||
|  |         base.OnAfterRender(firstRender); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  | @using BlazorComponent; | ||||||
|  | @using Microsoft.AspNetCore.Components.Web; | ||||||
|  | @using System.IO.Ports; | ||||||
|  | @using Masa.Blazor | ||||||
|  | <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||||
|  |     <div class="mb-4">通道配置</div> | ||||||
|  |     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||||
|  |         <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.PortName)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.PortName /> | ||||||
|  |         <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.BaudRate)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.BaudRate /> | ||||||
|  |         <MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.DataBits)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.DataBits /> | ||||||
|  |         <MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.Parity" Label="@(serialProperty.DescriptionWithOutSugar(x => x.Parity))" | ||||||
|  |                  Items=@(typeof(Parity).GetEnumListWithOutSugar()) | ||||||
|  |                  MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||||
|  |                  ItemText=@((u) =>u.Description) | ||||||
|  |                  ItemValue=@(u =>(Parity)u.Value) | ||||||
|  |                  HideDetails=@("auto") Height="30" | ||||||
|  |                  Dense> | ||||||
|  |         </MSelect> | ||||||
|  |         <MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.StopBits" Label="@(serialProperty.DescriptionWithOutSugar(x => x.StopBits))" | ||||||
|  |                  Items=@(typeof(StopBits).GetEnumListWithOutSugar()) | ||||||
|  |                  MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" | ||||||
|  |                  ItemText=@((u) =>u.Description) | ||||||
|  |                  ItemValue=@(u =>(StopBits)u.Value) | ||||||
|  |                  HideDetails=@("auto") Height="30" | ||||||
|  |                  Dense> | ||||||
|  |         </MSelect> | ||||||
|  |         <MButton Class="ma-1" OnClick=@ConnectAsync Color="primary"> | ||||||
|  |             连接 | ||||||
|  |         </MButton> | ||||||
|  |         <MButton Class="ma-1" OnClick=@DisConnect Color="red"> | ||||||
|  |             断开 | ||||||
|  |         </MButton> | ||||||
|  |     </MRow> | ||||||
|  | </MCard> | ||||||
| @@ -0,0 +1,106 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public partial class SerialSessionPage : IDisposable | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 日志输出 | ||||||
|  |     /// </summary> | ||||||
|  |     public Action<LogLevel, object, string, Exception> LogAction; | ||||||
|  |  | ||||||
|  |     private TouchSocketConfig config; | ||||||
|  |  | ||||||
|  |     private readonly SerialProperty serialProperty = new(); | ||||||
|  |  | ||||||
|  |     private SerialSession SerialSession { get; set; } = new(); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取对象 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public SerialSession GetSerialSession() | ||||||
|  |     { | ||||||
|  |         config ??= new TouchSocketConfig(); | ||||||
|  |         var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||||
|  |         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||||
|  |         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||||
|  |         config.SetSerialProperty(serialProperty); | ||||||
|  |         //载入配置 | ||||||
|  |         SerialSession.Setup(config); | ||||||
|  |         return SerialSession; | ||||||
|  |     } | ||||||
|  |     private async Task ConnectAsync() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             SerialSession.Close(); | ||||||
|  |             await GetSerialSession().ConnectAsync(); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     private void DisConnect() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             SerialSession.Close(); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override void OnInitialized() | ||||||
|  |     { | ||||||
|  |         config ??= new TouchSocketConfig(); | ||||||
|  |  | ||||||
|  |         base.OnInitialized(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="firstRender"></param> | ||||||
|  |     protected override void OnAfterRender(bool firstRender) | ||||||
|  |     { | ||||||
|  |         if (firstRender) | ||||||
|  |         { | ||||||
|  |             var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||||
|  |             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||||
|  |             config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||||
|  |             SerialSession.Setup(config); | ||||||
|  |         } | ||||||
|  |         base.OnAfterRender(firstRender); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception); | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     public void Dispose() | ||||||
|  |     { | ||||||
|  |         SerialSession.SafeDispose(); | ||||||
|  |     } | ||||||
|  |     internal void StateHasChangedAsync() | ||||||
|  |     { | ||||||
|  |         StateHasChanged(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  | @using BlazorComponent; | ||||||
|  | @using Microsoft.AspNetCore.Components.Web; | ||||||
|  | @using System.IO.Ports; | ||||||
|  | @using System.Collections.Concurrent; | ||||||
|  | @using ThingsGateway.Foundation.Core; | ||||||
|  | @using Masa.Blazor | ||||||
|  | <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||||
|  |     <div class="mb-4">通道配置</div> | ||||||
|  |     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||||
|  |  | ||||||
|  |         <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP /> | ||||||
|  |         <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port /> | ||||||
|  |  | ||||||
|  |         <MButton Class="ma-1" OnClick=@ConnectAsync Color="primary"> | ||||||
|  |             连接 | ||||||
|  |         </MButton> | ||||||
|  |         <MButton Class="ma-1" OnClick=@DisConnect Color="red"> | ||||||
|  |             断开 | ||||||
|  |         </MButton> | ||||||
|  |     </MRow> | ||||||
|  | </MCard> | ||||||
| @@ -0,0 +1,116 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public partial class TcpClientPage : IDisposable | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 日志输出 | ||||||
|  |     /// </summary> | ||||||
|  |     public Action<LogLevel, object, string, Exception> LogAction; | ||||||
|  |  | ||||||
|  |     private TouchSocketConfig config; | ||||||
|  |     /// <summary> | ||||||
|  |     /// IP | ||||||
|  |     /// </summary> | ||||||
|  |     private string IP = "127.0.0.1"; | ||||||
|  |     /// <summary> | ||||||
|  |     /// Port | ||||||
|  |     /// </summary> | ||||||
|  |     public int Port { get; set; } = 502; | ||||||
|  |  | ||||||
|  |     private TcpClient TcpClient { get; set; } = new(); | ||||||
|  |  | ||||||
|  |     private async Task ConnectAsync() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             TcpClient.Close(); | ||||||
|  |             await GetTcpClient().ConnectAsync(); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     private void DisConnect() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             TcpClient.Close(); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取对象 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public TcpClient GetTcpClient() | ||||||
|  |     { | ||||||
|  |         config ??= new TouchSocketConfig(); | ||||||
|  |         var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||||
|  |         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||||
|  |         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||||
|  |         config.SetRemoteIPHost(new IPHost(IP + ":" + Port)); | ||||||
|  |         //载入配置 | ||||||
|  |         TcpClient.Setup(config); | ||||||
|  |         return TcpClient; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override void OnInitialized() | ||||||
|  |     { | ||||||
|  |         config ??= new TouchSocketConfig(); | ||||||
|  |  | ||||||
|  |         base.OnInitialized(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="firstRender"></param> | ||||||
|  |     protected override void OnAfterRender(bool firstRender) | ||||||
|  |     { | ||||||
|  |         if (firstRender) | ||||||
|  |         { | ||||||
|  |             var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||||
|  |             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||||
|  |             config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||||
|  |             config.SetRemoteIPHost(new IPHost(IP + ":" + Port)); | ||||||
|  |             TcpClient.Setup(config); | ||||||
|  |         } | ||||||
|  |         base.OnAfterRender(firstRender); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     public void Dispose() | ||||||
|  |     { | ||||||
|  |         TcpClient.SafeDispose(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal void StateHasChangedAsync() | ||||||
|  |     { | ||||||
|  |         StateHasChanged(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,38 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  | @using BlazorComponent; | ||||||
|  | @using Microsoft.AspNetCore.Components.Web; | ||||||
|  | @using System.IO.Ports; | ||||||
|  | @using System.Collections.Concurrent; | ||||||
|  | @using ThingsGateway.Foundation.Core; | ||||||
|  | @using Masa.Blazor | ||||||
|  |  | ||||||
|  | <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||||
|  |     <div class="mb-4">通道配置</div> | ||||||
|  |     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||||
|  |  | ||||||
|  |         <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip /> | ||||||
|  |         <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port /> | ||||||
|  |  | ||||||
|  |         <MButton Class="ma-1" OnClick=@Connect Color="primary"> | ||||||
|  |             启动 | ||||||
|  |         </MButton> | ||||||
|  |         <MButton Class="ma-1" OnClick=@DisConnect Color="red"> | ||||||
|  |             停止 | ||||||
|  |         </MButton> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     </MRow> | ||||||
|  | </MCard> | ||||||
| @@ -0,0 +1,108 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public partial class TcpServerPage : IDisposable | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 日志输出 | ||||||
|  |     /// </summary> | ||||||
|  |     public Action<LogLevel, object, string, Exception> LogAction; | ||||||
|  |  | ||||||
|  |     private TouchSocketConfig config; | ||||||
|  |  | ||||||
|  |     private string ip = "127.0.0.1"; | ||||||
|  |  | ||||||
|  |     private int port = 502; | ||||||
|  |  | ||||||
|  |     private TcpService TcpServer { get; set; } = new(); | ||||||
|  |  | ||||||
|  |     private void Connect() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             TcpServer.Stop(); | ||||||
|  |             GetTcpServer().Start(); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     private void DisConnect() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             TcpServer.Stop(); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取对象 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public TcpService GetTcpServer() | ||||||
|  |     { | ||||||
|  |         config ??= new TouchSocketConfig(); | ||||||
|  |         var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||||
|  |         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||||
|  |         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||||
|  |         config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) }); | ||||||
|  |         //载入配置 | ||||||
|  |         TcpServer.Setup(config); | ||||||
|  |         return TcpServer; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override void OnInitialized() | ||||||
|  |     { | ||||||
|  |         config ??= new TouchSocketConfig(); | ||||||
|  |  | ||||||
|  |         base.OnInitialized(); | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="firstRender"></param> | ||||||
|  |     protected override void OnAfterRender(bool firstRender) | ||||||
|  |     { | ||||||
|  |         if (firstRender) | ||||||
|  |         { | ||||||
|  |             var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||||
|  |             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||||
|  |             config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||||
|  |             config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) }); | ||||||
|  |             TcpServer.Setup(config); | ||||||
|  |         } | ||||||
|  |         base.OnAfterRender(firstRender); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception); | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     public void Dispose() | ||||||
|  |     { | ||||||
|  |         TcpServer.SafeDispose(); | ||||||
|  |     } | ||||||
|  |     internal void StateHasChangedAsync() | ||||||
|  |     { | ||||||
|  |         StateHasChanged(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,37 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  | @using BlazorComponent; | ||||||
|  | @using Microsoft.AspNetCore.Components.Web; | ||||||
|  | @using System.IO.Ports; | ||||||
|  | @using System.Collections.Concurrent; | ||||||
|  | @using ThingsGateway.Foundation.Core; | ||||||
|  | @using Masa.Blazor | ||||||
|  |  | ||||||
|  | <MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%"> | ||||||
|  |     <div class="mb-4">通道配置</div> | ||||||
|  |     <MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center"> | ||||||
|  |  | ||||||
|  |         <MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP /> | ||||||
|  |         <MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port /> | ||||||
|  |  | ||||||
|  |         <MButton Class="ma-1" OnClick=Connect Color="primary"> | ||||||
|  |             连接 | ||||||
|  |         </MButton> | ||||||
|  |         <MButton Class="ma-1" OnClick=DisConnect Color="red"> | ||||||
|  |             断开 | ||||||
|  |         </MButton> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     </MRow> | ||||||
|  | </MCard> | ||||||
| @@ -0,0 +1,113 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public partial class UdpSessionPage : IDisposable | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 日志输出 | ||||||
|  |     /// </summary> | ||||||
|  |     public Action<LogLevel, object, string, Exception> LogAction; | ||||||
|  |  | ||||||
|  |     private TouchSocketConfig config; | ||||||
|  |     /// <summary> | ||||||
|  |     /// IP | ||||||
|  |     /// </summary> | ||||||
|  |     public string IP = "127.0.0.1"; | ||||||
|  |     /// <summary> | ||||||
|  |     /// Port | ||||||
|  |     /// </summary> | ||||||
|  |     public int Port = 502; | ||||||
|  |  | ||||||
|  |     private UdpSession UdpSession { get; set; } = new(); | ||||||
|  |  | ||||||
|  |     private void Connect() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             UdpSession.Stop(); | ||||||
|  |             GetUdpSession().Start(); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     private void DisConnect() | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             UdpSession.Stop(); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             LogAction?.Invoke(LogLevel.Error, null, null, ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取对象 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public UdpSession GetUdpSession() | ||||||
|  |     { | ||||||
|  |         config ??= new TouchSocketConfig(); | ||||||
|  |         var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||||
|  |         LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||||
|  |         config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||||
|  |         config.SetRemoteIPHost(new IPHost(IP + ":" + Port)); | ||||||
|  |         config.SetBindIPHost(new IPHost(0)); | ||||||
|  |         //载入配置 | ||||||
|  |         UdpSession.Setup(config); | ||||||
|  |         return UdpSession; | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override void OnInitialized() | ||||||
|  |     { | ||||||
|  |         config ??= new TouchSocketConfig(); | ||||||
|  |  | ||||||
|  |         base.OnInitialized(); | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="firstRender"></param> | ||||||
|  |     protected override void OnAfterRender(bool firstRender) | ||||||
|  |     { | ||||||
|  |         if (firstRender) | ||||||
|  |         { | ||||||
|  |             var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace }; | ||||||
|  |             LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace }); | ||||||
|  |             config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); | ||||||
|  |             config.SetRemoteIPHost(new IPHost(IP + ":" + Port)); | ||||||
|  |             config.SetBindIPHost(new IPHost(0)); | ||||||
|  |             UdpSession.Setup(config); | ||||||
|  |         } | ||||||
|  |         base.OnAfterRender(firstRender); | ||||||
|  |     } | ||||||
|  |     private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     public void Dispose() | ||||||
|  |     { | ||||||
|  |         UdpSession.SafeDispose(); | ||||||
|  |     } | ||||||
|  |     internal void StateHasChangedAsync() | ||||||
|  |     { | ||||||
|  |         StateHasChanged(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,42 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class DeviceVariableRunTime : IDeviceVariableRunTime | ||||||
|  | { | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     [Description("读取间隔")] | ||||||
|  |     public int IntervalTime { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     [Description("变量地址")] | ||||||
|  |     public string VariableAddress { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public int Index { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     [Description("数据类型")] | ||||||
|  |     public DataTypeEnum DataTypeEnum { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     [Description("实时值")] | ||||||
|  |     public object Value { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public OperResult SetValue(object value, DateTime dateTime = default, bool isOnline = true) | ||||||
|  |     { | ||||||
|  |         Value = value; | ||||||
|  |         return OperResult.CreateSuccessResult(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Collections.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class DeviceVariableSourceRead : IDeviceVariableSourceRead<IDeviceVariableRunTime> | ||||||
|  | { | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public TimerTick TimerTick { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public string VariableAddress { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public int Length { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public List<IDeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<IDeviceVariableRunTime>(); | ||||||
|  | } | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | global using System; | ||||||
|  | global using System.Threading; | ||||||
|  | global using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | global using ThingsGateway.Components; | ||||||
|  | global using ThingsGateway.Foundation.Core; | ||||||
|  | global using ThingsGateway.Foundation.Serial; | ||||||
|  | global using ThingsGateway.Foundation.Sockets; | ||||||
|  |  | ||||||
| @@ -0,0 +1,36 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @page "/" | ||||||
|  | @layout BaseLayout | ||||||
|  | @inject NavigationManager NavigationManager | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  | @using Microsoft.AspNetCore.Authorization; | ||||||
|  |  | ||||||
|  | @code { | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="firstRender"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     protected override async Task OnAfterRenderAsync(bool firstRender) | ||||||
|  |     { | ||||||
|  |         if (firstRender) | ||||||
|  |         { | ||||||
|  |             NavigationManager.NavigateTo("index"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         await base.OnAfterRenderAsync(firstRender); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,57 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @page "/index" | ||||||
|  |  | ||||||
|  | <div class="ml-2"> | ||||||
|  |     <div class="my-6 "> | ||||||
|  |         <MLabel Class="text-h3" Color="primary">ThingsGateway</MLabel> | ||||||
|  |     </div> | ||||||
|  |     <div> | ||||||
|  |         <strong class="text--lighten-1 text-h5 my-1">文档</strong> | ||||||
|  |     </div> | ||||||
|  |     <div class="my-2 ml-4"> | ||||||
|  |         <PCopyableText> | ||||||
|  |             https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  |         </PCopyableText> | ||||||
|  |     </div> | ||||||
|  |     <div> | ||||||
|  |         <strong class="text--lighten-1 text-h5 my-1">协议</strong> | ||||||
|  |     </div> | ||||||
|  |     <div class="my-2 ml-4"> | ||||||
|  |         <PCopyableText Text="https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh"> | ||||||
|  |             Apache-2.0开源协议 | ||||||
|  |         </PCopyableText> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <div> | ||||||
|  |         <strong class="text--lighten-1 text-h5 my-1">赞助</strong> | ||||||
|  |     </div> | ||||||
|  |     <div class="my-2 ml-4"> | ||||||
|  |         <PCopyableText Text="https://diego2098.gitee.io/thingsgateway-docs/docs/donate"> | ||||||
|  |             ThingsGateway赞助途径 | ||||||
|  |         </PCopyableText> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <div> | ||||||
|  |         <strong class="text--lighten-1 text-h5 my-1">社区</strong> | ||||||
|  |     </div> | ||||||
|  |     <div class="my-2 ml-4"> | ||||||
|  |         <PCopyableText Text="605534569"> | ||||||
|  |             QQ群:605534569 | ||||||
|  |         </PCopyableText> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |      | ||||||
|  |  | ||||||
|  | </div> | ||||||
|  |  | ||||||
| @@ -0,0 +1,42 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  | @inherits LayoutComponentBase | ||||||
|  |  | ||||||
|  | <CascadingValue Value="IsMobile" Name="IsMobile"> | ||||||
|  |     <MApp> | ||||||
|  |         <MErrorHandler> | ||||||
|  |             @Body | ||||||
|  |         </MErrorHandler> | ||||||
|  |     </MApp> | ||||||
|  | </CascadingValue> | ||||||
|  |  | ||||||
|  | @code { | ||||||
|  |     public bool IsMobile { get; set; } | ||||||
|  |     [Inject] | ||||||
|  |     public MasaBlazor MasaBlazor { get; set; } | ||||||
|  |     protected override void OnInitialized() | ||||||
|  |     { | ||||||
|  |         base.OnInitialized(); | ||||||
|  |         MasaBlazor.BreakpointChanged += BreakpointOnOnUpdate; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void BreakpointOnOnUpdate(object sender, BreakpointChangedEventArgs e) | ||||||
|  |     { | ||||||
|  |         IsMobile = MasaBlazor.Breakpoint.Mobile; | ||||||
|  |         if (e.MobileChanged) | ||||||
|  |         { | ||||||
|  |             StateHasChanged(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,77 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @namespace ThingsGateway.Foundation.Demo | ||||||
|  | @using System.Text; | ||||||
|  | @inherits LayoutComponentBase | ||||||
|  | @layout BaseLayout | ||||||
|  |  | ||||||
|  | <PPageTabsProvider> | ||||||
|  |  | ||||||
|  |     <CascadingValue Value="@this" IsFixed> | ||||||
|  |         <CascadingValue Value="@Changed" Name="Changed"> | ||||||
|  |  | ||||||
|  |             <MNavigationDrawer Color="barcolor" @bind-Value="_drawerOpen" App Width="200"> | ||||||
|  |                 <Logo CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT  CONFIG_TITLE=@CONFIG_TITLE HeightInt=@(IsMobile?BlazorResourceConst.AppBarHeight:BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight) /> | ||||||
|  |                 <AppList ClassString="overflow-y-auto" Routable | ||||||
|  |                          StyleString=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight}px);") | ||||||
|  |                          Items="Navs" /> | ||||||
|  |             </MNavigationDrawer> | ||||||
|  |  | ||||||
|  |             <MAppBar Color="barcolor" Style=@($"{(!(IsMobile||_drawerOpen!=true)? "left:200px;":"")}") Elevation="1" App Flat ClippedRight Dense ElevateOnScroll | ||||||
|  |                      MaxHeight="@(BlazorResourceConst.AppBarHeight)" Height="@(BlazorResourceConst.AppBarHeight)"> | ||||||
|  |                 <MButton Class="mr-0" Icon Small=IsMobile OnClick=@(() => _drawerOpen = !_drawerOpen)> | ||||||
|  |                     <MIcon>mdi-menu</MIcon> | ||||||
|  |                 </MButton> | ||||||
|  |  | ||||||
|  |             </MAppBar> | ||||||
|  |  | ||||||
|  |             <MMain Style=@($"{(!(IsMobile||_drawerOpen!=true)? "padding-left:200px;":"")}")> | ||||||
|  |                 <div class="full-width"> | ||||||
|  |                     <PageTabs @ref="_pageTabs" PageTabItems="pageTabItems" /> | ||||||
|  |                 </div> | ||||||
|  |                 <MDivider Center></MDivider> | ||||||
|  |                 <MCard Flat Class="overflow-y-auto overflow-x-hidden ma-auto pa-0 rounded-0" Style=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight+BlazorResourceConst.FooterHeight}px);")> | ||||||
|  |                     <PPageContainer PageTabs="@_pageTabs?.PPageTabs"> | ||||||
|  |                         @Body | ||||||
|  |                     </PPageContainer> | ||||||
|  |                 </MCard> | ||||||
|  |                 <MSheet Class="d-flex justify-center align-center rounded-0" Style=@($"height: {BlazorResourceConst.FooterHeight}px;")> | ||||||
|  |                     <Foter CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE></Foter> | ||||||
|  |                 </MSheet> | ||||||
|  |             </MMain> | ||||||
|  |         </CascadingValue> | ||||||
|  |     </CascadingValue> | ||||||
|  |  | ||||||
|  | </PPageTabsProvider> | ||||||
|  | @code { | ||||||
|  |     bool Changed { get; set; } | ||||||
|  |     private bool? _drawerOpen = true; | ||||||
|  |  | ||||||
|  |     private PageTabs _pageTabs; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// IsMobile | ||||||
|  |     /// </summary> | ||||||
|  |     [CascadingParameter(Name = "IsMobile")] | ||||||
|  |     public bool IsMobile { get; set; } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @code{ | ||||||
|  |     private string CONFIG_COPYRIGHT = "Diego"; | ||||||
|  |     private string CONFIG_COPYRIGHT_URL = "https://gitee.com/diego2098/ThingsGateway"; | ||||||
|  |     private string CONFIG_TITLE = "ThingsGateway"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   | ||||||
| @@ -0,0 +1,253 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Collections.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo; | ||||||
|  |  | ||||||
|  | public partial class MainLayout | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     private List<NavItem> Navs { get; set; } = new(); | ||||||
|  |     private List<PageTabItem> pageTabItems { get; set; } = new(); | ||||||
|  |     protected override void OnInitialized() | ||||||
|  |     { | ||||||
|  |         var dataString = | ||||||
|  | """ | ||||||
|  | [ | ||||||
|  |   { | ||||||
|  |     "Href": "/index", | ||||||
|  |     "Title": "首页" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "Modbus", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/ModbusRtu", | ||||||
|  |         "Title": "ModbusRtu" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/ModbusTcp", | ||||||
|  |         "Title": "ModbusTcp" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/ModbusRtuOverTcp", | ||||||
|  |         "Title": "ModbusRtuOverTcp" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/ModbusRtuOverUdp", | ||||||
|  |         "Title": "ModbusRtuOverUdp" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/ModbusUdp", | ||||||
|  |         "Title": "ModbusUdp" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/ModbusTcpDtu", | ||||||
|  |         "Title": "ModbusTcpDtu" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/ModbusTcpServer", | ||||||
|  |         "Title": "ModbusTcpServer" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/ModbusSerialServer", | ||||||
|  |         "Title": "ModbusSerialServer" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "Siemens", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/S7_1500", | ||||||
|  |         "Title": "S7_1500" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/S7_1200", | ||||||
|  |         "Title": "S7_1200" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/S7_200", | ||||||
|  |         "Title": "S7_200" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/S7_200SMART", | ||||||
|  |         "Title": "S7_200SMART" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/S7_300", | ||||||
|  |         "Title": "S7_400" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/S7_400", | ||||||
|  |         "Title": "S7_400" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "DLT645", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/DLT645_2007", | ||||||
|  |         "Title": "DLT645_2007" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/DLT645_2007OverTcp", | ||||||
|  |         "Title": "DLT645_2007OverTcp" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "OPCDA", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/OPCDAClient", | ||||||
|  |         "Title": "OPCDAClient" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "OPCUA", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/OPCUAClient", | ||||||
|  |         "Title": "OPCUAClient" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "Mqtt", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/MqttClient", | ||||||
|  |         "Title": "MqttClient" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   } | ||||||
|  | ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | """; | ||||||
|  |         Navs = dataString.FromJsonString<List<NavItem>>(); | ||||||
|  |  | ||||||
|  | #if Pro | ||||||
|  |         var dataStringPro = | ||||||
|  | """ | ||||||
|  | [ | ||||||
|  |   { | ||||||
|  |     "Title": "Melsec", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/QnA3E_Binary", | ||||||
|  |         "Title": "QnA3E_Binary" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "ABCIP", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/ABCIPTCP", | ||||||
|  |         "Title": "ABCIPTCP" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "Omron", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/OmronFinsTcp", | ||||||
|  |         "Title": "OmronFinsTcp" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/OmronFinsUdp", | ||||||
|  |         "Title": "OmronFinsUdp" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "Secs", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/SecsTcp", | ||||||
|  |         "Title": "SecsTcp" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "TS550", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/TS550", | ||||||
|  |         "Title": "TS550" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "Vigor", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/VigorSerial", | ||||||
|  |         "Title": "VigorSerial" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/VigorSerialOverTcp", | ||||||
|  |         "Title": "VigorSerialOverTcp" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "HZW_QTJC_01", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/HZW_QTJC_01Serial", | ||||||
|  |         "Title": "HZW_QTJC_01Serial" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/HZW_QTJC_01SerialOverTcp", | ||||||
|  |         "Title": "HZW_QTJC_01SerialOverTcp" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "LQTCP", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/LQTCP", | ||||||
|  |         "Title": "LQTCP" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     "Title": "KELID2008", | ||||||
|  |     "Children": [ | ||||||
|  |       { | ||||||
|  |         "Href": "/KELID2008", | ||||||
|  |         "Title": "KELID2008" | ||||||
|  |       }, | ||||||
|  |       { | ||||||
|  |         "Href": "/KELID2008OverTcp", | ||||||
|  |         "Title": "KELID2008OverTcp" | ||||||
|  |       } | ||||||
|  |     ] | ||||||
|  |   }, | ||||||
|  | ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | """; | ||||||
|  |         Navs.AddRange(dataStringPro.FromJsonString<List<NavItem>>()); | ||||||
|  | #endif | ||||||
|  |         pageTabItems = Navs.PasePageTabItem(); | ||||||
|  |         base.OnInitialized(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,162 @@ | |||||||
|  | <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" /> | ||||||
|  |  | ||||||
|  | 		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.LQTCP\Page\LQTCPDebugPage.razor.cs" Link="Pages\LQTCP\LQTCPDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.LQTCP\Page\LQTCPDebugPage.razor" Link="Pages\LQTCP\LQTCPDebugPage.razor" /> | ||||||
|  | 		<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.LQTCP\ThingsGateway.Foundation.Adapter.LQTCP.csproj" /> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008DebugPage.razor.cs" Link="Pages\KELID2008\KELID2008DebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008OverTcpDebugPage.razor.cs" Link="Pages\KELID2008\KELID2008OverTcpDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008DebugPage.razor" Link="Pages\KELID2008\KELID2008DebugPage.razor" /> | ||||||
|  | 		<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008OverTcpDebugPage.razor" Link="Pages\KELID2008\KELID2008OverTcpDebugPage.razor" /> | ||||||
|  | 		<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.KELID2008\ThingsGateway.Foundation.Adapter.KELID2008.csproj" /> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007DebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\MqttRpcNameVaueWithId.cs" Link="Pages\Mqtt\MqttRpcNameVaueWithId.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor.cs" Link="Pages\Mqtt\MqttClientDebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor.cs" Link="Pages\Mqtt\MqttClientPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClient.cs" Link="Pages\Mqtt\MqttRpcClient.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClientExtensions.cs" Link="Pages\Mqtt\MqttRpcClientExtensions.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcTopicPair.cs" Link="Pages\Mqtt\MqttRpcTopicPair.cs" /> | ||||||
|  | 		<Compile Include="..\..\Web\ThingsGateway.Gateway.Application\Workers\ManageGateway\MqttLoggerExtensions.cs" Link="Pages\Mqtt\MqttLoggerExtensions.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor" Link="Pages\DLT645\DLT645_2007DebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor" /> | ||||||
|  |  | ||||||
|  | 		 | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor" Link="Pages\Modbus\ModbusRtuDebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor" Link="Pages\Modbus\ModbusTcpDebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusUdpDebugPage.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor" Link="Pages\Modbus\ModbusUdpDebugPage.razor" /> | ||||||
|  |  | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor.cs" Link="Pages\OPCDA\OPCDAClientDebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor.cs" Link="Pages\OPCDA\OPCDAClientPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor.cs" Link="Pages\OPCDA\OPCDAImportVariable.razor.cs" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor" Link="Pages\OPCDA\OPCDAClientDebugPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor" Link="Pages\OPCDA\OPCDAClientPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor" Link="Pages\OPCDA\OPCDAImportVariable.razor" /> | ||||||
|  |  | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor" Link="Pages\OPCUA\OPCUAClientDebugPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor" Link="Pages\OPCUA\OPCUAClientPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor" Link="Pages\OPCUA\OPCUAImportVariable.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor.cs" Link="Pages\OPCUA\OPCUAClientDebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor.cs" Link="Pages\OPCUA\OPCUAClientPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor.cs" Link="Pages\OPCUA\OPCUAImportVariable.razor.cs" /> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor" Link="Pages\Siemens\S7_1200DebugPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor" Link="Pages\Siemens\S7_1500DebugPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor" Link="Pages\Siemens\S7_200DebugPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor" Link="Pages\Siemens\S7_200SMARTDebugPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor" Link="Pages\Siemens\S7_300DebugPage.razor" /> | ||||||
|  | 		<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor" Link="Pages\Siemens\S7_400DebugPage.razor" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor.cs" Link="Pages\Siemens\S7_1200DebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor.cs" Link="Pages\Siemens\S7_1500DebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor.cs" Link="Pages\Siemens\S7_200DebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor.cs" Link="Pages\Siemens\S7_200SMARTDebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor.cs" Link="Pages\Siemens\S7_300DebugPage.razor.cs" /> | ||||||
|  | 		<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor.cs" Link="Pages\Siemens\S7_400DebugPage.razor.cs" /> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<!--<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" /> | ||||||
|  | 		<PackageReference Include="ThingsGateway.Foundation.Adapter.Modbus" Version="*" /> | ||||||
|  | 		<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCDA" Version="*" /> | ||||||
|  | 		<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCUA" Version="*" /> | ||||||
|  | 		<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />--> | ||||||
|  | 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" /> | ||||||
|  | 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" /> | ||||||
|  | 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" /> | ||||||
|  | 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" /> | ||||||
|  | 		<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" /> | ||||||
|  |  | ||||||
|  | 	</ItemGroup> | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<Content Update="wwwroot\**"> | ||||||
|  | 			<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||||
|  | 		</Content> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 	  <Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor" Link="Pages\Mqtt\MqttClientDebugPage.razor" /> | ||||||
|  | 	  <Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor" Link="Pages\Mqtt\MqttClientPage.razor" /> | ||||||
|  | 		<PackageReference Include="MQTTnet" Version="4.3.1.873" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | </Project> | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | @* | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | *@ | ||||||
|  |  | ||||||
|  | @using System.Net.Http | ||||||
|  | @using Microsoft.AspNetCore.Components.Forms | ||||||
|  | @using Microsoft.AspNetCore.Components.Routing | ||||||
|  | @using Microsoft.AspNetCore.Components.Web | ||||||
|  | @using Microsoft.AspNetCore.Components.Web.Virtualization | ||||||
|  | @using Microsoft.JSInterop | ||||||
|  | @using BlazorComponent | ||||||
|  | @using Masa.Blazor | ||||||
|  | @using Masa.Blazor.Presets | ||||||
|  | @using ThingsGateway.Foundation.Core; | ||||||
|  | @using ThingsGateway.Components; | ||||||
|  | @using ThingsGateway.Core; | ||||||
|  | @using System.Net.Http.Json | ||||||
|  | @using System.IO; | ||||||
|  | @using System.Text.Json; | ||||||
|  | @using ThingsGateway.Foundation.Serial; | ||||||
|  | @using ThingsGateway.Foundation.Sockets; | ||||||
| After Width: | Height: | Size: 4.2 KiB | 
| After Width: | Height: | Size: 10 KiB | 
| @@ -0,0 +1,41 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  |     <meta charset="utf-8" /> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> | ||||||
|  |     <title>ThingsGateway.Foundation.Demo</title> | ||||||
|  |     <base href="/" /> | ||||||
|  |  | ||||||
|  |     <link rel="icon" href="favicon.ico" type="image/x-icon"> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     <link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet" /> | ||||||
|  |     <link href="_content/ThingsGateway.Components/css/materialdesign/v7.1.96/css/materialdesignicons.min.css" rel="stylesheet"> | ||||||
|  |     <link href="_content/ThingsGateway.Components/css/material/icons.css" rel="stylesheet"> | ||||||
|  |     <link href="_content/ThingsGateway.Components/css/fontawesome/v6.4.0/css/all.min.css" rel="stylesheet"> | ||||||
|  |     <link href="_content/ThingsGateway.Components/style/custom.css" rel="stylesheet"> | ||||||
|  |     <link href="_content/ThingsGateway.Components/prism/prism-material-dark-for-masa.css" rel="stylesheet"> | ||||||
|  |     <link href="_content/ThingsGateway.Components/prism/prism-line-highlight.min.css" rel="stylesheet"> | ||||||
|  | </head> | ||||||
|  |  | ||||||
|  | <body> | ||||||
|  |     <div id="app"></div> | ||||||
|  |  | ||||||
|  |     <div id="blazor-error-ui"> | ||||||
|  |         <span> | ||||||
|  |             <environment include="Staging,Production"> | ||||||
|  |                 An error has occurred. This application may no longer respond until reloaded. | ||||||
|  |             </environment> | ||||||
|  |             <environment include="Development"> | ||||||
|  |                 An unhandled exception has occurred. See browser dev tools for details. | ||||||
|  |             </environment> | ||||||
|  |         </span> | ||||||
|  |         <a href="" class="reload">Reload</a> | ||||||
|  |         <a class="dismiss">🗙</a> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|  |     <script src="_framework/blazor.webview.js" autostart="true"></script> | ||||||
|  |     <script src="_content/ThingsGateway.Components/prism/prism.min.js"></script> | ||||||
|  |     <script src="_content/BlazorComponent/js/blazor-component.js"></script> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | global using System; | ||||||
|  |  | ||||||
|  | global using System.Windows.Forms; | ||||||
|  |  | ||||||
							
								
								
									
										77
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Winform/MainFrom.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,77 @@ | |||||||
|  | #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.Drawing; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo.Winform | ||||||
|  | { | ||||||
|  |     partial class MainFrom | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         ///  Required designer variable. | ||||||
|  |         /// </summary> | ||||||
|  |         private System.ComponentModel.IContainer components = null; | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         ///  Clean up any resources being used. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> | ||||||
|  |         protected override void Dispose(bool disposing) | ||||||
|  |         { | ||||||
|  |             if (disposing && (components != null)) | ||||||
|  |             { | ||||||
|  |                 components.Dispose(); | ||||||
|  |             } | ||||||
|  |             base.Dispose(disposing); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         #region Windows Form Designer generated code | ||||||
|  |  | ||||||
|  |         /// <summary> | ||||||
|  |         ///  Required method for Designer support - do not modify | ||||||
|  |         ///  the contents of this method with the code editor. | ||||||
|  |         /// </summary> | ||||||
|  |         private void InitializeComponent() | ||||||
|  |         { | ||||||
|  |             System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainFrom)); | ||||||
|  |             blazorWebView1 = new Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView(); | ||||||
|  |             SuspendLayout(); | ||||||
|  |             //  | ||||||
|  |             // blazorWebView1 | ||||||
|  |             //  | ||||||
|  |             blazorWebView1.Dock = DockStyle.Fill; | ||||||
|  |             blazorWebView1.Location = new Point(0, 0); | ||||||
|  |             blazorWebView1.Margin = new Padding(4, 4, 4, 4); | ||||||
|  |             blazorWebView1.Name = "blazorWebView1"; | ||||||
|  |             blazorWebView1.Size = new Size(1029, 529); | ||||||
|  |             blazorWebView1.TabIndex = 0; | ||||||
|  |             blazorWebView1.Text = "blazorWebView1"; | ||||||
|  |             //  | ||||||
|  |             // MainFrom | ||||||
|  |             //  | ||||||
|  |             AutoScaleDimensions = new SizeF(9F, 20F); | ||||||
|  |             AutoScaleMode = AutoScaleMode.Font; | ||||||
|  |             ClientSize = new Size(1029, 529); | ||||||
|  |             Controls.Add(blazorWebView1); | ||||||
|  |             Icon = (Icon)resources.GetObject("$this.Icon"); | ||||||
|  |             Margin = new Padding(4, 4, 4, 4); | ||||||
|  |             Name = "MainFrom"; | ||||||
|  |             Text = "Form1"; | ||||||
|  |             ResumeLayout(false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         #endregion | ||||||
|  |  | ||||||
|  |         private Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView blazorWebView1; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,36 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD> | ||||||
|  | //  <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD> | ||||||
|  | //  Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD> | ||||||
|  | //  GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQȺ<51><C8BA>605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using Microsoft.AspNetCore.Components.WebView.WindowsForms; | ||||||
|  | using Microsoft.Extensions.DependencyInjection; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Components; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo.Winform | ||||||
|  | { | ||||||
|  |     public partial class MainFrom : Form | ||||||
|  |     { | ||||||
|  |         public MainFrom() | ||||||
|  |         { | ||||||
|  |             InitializeComponent(); | ||||||
|  |  | ||||||
|  |             var services = new ServiceCollection(); | ||||||
|  |             services.AddWindowsFormsBlazorWebView(); | ||||||
|  |             services.ThingsGatewayComponentsConfigureServices(); | ||||||
|  |  | ||||||
|  |             blazorWebView1.HostPage = "wwwroot/index.html"; | ||||||
|  |             blazorWebView1.Services = services.BuildServiceProvider(); | ||||||
|  |             this.Text = "ThingsGateway.Foundation.Demo"; | ||||||
|  |             blazorWebView1.RootComponents.Add<ThingsGateway.Foundation.Demo.App>("#app"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,197 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <root> | ||||||
|  |   <!-- | ||||||
|  |     Microsoft ResX Schema  | ||||||
|  |  | ||||||
|  |     Version 2.0 | ||||||
|  |  | ||||||
|  |     The primary goals of this format is to allow a simple XML format | ||||||
|  |     that is mostly human readable. The generation and parsing of the | ||||||
|  |     various data types are done through the TypeConverter classes | ||||||
|  |     associated with the data types. | ||||||
|  |  | ||||||
|  |     Example: | ||||||
|  |  | ||||||
|  |     ... ado.net/XML headers & schema ... | ||||||
|  |     <resheader name="resmimetype">text/microsoft-resx</resheader> | ||||||
|  |     <resheader name="version">2.0</resheader> | ||||||
|  |     <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | ||||||
|  |     <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | ||||||
|  |     <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | ||||||
|  |     <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | ||||||
|  |     <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | ||||||
|  |         <value>[base64 mime encoded serialized .NET Framework object]</value> | ||||||
|  |     </data> | ||||||
|  |     <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | ||||||
|  |         <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | ||||||
|  |         <comment>This is a comment</comment> | ||||||
|  |     </data> | ||||||
|  |  | ||||||
|  |     There are any number of "resheader" rows that contain simple | ||||||
|  |     name/value pairs. | ||||||
|  |  | ||||||
|  |     Each data row contains a name, and value. The row also contains a | ||||||
|  |     type or mimetype. Type corresponds to a .NET class that support | ||||||
|  |     text/value conversion through the TypeConverter architecture. | ||||||
|  |     Classes that don't support this are serialized and stored with the | ||||||
|  |     mimetype set. | ||||||
|  |  | ||||||
|  |     The mimetype is used for serialized objects, and tells the | ||||||
|  |     ResXResourceReader how to depersist the object. This is currently not | ||||||
|  |     extensible. For a given mimetype the value must be set accordingly: | ||||||
|  |  | ||||||
|  |     Note - application/x-microsoft.net.object.binary.base64 is the format | ||||||
|  |     that the ResXResourceWriter will generate, however the reader can | ||||||
|  |     read any of the formats listed below. | ||||||
|  |  | ||||||
|  |     mimetype: application/x-microsoft.net.object.binary.base64 | ||||||
|  |     value   : The object must be serialized with | ||||||
|  |             : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | ||||||
|  |             : and then encoded with base64 encoding. | ||||||
|  |      | ||||||
|  |     mimetype: application/x-microsoft.net.object.soap.base64 | ||||||
|  |     value   : The object must be serialized with | ||||||
|  |             : System.Runtime.Serialization.Formatters.Soap.SoapFormatter | ||||||
|  |             : and then encoded with base64 encoding. | ||||||
|  |  | ||||||
|  |     mimetype: application/x-microsoft.net.object.bytearray.base64 | ||||||
|  |     value   : The object must be serialized into a byte array | ||||||
|  |             : using a System.ComponentModel.TypeConverter | ||||||
|  |             : and then encoded with base64 encoding. | ||||||
|  |     --> | ||||||
|  |   <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | ||||||
|  |     <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | ||||||
|  |     <xsd:element name="root" msdata:IsDataSet="true"> | ||||||
|  |       <xsd:complexType> | ||||||
|  |         <xsd:choice maxOccurs="unbounded"> | ||||||
|  |           <xsd:element name="metadata"> | ||||||
|  |             <xsd:complexType> | ||||||
|  |               <xsd:sequence> | ||||||
|  |                 <xsd:element name="value" type="xsd:string" minOccurs="0" /> | ||||||
|  |               </xsd:sequence> | ||||||
|  |               <xsd:attribute name="name" use="required" type="xsd:string" /> | ||||||
|  |               <xsd:attribute name="type" type="xsd:string" /> | ||||||
|  |               <xsd:attribute name="mimetype" type="xsd:string" /> | ||||||
|  |               <xsd:attribute ref="xml:space" /> | ||||||
|  |             </xsd:complexType> | ||||||
|  |           </xsd:element> | ||||||
|  |           <xsd:element name="assembly"> | ||||||
|  |             <xsd:complexType> | ||||||
|  |               <xsd:attribute name="alias" type="xsd:string" /> | ||||||
|  |               <xsd:attribute name="name" type="xsd:string" /> | ||||||
|  |             </xsd:complexType> | ||||||
|  |           </xsd:element> | ||||||
|  |           <xsd:element name="data"> | ||||||
|  |             <xsd:complexType> | ||||||
|  |               <xsd:sequence> | ||||||
|  |                 <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | ||||||
|  |                 <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | ||||||
|  |               </xsd:sequence> | ||||||
|  |               <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | ||||||
|  |               <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | ||||||
|  |               <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | ||||||
|  |               <xsd:attribute ref="xml:space" /> | ||||||
|  |             </xsd:complexType> | ||||||
|  |           </xsd:element> | ||||||
|  |           <xsd:element name="resheader"> | ||||||
|  |             <xsd:complexType> | ||||||
|  |               <xsd:sequence> | ||||||
|  |                 <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | ||||||
|  |               </xsd:sequence> | ||||||
|  |               <xsd:attribute name="name" type="xsd:string" use="required" /> | ||||||
|  |             </xsd:complexType> | ||||||
|  |           </xsd:element> | ||||||
|  |         </xsd:choice> | ||||||
|  |       </xsd:complexType> | ||||||
|  |     </xsd:element> | ||||||
|  |   </xsd:schema> | ||||||
|  |   <resheader name="resmimetype"> | ||||||
|  |     <value>text/microsoft-resx</value> | ||||||
|  |   </resheader> | ||||||
|  |   <resheader name="version"> | ||||||
|  |     <value>2.0</value> | ||||||
|  |   </resheader> | ||||||
|  |   <resheader name="reader"> | ||||||
|  |     <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||||
|  |   </resheader> | ||||||
|  |   <resheader name="writer"> | ||||||
|  |     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||||
|  |   </resheader> | ||||||
|  |   <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> | ||||||
|  |   <data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | ||||||
|  |     <value> | ||||||
|  |         AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgvCjzYkwo82JsKPNifCjzYqwo82IcKPNgLCjzYAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAMKPNgDCjzYBwo82HsKPNknCjzZewo82QcKPNhLCjzYAwo82AAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82S8KPNtPCjzbiwo8258KPNuDCjzatwo82EsKP | ||||||
|  |         NgAAAAAAAAAAAAAAAADCjzYAwo82BsKPNmvCjzbbwo826MKPNtvCjzbhwo82vcKPNmDCjzYywo82AAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYPwo82O8KPNs7Cjzb/wo82iMKP | ||||||
|  |         Nh7CjzYCwo82AAAAAAAAAAAAwo82AMKPNgDCjzZnwo8298KPNsLCjzY6wo82GsKPNjTCjzbJwo82/8KP | ||||||
|  |         NpTCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYIwo82vsKP | ||||||
|  |         Nv/CjzZowo82AAAAAAAAAAAAAAAAAAAAAADCjzYAwo82JMKPNtnCjzbxwo82QcKPNgDCjzYAwo82AMKP | ||||||
|  |         NpHCjzb/wo82lsKPNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKP | ||||||
|  |         NgjCjza9wo82/8KPNmjCjzYAAAAAAAAAAAAAAAAAwo82AMKPNgDCjzZ2wo82/8KPNrnCjzYKwo82AAAA | ||||||
|  |         AADCjzYAwo82jcKPNv/CjzaWwo82AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AADCjzYAwo82CMKPNr3Cjzb/wo82aMKPNgAAAAAAAAAAAAAAAADCjzYAwo82CcKPNrrCjzb/wo82fcKP | ||||||
|  |         NgDCjzYAAAAAAMKPNgDCjzaLwo82/8KPNpbCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAMKPNgDCjzYIwo82vMKPNv/CjzZpwo82AAAAAAAAAAAAAAAAAMKPNgDCjzYfwo8238KP | ||||||
|  |         Nv3CjzZRwo82AMKPNgDCjzYBwo82B8KPNpnCjzb/wo82psKPNgrCjzYAwo82AAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgjCjza8wo82/8KPNmnCjzYAAAAAAAAAAAAAAAAAwo82AMKP | ||||||
|  |         NjbCjzbxwo829sKPNj7CjzYAwo82AMKPNiPCjzaywo827cKPNv/Cjzbywo82qMKPNhPCjzYAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82CMKPNrvCjzb/wo82acKPNgAAAAAAAAAAAAAA | ||||||
|  |         AADCjzYAwo82P8KPNvfCjzbzwo82OcKPNgDCjzYAwo82D8KPNlnCjzZiwo82X8KPNmDCjzZRwo82CcKP | ||||||
|  |         NgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYHwo82usKPNv/CjzZpwo82AAAA | ||||||
|  |         AAAAAAAAAAAAAMKPNgDCjzY+wo829sKPNvTCjzY8wo82AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgfCjza6wo82/8KP | ||||||
|  |         NmnCjzYAAAAAAAAAAAAAAAAAwo82AMKPNirCjzbpwo82+MKPNkDCjzYAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82B8KP | ||||||
|  |         NrnCjzb/wo82acKPNgAAAAAAAAAAAAAAAADCjzYAwo82FcKPNtLCjzb/wo82VsKPNgAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgLCjzYTwo82BMKP | ||||||
|  |         NgDCjzYHwo82t8KPNv/CjzZpwo82AMKPNgDCjzYMwo82E8KPNgDCjzYCwo82n8KPNv/CjzaEwo82AMKP | ||||||
|  |         NgAAAAAAwo82AMKPNgDCjzZPwo82XcKPNgLCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82F8KP | ||||||
|  |         NrTCjzY6wo82AMKPNgbCjza0wo82/8KPNmnCjzYAwo82AMKPNnPCjzaawo82A8KPNgDCjzZOwo82+MKP | ||||||
|  |         NsbCjzYRwo82AAAAAADCjzYAwo82DcKPNsLCjzagwo82AMKPNgAAAAAAAAAAAAAAAAAAAAAAAAAAAMKP | ||||||
|  |         NgDCjzYPwo82ysKPNpPCjzYBwo82BcKPNrHCjzb/wo82acKPNgDCjzYUwo82zMKPNpPCjzYAwo82AMKP | ||||||
|  |         NgrCjzanwo82/MKPNmnCjzYAwo82AMKPNgDCjzZRwo8298KPNnbCjzYAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAwo82AMKPNgDCjzaawo827cKPNmHCjzYswo82vMKPNv/CjzaEwo82K8KPNoDCjzb5wo82ZMKP | ||||||
|  |         NgAAAAAAwo82AMKPNi7CjzbWwo825sKPNlnCjzYdwo82SMKPNtTCjzb9wo82UsKPNgAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNmHCjzb3wo828cKPNt/Cjzbuwo8298KPNurCjzbjwo829MKP | ||||||
|  |         NurCjzY6wo82AAAAAADCjzYAwo82AMKPNjfCjza8wo826cKPNtvCjzbewo82ycKPNs7CjzYvwo82AAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82EsKPNj3CjzZAwo82QcKPNkDCjzY/wo82QMKP | ||||||
|  |         NkDCjzY/wo82OMKPNgrCjzYAAAAAAAAAAADCjzYAwo82AMKPNg/CjzY5wo82SsKPNjDCjzYOwo82G8KP | ||||||
|  |         NgXCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | ||||||
|  |         AAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////wH4H/8B8Af/AfAH/4f | ||||||
|  |         hx/+H4cf/h8PH/4fDAf+HwwH/h8MB/4fD//+Hw///h8P/+IZD4/iGIcP4BGHH+ABwB/wAeAf8AHwH/// | ||||||
|  |         //////////////////////////////////8= | ||||||
|  | </value> | ||||||
|  |   </data> | ||||||
|  | </root> | ||||||
| @@ -0,0 +1,19 @@ | |||||||
|  | using System.Windows.Forms; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Demo.Winform | ||||||
|  | { | ||||||
|  |     internal static class Program | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         ///  The main entry point for the application. | ||||||
|  |         /// </summary> | ||||||
|  |         [STAThread] | ||||||
|  |         static void Main() | ||||||
|  |         { | ||||||
|  |             // To customize application configuration such as set high DPI settings or default font, | ||||||
|  |             // see https://aka.ms/applicationconfiguration. | ||||||
|  |             ApplicationConfiguration.Initialize(); | ||||||
|  |             Application.Run(new MainFrom()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,34 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||||
|  |  | ||||||
|  | 	<PropertyGroup> | ||||||
|  | 		<OutputType>WinExe</OutputType> | ||||||
|  | 		<ApplicationIcon>favicon.ico</ApplicationIcon> | ||||||
|  | 		<TargetFrameworks>net7.0-windows</TargetFrameworks> | ||||||
|  | 		<UseWindowsForms>true</UseWindowsForms> | ||||||
|  | 	</PropertyGroup> | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 	  <None Remove="favicon.ico" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 	  <Content Include="favicon.ico"> | ||||||
|  | 	    <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||||
|  | 	    <ExcludeFromSingleFile>true</ExcludeFromSingleFile> | ||||||
|  | 	    <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||||
|  | 	  </Content> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="7.0.86" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	<ItemGroup> | ||||||
|  | 	  <ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | </Project> | ||||||
							
								
								
									
										
											BIN
										
									
								
								framework/Demo/ThingsGateway.Foundation.Demo.Winform/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										53
									
								
								framework/Foundation/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,53 @@ | |||||||
|  | <Project> | ||||||
|  | 	<PropertyGroup> | ||||||
|  | 		<Version>3.0.0.27</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> | ||||||
| @@ -0,0 +1,56 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// 控制码 | ||||||
|  | /// </summary> | ||||||
|  | public enum ControlCode : byte | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 读数据 | ||||||
|  |     /// </summary> | ||||||
|  |     Read = 0x11, | ||||||
|  |     /// <summary> | ||||||
|  |     /// 读后续数据 | ||||||
|  |     /// </summary> | ||||||
|  |     ReadSub = 0x12, | ||||||
|  |     /// <summary> | ||||||
|  |     /// 读站号 | ||||||
|  |     /// </summary> | ||||||
|  |     ReadStation = 0x13, | ||||||
|  |     /// <summary> | ||||||
|  |     /// 写数据 | ||||||
|  |     /// </summary> | ||||||
|  |     Write = 0x14, | ||||||
|  |     /// <summary> | ||||||
|  |     /// 写站号 | ||||||
|  |     /// </summary> | ||||||
|  |     WriteStation = 0x15, | ||||||
|  |     /// <summary> | ||||||
|  |     /// 广播校时 | ||||||
|  |     /// </summary> | ||||||
|  |     BroadcastTime = 0x08, | ||||||
|  |     /// <summary> | ||||||
|  |     /// 冻结 | ||||||
|  |     /// </summary> | ||||||
|  |     Freeze = 0x16, | ||||||
|  |     /// <summary> | ||||||
|  |     /// 更新波特率 | ||||||
|  |     /// </summary> | ||||||
|  |     WriteBaudRate = 0x17, | ||||||
|  |     /// <summary> | ||||||
|  |     /// 更新密码 | ||||||
|  |     /// </summary> | ||||||
|  |     WritePassword = 0x18, | ||||||
|  | } | ||||||
| @@ -0,0 +1,480 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||||
|  | /// <summary> | ||||||
|  | /// DLT645_2007 | ||||||
|  | /// </summary> | ||||||
|  | public class DLT645_2007 : ReadWriteDevicesSerialSessionBase | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// DLT645_2007 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="serialSession"></param> | ||||||
|  |     public DLT645_2007(SerialSession serialSession) : base(serialSession) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 增加FE FE FE FE的报文头部 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("前导符报文头")] | ||||||
|  |     public bool EnableFEHead { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 写入需操作员代码 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("操作员代码")] | ||||||
|  |     public string OperCode { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 写入密码 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("写入密码")] | ||||||
|  |     public string Password { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 通讯地址BCD码,一般应该是12个字符 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("通讯地址")] | ||||||
|  |     public string Station { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         var str = """ | ||||||
|  |             查看附带文档或者相关资料,下面列举一下常见的数据标识地址  | ||||||
|  |              | ||||||
|  |             地址                       说明                     | ||||||
|  |             ----------------------------------------- | ||||||
|  |             02010100    A相电压 | ||||||
|  |             02020100    A相电流 | ||||||
|  |             02030000    瞬时总有功功率 | ||||||
|  |             00000000    (当前)组合有功总电能 | ||||||
|  |             00010000    (当前)正向有功总电能 | ||||||
|  |              | ||||||
|  |             """; | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + str; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult<byte[]>(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult<byte[]>(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient = null) | ||||||
|  |     { | ||||||
|  |         var dataHandleAdapter = new DLT645_2007DataHandleAdapter | ||||||
|  |         { | ||||||
|  |             EnableFEHead = EnableFEHead | ||||||
|  |         }; | ||||||
|  |         SerialSession.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, string value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             Password ??= string.Empty; | ||||||
|  |             OperCode ??= string.Empty; | ||||||
|  |             if (Password.Length < 8) | ||||||
|  |                 Password = Password.PadLeft(8, '0'); | ||||||
|  |             if (OperCode.Length < 8) | ||||||
|  |                 OperCode = OperCode.PadLeft(8, '0'); | ||||||
|  |             var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes()); | ||||||
|  |             string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken); | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken); | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             Password ??= string.Empty; | ||||||
|  |             OperCode ??= string.Empty; | ||||||
|  |             if (Password.Length < 8) | ||||||
|  |                 Password = Password.PadLeft(8, '0'); | ||||||
|  |             if (OperCode.Length < 8) | ||||||
|  |                 OperCode = OperCode.PadLeft(8, '0'); | ||||||
|  |             var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes()); | ||||||
|  |             string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     #region 其他方法 | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 广播校时 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="dateTime"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}"; | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().ToArray(), "999999999999".ByHexStringToBytes()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 SerialSession.Send(commandResult.Content); | ||||||
|  |                 return OperResult.CreateSuccessResult(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 冻结 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="dateTime"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}"; | ||||||
|  |             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||||
|  |             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 读取通信地址 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     var buffer = result1.Content.SelectMiddle(0, 6).BytesAdd(-0x33); | ||||||
|  |                     return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString()); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult<string>(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult<string>(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 修改波特率 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="baudRate"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             byte baudRateByte; | ||||||
|  |             switch (baudRate) | ||||||
|  |             { | ||||||
|  |                 case 600: baudRateByte = 0x02; break; | ||||||
|  |                 case 1200: baudRateByte = 0x04; break; | ||||||
|  |                 case 2400: baudRateByte = 0x08; break; | ||||||
|  |                 case 4800: baudRateByte = 0x10; break; | ||||||
|  |                 case 9600: baudRateByte = 0x20; break; | ||||||
|  |                 case 19200: baudRateByte = 0x40; break; | ||||||
|  |                 default: return new OperResult<string>($"不支持此波特率:{baudRate}"); | ||||||
|  |             } | ||||||
|  |             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||||
|  |             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 更新通信地址 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="station"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 修改密码 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="level"></param> | ||||||
|  |     /// <param name="oldPassword"></param> | ||||||
|  |     /// <param name="newPassword"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |  | ||||||
|  |             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||||
|  |             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||||
|  |             string str = $"04000C{level + 1:D2}"; | ||||||
|  |  | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword, | ||||||
|  |                 str.ByHexStringToBytes().Reverse().ToArray() | ||||||
|  |                 .SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray()) | ||||||
|  |                 .SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray()) | ||||||
|  |                 , Station.ByHexStringToBytes().Reverse().ToArray()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     #endregion | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,109 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.String; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// DLT645_2007Address | ||||||
|  | /// </summary> | ||||||
|  | public class DLT645_2007Address : DeviceAddressBase | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     public DLT645_2007Address() | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 数据标识 | ||||||
|  |     /// </summary> | ||||||
|  |     public byte[] DataId { get; set; } = new byte[0]; | ||||||
|  |     /// <summary> | ||||||
|  |     /// 反转解析 | ||||||
|  |     /// </summary> | ||||||
|  |     public bool Reverse { get; set; } = true; | ||||||
|  |     /// <summary> | ||||||
|  |     /// 站号信息 | ||||||
|  |     /// </summary> | ||||||
|  |     public byte[] Station { get; set; } = new byte[0]; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 解析地址 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="address"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public static DLT645_2007Address ParseFrom(string address) | ||||||
|  |     { | ||||||
|  |         DLT645_2007Address dLT645_2007Address = new(); | ||||||
|  |         byte[] array; | ||||||
|  |         array = new byte[0]; | ||||||
|  |         if (address.IndexOf(';') < 0) | ||||||
|  |         { | ||||||
|  |             array = address.ByHexStringToBytes().Reverse().ToArray(); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||||
|  |  | ||||||
|  |             for (int index = 0; index < strArray.Length; ++index) | ||||||
|  |             { | ||||||
|  |                 if (strArray[index].ToUpper().StartsWith("S=")) | ||||||
|  |                 { | ||||||
|  |                     var station = strArray[index].Substring(2); | ||||||
|  |                     if (station.IsNullOrEmpty()) station = string.Empty; | ||||||
|  |                     if (station.Length < 12) | ||||||
|  |                         station = station.PadLeft(12, '0'); | ||||||
|  |                     dLT645_2007Address.Station = station.ByHexStringToBytes().Reverse().ToArray(); | ||||||
|  |                 } | ||||||
|  |                 else if (strArray[index].Contains("r=")) | ||||||
|  |                 { | ||||||
|  |                     dLT645_2007Address.Reverse = strArray[index].Substring(2).GetBoolValue(); | ||||||
|  |                 } | ||||||
|  |                 else if (!strArray[index].Contains("=")) | ||||||
|  |                 { | ||||||
|  |                     array = strArray[index].ByHexStringToBytes().Reverse().ToArray(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         dLT645_2007Address.DataId = array; | ||||||
|  |         return dLT645_2007Address; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string ToString() | ||||||
|  |     { | ||||||
|  |         StringBuilder stringGeter = new(); | ||||||
|  |         if (Station.Length > 0) | ||||||
|  |         { | ||||||
|  |             stringGeter.Append($"s={Station.Reverse().ToArray().ToHexString()};"); | ||||||
|  |         } | ||||||
|  |         if (DataId.Length > 0) | ||||||
|  |         { | ||||||
|  |             stringGeter.Append($"{DataId.Reverse().ToArray().ToHexString()};"); | ||||||
|  |         } | ||||||
|  |         if (!Reverse) | ||||||
|  |         { | ||||||
|  |             stringGeter.Append($"s={Reverse.ToString()};"); | ||||||
|  |         } | ||||||
|  |         return stringGeter.ToString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,117 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||||
|  | /// <summary> | ||||||
|  | /// DLT645_2007 | ||||||
|  | /// </summary> | ||||||
|  | public class DLT645_2007BitConverter : ThingsGatewayBitConverter | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// DLT645_2007 | ||||||
|  |     /// </summary> | ||||||
|  |     public DLT645_2007BitConverter(EndianType endianType) : base(endianType) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// DLT645协议转换double | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="buffer">带数据项标识</param> | ||||||
|  |     /// <param name="offset"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public override double ToDouble(byte[] buffer, int offset) | ||||||
|  |     { | ||||||
|  |         return Convert.ToDouble(this.ToString(buffer, offset, buffer.Length)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override IThingsGatewayBitConverter CopyNew() | ||||||
|  |     { | ||||||
|  |         return new DLT645_2007BitConverter(EndianType) | ||||||
|  |         { | ||||||
|  |             DataFormat = DataFormat, | ||||||
|  |             BcdFormat = BcdFormat, | ||||||
|  |             Encoding = Encoding, | ||||||
|  |             IsStringReverseByteWord = IsStringReverseByteWord, | ||||||
|  |             Length = Length, | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string ToString(byte[] buffer) | ||||||
|  |     { | ||||||
|  |         return this.ToString(buffer, 0, buffer.Length); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string ToString(byte[] buffer, int offset, int length) | ||||||
|  |     { | ||||||
|  |         buffer = buffer.RemoveBegin(offset); | ||||||
|  |         buffer = buffer.BytesAdd(-0x33); | ||||||
|  |         var dataInfos = DLT645Helper.GetDataInfos(buffer); | ||||||
|  |         StringBuilder stringBuilder = new(); | ||||||
|  |         foreach (var dataInfo in dataInfos) | ||||||
|  |         { | ||||||
|  |             //实际数据 | ||||||
|  |             var content = buffer.SelectMiddle(4, dataInfo.ByteLength).Reverse().ToArray(); | ||||||
|  |             if (dataInfo.IsSigned)//可能为负数 | ||||||
|  |             { | ||||||
|  |                 if (content[0] > 0x80)//最高位是表示正负 | ||||||
|  |                 { | ||||||
|  |                     content[0] = (byte)(content[0] - 0x80); | ||||||
|  |                     if (dataInfo.Digtal == 0)//无小数点 | ||||||
|  |                     { | ||||||
|  |                         stringBuilder.Append($"-{content.ToHexString()}"); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         stringBuilder.Append((-(Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString()); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     ToString(stringBuilder, dataInfo, content); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 ToString(stringBuilder, dataInfo, content); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return stringBuilder.ToString(); | ||||||
|  |  | ||||||
|  |         static void ToString(StringBuilder stringBuilder, DataInfo dataInfo, byte[] content) | ||||||
|  |         { | ||||||
|  |             if (dataInfo.Digtal < 0) | ||||||
|  |             { | ||||||
|  |                 stringBuilder.Append($"{Encoding.ASCII.GetString(content)}"); | ||||||
|  |             } | ||||||
|  |             else if (dataInfo.Digtal == 0)//无小数点 | ||||||
|  |             { | ||||||
|  |                 stringBuilder.Append($"{content.ToHexString()}"); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 stringBuilder.Append(((Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,191 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// DLT645_2007DataHandleAdapter | ||||||
|  | /// </summary> | ||||||
|  | public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<DLT645_2007Message> | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 增加FE FE FE FE的报文头部 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("前导符报文头")] | ||||||
|  |     public bool EnableFEHead { get; set; } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override byte[] PackCommand(byte[] command) | ||||||
|  |     { | ||||||
|  |         //打包时加上4个FE字节 | ||||||
|  |         if (EnableFEHead) | ||||||
|  |         { | ||||||
|  |             return DataTransUtil.SpliceArray(new byte[4] { 0xFE, 0xFE, 0xFE, 0xFE }, command); | ||||||
|  |         } | ||||||
|  |         return command; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override DLT645_2007Message GetInstance() | ||||||
|  |     { | ||||||
|  |         return new DLT645_2007Message(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override FilterResult UnpackResponse(DLT645_2007Message request, byte[] send, byte[] body, byte[] response) | ||||||
|  |     { | ||||||
|  |         //因为设备可能带有FE前导符开头,这里找到0x68的位置 | ||||||
|  |         int headCodeIndex = -1; | ||||||
|  |         if (response != null) | ||||||
|  |         { | ||||||
|  |             for (int index = 0; index < response.Length; index++) | ||||||
|  |             { | ||||||
|  |                 if (response[index] == 0x68) | ||||||
|  |                 { | ||||||
|  |                     headCodeIndex = index; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         int sendHeadCodeIndex = 0; | ||||||
|  |         if (send != null) | ||||||
|  |         { | ||||||
|  |             for (int index = 0; index < send.Length; index++) | ||||||
|  |             { | ||||||
|  |                 if (send[index] == 0x68) | ||||||
|  |                 { | ||||||
|  |                     sendHeadCodeIndex = index; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         //帧起始符 地址域  帧起始符 控制码 数据域长度共10个字节 | ||||||
|  |         if (headCodeIndex < 0 || headCodeIndex + 10 > response.Length) | ||||||
|  |             return FilterResult.Cache; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var len = 10 + response[headCodeIndex + 9] + 2; | ||||||
|  |  | ||||||
|  |         if (response.Length - headCodeIndex < len) | ||||||
|  |         { | ||||||
|  |             return FilterResult.Cache; | ||||||
|  |         } | ||||||
|  |         if (response.Length - headCodeIndex >= len && response[len + headCodeIndex - 1] == 0x16) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             //检查校验码 | ||||||
|  |             int sumCheck = 0; | ||||||
|  |             for (int i = headCodeIndex; i < len + headCodeIndex - 2; i++) | ||||||
|  |                 sumCheck += response[i]; | ||||||
|  |             if ((byte)sumCheck != response[len + headCodeIndex - 2]) | ||||||
|  |             { | ||||||
|  |                 //校验错误 | ||||||
|  |                 request.Message = "和校验错误"; | ||||||
|  |                 request.ErrorCode = 999; | ||||||
|  |                 return FilterResult.Success; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if ( | ||||||
|  |                 (response[headCodeIndex + 1] != send[sendHeadCodeIndex + 1]) || | ||||||
|  |                 (response[headCodeIndex + 2] != send[sendHeadCodeIndex + 2]) || | ||||||
|  |                 (response[headCodeIndex + 3] != send[sendHeadCodeIndex + 3]) || | ||||||
|  |                 (response[headCodeIndex + 4] != send[sendHeadCodeIndex + 4]) || | ||||||
|  |                 (response[headCodeIndex + 5] != send[sendHeadCodeIndex + 5]) || | ||||||
|  |                 (response[headCodeIndex + 6] != send[sendHeadCodeIndex + 6]) | ||||||
|  |                 )//设备地址不符合时,返回错误 | ||||||
|  |             { | ||||||
|  |  | ||||||
|  |                 if ( | ||||||
|  |                 (send[sendHeadCodeIndex + 1] == 0xAA) && | ||||||
|  |                 (send[sendHeadCodeIndex + 2] == 0xAA) && | ||||||
|  |                 (send[sendHeadCodeIndex + 3] == 0xAA) && | ||||||
|  |                 (send[sendHeadCodeIndex + 4] == 0xAA) && | ||||||
|  |                 (send[sendHeadCodeIndex + 5] == 0xAA) && | ||||||
|  |                 (send[sendHeadCodeIndex + 6] == 0xAA) | ||||||
|  |                 )//读写通讯地址例外 | ||||||
|  |                 { | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     request.Message = "返回地址不符合规则"; | ||||||
|  |                     request.ErrorCode = 999; | ||||||
|  |                     return FilterResult.Success; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             if ((response[headCodeIndex + 8] != send[sendHeadCodeIndex + 8] + 0x80))//控制码不符合时,返回错误 | ||||||
|  |             { | ||||||
|  |                 request.Message = $"返回控制码:0x{response[headCodeIndex + 8]:X2},请求控制码:0x{send[sendHeadCodeIndex + 8]:X2},不符合规则"; | ||||||
|  |                 request.ErrorCode = 999; | ||||||
|  |                 return FilterResult.Success; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             if ((response[headCodeIndex + 8] & 0x40) == 0x40)//控制码bit6为1时,返回错误 | ||||||
|  |             { | ||||||
|  |                 byte byte1 = (byte)(response[headCodeIndex + 10] - 0x33); | ||||||
|  |                 var error = DLT645Helper.Get2007ErrorMessage(byte1); | ||||||
|  |                 request.Message = $"异常控制码:0x{response[headCodeIndex + 8]:X2},错误信息:{error}"; | ||||||
|  |                 request.ErrorCode = 999; | ||||||
|  |                 return FilterResult.Success; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (send[sendHeadCodeIndex + 8] == (byte)ControlCode.Read || | ||||||
|  |     send[sendHeadCodeIndex + 8] == (byte)ControlCode.Write | ||||||
|  |     ) | ||||||
|  |             { | ||||||
|  |                 //数据标识不符合时,返回错误 | ||||||
|  |                 if ( | ||||||
|  |                 (response[headCodeIndex + 10] == send[sendHeadCodeIndex + 10]) && | ||||||
|  |                 (response[headCodeIndex + 11] == send[sendHeadCodeIndex + 11]) && | ||||||
|  |                 (response[headCodeIndex + 12] == send[sendHeadCodeIndex + 12]) && | ||||||
|  |                 (response[headCodeIndex + 13] == send[sendHeadCodeIndex + 13]) | ||||||
|  |                 ) | ||||||
|  |                 { | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     request.Message = "返回数据标识不符合规则"; | ||||||
|  |                     request.ErrorCode = 999; | ||||||
|  |                     return FilterResult.Success; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             request.Content = response.RemoveBegin(headCodeIndex + 10).RemoveLast(response.Length + 2 - len - headCodeIndex); | ||||||
|  |             request.ErrorCode = 0; | ||||||
|  |             return FilterResult.Success; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             request.ErrorCode = 999; | ||||||
|  |             return FilterResult.Success; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||||
|  | /// <summary> | ||||||
|  | /// <inheritdoc/> | ||||||
|  | /// </summary> | ||||||
|  | public class DLT645_2007Message : MessageBase, IMessage | ||||||
|  | { | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override int HeadBytesLength => -1; | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override bool CheckHeadBytes(byte[] heads) | ||||||
|  |     { | ||||||
|  |         BodyLength = -1; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,480 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||||
|  | /// <summary> | ||||||
|  | /// DLT645_2007 | ||||||
|  | /// </summary> | ||||||
|  | public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// DLT645_2007 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="tcpClient"></param> | ||||||
|  |     public DLT645_2007OverTcp(TcpClient tcpClient) : base(tcpClient) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 增加FE FE FE FE的报文头部 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("前导符报文头")] | ||||||
|  |     public bool EnableFEHead { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 写入需操作员代码 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("操作员代码")] | ||||||
|  |     public string OperCode { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 写入密码 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("写入密码")] | ||||||
|  |     public string Password { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 通讯地址BCD码,一般应该是12个字符 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("通讯地址")] | ||||||
|  |     public string Station { get; set; } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         var str = """ | ||||||
|  |             查看附带文档或者相关资料,下面列举一下常见的数据标识地址  | ||||||
|  |              | ||||||
|  |             地址                       说明                     | ||||||
|  |             ----------------------------------------- | ||||||
|  |             02010100    A相电压 | ||||||
|  |             02020100    A相电流 | ||||||
|  |             02030000    瞬时总有功功率 | ||||||
|  |             00000000    (当前)组合有功总电能 | ||||||
|  |             00010000    (当前)正向有功总电能 | ||||||
|  |              | ||||||
|  |             """; | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + str; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult<byte[]>(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult<byte[]>(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient = null) | ||||||
|  |     { | ||||||
|  |         var dataHandleAdapter = new DLT645_2007DataHandleAdapter | ||||||
|  |         { | ||||||
|  |             EnableFEHead = EnableFEHead | ||||||
|  |         }; | ||||||
|  |         TcpClient.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, string value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             Password ??= string.Empty; | ||||||
|  |             OperCode ??= string.Empty; | ||||||
|  |             if (Password.Length < 8) | ||||||
|  |                 Password = Password.PadLeft(8, '0'); | ||||||
|  |             if (OperCode.Length < 8) | ||||||
|  |                 OperCode = OperCode.PadLeft(8, '0'); | ||||||
|  |             var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes()); | ||||||
|  |             string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken); | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken); | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             Password ??= string.Empty; | ||||||
|  |             OperCode ??= string.Empty; | ||||||
|  |             if (Password.Length < 8) | ||||||
|  |                 Password = Password.PadLeft(8, '0'); | ||||||
|  |             if (OperCode.Length < 8) | ||||||
|  |                 OperCode = OperCode.PadLeft(8, '0'); | ||||||
|  |             var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes()); | ||||||
|  |             string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     #region 其他方法 | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 广播校时 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="dateTime"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}"; | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().Reverse().ToArray(), "999999999999".ByHexStringToBytes()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 TcpClient.Send(commandResult.Content); | ||||||
|  |                 return OperResult.CreateSuccessResult(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 冻结 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="dateTime"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}"; | ||||||
|  |             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||||
|  |             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().Reverse().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 读取通信地址 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     var buffer = result1.Content.SelectMiddle(0, 6).BytesAdd(-0x33); | ||||||
|  |                     return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString()); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult<string>(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult<string>(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 修改波特率 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="baudRate"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             byte baudRateByte; | ||||||
|  |             switch (baudRate) | ||||||
|  |             { | ||||||
|  |                 case 600: baudRateByte = 0x02; break; | ||||||
|  |                 case 1200: baudRateByte = 0x04; break; | ||||||
|  |                 case 2400: baudRateByte = 0x08; break; | ||||||
|  |                 case 4800: baudRateByte = 0x10; break; | ||||||
|  |                 case 9600: baudRateByte = 0x20; break; | ||||||
|  |                 case 19200: baudRateByte = 0x40; break; | ||||||
|  |                 default: return new OperResult<string>($"不支持此波特率:{baudRate}"); | ||||||
|  |             } | ||||||
|  |             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||||
|  |             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 更新通信地址 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="station"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 修改密码 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="level"></param> | ||||||
|  |     /// <param name="oldPassword"></param> | ||||||
|  |     /// <param name="newPassword"></param> | ||||||
|  |     /// <param name="cancellationToken"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |  | ||||||
|  |             if (Station.IsNullOrEmpty()) Station = string.Empty; | ||||||
|  |             if (Station.Length < 12) Station = Station.PadLeft(12, '0'); | ||||||
|  |             string str = $"04000C{level:D2}"; | ||||||
|  |  | ||||||
|  |             var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword, | ||||||
|  |                 str.ByHexStringToBytes().Reverse().ToArray() | ||||||
|  |                 .SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray()) | ||||||
|  |                 .SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray()) | ||||||
|  |                 , Station.ByHexStringToBytes().Reverse().ToArray()); | ||||||
|  |             if (commandResult.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken); | ||||||
|  |                 var result1 = ((MessageBase)result.RequestInfo); | ||||||
|  |                 if (result1.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     return new OperResult(result1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult(commandResult); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<string>(ex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     #endregion | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.DLT645; | ||||||
|  |  | ||||||
|  | internal static class PackHelper | ||||||
|  | { | ||||||
|  |     public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new() | ||||||
|  |     { | ||||||
|  |         var byteConverter = device.ThingsGatewayBitConverter; | ||||||
|  |         var result = new List<T>(); | ||||||
|  |         //需要先剔除额外信息,比如dataformat等 | ||||||
|  |         foreach (var item in deviceVariables) | ||||||
|  |         { | ||||||
|  |             var address = item.VariableAddress; | ||||||
|  |  | ||||||
|  |             IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter); | ||||||
|  |             item.ThingsGatewayBitConverter = transformParameter; | ||||||
|  |             //item.VariableAddress = address; | ||||||
|  |             item.Index = device.GetBitOffset(item.VariableAddress); | ||||||
|  |  | ||||||
|  |             result.Add(new() | ||||||
|  |             { | ||||||
|  |                 DeviceVariableRunTimes = new() { item }, | ||||||
|  |                 VariableAddress = address, | ||||||
|  |                 Length = 1, | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | global using System; | ||||||
|  | global using System.Collections.Generic; | ||||||
|  | global using System.Linq; | ||||||
|  | global using System.Threading; | ||||||
|  | global using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | global using ThingsGateway.Foundation.Core; | ||||||
|  | global using ThingsGateway.Foundation.Serial; | ||||||
|  | global using ThingsGateway.Foundation.Sockets; | ||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  |   <ItemGroup> | ||||||
|  |     <ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" /> | ||||||
|  |   </ItemGroup> | ||||||
|  | </Project> | ||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | global using System; | ||||||
|  | global using System.Collections.Generic; | ||||||
|  | global using System.Linq; | ||||||
|  | global using System.Threading; | ||||||
|  | global using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | global using ThingsGateway.Foundation.Core; | ||||||
|  | global using ThingsGateway.Foundation.Serial; | ||||||
|  | global using ThingsGateway.Foundation.Sockets; | ||||||
| @@ -0,0 +1,175 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension; | ||||||
|  | using ThingsGateway.Foundation.Extension.String; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// Modbus协议地址 | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusAddress : DeviceAddressBase | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     public ModbusAddress() | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 读取功能码 | ||||||
|  |     /// </summary> | ||||||
|  |     public ushort AddressStart => Address.ToUShort(); | ||||||
|  |     /// <summary> | ||||||
|  |     /// 读取功能码 | ||||||
|  |     /// </summary> | ||||||
|  |     public byte ReadFunction { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 站号信息 | ||||||
|  |     /// </summary> | ||||||
|  |     public byte Station { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 写入功能码 | ||||||
|  |     /// </summary> | ||||||
|  |     public byte WriteFunction { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 打包临时写入,需要读取的字节长度 | ||||||
|  |     /// </summary> | ||||||
|  |     public int ByteLength { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// BitIndex | ||||||
|  |     /// </summary> | ||||||
|  |     public int BitIndex => (int)(Address.SplitDot().LastOrDefault().ToInt()); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 读取功能码 | ||||||
|  |     /// </summary> | ||||||
|  |     public ushort AddressEnd => (ushort)(AddressStart + (Math.Ceiling(ByteLength / 2.0) > 0 ? Math.Ceiling(ByteLength / 2.0) : 1)); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 作为Slave时需提供的SocketId,用于分辨Socket客户端,通常对比的是初始链接时的注册包 | ||||||
|  |     /// </summary> | ||||||
|  |     public string SocketId { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 解析地址 | ||||||
|  |     /// </summary> | ||||||
|  |     public static ModbusAddress ParseFrom(string address, byte station) | ||||||
|  |     { | ||||||
|  |         ModbusAddress modbusAddress = new() | ||||||
|  |         { | ||||||
|  |             Station = station | ||||||
|  |         }; | ||||||
|  |         return ParseFrom(address, modbusAddress); | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 解析地址 | ||||||
|  |     /// </summary> | ||||||
|  |     public static ModbusAddress ParseFrom(string address, ModbusAddress modbusAddress = null) | ||||||
|  |     { | ||||||
|  |         modbusAddress ??= new(); | ||||||
|  |         if (address.IndexOf(';') < 0) | ||||||
|  |         { | ||||||
|  |             Address(address); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); | ||||||
|  |             for (int index = 0; index < strArray.Length; ++index) | ||||||
|  |             { | ||||||
|  |                 if (strArray[index].ToUpper().StartsWith("S=")) | ||||||
|  |                 { | ||||||
|  |                     if (Convert.ToInt16(strArray[index].Substring(2)) > 0) | ||||||
|  |                         modbusAddress.Station = byte.Parse(strArray[index].Substring(2)); | ||||||
|  |                 } | ||||||
|  |                 else if (strArray[index].ToUpper().StartsWith("W=")) | ||||||
|  |                 { | ||||||
|  |                     if (Convert.ToInt16(strArray[index].Substring(2)) > 0) | ||||||
|  |                         modbusAddress.WriteFunction = byte.Parse(strArray[index].Substring(2)); | ||||||
|  |                 } | ||||||
|  |                 else if (strArray[index].ToUpper().StartsWith("ID=")) | ||||||
|  |                 { | ||||||
|  |                     modbusAddress.SocketId = strArray[index].Substring(3); | ||||||
|  |                 } | ||||||
|  |                 else if (!strArray[index].Contains("=")) | ||||||
|  |                 { | ||||||
|  |                     Address(strArray[index]); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return modbusAddress; | ||||||
|  |  | ||||||
|  |         void Address(string address) | ||||||
|  |         { | ||||||
|  |             var readF = ushort.Parse(address.Substring(0, 1)); | ||||||
|  |             if (readF > 4) | ||||||
|  |                 throw new("功能码错误"); | ||||||
|  |             switch (readF) | ||||||
|  |             { | ||||||
|  |                 case 0: | ||||||
|  |                     modbusAddress.ReadFunction = 1; | ||||||
|  |                     break; | ||||||
|  |                 case 1: | ||||||
|  |                     modbusAddress.ReadFunction = 2; | ||||||
|  |                     break; | ||||||
|  |                 case 3: | ||||||
|  |                     modbusAddress.ReadFunction = 4; | ||||||
|  |                     break; | ||||||
|  |                 case 4: | ||||||
|  |                     modbusAddress.ReadFunction = 3; | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |             modbusAddress.Address = (double.Parse(address.Substring(1)) - 1).ToString(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string ToString() | ||||||
|  |     { | ||||||
|  |         StringBuilder stringGeter = new(); | ||||||
|  |         if (Station > 0) | ||||||
|  |         { | ||||||
|  |             stringGeter.Append($"s={Station.ToString()};"); | ||||||
|  |         } | ||||||
|  |         if (WriteFunction > 0) | ||||||
|  |         { | ||||||
|  |             stringGeter.Append($"w={WriteFunction.ToString()};"); | ||||||
|  |         } | ||||||
|  |         if (!string.IsNullOrEmpty(SocketId)) | ||||||
|  |         { | ||||||
|  |             stringGeter.Append($"id={SocketId};"); | ||||||
|  |         } | ||||||
|  |         stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString()); | ||||||
|  |         return stringGeter.ToString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private string GetFunctionString(int readF) | ||||||
|  |     { | ||||||
|  |         return readF switch | ||||||
|  |         { | ||||||
|  |             1 => "0", | ||||||
|  |             2 => "1", | ||||||
|  |             3 => "4", | ||||||
|  |             4 => "3", | ||||||
|  |             _ => "4", | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,334 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Bool; | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | internal class ModbusHelper | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 添加Crc16 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static byte[] AddCrc(byte[] command) | ||||||
|  |     { | ||||||
|  |         return EasyCRC16.CRC16(command); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 添加ModbusTcp报文头 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id) | ||||||
|  |     { | ||||||
|  |         byte[] tcp = new byte[modbus.Length + 6]; | ||||||
|  |         tcp[0] = BitConverter.GetBytes(id)[1]; | ||||||
|  |         tcp[1] = BitConverter.GetBytes(id)[0]; | ||||||
|  |         tcp[4] = BitConverter.GetBytes(modbus.Length)[1]; | ||||||
|  |         tcp[5] = BitConverter.GetBytes(modbus.Length)[0]; | ||||||
|  |         modbus.CopyTo(tcp, 6); | ||||||
|  |         return tcp; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// modbus地址格式说明 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     internal static string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         StringBuilder stringBuilder = new(); | ||||||
|  |         stringBuilder.AppendLine("Modbus寄存器"); | ||||||
|  |         stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。"); | ||||||
|  |         stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。"); | ||||||
|  |         stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。"); | ||||||
|  |         stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。"); | ||||||
|  |         stringBuilder.AppendLine("举例:"); | ||||||
|  |         stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器"); | ||||||
|  |         stringBuilder.AppendLine("额外格式:"); | ||||||
|  |         stringBuilder.AppendLine("设备站号 ,比如40001;s=2; ,代表设备地址为2的保持寄存器第一个寄存器"); | ||||||
|  |         stringBuilder.AppendLine("写入功能码 ,比如40001;w=16; ,代表保持寄存器第一个寄存器,写入值时采用0x10功能码,而不是默认的0x06功能码"); | ||||||
|  |         return stringBuilder.ToString(); | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 通过错误码来获取到对应的文本消息 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static string GetDescriptionByErrorCode(byte code) | ||||||
|  |     { | ||||||
|  |         return code switch | ||||||
|  |         { | ||||||
|  |             1 => "不支持的功能码", | ||||||
|  |             2 => "读取寄存器越界", | ||||||
|  |             3 => "读取长度超限", | ||||||
|  |             4 => "读写异常", | ||||||
|  |             _ => "未知错误", | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="send">发送数据</param> | ||||||
|  |     /// <param name="response">返回数据</param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     internal static OperResult<byte[], FilterResult> GetModbusData(byte[] send, byte[] response) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             if (response.Length < 3) | ||||||
|  |                 return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             if (response[1] >= 0x80)//错误码 | ||||||
|  |                 return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success }; | ||||||
|  |             if (response[1] <= 0x05) | ||||||
|  |             { | ||||||
|  |                 if ((response.Length < response[2] + 3)) | ||||||
|  |                     return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if ((response.Length < 6)) | ||||||
|  |                     return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (send.Length == 0) | ||||||
|  |             { | ||||||
|  |                 var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success); | ||||||
|  |                 result.Message = "接收数据正确,但主机并没有主动请求数据"; | ||||||
|  |                 return result; | ||||||
|  |             } | ||||||
|  |             if (send[0] != response[0]) | ||||||
|  |                 return new OperResult<byte[], FilterResult>(string.Format("站号不一致", send[0], response[0])) { Content2 = FilterResult.Success }; | ||||||
|  |             if (send[1] != response[1]) | ||||||
|  |                 return new OperResult<byte[], FilterResult>() { Message = "功能码不一致", Content2 = FilterResult.Success }; | ||||||
|  |             return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[], FilterResult>(ex) { Content2 = FilterResult.Success }; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 去除Crc,返回modbus数据区 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="send"></param> | ||||||
|  |     /// <param name="response"></param> | ||||||
|  |     /// <param name="crcCheck"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     internal static OperResult<byte[], FilterResult> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true) | ||||||
|  |     { | ||||||
|  |         if (response.Length < 3) | ||||||
|  |             return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||||
|  |  | ||||||
|  |         if (response[1] >= 0x80)//错误码 | ||||||
|  |             return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success }; | ||||||
|  |         if (response[1] <= 0x05) | ||||||
|  |         { | ||||||
|  |             if ((response.Length < response[2] + 5)) | ||||||
|  |                 return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             if ((response.Length < 8)) | ||||||
|  |                 return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache }; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         var data = response.SelectMiddle(0, response[2] != 0 ? response[2] + 5 : 8); | ||||||
|  |         if (crcCheck && !EasyCRC16.CheckCRC16(data)) | ||||||
|  |             return new OperResult<byte[], FilterResult>("Crc校验失败" + DataTransUtil.ByteToHexString(data, ' ')) { Content2 = FilterResult.Success }; | ||||||
|  |         return GetModbusData(send, data.RemoveLast(2)); | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取读取报文 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, station); | ||||||
|  |             return OperResult.CreateSuccessResult(GetReadModbusCommand(mAddress, length)); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取写入布尔量报文,根据地址识别功能码 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, station); | ||||||
|  |  | ||||||
|  |             //功能码或实际长度 | ||||||
|  |             if (values?.Length > 1 || mAddress.WriteFunction == 15) | ||||||
|  |                 return GetWriteBoolModbusCommand(mAddress, values, values.Length); | ||||||
|  |             else | ||||||
|  |                 return GetWriteBoolModbusCommand(address, values[0], station); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取写入字报文,根据地址识别功能码 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, station); | ||||||
|  |  | ||||||
|  |             //功能码或实际长度 | ||||||
|  |             if (value?.Length > 2 || mAddress.WriteFunction == 16) | ||||||
|  |                 return OperResult.CreateSuccessResult(GetWriteModbusCommand(mAddress, value)); | ||||||
|  |             else | ||||||
|  |                 return OperResult.CreateSuccessResult(GetWriteOneModbusCommand(mAddress, value)); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取读取报文 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static byte[] GetReadModbusCommand(ModbusAddress mAddress, int length) | ||||||
|  |     { | ||||||
|  |         byte[] array = new byte[6] | ||||||
|  |         { | ||||||
|  |         (byte) mAddress.Station, | ||||||
|  |         (byte) mAddress.ReadFunction, | ||||||
|  |         BitConverter.GetBytes(mAddress.AddressStart)[1], | ||||||
|  |         BitConverter.GetBytes(mAddress.AddressStart)[0], | ||||||
|  |         BitConverter.GetBytes(length)[1], | ||||||
|  |         BitConverter.GetBytes(length)[0] | ||||||
|  |         }; | ||||||
|  |         return array; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取05写入布尔量报文 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             if (address.IndexOf('.') <= 0) | ||||||
|  |             { | ||||||
|  |                 var mAddress = ModbusAddress.ParseFrom(address, station); | ||||||
|  |  | ||||||
|  |                 return OperResult.CreateSuccessResult(GetWriteBoolModbusCommand(mAddress, value)); | ||||||
|  |             } | ||||||
|  |             return new("不支持写入字寄存器的某一位"); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取05写入布尔量报文 | ||||||
|  |     /// </summary> | ||||||
|  |     private static byte[] GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value) | ||||||
|  |     { | ||||||
|  |         byte[] array = new byte[6] | ||||||
|  |         { | ||||||
|  |     (byte) mAddress.Station, | ||||||
|  |     (byte)5, | ||||||
|  |     BitConverter.GetBytes(mAddress.AddressStart)[1], | ||||||
|  |     BitConverter.GetBytes(mAddress.AddressStart)[0], | ||||||
|  |      0, | ||||||
|  |      0 | ||||||
|  |         }; | ||||||
|  |         if (value) | ||||||
|  |         { | ||||||
|  |             array[4] = 0xFF; | ||||||
|  |             array[5] = 0; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             array[4] = 0; | ||||||
|  |             array[5] = 0; | ||||||
|  |         } | ||||||
|  |         return array; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取15写入布尔量报文 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             byte[] numArray1 = values.BoolArrayToByte(); | ||||||
|  |             byte[] numArray2 = new byte[7 + numArray1.Length]; | ||||||
|  |             numArray2[0] = (byte)mAddress.Station; | ||||||
|  |             numArray2[1] = (byte)15; | ||||||
|  |             numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1]; | ||||||
|  |             numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0]; | ||||||
|  |             numArray2[4] = (byte)(length / 256); | ||||||
|  |             numArray2[5] = (byte)(length % 256); | ||||||
|  |             numArray2[6] = (byte)numArray1.Length; | ||||||
|  |             numArray1.CopyTo(numArray2, 7); | ||||||
|  |             return OperResult.CreateSuccessResult(numArray2); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取16写入字报文 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static byte[] GetWriteModbusCommand(ModbusAddress mAddress, byte[] values) | ||||||
|  |     { | ||||||
|  |         byte[] numArray = new byte[7 + values.Length]; | ||||||
|  |         numArray[0] = (byte)mAddress.Station; | ||||||
|  |         numArray[1] = (byte)16; | ||||||
|  |         numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1]; | ||||||
|  |         numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0]; | ||||||
|  |         numArray[4] = (byte)(values.Length / 2 / 256); | ||||||
|  |         numArray[5] = (byte)(values.Length / 2 % 256); | ||||||
|  |         numArray[6] = (byte)values.Length; | ||||||
|  |         values.CopyTo(numArray, 7); | ||||||
|  |         return numArray; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取6写入字报文 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static byte[] GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values) | ||||||
|  |     { | ||||||
|  |         byte[] numArray = new byte[4 + values.Length]; | ||||||
|  |         numArray[0] = (byte)mAddress.Station; | ||||||
|  |         numArray[1] = (byte)6; | ||||||
|  |         numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1]; | ||||||
|  |         numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0]; | ||||||
|  |         values.CopyTo(numArray, 4); | ||||||
|  |         return numArray; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,191 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <summary> | ||||||
|  | /// ModbusRtu | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusRtu : ReadWriteDevicesSerialSessionBase | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// ModbusRtu | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="serialSession"></param> | ||||||
|  |     public ModbusRtu(SerialSession serialSession) : base(serialSession) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Crc校验 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("Crc校验")] | ||||||
|  |     public bool Crc16CheckEnable { get; set; } = true; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 站号 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("站号")] | ||||||
|  |     public byte Station { get; set; } = 1; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient = null) | ||||||
|  |     { | ||||||
|  |         ModbusRtuDataHandleAdapter dataHandleAdapter = new() | ||||||
|  |         { | ||||||
|  |             Crc16CheckEnable = Crc16CheckEnable, | ||||||
|  |             CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||||
|  |         }; | ||||||
|  |         SerialSession.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             if (FrameTime != 0) | ||||||
|  |                 Thread.Sleep(FrameTime); | ||||||
|  |             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             await Task.Delay(FrameTime, cancellationToken); | ||||||
|  |             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,85 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// Rtu适配器 | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusRtuDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusRtuMessage> | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 检测CRC | ||||||
|  |     /// </summary> | ||||||
|  |     public bool Crc16CheckEnable { get; set; } = true; | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="command"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public override byte[] PackCommand(byte[] command) | ||||||
|  |     { | ||||||
|  |         return ModbusHelper.AddCrc(command); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override ModbusRtuMessage GetInstance() | ||||||
|  |     { | ||||||
|  |         return new ModbusRtuMessage(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override FilterResult UnpackResponse(ModbusRtuMessage request, byte[] send, byte[] body, byte[] response) | ||||||
|  |     { | ||||||
|  |         //链路干扰时需剔除前缀中的多于字节,初步按站号+功能码找寻初始字节 | ||||||
|  |         if (send?.Length > 0) | ||||||
|  |         { | ||||||
|  |             int index = -1; | ||||||
|  |             for (int i = 0; i < response.Length - 1; i++) | ||||||
|  |             { | ||||||
|  |                 if (response[i] == send[0] && (response[i + 1] == send[1] || response[i + 1] == (send[1] + 0x80))) | ||||||
|  |                 { | ||||||
|  |                     index = i; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if (index >= 0) | ||||||
|  |             { | ||||||
|  |                 response = response.RemoveBegin(index); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             //理想状态检测 | ||||||
|  |             var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||||
|  |             if (result.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 request.ErrorCode = result.ErrorCode; | ||||||
|  |                 request.Message = result.Message; | ||||||
|  |                 request.Content = result.Content; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 request.ErrorCode = result.ErrorCode; | ||||||
|  |                 request.Message = result.Message; | ||||||
|  |             } | ||||||
|  |             return result.Content2; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return FilterResult.Success; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,31 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <summary> | ||||||
|  | /// <inheritdoc/> | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusRtuMessage : MessageBase, IMessage | ||||||
|  | { | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override int HeadBytesLength => -1; | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override bool CheckHeadBytes(byte[] heads) | ||||||
|  |     { | ||||||
|  |         BodyLength = -1; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,185 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase | ||||||
|  | { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public ModbusRtuOverTcp(TcpClient tcpClient) : base(tcpClient) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Crc校验 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("Crc校验")] | ||||||
|  |     public bool Crc16CheckEnable { get; set; } = true; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 站号 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("站号")] | ||||||
|  |     public byte Station { get; set; } = 1; | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient = null) | ||||||
|  |     { | ||||||
|  |         ModbusRtuDataHandleAdapter dataHandleAdapter = new() | ||||||
|  |         { | ||||||
|  |             Crc16CheckEnable = Crc16CheckEnable, | ||||||
|  |             CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||||
|  |         }; | ||||||
|  |         TcpClient.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             if (FrameTime != 0) | ||||||
|  |                 Thread.Sleep(FrameTime); | ||||||
|  |             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             await Task.Delay(FrameTime, cancellationToken); | ||||||
|  |             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,184 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class ModbusRtuOverUdp : ReadWriteDevicesUdpSessionBase | ||||||
|  | { | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public ModbusRtuOverUdp(UdpSession udpSession) : base(udpSession) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Crc校验 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("Crc校验")] | ||||||
|  |     public bool Crc16CheckEnable { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 站号 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("站号")] | ||||||
|  |     public byte Station { get; set; } = 1; | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient = null) | ||||||
|  |     { | ||||||
|  |         ModbusRtuOverUdpDataHandleAdapter dataHandleAdapter = new() | ||||||
|  |         { | ||||||
|  |             Crc16CheckEnable = Crc16CheckEnable, | ||||||
|  |         }; | ||||||
|  |         UdpSession.Config.SetUdpDataHandlingAdapter(() => | ||||||
|  |         { | ||||||
|  |             return dataHandleAdapter; | ||||||
|  |         }); | ||||||
|  |         UdpSession.Setup(UdpSession.Config); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             if (FrameTime != 0) | ||||||
|  |                 Thread.Sleep(FrameTime); | ||||||
|  |             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             await Task.Delay(FrameTime, cancellationToken); | ||||||
|  |             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// <inheritdoc/> | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusRtuMessage> | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 检测CRC | ||||||
|  |     /// </summary> | ||||||
|  |     public bool Crc16CheckEnable { get; set; } = true; | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override byte[] PackCommand(byte[] command) | ||||||
|  |     { | ||||||
|  |         return ModbusHelper.AddCrc(command); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override ModbusRtuMessage GetInstance() | ||||||
|  |     { | ||||||
|  |         return new ModbusRtuMessage(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response) | ||||||
|  |     { | ||||||
|  |         var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,452 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Bool; | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <summary> | ||||||
|  | /// <inheritdoc/> | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||||
|  |     /// </summary> | ||||||
|  |     public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SerialSession, Task<OperResult>> WriteData; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 继电器 | ||||||
|  |     /// </summary> | ||||||
|  |     private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new(); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 开关输入 | ||||||
|  |     /// </summary> | ||||||
|  |     private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new(); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 输入寄存器 | ||||||
|  |     /// </summary> | ||||||
|  |     private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new(); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 保持寄存器 | ||||||
|  |     /// </summary> | ||||||
|  |     private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new(); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public ModbusSerialServer(SerialSession serialSession) : base(serialSession) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 多站点 | ||||||
|  |     /// </summary> | ||||||
|  |     public bool MulStation { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 默认站点 | ||||||
|  |     /// </summary> | ||||||
|  |     public byte Station { get; set; } = 1; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void Dispose() | ||||||
|  |     { | ||||||
|  |         foreach (var item in ModbusServer01ByteBlocks) | ||||||
|  |         { | ||||||
|  |             item.Value.SafeDispose(); | ||||||
|  |         } | ||||||
|  |         foreach (var item in ModbusServer02ByteBlocks) | ||||||
|  |         { | ||||||
|  |             item.Value.SafeDispose(); | ||||||
|  |         } | ||||||
|  |         foreach (var item in ModbusServer03ByteBlocks) | ||||||
|  |         { | ||||||
|  |             item.Value.SafeDispose(); | ||||||
|  |         } | ||||||
|  |         foreach (var item in ModbusServer04ByteBlocks) | ||||||
|  |         { | ||||||
|  |             item.Value.SafeDispose(); | ||||||
|  |         } | ||||||
|  |         ModbusServer01ByteBlocks.Clear(); | ||||||
|  |         ModbusServer02ByteBlocks.Clear(); | ||||||
|  |         ModbusServer03ByteBlocks.Clear(); | ||||||
|  |         ModbusServer04ByteBlocks.Clear(); | ||||||
|  |         base.Dispose(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     EasyLock easyLock = new(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             easyLock.Wait(); | ||||||
|  |  | ||||||
|  |             ModbusAddress mAddress; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 return new OperResult<byte[]>(ex); | ||||||
|  |             } | ||||||
|  |             if (MulStation) | ||||||
|  |             { | ||||||
|  |                 Init(mAddress); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (Station != mAddress.Station) | ||||||
|  |                 { | ||||||
|  |                     return new OperResult<byte[]>("地址错误"); | ||||||
|  |                 } | ||||||
|  |                 Init(mAddress); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station]; | ||||||
|  |             int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength; | ||||||
|  |             switch (mAddress.ReadFunction) | ||||||
|  |             { | ||||||
|  |                 case 1: | ||||||
|  |                     byte[] bytes0 = new byte[len]; | ||||||
|  |                     ModbusServer01ByteBlock.Pos = mAddress.AddressStart; | ||||||
|  |                     ModbusServer01ByteBlock.Read(bytes0); | ||||||
|  |                     return OperResult.CreateSuccessResult(bytes0); | ||||||
|  |                 case 2: | ||||||
|  |                     byte[] bytes1 = new byte[len]; | ||||||
|  |                     ModbusServer02ByteBlock.Pos = mAddress.AddressStart; | ||||||
|  |                     ModbusServer02ByteBlock.Read(bytes1); | ||||||
|  |                     return OperResult.CreateSuccessResult(bytes1); | ||||||
|  |                 case 3: | ||||||
|  |  | ||||||
|  |                     byte[] bytes3 = new byte[len]; | ||||||
|  |                     ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||||
|  |                     ModbusServer03ByteBlock.Read(bytes3); | ||||||
|  |                     return OperResult.CreateSuccessResult(bytes3); | ||||||
|  |                 case 4: | ||||||
|  |                     byte[] bytes4 = new byte[len]; | ||||||
|  |                     ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||||
|  |                     ModbusServer04ByteBlock.Read(bytes4); | ||||||
|  |                     return OperResult.CreateSuccessResult(bytes4); | ||||||
|  |             } | ||||||
|  |             return new OperResult<byte[]>("功能码错误"); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             easyLock.Release(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         return Task.FromResult(Read(address, length)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient) | ||||||
|  |     { | ||||||
|  |         ModbusSerialServerDataHandleAdapter dataHandleAdapter = new(); | ||||||
|  |         dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout); | ||||||
|  |         SerialSession.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             easyLock.Wait(); | ||||||
|  |             ModbusAddress mAddress; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 return new OperResult(ex); | ||||||
|  |             } | ||||||
|  |             if (MulStation) | ||||||
|  |             { | ||||||
|  |                 Init(mAddress); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (Station != mAddress.Station) | ||||||
|  |                 { | ||||||
|  |                     return new OperResult("地址错误"); | ||||||
|  |                 } | ||||||
|  |                 Init(mAddress); | ||||||
|  |             } | ||||||
|  |             var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station]; | ||||||
|  |             switch (mAddress.ReadFunction) | ||||||
|  |             { | ||||||
|  |                 case 3: | ||||||
|  |                     ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||||
|  |                     ModbusServer03ByteBlock.Write(value); | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |                 case 4: | ||||||
|  |                     ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||||
|  |                     ModbusServer04ByteBlock.Write(value); | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |             } | ||||||
|  |             return new OperResult("功能码错误"); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             easyLock.Release(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             easyLock.Wait(); | ||||||
|  |             ModbusAddress mAddress; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 return (new OperResult(ex)); | ||||||
|  |             } | ||||||
|  |             if (MulStation) | ||||||
|  |             { | ||||||
|  |                 Init(mAddress); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (Station != mAddress.Station) | ||||||
|  |                 { | ||||||
|  |                     return (new OperResult("地址错误")); | ||||||
|  |                 } | ||||||
|  |                 Init(mAddress); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station]; | ||||||
|  |             switch (mAddress.ReadFunction) | ||||||
|  |             { | ||||||
|  |                 case 1: | ||||||
|  |                     ModbusServer01ByteBlock.Pos = mAddress.AddressStart; | ||||||
|  |                     ModbusServer01ByteBlock.Write(value.BoolArrayToByte()); | ||||||
|  |                     return (OperResult.CreateSuccessResult()); | ||||||
|  |                 case 2: | ||||||
|  |                     ModbusServer02ByteBlock.Pos = mAddress.AddressStart; | ||||||
|  |                     ModbusServer02ByteBlock.Write(value.BoolArrayToByte()); | ||||||
|  |                     return (OperResult.CreateSuccessResult()); | ||||||
|  |             } | ||||||
|  |             return new OperResult("功能码错误"); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             easyLock.Release(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         return Task.FromResult(Write(address, value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         return Task.FromResult(Write(address, value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override async Task Received(SerialSession client, ReceivedDataEventArgs e) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var requestInfo = e.RequestInfo; | ||||||
|  |             //接收外部报文 | ||||||
|  |             if (requestInfo is ModbusSerialServerMessage modbusServerMessage) | ||||||
|  |             { | ||||||
|  |                 if (modbusServerMessage.CurModbusAddress == null) | ||||||
|  |                 { | ||||||
|  |                     return;//无法解析直接返回 | ||||||
|  |                 } | ||||||
|  |                 if (!modbusServerMessage.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return;//无法解析直接返回 | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取 | ||||||
|  |                 { | ||||||
|  |                     var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length); | ||||||
|  |                     if (data.IsSuccess) | ||||||
|  |                     { | ||||||
|  |                         var coreData = data.Content; | ||||||
|  |                         if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||||
|  |                         { | ||||||
|  |                             coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0)); | ||||||
|  |                         } | ||||||
|  |                         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2).SpliceArray(new byte[] { (byte)coreData.Length }, coreData); | ||||||
|  |                         SerialSession.Send(sendData); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         WriteError(SerialSession, modbusServerMessage);//返回错误码 | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else//写入 | ||||||
|  |                 { | ||||||
|  |                     var coreData = modbusServerMessage.Content; | ||||||
|  |                     if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||||
|  |                     { | ||||||
|  |                         //写入继电器 | ||||||
|  |                         if (WriteData != null) | ||||||
|  |                         { | ||||||
|  |                             // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||||
|  |                             if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess) | ||||||
|  |                             { | ||||||
|  |                                 var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); | ||||||
|  |                                 if (result.IsSuccess) | ||||||
|  |                                 { | ||||||
|  |                                     WriteSuccess03(SerialSession, modbusServerMessage); | ||||||
|  |                                 } | ||||||
|  |                                 else | ||||||
|  |                                 { | ||||||
|  |                                     WriteError(SerialSession, modbusServerMessage); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 WriteError(SerialSession, modbusServerMessage); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             //写入内存区 | ||||||
|  |                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); | ||||||
|  |                             if (result.IsSuccess) | ||||||
|  |                             { | ||||||
|  |                                 WriteSuccess03(SerialSession, modbusServerMessage); | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 WriteError(SerialSession, modbusServerMessage); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         //写入寄存器 | ||||||
|  |                         if (WriteData != null) | ||||||
|  |                         { | ||||||
|  |  | ||||||
|  |                             if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess) | ||||||
|  |                             { | ||||||
|  |                                 var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||||
|  |                                 if (result.IsSuccess) | ||||||
|  |                                 { | ||||||
|  |                                     WriteSuccess03(SerialSession, modbusServerMessage); | ||||||
|  |                                 } | ||||||
|  |                                 else | ||||||
|  |                                 { | ||||||
|  |                                     WriteError(SerialSession, modbusServerMessage); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 WriteError(SerialSession, modbusServerMessage); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||||
|  |                             if (result.IsSuccess) | ||||||
|  |                             { | ||||||
|  |                                 WriteSuccess03(SerialSession, modbusServerMessage); | ||||||
|  |  | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 WriteError(SerialSession, modbusServerMessage); | ||||||
|  |  | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logger.LogError(ex, ToString()); | ||||||
|  |         } | ||||||
|  |         //返回错误码 | ||||||
|  |         static void WriteError(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage) | ||||||
|  |         { | ||||||
|  |             var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2) | ||||||
|  | .SpliceArray(new byte[] { (byte)1 });//01 lllegal function | ||||||
|  |             sendData[1] = (byte)(sendData[1] + 128); | ||||||
|  |             SerialSession.Send(sendData); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static void WriteSuccess03(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage) | ||||||
|  |     { | ||||||
|  |         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 6); | ||||||
|  |         SerialSession.Send(sendData); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void Init(ModbusAddress mAddress) | ||||||
|  |     { | ||||||
|  |         ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||||
|  |         ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||||
|  |         ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||||
|  |         ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,141 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class ModbusSerialServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusSerialServerMessage> | ||||||
|  | { | ||||||
|  |     private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="command"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public override byte[] PackCommand(byte[] command) | ||||||
|  |     { | ||||||
|  |         return ModbusHelper.AddCrc(command); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取modbus写入数据区内容 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="response">返回数据</param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     internal OperResult<byte[]> GetModbusData(byte[] response) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var func = ThingsGatewayBitConverter.ToByte(response, 1); | ||||||
|  |             if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6) | ||||||
|  |             { | ||||||
|  |                 if (response.Length == 6) | ||||||
|  |                     return OperResult.CreateSuccessResult(response); | ||||||
|  |             } | ||||||
|  |             else if (func == 15 || func == 16) | ||||||
|  |             { | ||||||
|  |                 var length = ThingsGatewayBitConverter.ToByte(response, 6); | ||||||
|  |                 if (response.Length == 7 + length) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(response); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" }; | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override ModbusSerialServerMessage GetInstance() | ||||||
|  |     { | ||||||
|  |         return new ModbusSerialServerMessage(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override FilterResult UnpackResponse(ModbusSerialServerMessage request, byte[] send, byte[] body, byte[] response) | ||||||
|  |     { | ||||||
|  |         var result1 = ModbusHelper.GetModbusRtuData(new byte[0], response, true); | ||||||
|  |         if (result1.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var result = GetModbusData(response.RemoveLast(2)); | ||||||
|  |             if (result.IsSuccess) | ||||||
|  |             { | ||||||
|  |                 //解析01 03 00 00 00 0A | ||||||
|  |                 var station = ThingsGatewayBitConverter.ToByte(response, 0); | ||||||
|  |                 var function = ThingsGatewayBitConverter.ToByte(response, 1); | ||||||
|  |                 int addressStart = ThingsGatewayBitConverter.ToInt16(response, 2); | ||||||
|  |                 if (addressStart == -1) | ||||||
|  |                 { | ||||||
|  |                     addressStart = 65535; | ||||||
|  |                 } | ||||||
|  |                 if (function > 4) | ||||||
|  |                 { | ||||||
|  |                     if (function > 6) | ||||||
|  |                     { | ||||||
|  |                         request.CurModbusAddress = new ModbusAddress() | ||||||
|  |                         { | ||||||
|  |                             Station = station, | ||||||
|  |                             Address = addressStart.ToString(), | ||||||
|  |                             WriteFunction = function, | ||||||
|  |                             ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3), | ||||||
|  |                         }; | ||||||
|  |                         request.Length = ThingsGatewayBitConverter.ToByte(response, 5); | ||||||
|  |                         request.Content = result.Content.RemoveBegin(7); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         request.CurModbusAddress = new ModbusAddress() | ||||||
|  |                         { | ||||||
|  |                             Station = station, | ||||||
|  |                             Address = addressStart.ToString(), | ||||||
|  |                             WriteFunction = function, | ||||||
|  |                             ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3), | ||||||
|  |                         }; | ||||||
|  |                         request.Length = 1; | ||||||
|  |                         request.Content = result.Content.RemoveBegin(4); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     request.CurModbusAddress = new ModbusAddress() | ||||||
|  |                     { | ||||||
|  |                         Station = station, | ||||||
|  |                         Address = addressStart.ToString(), | ||||||
|  |                         ReadFunction = function, | ||||||
|  |                     }; | ||||||
|  |                     request.Length = ThingsGatewayBitConverter.ToByte(response, 5); | ||||||
|  |                 } | ||||||
|  |                 request.ErrorCode = result.ErrorCode; | ||||||
|  |                 request.Message = result.Message; | ||||||
|  |                 return FilterResult.Success; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 request.ErrorCode = result.ErrorCode; | ||||||
|  |                 request.Message = result.Message; | ||||||
|  |                 return FilterResult.Cache; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return result1.Content2; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// <inheritdoc/> | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusSerialServerMessage : MessageBase, IMessage | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 当前关联的地址 | ||||||
|  |     /// </summary> | ||||||
|  |     public ModbusAddress CurModbusAddress { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 当前读写的数据长度 | ||||||
|  |     /// </summary> | ||||||
|  |     public int Length { get; set; } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override int HeadBytesLength => -1; | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override bool CheckHeadBytes(byte[] heads) | ||||||
|  |     { | ||||||
|  |         BodyLength = -1; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,184 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class ModbusTcp : ReadWriteDevicesTcpClientBase | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public ModbusTcp(TcpClient tcpClient) : base(tcpClient) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 检测事务标识符 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("检测事务标识符")] | ||||||
|  |     public bool IsCheckMessageId { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 站号 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("站号")] | ||||||
|  |     public byte Station { get; set; } = 1; | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             var data = SendThenReturn(commandResult, cancellationToken); | ||||||
|  |             return data; | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |  | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             var data = await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |             return data; | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient = null) | ||||||
|  |     { | ||||||
|  |         ModbusTcpDataHandleAdapter dataHandleAdapter = new() | ||||||
|  |         { | ||||||
|  |             IsCheckMessageId = IsCheckMessageId, | ||||||
|  |             CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||||
|  |         }; | ||||||
|  |         TcpClient.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             if (FrameTime != 0) | ||||||
|  |                 Thread.Sleep(FrameTime); | ||||||
|  |             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             await Task.Delay(FrameTime, cancellationToken); | ||||||
|  |             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,70 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// ModbusTcpDataHandleAdapter | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage> | ||||||
|  | { | ||||||
|  |     private readonly EasyIncrementCount easyIncrementCount = new(ushort.MaxValue); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 检测事务标识符 | ||||||
|  |     /// </summary> | ||||||
|  |     public bool IsCheckMessageId | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return Request?.IsCheckMessageId ?? false; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             Request.IsCheckMessageId = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override byte[] PackCommand(byte[] command) | ||||||
|  |     { | ||||||
|  |         return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override ModbusTcpMessage GetInstance() | ||||||
|  |     { | ||||||
|  |         return new ModbusTcpMessage(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override FilterResult UnpackResponse(ModbusTcpMessage request, byte[] send, byte[] body, byte[] response) | ||||||
|  |     { | ||||||
|  |         //理想状态检测 | ||||||
|  |         var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6)); | ||||||
|  |         if (result.IsSuccess) | ||||||
|  |         { | ||||||
|  |             request.ErrorCode = result.ErrorCode; | ||||||
|  |             request.Message = result.Message; | ||||||
|  |             request.Content = result.Content; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             request.ErrorCode = result.ErrorCode; | ||||||
|  |             request.Message = result.Message; | ||||||
|  |         } | ||||||
|  |         return result.Content2; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,41 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <summary> | ||||||
|  | /// <inheritdoc/> | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusTcpMessage : MessageBase, IMessage | ||||||
|  | { | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override int HeadBytesLength => 6; | ||||||
|  |     /// <summary> | ||||||
|  |     /// 检测事务标识符 | ||||||
|  |     /// </summary> | ||||||
|  |     public bool IsCheckMessageId { get; set; } = false; | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override bool CheckHeadBytes(byte[] heads) | ||||||
|  |     { | ||||||
|  |         if (heads == null || heads.Length <= 0) return false; | ||||||
|  |         HeadBytes = heads; | ||||||
|  |  | ||||||
|  |         int num = (HeadBytes[4] * 256) + HeadBytes[5]; | ||||||
|  |         BodyLength = num; | ||||||
|  |  | ||||||
|  |         if (!IsCheckMessageId) | ||||||
|  |             return true; | ||||||
|  |         else | ||||||
|  |             return SendBytes[0] == HeadBytes[0] && SendBytes[1] == HeadBytes[1] && HeadBytes[2] == 0 && HeadBytes[3] == 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,249 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase | ||||||
|  | { | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public ModbusTcpDtu(TcpService tcpService) : base(tcpService) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |         ModbusTcpDtuPlugin modbusTcpSalvePlugin = new ModbusTcpDtuPlugin(); | ||||||
|  |         tcpService.Config.ConfigurePlugins(a => | ||||||
|  |          { | ||||||
|  |              a.Add(modbusTcpSalvePlugin); | ||||||
|  |          }); | ||||||
|  |         tcpService.Setup(tcpService.Config); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 检测事务标识符 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("检测事务标识符")] | ||||||
|  |     public bool IsCheckMessageId { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 站号 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("站号")] | ||||||
|  |     public byte Station { get; set; } = 1; | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient = null) | ||||||
|  |     { | ||||||
|  |         if (socketClient is SocketClient client) | ||||||
|  |         { | ||||||
|  |             ModbusTcpDataHandleAdapter dataHandleAdapter = new() | ||||||
|  |             { | ||||||
|  |                 IsCheckMessageId = IsCheckMessageId, | ||||||
|  |                 CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||||
|  |             }; | ||||||
|  |             client.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             foreach (var item in TcpService.GetClients()) | ||||||
|  |             { | ||||||
|  |                 ModbusTcpDataHandleAdapter dataHandleAdapter = new() | ||||||
|  |                 { | ||||||
|  |                     IsCheckMessageId = IsCheckMessageId, | ||||||
|  |                     CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||||
|  |                 }; | ||||||
|  |                 item.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private OperResult<byte[]> SendThenReturn(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             if (TcpService.TryGetSocketClient($"ID={id}", out var client)) | ||||||
|  |             { | ||||||
|  |                 SetDataAdapter(client); | ||||||
|  |  | ||||||
|  |                 var item = commandResult.Content; | ||||||
|  |                 if (FrameTime != 0) | ||||||
|  |                     Thread.Sleep(FrameTime); | ||||||
|  |                 var WaitingClientEx = client.CreateWaitingClient(new() { }); | ||||||
|  |                 var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult<byte[]>("客户端未连接"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return commandResult; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task<OperResult<byte[]>> SendThenReturnAsync(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             if (TcpService.TryGetSocketClient($"ID={id}", out var client)) | ||||||
|  |             { | ||||||
|  |                 SetDataAdapter(client); | ||||||
|  |  | ||||||
|  |                 var item = commandResult.Content; | ||||||
|  |                 await Task.Delay(FrameTime, cancellationToken); | ||||||
|  |                 var WaitingClientEx = client.CreateWaitingClient(new() { }); | ||||||
|  |                 var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||||
|  |                 return (MessageBase)result.RequestInfo; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return new OperResult<byte[]>("客户端未连接"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return commandResult; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal class ModbusTcpDtuPlugin : PluginBase, ITcpReceivingPlugin | ||||||
|  |     { | ||||||
|  |         public Task OnTcpReceiving(ITcpClientBase client, ByteBlockEventArgs e) | ||||||
|  |         { | ||||||
|  |             if (client is ISocketClient socket) | ||||||
|  |             { | ||||||
|  |                 if (!socket.Id.StartsWith("ID=")) | ||||||
|  |                 { | ||||||
|  |                     ByteBlock byteBlock = e.ByteBlock; | ||||||
|  |                     var id = $"ID={byteBlock.ToArray().ToHexString()}"; | ||||||
|  |                     socket.ResetId(id); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return e.InvokeNext();//如果本插件无法处理当前数据,请将数据转至下一个插件。 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,470 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Bool; | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <summary> | ||||||
|  | /// <inheritdoc/> | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusTcpServer : ReadWriteDevicesTcpServerBase | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||||
|  |     /// </summary> | ||||||
|  |     public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<OperResult>> WriteData; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 继电器 | ||||||
|  |     /// </summary> | ||||||
|  |     private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new(); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 开关输入 | ||||||
|  |     /// </summary> | ||||||
|  |     private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new(); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 输入寄存器 | ||||||
|  |     /// </summary> | ||||||
|  |     private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new(); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 保持寄存器 | ||||||
|  |     /// </summary> | ||||||
|  |     private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new(); | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public ModbusTcpServer(TcpService tcpService) : base(tcpService) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 多站点 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("多站点")] | ||||||
|  |     public bool MulStation { get; set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 默认站点 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("默认站点")] | ||||||
|  |     public byte Station { get; set; } = 1; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void Dispose() | ||||||
|  |     { | ||||||
|  |         foreach (var item in ModbusServer01ByteBlocks) | ||||||
|  |         { | ||||||
|  |             item.Value.SafeDispose(); | ||||||
|  |         } | ||||||
|  |         foreach (var item in ModbusServer02ByteBlocks) | ||||||
|  |         { | ||||||
|  |             item.Value.SafeDispose(); | ||||||
|  |         } | ||||||
|  |         foreach (var item in ModbusServer03ByteBlocks) | ||||||
|  |         { | ||||||
|  |             item.Value.SafeDispose(); | ||||||
|  |         } | ||||||
|  |         foreach (var item in ModbusServer04ByteBlocks) | ||||||
|  |         { | ||||||
|  |             item.Value.SafeDispose(); | ||||||
|  |         } | ||||||
|  |         ModbusServer01ByteBlocks.Clear(); | ||||||
|  |         ModbusServer02ByteBlocks.Clear(); | ||||||
|  |         ModbusServer03ByteBlocks.Clear(); | ||||||
|  |         ModbusServer04ByteBlocks.Clear(); | ||||||
|  |         base.Dispose(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     EasyLock easyLock = new(); | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             easyLock.Wait(); | ||||||
|  |  | ||||||
|  |             ModbusAddress mAddress; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 return new OperResult<byte[]>(ex); | ||||||
|  |             } | ||||||
|  |             if (MulStation) | ||||||
|  |             { | ||||||
|  |                 Init(mAddress); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (Station != mAddress.Station) | ||||||
|  |                 { | ||||||
|  |                     return new OperResult<byte[]>("地址错误"); | ||||||
|  |                 } | ||||||
|  |                 Init(mAddress); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station]; | ||||||
|  |             int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength; | ||||||
|  |             switch (mAddress.ReadFunction) | ||||||
|  |             { | ||||||
|  |                 case 1: | ||||||
|  |                     byte[] bytes0 = new byte[len]; | ||||||
|  |                     ModbusServer01ByteBlock.Pos = mAddress.AddressStart; | ||||||
|  |                     ModbusServer01ByteBlock.Read(bytes0); | ||||||
|  |                     return OperResult.CreateSuccessResult(bytes0); | ||||||
|  |                 case 2: | ||||||
|  |                     byte[] bytes1 = new byte[len]; | ||||||
|  |                     ModbusServer02ByteBlock.Pos = mAddress.AddressStart; | ||||||
|  |                     ModbusServer02ByteBlock.Read(bytes1); | ||||||
|  |                     return OperResult.CreateSuccessResult(bytes1); | ||||||
|  |                 case 3: | ||||||
|  |  | ||||||
|  |                     byte[] bytes3 = new byte[len]; | ||||||
|  |                     ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||||
|  |                     ModbusServer03ByteBlock.Read(bytes3); | ||||||
|  |                     return OperResult.CreateSuccessResult(bytes3); | ||||||
|  |                 case 4: | ||||||
|  |                     byte[] bytes4 = new byte[len]; | ||||||
|  |                     ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||||
|  |                     ModbusServer04ByteBlock.Read(bytes4); | ||||||
|  |                     return OperResult.CreateSuccessResult(bytes4); | ||||||
|  |             } | ||||||
|  |             return new OperResult<byte[]>("功能码错误"); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             easyLock.Release(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         return Task.FromResult(Read(address, length)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient) | ||||||
|  |     { | ||||||
|  |         if (socketClient is SocketClient client) | ||||||
|  |         { | ||||||
|  |             ModbusTcpServerDataHandleAdapter dataHandleAdapter = new(); | ||||||
|  |             dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout); | ||||||
|  |             client.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             foreach (var item in TcpService.GetClients()) | ||||||
|  |             { | ||||||
|  |                 ModbusTcpDataHandleAdapter dataHandleAdapter = new() | ||||||
|  |                 { | ||||||
|  |                     CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) | ||||||
|  |                 }; | ||||||
|  |                 item.SetDataHandlingAdapter(dataHandleAdapter); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             easyLock.Wait(); | ||||||
|  |             ModbusAddress mAddress; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 return new OperResult(ex); | ||||||
|  |             } | ||||||
|  |             if (MulStation) | ||||||
|  |             { | ||||||
|  |                 Init(mAddress); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (Station != mAddress.Station) | ||||||
|  |                 { | ||||||
|  |                     return new OperResult("地址错误"); | ||||||
|  |                 } | ||||||
|  |                 Init(mAddress); | ||||||
|  |             } | ||||||
|  |             var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station]; | ||||||
|  |             switch (mAddress.ReadFunction) | ||||||
|  |             { | ||||||
|  |                 case 3: | ||||||
|  |                     ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||||
|  |                     ModbusServer03ByteBlock.Write(value); | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |                 case 4: | ||||||
|  |                     ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||||
|  |                     ModbusServer04ByteBlock.Write(value); | ||||||
|  |                     return OperResult.CreateSuccessResult(); | ||||||
|  |             } | ||||||
|  |             return new OperResult("功能码错误"); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             easyLock.Release(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             easyLock.Wait(); | ||||||
|  |             ModbusAddress mAddress; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 mAddress = ModbusAddress.ParseFrom(address, Station); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 return (new OperResult(ex)); | ||||||
|  |             } | ||||||
|  |             if (MulStation) | ||||||
|  |             { | ||||||
|  |                 Init(mAddress); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 if (Station != mAddress.Station) | ||||||
|  |                 { | ||||||
|  |                     return (new OperResult("地址错误")); | ||||||
|  |                 } | ||||||
|  |                 Init(mAddress); | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station]; | ||||||
|  |             var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station]; | ||||||
|  |             switch (mAddress.ReadFunction) | ||||||
|  |             { | ||||||
|  |                 case 1: | ||||||
|  |                     ModbusServer01ByteBlock.Pos = mAddress.AddressStart; | ||||||
|  |                     ModbusServer01ByteBlock.Write(value.BoolArrayToByte()); | ||||||
|  |                     return (OperResult.CreateSuccessResult()); | ||||||
|  |                 case 2: | ||||||
|  |                     ModbusServer02ByteBlock.Pos = mAddress.AddressStart; | ||||||
|  |                     ModbusServer02ByteBlock.Write(value.BoolArrayToByte()); | ||||||
|  |                     return (OperResult.CreateSuccessResult()); | ||||||
|  |             } | ||||||
|  |             return new OperResult("功能码错误"); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             easyLock.Release(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         return Task.FromResult(Write(address, value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         return Task.FromResult(Write(address, value)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override async Task Received(SocketClient client, ReceivedDataEventArgs e) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var requestInfo = e.RequestInfo; | ||||||
|  |             //接收外部报文 | ||||||
|  |             if (requestInfo is ModbusTcpServerMessage modbusServerMessage) | ||||||
|  |             { | ||||||
|  |                 if (modbusServerMessage.CurModbusAddress == null) | ||||||
|  |                 { | ||||||
|  |                     return;//无法解析直接返回 | ||||||
|  |                 } | ||||||
|  |                 if (!modbusServerMessage.IsSuccess) | ||||||
|  |                 { | ||||||
|  |                     return;//无法解析直接返回 | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取 | ||||||
|  |                 { | ||||||
|  |                     var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length); | ||||||
|  |                     if (data.IsSuccess) | ||||||
|  |                     { | ||||||
|  |                         var coreData = data.Content; | ||||||
|  |                         if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||||
|  |                         { | ||||||
|  |                             coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0)); | ||||||
|  |                         } | ||||||
|  |                         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8).SpliceArray(new byte[] { (byte)coreData.Length }, coreData); | ||||||
|  |                         sendData[5] = (byte)(sendData.Length - 6); | ||||||
|  |                         client.Send(sendData); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         WriteError(client, modbusServerMessage);//返回错误码 | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else//写入 | ||||||
|  |                 { | ||||||
|  |                     var coreData = modbusServerMessage.Content; | ||||||
|  |                     if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||||
|  |                     { | ||||||
|  |                         //写入继电器 | ||||||
|  |                         if (WriteData != null) | ||||||
|  |                         { | ||||||
|  |                             // 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||||
|  |                             if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) | ||||||
|  |                             { | ||||||
|  |                                 var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); | ||||||
|  |                                 if (result.IsSuccess) | ||||||
|  |                                 { | ||||||
|  |                                     WriteSuccess03(client, modbusServerMessage); | ||||||
|  |                                 } | ||||||
|  |                                 else | ||||||
|  |                                 { | ||||||
|  |                                     WriteError(client, modbusServerMessage); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 WriteError(client, modbusServerMessage); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             //写入内存区 | ||||||
|  |                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length)); | ||||||
|  |                             if (result.IsSuccess) | ||||||
|  |                             { | ||||||
|  |                                 WriteSuccess03(client, modbusServerMessage); | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 WriteError(client, modbusServerMessage); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         //写入寄存器 | ||||||
|  |                         if (WriteData != null) | ||||||
|  |                         { | ||||||
|  |  | ||||||
|  |                             if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) | ||||||
|  |                             { | ||||||
|  |                                 var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||||
|  |                                 if (result.IsSuccess) | ||||||
|  |                                 { | ||||||
|  |                                     WriteSuccess03(client, modbusServerMessage); | ||||||
|  |                                 } | ||||||
|  |                                 else | ||||||
|  |                                 { | ||||||
|  |                                     WriteError(client, modbusServerMessage); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 WriteError(client, modbusServerMessage); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||||
|  |                             if (result.IsSuccess) | ||||||
|  |                             { | ||||||
|  |                                 WriteSuccess03(client, modbusServerMessage); | ||||||
|  |  | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 WriteError(client, modbusServerMessage); | ||||||
|  |  | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             Logger.LogError(ex, ToString()); | ||||||
|  |         } | ||||||
|  |         //返回错误码 | ||||||
|  |         static void WriteError(SocketClient client, ModbusTcpServerMessage modbusServerMessage) | ||||||
|  |         { | ||||||
|  |             var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8) | ||||||
|  | .SpliceArray(new byte[] { (byte)1 });//01 lllegal function | ||||||
|  |             sendData[5] = (byte)(sendData.Length - 6); | ||||||
|  |             sendData[7] = (byte)(sendData[7] + 128); | ||||||
|  |             client.Send(sendData); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static void WriteSuccess03(SocketClient client, ModbusTcpServerMessage modbusServerMessage) | ||||||
|  |     { | ||||||
|  |         var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12); | ||||||
|  |         sendData[5] = (byte)(sendData.Length - 6); | ||||||
|  |         client.Send(sendData); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void Init(ModbusAddress mAddress) | ||||||
|  |     { | ||||||
|  |         ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||||
|  |         ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||||
|  |         ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||||
|  |         ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128)); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,132 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpServerMessage> | ||||||
|  | { | ||||||
|  |     private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="command"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public override byte[] PackCommand(byte[] command) | ||||||
|  |     { | ||||||
|  |         return command; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取modbus写入数据区内容 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="response">返回数据</param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     internal OperResult<byte[]> GetModbusData(byte[] response) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             var func = ThingsGatewayBitConverter.ToByte(response, 1); | ||||||
|  |             if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6) | ||||||
|  |             { | ||||||
|  |                 if (response.Length == 6) | ||||||
|  |                     return OperResult.CreateSuccessResult(response); | ||||||
|  |             } | ||||||
|  |             else if (func == 15 || func == 16) | ||||||
|  |             { | ||||||
|  |                 var length = ThingsGatewayBitConverter.ToByte(response, 6); | ||||||
|  |                 if (response.Length == 7 + length) | ||||||
|  |                 { | ||||||
|  |                     return OperResult.CreateSuccessResult(response); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" }; | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override ModbusTcpServerMessage GetInstance() | ||||||
|  |     { | ||||||
|  |         return new ModbusTcpServerMessage(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override FilterResult UnpackResponse(ModbusTcpServerMessage request, byte[] send, byte[] body, byte[] response) | ||||||
|  |     { | ||||||
|  |         var result = GetModbusData(response.RemoveBegin(6)); | ||||||
|  |         if (result.IsSuccess) | ||||||
|  |         { | ||||||
|  |             //解析01 03 00 00 00 0A | ||||||
|  |             var station = ThingsGatewayBitConverter.ToByte(response, 6); | ||||||
|  |             var function = ThingsGatewayBitConverter.ToByte(response, 7); | ||||||
|  |             int addressStart = ThingsGatewayBitConverter.ToInt16(response, 8); | ||||||
|  |             if (addressStart == -1) | ||||||
|  |             { | ||||||
|  |                 addressStart = 65535; | ||||||
|  |             } | ||||||
|  |             if (function > 4) | ||||||
|  |             { | ||||||
|  |                 if (function > 6) | ||||||
|  |                 { | ||||||
|  |                     request.CurModbusAddress = new ModbusAddress() | ||||||
|  |                     { | ||||||
|  |                         Station = station, | ||||||
|  |                         Address = addressStart.ToString(), | ||||||
|  |                         WriteFunction = function, | ||||||
|  |                         ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3), | ||||||
|  |                     }; | ||||||
|  |                     request.Length = ThingsGatewayBitConverter.ToByte(response, 11); | ||||||
|  |                     request.Content = result.Content.RemoveBegin(7); | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     request.CurModbusAddress = new ModbusAddress() | ||||||
|  |                     { | ||||||
|  |                         Station = station, | ||||||
|  |                         Address = addressStart.ToString(), | ||||||
|  |                         WriteFunction = function, | ||||||
|  |                         ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3), | ||||||
|  |                     }; | ||||||
|  |                     request.Length = 1; | ||||||
|  |                     request.Content = result.Content.RemoveBegin(4); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 request.CurModbusAddress = new ModbusAddress() | ||||||
|  |                 { | ||||||
|  |                     Station = station, | ||||||
|  |                     Address = addressStart.ToString(), | ||||||
|  |                     ReadFunction = function, | ||||||
|  |                 }; | ||||||
|  |                 request.Length = ThingsGatewayBitConverter.ToByte(response, 11); | ||||||
|  |             } | ||||||
|  |             request.ErrorCode = result.ErrorCode; | ||||||
|  |             request.Message = result.Message; | ||||||
|  |             return FilterResult.Success; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             request.ErrorCode = result.ErrorCode; | ||||||
|  |             request.Message = result.Message; | ||||||
|  |             return FilterResult.Cache; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,43 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     /// </summary> | ||||||
|  |     public class ModbusTcpServerMessage : MessageBase, IMessage | ||||||
|  |     { | ||||||
|  |         /// <summary> | ||||||
|  |         /// 当前关联的地址 | ||||||
|  |         /// </summary> | ||||||
|  |         public ModbusAddress CurModbusAddress { get; set; } | ||||||
|  |         /// <summary> | ||||||
|  |         /// 当前读写的数据长度 | ||||||
|  |         /// </summary> | ||||||
|  |         public int Length { get; set; } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc/> | ||||||
|  |         public override int HeadBytesLength => 6; | ||||||
|  |         /// <inheritdoc/> | ||||||
|  |         public override bool CheckHeadBytes(byte[] heads) | ||||||
|  |         { | ||||||
|  |             if (heads == null || heads.Length != 6) return false; | ||||||
|  |             HeadBytes = heads; | ||||||
|  |  | ||||||
|  |             int num = (HeadBytes[4] * 256) + HeadBytes[5]; | ||||||
|  |             BodyLength = num; | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,187 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.ComponentModel; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <inheritdoc/> | ||||||
|  | public class ModbusUdp : ReadWriteDevicesUdpSessionBase | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public ModbusUdp(UdpSession udpSession) : base(udpSession) | ||||||
|  |     { | ||||||
|  |         ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||||
|  |         RegisterByteLength = 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 检测事务标识符 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("检测事务标识符")] | ||||||
|  |     public bool IsCheckMessageId { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 站号 | ||||||
|  |     /// </summary> | ||||||
|  |     [Description("站号")] | ||||||
|  |     public byte Station { get; set; } = 1; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack) | ||||||
|  |     { | ||||||
|  |         return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override string GetAddressDescription() | ||||||
|  |     { | ||||||
|  |         return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||||
|  |     } | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override void SetDataAdapter(object socketClient = null) | ||||||
|  |     { | ||||||
|  |         ModbusUdpDataHandleAdapter dataHandleAdapter = new() | ||||||
|  |         { | ||||||
|  |             IsCheckMessageId = IsCheckMessageId | ||||||
|  |         }; | ||||||
|  |         UdpSession.Config.SetUdpDataHandlingAdapter(() => | ||||||
|  |         { | ||||||
|  |             return dataHandleAdapter; | ||||||
|  |         }); | ||||||
|  |         UdpSession.Setup(UdpSession.Config); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Connect(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return SendThenReturn(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) | ||||||
|  |     { | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             await ConnectAsync(cancellationToken); | ||||||
|  |             var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||||
|  |             return await SendThenReturnAsync(commandResult, cancellationToken); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             return new OperResult(ex); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             if (FrameTime != 0) | ||||||
|  |                 Thread.Sleep(FrameTime); | ||||||
|  |             var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken) | ||||||
|  |     { | ||||||
|  |         if (commandResult.IsSuccess) | ||||||
|  |         { | ||||||
|  |             var item = commandResult.Content; | ||||||
|  |             await Task.Delay(FrameTime, cancellationToken); | ||||||
|  |             var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken); | ||||||
|  |             return (MessageBase)result.RequestInfo; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             return new OperResult<byte[]>(commandResult.Message); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,56 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  | /// <summary> | ||||||
|  | /// <inheritdoc/> | ||||||
|  | /// </summary> | ||||||
|  | public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusTcpMessage> | ||||||
|  | { | ||||||
|  |     private readonly EasyIncrementCount easyIncrementCount = new(ushort.MaxValue); | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 检测事务标识符 | ||||||
|  |     /// </summary> | ||||||
|  |     public bool IsCheckMessageId | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return Request?.IsCheckMessageId ?? false; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             Request.IsCheckMessageId = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     public override byte[] PackCommand(byte[] command) | ||||||
|  |     { | ||||||
|  |         return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override ModbusTcpMessage GetInstance() | ||||||
|  |     { | ||||||
|  |         return new ModbusTcpMessage(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <inheritdoc/> | ||||||
|  |     protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response) | ||||||
|  |     { | ||||||
|  |         var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6)); | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,169 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Extension.Generic; | ||||||
|  | using ThingsGateway.Foundation.Extension.String; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.Modbus; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// PackHelper | ||||||
|  | /// </summary> | ||||||
|  | public class PackHelper | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// 打包变量,添加到<see href="deviceVariableSourceReads"></see> | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="device"></param> | ||||||
|  |     /// <param name="deviceVariables"></param> | ||||||
|  |     /// <param name="maxPack">最大打包长度</param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new() | ||||||
|  |     { | ||||||
|  |         if (deviceVariables == null) | ||||||
|  |             throw new ArgumentNullException(nameof(deviceVariables)); | ||||||
|  |  | ||||||
|  |         var deviceVariableSourceReads = new List<T>(); | ||||||
|  |         var byteConverter = device.ThingsGatewayBitConverter; | ||||||
|  |         //需要先剔除额外信息,比如dataformat等 | ||||||
|  |         foreach (var item in deviceVariables) | ||||||
|  |         { | ||||||
|  |             var address = item.VariableAddress; | ||||||
|  |             IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter); | ||||||
|  |             item.ThingsGatewayBitConverter = transformParameter; | ||||||
|  |             //item.VariableAddress = address; | ||||||
|  |             item.Index = device.GetBitOffset(item.VariableAddress); | ||||||
|  |         } | ||||||
|  |         var deviceVariableRunTimeGroups = deviceVariables.GroupBy(it => it.IntervalTime); | ||||||
|  |         foreach (var group in deviceVariableRunTimeGroups) | ||||||
|  |         { | ||||||
|  |             Dictionary<ModbusAddress, T2> map = group.ToDictionary(it => | ||||||
|  |             { | ||||||
|  |                 var lastLen = it.DataTypeEnum.GetByteLength(); | ||||||
|  |                 if (lastLen <= 0) | ||||||
|  |                 { | ||||||
|  |                     switch (it.DataTypeEnum) | ||||||
|  |                     { | ||||||
|  |                         case DataTypeEnum.String: | ||||||
|  |                             lastLen = it.ThingsGatewayBitConverter.Length == null ? throw new("数据类型为字符串时,必须指定字符串长度,才能进行打包") : it.ThingsGatewayBitConverter.Length.Value; | ||||||
|  |                             break; | ||||||
|  |                         default: | ||||||
|  |                             lastLen = 2; | ||||||
|  |                             break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 if (it.ThingsGatewayBitConverter.Length != null && it.DataTypeEnum != DataTypeEnum.String) | ||||||
|  |                 { | ||||||
|  |                     lastLen *= it.ThingsGatewayBitConverter.Length.Value; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 var address = it.VariableAddress; | ||||||
|  |                 if (address.IndexOf('.') > 0) | ||||||
|  |                 { | ||||||
|  |                     var addressSplits = address.SplitDot(); | ||||||
|  |                     address = addressSplits.RemoveLast(1).ArrayToString("."); | ||||||
|  |                 } | ||||||
|  |                 var result = ModbusAddress.ParseFrom(address); | ||||||
|  |                 result.ByteLength = lastLen; | ||||||
|  |                 return result; | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             //获取变量的地址 | ||||||
|  |             var modbusAddressList = map.Keys; | ||||||
|  |  | ||||||
|  |             //获取功能码 | ||||||
|  |             var functionCodes = modbusAddressList.Select(t => t.ReadFunction).Distinct(); | ||||||
|  |             foreach (var functionCode in functionCodes) | ||||||
|  |             { | ||||||
|  |                 var modbusAddressSameFunList = modbusAddressList.Where(t => t.ReadFunction == functionCode); | ||||||
|  |                 var stationNumbers = modbusAddressSameFunList.Select(t => t.Station).Distinct(); | ||||||
|  |                 foreach (var stationNumber in stationNumbers) | ||||||
|  |                 { | ||||||
|  |                     var addressList = modbusAddressSameFunList | ||||||
|  |                         .Where(t => t.Station == stationNumber) | ||||||
|  |                         .ToDictionary(t => t, t => map[t]); | ||||||
|  |  | ||||||
|  |                     var tempResult = LoadSourceRead<T, T2>(addressList, functionCode, group.Key, maxPack); | ||||||
|  |                     deviceVariableSourceReads.AddRange(tempResult); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return deviceVariableSourceReads; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static List<T> LoadSourceRead<T, T2>(Dictionary<ModbusAddress, T2> addressList, int functionCode, int intervalTime, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new() | ||||||
|  |     { | ||||||
|  |         List<T> sourceReads = new(); | ||||||
|  |         //按地址和长度排序 | ||||||
|  |         var orderByAddressEnd = addressList.Keys.OrderBy(it => it.AddressEnd); | ||||||
|  |         //按地址和长度排序 | ||||||
|  |         var orderByAddressStart = addressList.Keys.OrderBy(it => it.AddressStart); | ||||||
|  |         //地址最小,在循环中更改 | ||||||
|  |         var minAddress = orderByAddressStart.First().AddressStart; | ||||||
|  |         //地址最大 | ||||||
|  |         var maxAddress = orderByAddressStart.Last().AddressStart; | ||||||
|  |  | ||||||
|  |         while (maxAddress >= minAddress) | ||||||
|  |         { | ||||||
|  |             //最大的打包长度 | ||||||
|  |             int readLength = maxPack; | ||||||
|  |             if (functionCode == 1 || functionCode == 2) | ||||||
|  |             { | ||||||
|  |                 readLength = maxPack * 8 * 2; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             //获取当前的一组打包地址信息, | ||||||
|  |             var tempAddressEnd = orderByAddressEnd.Where(t => t.AddressEnd <= minAddress + readLength).ToList(); | ||||||
|  |             //起始地址 | ||||||
|  |             var startAddress = tempAddressEnd.OrderBy(it => it.AddressStart).First(); | ||||||
|  |             //读取寄存器长度 | ||||||
|  |             var sourceLen = tempAddressEnd.Last().AddressEnd - startAddress.AddressStart; | ||||||
|  |  | ||||||
|  |             T sourceRead = new() | ||||||
|  |             { | ||||||
|  |                 TimerTick = new TimerTick(intervalTime), | ||||||
|  |                 //这里只需要根据地址排序的第一个地址,作为实际打包报文中的起始地址 | ||||||
|  |                 VariableAddress = startAddress.ToString(), | ||||||
|  |                 Length = sourceLen | ||||||
|  |             }; | ||||||
|  |             foreach (var item in tempAddressEnd) | ||||||
|  |             { | ||||||
|  |                 var readNode = addressList[item]; | ||||||
|  |                 if ((functionCode == -1 || functionCode == 3 || functionCode == 4) && readNode.DataTypeEnum == DataTypeEnum.Boolean) | ||||||
|  |                 { | ||||||
|  |                     readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 16) + readNode.Index; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     if (functionCode == 1 || functionCode == 2) | ||||||
|  |                         readNode.Index = item.AddressStart - startAddress.AddressStart + readNode.Index; | ||||||
|  |                     else | ||||||
|  |                         readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 2) + readNode.Index; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 sourceRead.DeviceVariableRunTimes.Add(readNode); | ||||||
|  |                 addressList.Remove(item); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             sourceReads.Add(sourceRead); | ||||||
|  |             if (orderByAddressEnd.Count() > 0) | ||||||
|  |                 minAddress = orderByAddressStart.First().AddressStart; | ||||||
|  |             else | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |         return sourceReads; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | <Project Sdk="Microsoft.NET.Sdk"> | ||||||
|  | 	<ItemGroup> | ||||||
|  | 		<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" /> | ||||||
|  | 	</ItemGroup> | ||||||
|  | </Project> | ||||||
| @@ -0,0 +1,611 @@ | |||||||
|  | <?xml version="1.0"?> | ||||||
|  | <doc> | ||||||
|  |     <assembly> | ||||||
|  |         <name>ThingsGateway.Foundation.Adapter.Modbus</name> | ||||||
|  |     </assembly> | ||||||
|  |     <members> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress"> | ||||||
|  |             <summary> | ||||||
|  |             Modbus协议地址 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressStart"> | ||||||
|  |             <summary> | ||||||
|  |             读取功能码 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ReadFunction"> | ||||||
|  |             <summary> | ||||||
|  |             读取功能码 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Station"> | ||||||
|  |             <summary> | ||||||
|  |             站号信息 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.WriteFunction"> | ||||||
|  |             <summary> | ||||||
|  |             写入功能码 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ByteLength"> | ||||||
|  |             <summary> | ||||||
|  |             打包临时写入,需要读取的字节长度 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressEnd"> | ||||||
|  |             <summary> | ||||||
|  |             读取功能码 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Parse(System.String)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,System.Byte)"> | ||||||
|  |             <summary> | ||||||
|  |             解析地址 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress)"> | ||||||
|  |             <summary> | ||||||
|  |             解析地址 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ToString"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.AddCrc(System.Byte[])"> | ||||||
|  |             <summary> | ||||||
|  |             添加Crc16 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.AddModbusTcpHead(System.Byte[],System.UInt16)"> | ||||||
|  |             <summary> | ||||||
|  |             添加ModbusTcp报文头 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetAddressDescription"> | ||||||
|  |             <summary> | ||||||
|  |             modbus地址格式说明 | ||||||
|  |             </summary> | ||||||
|  |             <returns></returns> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetDescriptionByErrorCode(System.Byte)"> | ||||||
|  |             <summary> | ||||||
|  |             通过错误码来获取到对应的文本消息 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetModbusData(System.Byte[],System.Byte[])"> | ||||||
|  |             <summary> | ||||||
|  |             获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头 | ||||||
|  |             </summary> | ||||||
|  |             <param name="send">发送数据</param> | ||||||
|  |             <param name="response">返回数据</param> | ||||||
|  |             <returns></returns> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetModbusRtuData(System.Byte[],System.Byte[],System.Boolean)"> | ||||||
|  |             <summary> | ||||||
|  |             去除Crc,返回modbus数据区 | ||||||
|  |             </summary> | ||||||
|  |             <param name="send"></param> | ||||||
|  |             <param name="response"></param> | ||||||
|  |             <param name="crcCheck"></param> | ||||||
|  |             <returns></returns> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetReadModbusCommand(System.String,System.Int32,System.Byte)"> | ||||||
|  |             <summary> | ||||||
|  |             获取读取报文 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(System.String,System.Boolean[],System.Byte)"> | ||||||
|  |             <summary> | ||||||
|  |             获取写入布尔量报文,根据地址识别功能码 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteModbusCommand(System.String,System.Byte[],System.Byte)"> | ||||||
|  |             <summary> | ||||||
|  |             获取写入字报文,根据地址识别功能码 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetReadModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Int32)"> | ||||||
|  |             <summary> | ||||||
|  |             获取读取报文 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(System.String,System.Boolean,System.Byte)"> | ||||||
|  |             <summary> | ||||||
|  |             获取05写入布尔量报文 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Boolean)"> | ||||||
|  |             <summary> | ||||||
|  |             获取05写入布尔量报文 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Boolean[],System.Int32)"> | ||||||
|  |             <summary> | ||||||
|  |             获取15写入布尔量报文 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Byte[])"> | ||||||
|  |             <summary> | ||||||
|  |             获取16写入字报文 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteOneModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Byte[])"> | ||||||
|  |             <summary> | ||||||
|  |             获取6写入字报文 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Crc16CheckEnable"> | ||||||
|  |             <summary> | ||||||
|  |             Crc校验 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Station"> | ||||||
|  |             <summary> | ||||||
|  |             站号 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.GetAddressDescription"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SetDataAdapter(System.Object)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Crc16CheckEnable"> | ||||||
|  |             <summary> | ||||||
|  |             Crc校验 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Station"> | ||||||
|  |             <summary> | ||||||
|  |             站号 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.GetAddressDescription"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SetDataAdapter(System.Object)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.Crc16CheckEnable"> | ||||||
|  |             <summary> | ||||||
|  |             检测CRC | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.PackCommand(System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.GetInstance"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.UnpackResponse(System.Byte[],System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu"> | ||||||
|  |             <summary> | ||||||
|  |             ModbusRtu | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.#ctor(ThingsGateway.Foundation.Serial.SerialSession)"> | ||||||
|  |             <summary> | ||||||
|  |             ModbusRtu | ||||||
|  |             </summary> | ||||||
|  |             <param name="serialSession"></param> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Crc16CheckEnable"> | ||||||
|  |             <summary> | ||||||
|  |             Crc校验 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Station"> | ||||||
|  |             <summary> | ||||||
|  |             站号 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.GetAddressDescription"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.SetDataAdapter(System.Object)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter"> | ||||||
|  |             <summary> | ||||||
|  |             Rtu适配器 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.Crc16CheckEnable"> | ||||||
|  |             <summary> | ||||||
|  |             检测CRC | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.PackCommand(System.Byte[])"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |             <param name="command"></param> | ||||||
|  |             <returns></returns> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.GetInstance"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage,System.Byte[],System.Byte[],System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.HeadBytesLength"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.CheckHeadBytes(System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.SendBytesThen"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer01ByteBlocks"> | ||||||
|  |             <summary> | ||||||
|  |             继电器 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer02ByteBlocks"> | ||||||
|  |             <summary> | ||||||
|  |             开关输入 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer03ByteBlocks"> | ||||||
|  |             <summary> | ||||||
|  |             输入寄存器 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer04ByteBlocks"> | ||||||
|  |             <summary> | ||||||
|  |             保持寄存器 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteData"> | ||||||
|  |             <summary> | ||||||
|  |             接收外部写入时,传出变量地址/写入字节组/转换规则/客户端 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(ThingsGateway.Foundation.Sockets.TcpService)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.MulStation"> | ||||||
|  |             <summary> | ||||||
|  |             多站点 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Station"> | ||||||
|  |             <summary> | ||||||
|  |             默认站点 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Dispose"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.GetAddressDescription"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.SetDataAdapter(System.Object)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Received(ThingsGateway.Foundation.Sockets.SocketClient,ThingsGateway.Foundation.Core.IRequestInfo)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.PackCommand(System.Byte[])"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |             <param name="command"></param> | ||||||
|  |             <returns></returns> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.GetModbusData(System.Byte[])"> | ||||||
|  |             <summary> | ||||||
|  |             获取modbus写入数据区内容 | ||||||
|  |             </summary> | ||||||
|  |             <param name="response">返回数据</param> | ||||||
|  |             <returns></returns> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.GetInstance"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage,System.Byte[],System.Byte[],System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.CurModbusAddress"> | ||||||
|  |             <summary> | ||||||
|  |             当前关联的地址 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.Length"> | ||||||
|  |             <summary> | ||||||
|  |             当前读写的数据长度 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.HeadBytesLength"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.CheckHeadBytes(System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.IsCheckMessageId"> | ||||||
|  |             <summary> | ||||||
|  |             检测事务标识符 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Station"> | ||||||
|  |             <summary> | ||||||
|  |             站号 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.GetAddressDescription"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SetDataAdapter(System.Object)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter"> | ||||||
|  |             <summary> | ||||||
|  |             ModbusTcpDataHandleAdapter | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.IsCheckMessageId"> | ||||||
|  |             <summary> | ||||||
|  |             检测事务标识符 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.PackCommand(System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.GetInstance"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage,System.Byte[],System.Byte[],System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.HeadBytesLength"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.IsCheckMessageId"> | ||||||
|  |             <summary> | ||||||
|  |             检测事务标识符 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.CheckHeadBytes(System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.IsCheckMessageId"> | ||||||
|  |             <summary> | ||||||
|  |             检测事务标识符 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Station"> | ||||||
|  |             <summary> | ||||||
|  |             站号 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.GetAddressDescription"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SetDataAdapter(System.Object)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter"> | ||||||
|  |             <summary> | ||||||
|  |             <inheritdoc/> | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.IsCheckMessageId"> | ||||||
|  |             <summary> | ||||||
|  |             检测事务标识符 | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.PackCommand(System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.GetInstance"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.UnpackResponse(System.Byte[],System.Byte[])"> | ||||||
|  |             <inheritdoc/> | ||||||
|  |         </member> | ||||||
|  |         <member name="T:ThingsGateway.Foundation.Adapter.Modbus.PackHelper"> | ||||||
|  |             <summary> | ||||||
|  |             PackHelper | ||||||
|  |             </summary> | ||||||
|  |         </member> | ||||||
|  |         <member name="M:ThingsGateway.Foundation.Adapter.Modbus.PackHelper.ModbusLoadSourceRead``2(ThingsGateway.Foundation.Core.IReadWrite,System.Collections.Generic.List{``1},System.Int32)"> | ||||||
|  |             <summary> | ||||||
|  |             打包变量,添加到<see href="deviceVariableSourceReads"></see> | ||||||
|  |             </summary> | ||||||
|  |             <param name="device"></param> | ||||||
|  |             <param name="deviceVariables"></param> | ||||||
|  |             <param name="MaxPack">最大打包长度</param> | ||||||
|  |             <returns></returns> | ||||||
|  |         </member> | ||||||
|  |     </members> | ||||||
|  | </doc> | ||||||
| @@ -0,0 +1,371 @@ | |||||||
|  | #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 ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn; | ||||||
|  | #pragma warning disable CA1416 // 验证平台兼容性 | ||||||
|  | #pragma warning disable IDE0090 | ||||||
|  | #pragma warning disable IDE0051 | ||||||
|  |  | ||||||
|  | internal class ComInterop | ||||||
|  | { | ||||||
|  |     private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046"); | ||||||
|  |  | ||||||
|  |     #region const | ||||||
|  |  | ||||||
|  |     private const uint EOAC_ACCESS_CONTROL = 0x04; | ||||||
|  |     private const uint EOAC_APPID = 0x08; | ||||||
|  |     private const uint EOAC_CLOAKING = 0x10; | ||||||
|  |     private const uint EOAC_DYNAMIC_CLOAKING = 0x40; | ||||||
|  |     private const uint EOAC_MUTUAL_AUTH = 0x01; | ||||||
|  |     private const uint EOAC_NONE = 0x00; | ||||||
|  |     private const uint EOAC_SECURE_REFS = 0x02; | ||||||
|  |     private const uint EOAC_STATIC_CLOAKING = 0x20; | ||||||
|  |     /// <summary> | ||||||
|  |     /// The WIN32 system default locale. | ||||||
|  |     /// </summary> | ||||||
|  |     private const int LOCALE_SYSTEM_DEFAULT = 0x800; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// The WIN32 user default locale. | ||||||
|  |     /// </summary> | ||||||
|  |     private const int LOCALE_USER_DEFAULT = 0x400; | ||||||
|  |  | ||||||
|  |     private const uint RPC_C_AUTHN_DCE_PRIVATE = 1; | ||||||
|  |     private const uint RPC_C_AUTHN_DCE_PUBLIC = 2; | ||||||
|  |     private const uint RPC_C_AUTHN_DEC_PUBLIC = 4; | ||||||
|  |     private const uint RPC_C_AUTHN_DEFAULT = 0xFFFFFFFF; | ||||||
|  |     private const uint RPC_C_AUTHN_DIGEST = 21; | ||||||
|  |     private const uint RPC_C_AUTHN_DPA = 17; | ||||||
|  |     private const uint RPC_C_AUTHN_GSS_KERBEROS = 16; | ||||||
|  |     private const uint RPC_C_AUTHN_GSS_NEGOTIATE = 9; | ||||||
|  |     private const uint RPC_C_AUTHN_GSS_SCHANNEL = 14; | ||||||
|  |     private const uint RPC_C_AUTHN_LEVEL_CALL = 3; | ||||||
|  |     private const uint RPC_C_AUTHN_LEVEL_CONNECT = 2; | ||||||
|  |     private const uint RPC_C_AUTHN_LEVEL_DEFAULT = 0; | ||||||
|  |     private const uint RPC_C_AUTHN_LEVEL_NONE = 1; | ||||||
|  |     private const uint RPC_C_AUTHN_LEVEL_PKT = 4; | ||||||
|  |     private const uint RPC_C_AUTHN_LEVEL_PKT_INTEGRITY = 5; | ||||||
|  |     private const uint RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6; | ||||||
|  |     private const uint RPC_C_AUTHN_MQ = 100; | ||||||
|  |     private const uint RPC_C_AUTHN_MSN = 18; | ||||||
|  |     private const uint RPC_C_AUTHN_NONE = 0; | ||||||
|  |     private const uint RPC_C_AUTHN_WINNT = 10; | ||||||
|  |     private const uint RPC_C_AUTHZ_DCE = 2; | ||||||
|  |     private const uint RPC_C_AUTHZ_DEFAULT = 0xffffffff; | ||||||
|  |     private const uint RPC_C_AUTHZ_NAME = 1; | ||||||
|  |     private const uint RPC_C_AUTHZ_NONE = 0; | ||||||
|  |     private const uint RPC_C_IMP_LEVEL_ANONYMOUS = 1; | ||||||
|  |     private const uint RPC_C_IMP_LEVEL_DELEGATE = 4; | ||||||
|  |     private const uint RPC_C_IMP_LEVEL_IDENTIFY = 2; | ||||||
|  |     private const uint RPC_C_IMP_LEVEL_IMPERSONATE = 3; | ||||||
|  |     #endregion const | ||||||
|  |  | ||||||
|  |     #region struct | ||||||
|  |  | ||||||
|  |     public struct COSERVERINFO | ||||||
|  |     { | ||||||
|  |         public uint dwReserved1; | ||||||
|  |  | ||||||
|  |         [MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         public string pwszName; | ||||||
|  |  | ||||||
|  |         public IntPtr pAuthInfo; | ||||||
|  |         public uint dwReserved2; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     public struct MULTI_QI | ||||||
|  |     { | ||||||
|  |         public IntPtr iid; | ||||||
|  |  | ||||||
|  |         [MarshalAs(UnmanagedType.IUnknown)] | ||||||
|  |         public object pItf; | ||||||
|  |  | ||||||
|  |         public uint hr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | ||||||
|  |     public struct SOLE_AUTHENTICATION_SERVICE | ||||||
|  |     { | ||||||
|  |         public uint dwAuthnSvc; | ||||||
|  |         public uint dwAuthzSvc; | ||||||
|  |  | ||||||
|  |         [MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         public string pPrincipalName; | ||||||
|  |  | ||||||
|  |         public int hr; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #endregion struct | ||||||
|  |  | ||||||
|  |     #region win32 api | ||||||
|  |  | ||||||
|  |     [DllImport("ole32.dll")] | ||||||
|  |     private static extern void CoCreateInstanceEx(ref Guid clsid, | ||||||
|  |                                         [MarshalAs(UnmanagedType.IUnknown)] object punkOuter, | ||||||
|  |                                         uint dwClsCtx, | ||||||
|  |                                         [In] ref COSERVERINFO pServerInfo, | ||||||
|  |                                         uint dwCount, | ||||||
|  |                                         [In, Out] MULTI_QI[] pResults); | ||||||
|  |  | ||||||
|  |     [DllImport("ole32.dll")] | ||||||
|  |     private static extern int CoInitializeSecurity( | ||||||
|  |                                                 IntPtr pSecDesc, | ||||||
|  |                                                 int cAuthSvc, | ||||||
|  |                                                 SOLE_AUTHENTICATION_SERVICE[] asAuthSvc, | ||||||
|  |                                                 IntPtr pReserved1, | ||||||
|  |                                                 uint dwAuthnLevel, | ||||||
|  |                                                 uint dwImpLevel, | ||||||
|  |                                                 IntPtr pAuthList, | ||||||
|  |                                                 uint dwCapabilities, | ||||||
|  |                                                 IntPtr pReserved3); | ||||||
|  |  | ||||||
|  |     [DllImport("Kernel32.dll")] | ||||||
|  |     private static extern int FormatMessageW( | ||||||
|  |                                                 int dwFlags, | ||||||
|  |                                                 IntPtr lpSource, | ||||||
|  |                                                 int dwMessageId, | ||||||
|  |                                                 int dwLanguageId, | ||||||
|  |                                                 IntPtr lpBuffer, | ||||||
|  |                                                 int nSize, | ||||||
|  |                                                 IntPtr Arguments); | ||||||
|  |  | ||||||
|  |     [DllImport("Kernel32.dll")] | ||||||
|  |     private static extern int GetSystemDefaultLangID(); | ||||||
|  |  | ||||||
|  |     [DllImport("Kernel32.dll")] | ||||||
|  |     private static extern int GetUserDefaultLangID(); | ||||||
|  |     #endregion win32 api | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 创建一个COM服务器的实例。 | ||||||
|  |     /// </summary> | ||||||
|  |     public static object CreateInstance(Guid clsid, string hostName) | ||||||
|  |     { | ||||||
|  |         COSERVERINFO coserverInfo = new() | ||||||
|  |         { | ||||||
|  |             pwszName = hostName, | ||||||
|  |             pAuthInfo = IntPtr.Zero, | ||||||
|  |             dwReserved1 = 0, | ||||||
|  |             dwReserved2 = 0 | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         GCHandle hIID = GCHandle.Alloc(IID_IUnknown, GCHandleType.Pinned); | ||||||
|  |  | ||||||
|  |         MULTI_QI[] results = new MULTI_QI[1]; | ||||||
|  |  | ||||||
|  |         results[0].iid = hIID.AddrOfPinnedObject(); | ||||||
|  |         results[0].pItf = null; | ||||||
|  |         results[0].hr = 0; | ||||||
|  |  | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             // 检查是否在本地或远程连接。 | ||||||
|  |             uint clsctx = 0x01 | 0x04; | ||||||
|  |  | ||||||
|  |             if (hostName != null && hostName.Length > 0 && hostName.ToLower() != "localhost" && hostName != "127.0.0.1") | ||||||
|  |             { | ||||||
|  |                 clsctx = 0x04 | 0x10; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // create an instance. | ||||||
|  |             CoCreateInstanceEx( | ||||||
|  |                 ref clsid, | ||||||
|  |                 null, | ||||||
|  |                 clsctx, | ||||||
|  |                 ref coserverInfo, | ||||||
|  |                 1, | ||||||
|  |                 results); | ||||||
|  |         } | ||||||
|  |         catch (Exception ex) | ||||||
|  |         { | ||||||
|  |             throw new ExternalException("CoCreateInstanceEx: " + ex.Message); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             if (hIID.IsAllocated) hIID.Free(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (results[0].hr != 0) | ||||||
|  |         { | ||||||
|  |             throw new ExternalException("CoCreateInstanceEx: " + GetSystemMessage((int)results[0].hr)); | ||||||
|  |         } | ||||||
|  |         return results[0].pItf; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 指定错误消息文本检索系统。 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static string GetSystemMessage(int error) | ||||||
|  |     { | ||||||
|  |         const int MAX_MESSAGE_LENGTH = 1024; | ||||||
|  |         const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; | ||||||
|  |         const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; | ||||||
|  |  | ||||||
|  |         IntPtr buffer = Marshal.AllocCoTaskMem(MAX_MESSAGE_LENGTH); | ||||||
|  |  | ||||||
|  |         _ = FormatMessageW( | ||||||
|  |              (int)(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM), | ||||||
|  |              IntPtr.Zero, | ||||||
|  |              error, | ||||||
|  |              0, | ||||||
|  |              buffer, | ||||||
|  |              MAX_MESSAGE_LENGTH - 1, | ||||||
|  |              IntPtr.Zero); | ||||||
|  |  | ||||||
|  |         string msg = Marshal.PtrToStringUni(buffer); | ||||||
|  |         Marshal.FreeCoTaskMem(buffer); | ||||||
|  |  | ||||||
|  |         if (!string.IsNullOrEmpty(msg)) | ||||||
|  |         { | ||||||
|  |             return msg; | ||||||
|  |         } | ||||||
|  |         return string.Format("0x{0,0:X}", error); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 初始化COM安全。 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static void InitializeSecurity() | ||||||
|  |     { | ||||||
|  |         int error = CoInitializeSecurity( | ||||||
|  |             IntPtr.Zero, | ||||||
|  |             -1, | ||||||
|  |             null, | ||||||
|  |             IntPtr.Zero, | ||||||
|  |             RPC_C_AUTHN_LEVEL_CONNECT, | ||||||
|  |             RPC_C_IMP_LEVEL_IMPERSONATE, | ||||||
|  |             IntPtr.Zero, | ||||||
|  |             EOAC_NONE, | ||||||
|  |             IntPtr.Zero); | ||||||
|  |  | ||||||
|  |         if (error != 0) | ||||||
|  |         { | ||||||
|  |             throw new ExternalException("COM初始化安全: " + GetSystemMessage(error), error); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 从枚举器读取guid。 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static Guid[] ReadClasses(IOPCEnumGUID enumerator) | ||||||
|  |     { | ||||||
|  |         List<Guid> guids = new List<Guid>(); | ||||||
|  |  | ||||||
|  |         int fetched = 0; | ||||||
|  |         Guid[] buffer = new Guid[10]; | ||||||
|  |         do | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 IntPtr pGuids = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)) * buffer.Length); | ||||||
|  |  | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     enumerator.Next(buffer.Length, pGuids, out fetched); | ||||||
|  |  | ||||||
|  |                     if (fetched > 0) | ||||||
|  |                     { | ||||||
|  |                         IntPtr pos = pGuids; | ||||||
|  |  | ||||||
|  |                         for (int ii = 0; ii < fetched; ii++) | ||||||
|  |                         { | ||||||
|  |                             object o = Marshal.PtrToStructure(pos, typeof(Guid)); | ||||||
|  |                             if (o != null) | ||||||
|  |                             { | ||||||
|  |                                 buffer[ii] = (Guid)o; | ||||||
|  |                             } | ||||||
|  |                             pos = IntPtr.Add(pos, Marshal.SizeOf(typeof(Guid))); | ||||||
|  |                             guids.Add(buffer[ii]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 finally | ||||||
|  |                 { | ||||||
|  |                     Marshal.FreeCoTaskMem(pGuids); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             catch (Exception) | ||||||
|  |             { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         while (fetched > 0); | ||||||
|  |  | ||||||
|  |         return guids.ToArray(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 从枚举器读取guid。 | ||||||
|  |     /// </summary> | ||||||
|  |     internal static Guid[] ReadClasses(IEnumGUID enumerator) | ||||||
|  |     { | ||||||
|  |         List<Guid> guids = new List<Guid>(); | ||||||
|  |  | ||||||
|  |         int fetched = 0; | ||||||
|  |         Guid[] buffer = new Guid[10]; | ||||||
|  |  | ||||||
|  |         do | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 IntPtr pGuids = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)) * buffer.Length); | ||||||
|  |  | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     enumerator.Next(buffer.Length, pGuids, out fetched); | ||||||
|  |  | ||||||
|  |                     if (fetched > 0) | ||||||
|  |                     { | ||||||
|  |                         IntPtr pos = pGuids; | ||||||
|  |  | ||||||
|  |                         for (int ii = 0; ii < fetched; ii++) | ||||||
|  |                         { | ||||||
|  |                             object o = Marshal.PtrToStructure(pos, typeof(Guid)); | ||||||
|  |                             if (o != null) | ||||||
|  |                             { | ||||||
|  |                                 buffer[ii] = (Guid)o; | ||||||
|  |                             } | ||||||
|  |                             pos = IntPtr.Add(pos, Marshal.SizeOf(typeof(Guid))); | ||||||
|  |                             guids.Add(buffer[ii]); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 finally | ||||||
|  |                 { | ||||||
|  |                     Marshal.FreeCoTaskMem(pGuids); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             catch (Exception) | ||||||
|  |             { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         while (fetched > 0); | ||||||
|  |  | ||||||
|  |         return guids.ToArray(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 释放 COM 对象 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="m_server"></param> | ||||||
|  |     internal static void RealseComServer(object m_server) | ||||||
|  |     { | ||||||
|  |         if (m_server != null && m_server.GetType().IsCOMObject) | ||||||
|  |         { | ||||||
|  |             Marshal.ReleaseComObject(m_server); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,83 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn; | ||||||
|  |  | ||||||
|  | internal static class Convert | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// windows的filetime是从1601-1-1 00:00:00开始的,datetime是从1-1-1 00:00:00开始的 | ||||||
|  |     /// datetime和filetime的滴答单位都是100ns(100纳秒,千万分之一秒),所以转换时只需要考虑开始时间即可 | ||||||
|  |     /// </summary> | ||||||
|  |     private static readonly DateTime FILETIME_BaseTime = new DateTime(1601, 1, 1); | ||||||
|  |  | ||||||
|  |     private static bool m_preserveUTC = false; | ||||||
|  |  | ||||||
|  |     internal static object Clone(object source) | ||||||
|  |     { | ||||||
|  |         if (source == null) | ||||||
|  |         { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (source.GetType().IsValueType) | ||||||
|  |         { | ||||||
|  |             return source; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (source.GetType().IsArray || source.GetType() == typeof(Array)) | ||||||
|  |         { | ||||||
|  |             Array array = (Array)((Array)source).Clone(); | ||||||
|  |             for (int i = 0; i < array.Length; i++) | ||||||
|  |             { | ||||||
|  |                 array.SetValue(Clone(array.GetValue(i)), i); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return array; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             return ((ICloneable)source).Clone(); | ||||||
|  |         } | ||||||
|  |         catch | ||||||
|  |         { | ||||||
|  |             throw new NotSupportedException("Object cannot be cloned."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     internal static DateTime FileTimeToDateTime(System.Runtime.InteropServices.ComTypes.FILETIME filetime) | ||||||
|  |     { | ||||||
|  |         long num = filetime.dwHighDateTime; | ||||||
|  |         if (num < 0) | ||||||
|  |         { | ||||||
|  |             num += 4294967296L; | ||||||
|  |         } | ||||||
|  |         long num2 = num << 32; | ||||||
|  |         num = filetime.dwLowDateTime; | ||||||
|  |         if (num < 0) | ||||||
|  |         { | ||||||
|  |             num += 4294967296L; | ||||||
|  |         } | ||||||
|  |         num2 += num; | ||||||
|  |         if (num2 == 0) | ||||||
|  |         { | ||||||
|  |             return DateTime.MinValue; | ||||||
|  |         } | ||||||
|  |         if (m_preserveUTC) | ||||||
|  |         { | ||||||
|  |             DateTime fILETIME_BaseTime = FILETIME_BaseTime; | ||||||
|  |             return fILETIME_BaseTime.Add(new TimeSpan(num2)); | ||||||
|  |         } | ||||||
|  |         DateTime fILETIME_BaseTime2 = FILETIME_BaseTime; | ||||||
|  |         return fILETIME_BaseTime2.Add(new TimeSpan(num2)).ToLocalTime(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,57 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// 值变化 | ||||||
|  | /// </summary> | ||||||
|  | /// <param name="opcItems"></param> | ||||||
|  | public delegate void OnDataChangedHandler(List<ItemReadResult> opcItems); | ||||||
|  | /// <summary> | ||||||
|  | /// 读取 | ||||||
|  | /// </summary> | ||||||
|  | /// <param name="opcItems"></param> | ||||||
|  | public delegate void OnReadCompletedHandler(List<ItemReadResult> opcItems); | ||||||
|  | /// <summary> | ||||||
|  | /// 写入 | ||||||
|  | /// </summary> | ||||||
|  | /// <param name="opcItems"></param> | ||||||
|  | internal delegate void OnWriteCompletedHandler(List<ItemWriteResult> opcItems); | ||||||
|  | /// <summary> | ||||||
|  | /// 返回结果 | ||||||
|  | /// </summary> | ||||||
|  | public class ItemReadResult | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// ID | ||||||
|  |     /// </summary> | ||||||
|  |     public string Name { get; set; } = ""; | ||||||
|  |     /// <summary> | ||||||
|  |     /// Quality | ||||||
|  |     /// </summary> | ||||||
|  |     public short Quality { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// TimeStamp | ||||||
|  |     /// </summary> | ||||||
|  |     public DateTime TimeStamp { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// Value | ||||||
|  |     /// </summary> | ||||||
|  |     public object Value { get; set; } = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | internal class ItemWriteResult | ||||||
|  | { | ||||||
|  |     internal int Exception { get; set; } = 0; | ||||||
|  |     internal string Name { get; set; } = ""; | ||||||
|  | } | ||||||
| @@ -0,0 +1,457 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using System.Runtime.InteropServices; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||||
|  | #pragma warning disable CA1416 // 验证平台兼容性 | ||||||
|  |  | ||||||
|  | internal class OpcGroup : IOPCDataCallback, IDisposable | ||||||
|  | { | ||||||
|  |     internal object groupPointer = null; | ||||||
|  |     internal int revisedUpdateRate = 0; | ||||||
|  |     internal int serverGroupHandle = 0; | ||||||
|  |     private static int _handle = 0; | ||||||
|  |     private bool _bSubscribe = false; | ||||||
|  |     private bool disposedValue; | ||||||
|  |     private int lcid = 0x0; | ||||||
|  |     private IOPCAsyncIO2 m_Async2IO = null; | ||||||
|  |     private IConnectionPoint m_ConnectionPoint = null; | ||||||
|  |     private int m_connectionpoint_cookie = 0; | ||||||
|  |     private IConnectionPointContainer m_ConnectionPointContainer = null; | ||||||
|  |     private IOPCItemMgt m_ItemManagement = null; | ||||||
|  |     private IOPCGroupStateMgt m_StateManagement = null; | ||||||
|  |     private IOPCSyncIO m_SyncIO = null; | ||||||
|  |     private GCHandle percendDeadBand = GCHandle.Alloc(0, GCHandleType.Pinned); | ||||||
|  |     private GCHandle timeBias = GCHandle.Alloc(0, GCHandleType.Pinned); | ||||||
|  |     internal OpcGroup(string name) | ||||||
|  |     { | ||||||
|  |         Name = name; | ||||||
|  |         ClientGroupHandle = ++_handle; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal OpcGroup(string groupName, bool active, int reqUpdateRate, float deadBand) | ||||||
|  |     { | ||||||
|  |         Name = groupName; | ||||||
|  |         IsActive = active; | ||||||
|  |         RequestUpdateRate = reqUpdateRate; | ||||||
|  |         DeadBand = deadBand; | ||||||
|  |         ClientGroupHandle = ++_handle; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal delegate void CancelCompletedHandler(int dwTransid, int hGroup); | ||||||
|  |  | ||||||
|  |     internal event CancelCompletedHandler OnCancelCompleted; | ||||||
|  |  | ||||||
|  |     internal event OnDataChangedHandler OnDataChanged; | ||||||
|  |  | ||||||
|  |     internal event OnReadCompletedHandler OnReadCompleted; | ||||||
|  |  | ||||||
|  |     internal event OnWriteCompletedHandler OnWriteCompleted; | ||||||
|  |  | ||||||
|  |     internal bool ActiveSubscribe | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return _bSubscribe; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             _bSubscribe = value; | ||||||
|  |             ActiveDataChanged(_bSubscribe); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal int ClientGroupHandle { get; private set; } | ||||||
|  |     internal float DeadBand { get; set; } = 0.0f; | ||||||
|  |     internal object GroupPointer => groupPointer; | ||||||
|  |  | ||||||
|  |     internal bool IsActive { get; set; } = true; | ||||||
|  |     internal int LCID | ||||||
|  |     { | ||||||
|  |         get => lcid; | ||||||
|  |         set => lcid = value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal string Name { get; private set; } = string.Empty; | ||||||
|  |     internal List<OpcItem> OpcItems { get; private set; } = new List<OpcItem> { }; | ||||||
|  |     internal GCHandle PercendDeadBand | ||||||
|  |     { | ||||||
|  |         get => percendDeadBand; | ||||||
|  |         set => percendDeadBand = value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal int RequestUpdateRate { get; set; } = 1000; | ||||||
|  |     internal int RevisedUpdateRate => revisedUpdateRate; | ||||||
|  |     internal int ServerGroupHandle => serverGroupHandle; | ||||||
|  |     internal GCHandle TimeBias | ||||||
|  |     { | ||||||
|  |         get => timeBias; | ||||||
|  |         set => timeBias = value; | ||||||
|  |     } | ||||||
|  |     public void Dispose() | ||||||
|  |     { | ||||||
|  |         Dispose(disposing: true); | ||||||
|  |         GC.SuppressFinalize(this); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void OnCancelComplete(int dwTransid, int hGroup) | ||||||
|  |     { | ||||||
|  |         OnCancelCompleted?.Invoke(dwTransid, hGroup); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void OnDataChange(int dwTransid, | ||||||
|  |                             int hGroup, | ||||||
|  |                             int hrMasterquality, | ||||||
|  |                             int hrMastererror, | ||||||
|  |                             int dwCount, | ||||||
|  |                             int[] phClientItems, | ||||||
|  |                             object[] pvValues, | ||||||
|  |                             short[] pwQualities, | ||||||
|  |                             System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps, | ||||||
|  |                             int[] pErrors) | ||||||
|  |     { | ||||||
|  |         List<ItemReadResult> itemChanged = new(); | ||||||
|  |         for (int i = 0; i < dwCount; i++) | ||||||
|  |         { | ||||||
|  |             int index = OpcItems.FindIndex(x => x.ClientHandle == phClientItems[i]); | ||||||
|  |             if (index >= 0) | ||||||
|  |             { | ||||||
|  |                 OpcItems[index].Value = pvValues[i]; | ||||||
|  |                 OpcItems[index].Quality = pwQualities[i]; | ||||||
|  |                 OpcItems[index].TimeStamp = Comn.Convert.FileTimeToDateTime(pftTimeStamps[i]); | ||||||
|  |                 itemChanged.Add(new ItemReadResult | ||||||
|  |                 { | ||||||
|  |                     Name = OpcItems[index].ItemID, | ||||||
|  |                     Value = pvValues[i], | ||||||
|  |                     Quality = pwQualities[i], | ||||||
|  |                     TimeStamp = OpcItems[index].TimeStamp | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         OnDataChanged?.Invoke(itemChanged); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void OnReadComplete(int dwTransid, | ||||||
|  |                                 int hGroup, | ||||||
|  |                                 int hrMasterquality, | ||||||
|  |                                 int hrMastererror, | ||||||
|  |                                 int dwCount, | ||||||
|  |                                 int[] phClientItems, | ||||||
|  |                                 object[] pvValues, | ||||||
|  |                                 short[] pwQualities, | ||||||
|  |                                 System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps, | ||||||
|  |                                 int[] pErrors) | ||||||
|  |     { | ||||||
|  |         List<ItemReadResult> itemChanged = new(); | ||||||
|  |         for (int i = 0; i < dwCount; i++) | ||||||
|  |         { | ||||||
|  |             int index = OpcItems.FindIndex(x => x.ClientHandle == phClientItems[i]); | ||||||
|  |             if (index >= 0) | ||||||
|  |             { | ||||||
|  |                 OpcItems[index].Value = pvValues[i]; | ||||||
|  |                 OpcItems[index].Quality = pwQualities[i]; | ||||||
|  |                 OpcItems[index].TimeStamp = Comn.Convert.FileTimeToDateTime(pftTimeStamps[i]); | ||||||
|  |                 itemChanged.Add(new ItemReadResult | ||||||
|  |                 { | ||||||
|  |                     Name = OpcItems[index].ItemID, | ||||||
|  |                     Value = pvValues[i], | ||||||
|  |                     Quality = pwQualities[i], | ||||||
|  |                     TimeStamp = OpcItems[index].TimeStamp | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         OnReadCompleted?.Invoke(itemChanged); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public void OnWriteComplete(int dwTransid, | ||||||
|  |                                 int hGroup, | ||||||
|  |                                 int hrMastererr, | ||||||
|  |                                 int dwCount, | ||||||
|  |                                 int[] pClienthandles, | ||||||
|  |                                 int[] pErrors) | ||||||
|  |     { | ||||||
|  |         List<ItemWriteResult> itemwrite = new(); | ||||||
|  |         for (int i = 0; i < dwCount; i++) | ||||||
|  |         { | ||||||
|  |             int index = OpcItems.FindIndex(x => x.ClientHandle == pClienthandles[i]); | ||||||
|  |             if (index >= 0) | ||||||
|  |             { | ||||||
|  |                 itemwrite.Add(new ItemWriteResult | ||||||
|  |                 { | ||||||
|  |                     Name = OpcItems[index].ItemID, | ||||||
|  |                     Exception = pErrors[i] | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         OnWriteCompleted?.Invoke(itemwrite); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal List<Tuple<OpcItem, int>> AddOpcItem(OpcItem[] items) | ||||||
|  |     { | ||||||
|  |         IntPtr pResults = IntPtr.Zero; | ||||||
|  |         IntPtr pErrors = IntPtr.Zero; | ||||||
|  |         OPCITEMDEF[] itemDefyArray = new OPCITEMDEF[items.Length]; | ||||||
|  |         int i = 0; | ||||||
|  |         int[] errors = new int[items.Length]; | ||||||
|  |         int[] itemServerHandle = new int[items.Length]; | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             foreach (OpcItem item in items) | ||||||
|  |             { | ||||||
|  |                 if (item != null) | ||||||
|  |                 { | ||||||
|  |                     itemDefyArray[i].szAccessPath = item.AccessPath; | ||||||
|  |                     itemDefyArray[i].szItemID = item.ItemID; | ||||||
|  |                     itemDefyArray[i].bActive = item.IsActive ? 1 : 0; | ||||||
|  |                     itemDefyArray[i].hClient = item.ClientHandle; | ||||||
|  |                     itemDefyArray[i].dwBlobSize = item.BlobSize; | ||||||
|  |                     itemDefyArray[i].pBlob = item.Blob; | ||||||
|  |                     i++; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             //添加OPC项组 | ||||||
|  |             m_ItemManagement?.AddItems(items.Length, itemDefyArray, out pResults, out pErrors); | ||||||
|  |             IntPtr Pos = pResults; | ||||||
|  |             Marshal.Copy(pErrors, errors, 0, items.Length); | ||||||
|  |             List<Tuple<OpcItem, int>> results = new(); | ||||||
|  |             for (int j = 0; j < items.Length; j++) | ||||||
|  |             { | ||||||
|  |                 if (errors[j] == 0) | ||||||
|  |                 { | ||||||
|  |                     if (j != 0) | ||||||
|  |                     { | ||||||
|  |                         Pos = IntPtr.Add(Pos, Marshal.SizeOf(typeof(OPCITEMRESULT))); | ||||||
|  |                     } | ||||||
|  |                     object o = Marshal.PtrToStructure(Pos, typeof(OPCITEMRESULT)); | ||||||
|  |                     if (o != null) | ||||||
|  |                     { | ||||||
|  |                         var result = (OPCITEMRESULT)o; | ||||||
|  |  | ||||||
|  |                         items[j].RunTimeDataType = result.vtCanonicalDataType; | ||||||
|  |                         itemServerHandle[j] = items[j].ServerHandle = result.hServer; | ||||||
|  |                         Marshal.DestroyStructure(Pos, typeof(OPCITEMRESULT)); | ||||||
|  |                         OpcItems.Add(items[j]); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     results.Add(Tuple.Create(items[j], errors[j])); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return results; | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             if (pResults != IntPtr.Zero) | ||||||
|  |             { | ||||||
|  |                 Marshal.FreeCoTaskMem(pResults); | ||||||
|  |             } | ||||||
|  |             if (pErrors != IntPtr.Zero) | ||||||
|  |             { | ||||||
|  |                 Marshal.FreeCoTaskMem(pErrors); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 建立连接 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="handle"></param> | ||||||
|  |     internal void InitIoInterfaces(object handle) | ||||||
|  |     { | ||||||
|  |         groupPointer = handle; | ||||||
|  |         m_ItemManagement = (IOPCItemMgt)groupPointer; | ||||||
|  |         m_Async2IO = (IOPCAsyncIO2)groupPointer; | ||||||
|  |         m_SyncIO = (IOPCSyncIO)groupPointer; | ||||||
|  |         m_StateManagement = (IOPCGroupStateMgt)groupPointer; | ||||||
|  |         m_ConnectionPointContainer = (IConnectionPointContainer)groupPointer; | ||||||
|  |         Guid iid = typeof(IOPCDataCallback).GUID; | ||||||
|  |         m_ConnectionPointContainer.FindConnectionPoint(ref iid, out m_ConnectionPoint); | ||||||
|  |         //创建客户端与服务端之间的连接 | ||||||
|  |         m_ConnectionPoint.Advise(this, out m_connectionpoint_cookie); | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// 组读取 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <exception cref="ExternalException"></exception> | ||||||
|  |     internal void ReadAsync() | ||||||
|  |     { | ||||||
|  |         IntPtr pErrors = IntPtr.Zero; | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             if (m_Async2IO != null) | ||||||
|  |             { | ||||||
|  |                 int[] serverHandle = new int[OpcItems.Count]; | ||||||
|  |                 int[] PErrors = new int[OpcItems.Count]; | ||||||
|  |                 for (int j = 0; j < OpcItems.Count; j++) | ||||||
|  |                 { | ||||||
|  |                     serverHandle[j] = OpcItems[j].ServerHandle; | ||||||
|  |                 } | ||||||
|  |                 m_Async2IO.Read(OpcItems.Count, serverHandle, 2, out int cancelId, out pErrors); | ||||||
|  |                 Marshal.Copy(pErrors, PErrors, 0, OpcItems.Count); | ||||||
|  |                 if (PErrors.Any(a => a > 0)) | ||||||
|  |                 { | ||||||
|  |                     throw new("读取错误,错误代码:" + pErrors); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 throw new("连接无效"); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             if (pErrors != IntPtr.Zero) | ||||||
|  |             { | ||||||
|  |                 Marshal.FreeCoTaskMem(pErrors); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal List<Tuple<OpcItem, int>> RemoveItem(OpcItem[] items) | ||||||
|  |     { | ||||||
|  |         IntPtr pErrors = IntPtr.Zero; | ||||||
|  |         int[] errors = new int[items.Length]; | ||||||
|  |         int[] handles = new int[items.Length]; | ||||||
|  |         for (int i = 0; i < items.Length; i++) | ||||||
|  |         { | ||||||
|  |             handles[i] = items[i].ServerHandle; | ||||||
|  |         } | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             m_ItemManagement?.RemoveItems(handles.Length, handles, out pErrors); | ||||||
|  |             Marshal.Copy(pErrors, errors, 0, items.Length); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             if (pErrors != IntPtr.Zero) | ||||||
|  |             { | ||||||
|  |                 Marshal.FreeCoTaskMem(pErrors); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         List<Tuple<OpcItem, int>> results = new(); | ||||||
|  |         for (int i = 0; i < errors.Length; i++) | ||||||
|  |         { | ||||||
|  |             if (errors[i] != 0) | ||||||
|  |             { | ||||||
|  |                 results.Add(Tuple.Create(items[i], errors[i])); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 OpcItems.Remove(items[i]); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return results; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     internal List<Tuple<int, int>> Write(object[] values, int[] serverHandle) | ||||||
|  |     { | ||||||
|  |         IntPtr pErrors = IntPtr.Zero; | ||||||
|  |         var errors = new int[values.Length]; | ||||||
|  |         if (m_Async2IO != null) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 m_SyncIO.Write(values.Length, serverHandle, values, out pErrors); | ||||||
|  |                 Marshal.Copy(pErrors, errors, 0, values.Length); | ||||||
|  |                 List<Tuple<int, int>> results = new(); | ||||||
|  |                 for (int i = 0; i < errors.Length; i++) | ||||||
|  |                 { | ||||||
|  |                     if (errors[i] != 0) | ||||||
|  |                     { | ||||||
|  |                         results.Add(Tuple.Create(serverHandle[i], errors[i])); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return results; | ||||||
|  |             } | ||||||
|  |             finally | ||||||
|  |             { | ||||||
|  |                 if (pErrors != IntPtr.Zero) | ||||||
|  |                 { | ||||||
|  |                     Marshal.FreeCoTaskMem(pErrors); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             throw new("连接无效"); | ||||||
|  |     } | ||||||
|  |     protected virtual void Dispose(bool disposing) | ||||||
|  |     { | ||||||
|  |         if (!disposedValue) | ||||||
|  |         { | ||||||
|  |             if (TimeBias.IsAllocated) | ||||||
|  |             { | ||||||
|  |                 TimeBias.Free(); | ||||||
|  |             } | ||||||
|  |             if (PercendDeadBand.IsAllocated) | ||||||
|  |             { | ||||||
|  |                 PercendDeadBand.Free(); | ||||||
|  |             } | ||||||
|  |             ActiveSubscribe = false; | ||||||
|  |             m_ConnectionPoint?.Unadvise(m_connectionpoint_cookie); | ||||||
|  |             m_connectionpoint_cookie = 0; | ||||||
|  |             if (null != m_ConnectionPoint) Marshal.ReleaseComObject(m_ConnectionPoint); | ||||||
|  |             m_ConnectionPoint = null; | ||||||
|  |             if (null != m_ConnectionPointContainer) Marshal.ReleaseComObject(m_ConnectionPointContainer); | ||||||
|  |             m_ConnectionPointContainer = null; | ||||||
|  |             if (m_Async2IO != null) | ||||||
|  |             { | ||||||
|  |                 Marshal.ReleaseComObject(m_Async2IO); | ||||||
|  |                 m_Async2IO = null; | ||||||
|  |             } | ||||||
|  |             if (m_SyncIO != null) | ||||||
|  |             { | ||||||
|  |                 Marshal.ReleaseComObject(m_SyncIO); | ||||||
|  |                 m_SyncIO = null; | ||||||
|  |             } | ||||||
|  |             if (m_StateManagement != null) | ||||||
|  |             { | ||||||
|  |                 Marshal.ReleaseComObject(m_StateManagement); | ||||||
|  |                 m_StateManagement = null; | ||||||
|  |             } | ||||||
|  |             if (groupPointer != null) | ||||||
|  |             { | ||||||
|  |                 Marshal.ReleaseComObject(groupPointer); | ||||||
|  |                 groupPointer = null; | ||||||
|  |             } | ||||||
|  |             m_ItemManagement = null; | ||||||
|  |             disposedValue = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void ActiveDataChanged(bool active) | ||||||
|  |     { | ||||||
|  |         IntPtr pRequestedUpdateRate = IntPtr.Zero; | ||||||
|  |         IntPtr hClientGroup = IntPtr.Zero; | ||||||
|  |         IntPtr pTimeBias = IntPtr.Zero; | ||||||
|  |         IntPtr pDeadband = IntPtr.Zero; | ||||||
|  |         IntPtr pLCID = IntPtr.Zero; | ||||||
|  |         int nActive = 0; | ||||||
|  |         GCHandle hActive = GCHandle.Alloc(nActive, GCHandleType.Pinned); | ||||||
|  |         hActive.Target = active ? 1 : 0; | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             m_StateManagement?.SetState(pRequestedUpdateRate, | ||||||
|  |                                         out int nRevUpdateRate, | ||||||
|  |                                         hActive.AddrOfPinnedObject(), | ||||||
|  |                                         pTimeBias, | ||||||
|  |                                         pDeadband, | ||||||
|  |                                         pLCID, | ||||||
|  |                                         hClientGroup); | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             hActive.Free(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,77 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||||
|  | /// <summary> | ||||||
|  | /// OpcItem | ||||||
|  | /// </summary> | ||||||
|  | public class OpcItem | ||||||
|  | { | ||||||
|  |     private static int _hanle = 0; | ||||||
|  |     /// <summary> | ||||||
|  |     /// OpcItem | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="itemId"></param> | ||||||
|  |     public OpcItem(string itemId) | ||||||
|  |     { | ||||||
|  |         ItemID = itemId; | ||||||
|  |         ClientHandle = ++_hanle; | ||||||
|  |     } | ||||||
|  |     /// <summary> | ||||||
|  |     /// AccessPath | ||||||
|  |     /// </summary> | ||||||
|  |     public string AccessPath { get; private set; } = ""; | ||||||
|  |     /// <summary> | ||||||
|  |     /// Blob | ||||||
|  |     /// </summary> | ||||||
|  |     public IntPtr Blob { get; set; } = IntPtr.Zero; | ||||||
|  |     /// <summary> | ||||||
|  |     /// BlobSize | ||||||
|  |     /// </summary> | ||||||
|  |     public int BlobSize { get; set; } = 0; | ||||||
|  |     /// <summary> | ||||||
|  |     /// ClientHandle | ||||||
|  |     /// </summary> | ||||||
|  |     public int ClientHandle { get; private set; } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// active(1) or not(0) | ||||||
|  |     /// </summary> | ||||||
|  |     public bool IsActive { get; set; } = true; | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 数据项在opc server的完全名称 | ||||||
|  |     /// </summary> | ||||||
|  |     public string ItemID { get; private set; } = String.Empty; | ||||||
|  |     /// <summary> | ||||||
|  |     /// Quality | ||||||
|  |     /// </summary> | ||||||
|  |     public int Quality { get; set; } = Qualities.OPC_QUALITY_BAD; | ||||||
|  |     /// <summary> | ||||||
|  |     /// RunTimeDataType | ||||||
|  |     /// </summary> | ||||||
|  |     public short RunTimeDataType { get; set; } = 0; | ||||||
|  |     /// <summary> | ||||||
|  |     /// ServerHandle | ||||||
|  |     /// </summary> | ||||||
|  |     public int ServerHandle { get; set; } | ||||||
|  |     /// <summary> | ||||||
|  |     /// TimeStamp | ||||||
|  |     /// </summary> | ||||||
|  |     public DateTime TimeStamp { get; set; } = new DateTime(0); | ||||||
|  |     /// <summary> | ||||||
|  |     /// Value | ||||||
|  |     /// </summary> | ||||||
|  |     public object Value { get; set; } | ||||||
|  | } | ||||||
| @@ -0,0 +1,275 @@ | |||||||
|  | #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 ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||||
|  | #pragma warning disable CA1416 // 验证平台兼容性 | ||||||
|  |  | ||||||
|  | internal class OpcServer : IDisposable | ||||||
|  | { | ||||||
|  |  | ||||||
|  |     private bool disposedValue; | ||||||
|  |  | ||||||
|  |     private IOPCServer m_OpcServer = null; | ||||||
|  |  | ||||||
|  |     internal OpcServer(string name, string host = "localhost") | ||||||
|  |     { | ||||||
|  |         Name = name; | ||||||
|  |         if (string.IsNullOrEmpty(host)) | ||||||
|  |         { | ||||||
|  |             Host = "localhost"; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             Host = host; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal string Host { get; private set; } | ||||||
|  |     internal bool IsConnected { get; private set; } = false; | ||||||
|  |     internal string Name { get; private set; } | ||||||
|  |     internal List<OpcGroup> OpcGroups { get; private set; } = new List<OpcGroup>(10); | ||||||
|  |     internal ServerStatus ServerStatus { get; private set; } = new ServerStatus(); | ||||||
|  |  | ||||||
|  |     public void Dispose() | ||||||
|  |     { | ||||||
|  |         Dispose(disposing: true); | ||||||
|  |         GC.SuppressFinalize(this); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal OpcGroup AddGroup(string groupName) | ||||||
|  |     { | ||||||
|  |         return AddGroup(groupName, true, 1000, 0); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <returns></returns> | ||||||
|  |     internal OpcGroup AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand) | ||||||
|  |     { | ||||||
|  |         if (null == m_OpcServer || IsConnected == false) | ||||||
|  |             throw new("未初始化连接!"); | ||||||
|  |         OpcGroup group = new(groupName, active, reqUpdateRate, deadBand); | ||||||
|  |         Guid riid = typeof(IOPCItemMgt).GUID; | ||||||
|  |         m_OpcServer?.AddGroup(group.Name, | ||||||
|  |             group.IsActive ? 1 : 0,//IsActive | ||||||
|  |             group.RequestUpdateRate,//RequestedUpdateRate 1000ms | ||||||
|  |             group.ClientGroupHandle, | ||||||
|  |             group.TimeBias.AddrOfPinnedObject(), | ||||||
|  |             group.PercendDeadBand.AddrOfPinnedObject(), | ||||||
|  |             group.LCID, | ||||||
|  |             out group.serverGroupHandle, | ||||||
|  |             out group.revisedUpdateRate, | ||||||
|  |             ref riid, | ||||||
|  |             out group.groupPointer); | ||||||
|  |         if (group.groupPointer != null) | ||||||
|  |         { | ||||||
|  |             group.InitIoInterfaces(group.groupPointer); | ||||||
|  |             OpcGroups.Add(group); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             throw new("添加OPC组错误,OPC服务器返回null"); | ||||||
|  |         } | ||||||
|  |         return group; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 获取节点 | ||||||
|  |     /// </summary> | ||||||
|  |     internal List<BrowseElement> Browse(string itemId = null) | ||||||
|  |     { | ||||||
|  |         lock (this) | ||||||
|  |         { | ||||||
|  |             if (null == m_OpcServer || IsConnected == false) | ||||||
|  |                 throw new("未初始化连接!"); | ||||||
|  |  | ||||||
|  |             var count = 0; | ||||||
|  |             var moreElements = 0; | ||||||
|  |  | ||||||
|  |             var pContinuationPoint = IntPtr.Zero; | ||||||
|  |             var pElements = IntPtr.Zero; | ||||||
|  |             var filterId = new PropertyID[] | ||||||
|  |      { | ||||||
|  |                            new PropertyID(1), | ||||||
|  |                            new PropertyID(3), | ||||||
|  |                            new PropertyID(4), | ||||||
|  |                            new PropertyID(5), | ||||||
|  |                            new PropertyID(6), | ||||||
|  |                            new PropertyID(101), | ||||||
|  |                          }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             var server = m_OpcServer as IOPCBrowse; | ||||||
|  |             server.Browse( | ||||||
|  |                      string.IsNullOrEmpty(itemId) ? "" : itemId, | ||||||
|  |                  ref pContinuationPoint, | ||||||
|  |                  int.MaxValue, | ||||||
|  |                     OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL, | ||||||
|  |                       "", | ||||||
|  |                      "", | ||||||
|  |                      0, | ||||||
|  |                      1, | ||||||
|  |                      filterId.Length, | ||||||
|  |                      Interop.GetPropertyIDs(filterId), | ||||||
|  |                  out moreElements, | ||||||
|  |                  out count, | ||||||
|  |                  out pElements); | ||||||
|  |             BrowseElement[] browseElements = Interop.GetBrowseElements(ref pElements, count, true); | ||||||
|  |             string stringUni = Marshal.PtrToStringUni(pContinuationPoint); | ||||||
|  |             Marshal.FreeCoTaskMem(pContinuationPoint); | ||||||
|  |             this.ProcessResults(browseElements, filterId); | ||||||
|  |             return browseElements?.ToList(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal void Connect() | ||||||
|  |     { | ||||||
|  |         if (!string.IsNullOrEmpty(Host) && !string.IsNullOrEmpty(Name)) | ||||||
|  |         { | ||||||
|  |             var info = Discovery.OpcDiscovery.GetOpcServer(Name, Host); | ||||||
|  |             object o = Comn.ComInterop.CreateInstance(info.CLSID, Host); | ||||||
|  |             if (o == null) | ||||||
|  |             { | ||||||
|  |                 throw new(string.Format("{0}{1}无法创建com对象", info.CLSID, Host)); | ||||||
|  |             } | ||||||
|  |             m_OpcServer = (IOPCServer)o; | ||||||
|  |             IsConnected = true; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |             throw new("应初始化Host与Name"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// 服务器状态 | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     internal ServerStatus GetServerStatus() | ||||||
|  |     { | ||||||
|  |         ServerStatus serverStatus = null; | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             if (null == m_OpcServer || IsConnected == false) | ||||||
|  |                 throw new("未初始化连接!"); | ||||||
|  |             IntPtr statusPtr = IntPtr.Zero; | ||||||
|  |             m_OpcServer?.GetStatus(out statusPtr); | ||||||
|  |             OPCSERVERSTATUS status; | ||||||
|  |             if (statusPtr != IntPtr.Zero) | ||||||
|  |             { | ||||||
|  |  | ||||||
|  |                 object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS)); | ||||||
|  |  | ||||||
|  |                 Marshal.FreeCoTaskMem(statusPtr); | ||||||
|  |  | ||||||
|  |                 if (o != null) | ||||||
|  |                 { | ||||||
|  |                     status = (OPCSERVERSTATUS)o; | ||||||
|  |                     serverStatus = new(); | ||||||
|  |                     serverStatus.Version = $"{status.wMajorVersion.ToString()}.{status.wMinorVersion.ToString()}.{status.wBuildNumber.ToString()}"; | ||||||
|  |                     serverStatus.ServerState = status.dwServerState; | ||||||
|  |                     serverStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime); | ||||||
|  |                     serverStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime); | ||||||
|  |                     serverStatus.LastUpdateTime = Comn.Convert.FileTimeToDateTime(status.ftLastUpdateTime); | ||||||
|  |                     serverStatus.VendorInfo = status.szVendorInfo; | ||||||
|  |                     IsConnected = true; | ||||||
|  |  | ||||||
|  |                     return serverStatus; | ||||||
|  |                 } | ||||||
|  |                 else | ||||||
|  |                 { | ||||||
|  |                     IsConnected = false; | ||||||
|  |                     throw new("未知错误"); | ||||||
|  |  | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 IsConnected = false; | ||||||
|  |                 throw new("未知错误"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             if (serverStatus != null) | ||||||
|  |                 IsConnected = true; | ||||||
|  |             else | ||||||
|  |                 IsConnected = false; | ||||||
|  |             ServerStatus = serverStatus; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     internal void RemoveGroup(OpcGroup group) | ||||||
|  |     { | ||||||
|  |         if (OpcGroups.Contains(group)) | ||||||
|  |         { | ||||||
|  |             m_OpcServer?.RemoveGroup(group.ServerGroupHandle, 1); | ||||||
|  |             OpcGroups.Remove(group); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected virtual void Dispose(bool disposing) | ||||||
|  |     { | ||||||
|  |         if (!disposedValue) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 for (int i = 0; i < OpcGroups.Count; i++) | ||||||
|  |                     RemoveGroup(OpcGroups[i]); | ||||||
|  |             } | ||||||
|  |             catch | ||||||
|  |             { | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             if (m_OpcServer != null) | ||||||
|  |             { | ||||||
|  |                 Marshal.ReleaseComObject(m_OpcServer); | ||||||
|  |                 m_OpcServer = null; | ||||||
|  |             } | ||||||
|  |             if (disposing) | ||||||
|  |             { | ||||||
|  |                 OpcGroups.Clear(); | ||||||
|  |             } | ||||||
|  |             disposedValue = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void ProcessResults(BrowseElement[] elements, PropertyID[] propertyIDs) | ||||||
|  |     { | ||||||
|  |         if (elements == null) | ||||||
|  |             return; | ||||||
|  |         foreach (BrowseElement element in elements) | ||||||
|  |         { | ||||||
|  |             if (element.Properties != null) | ||||||
|  |             { | ||||||
|  |                 foreach (ItemProperty property in element.Properties) | ||||||
|  |                 { | ||||||
|  |                     if (propertyIDs != null) | ||||||
|  |                     { | ||||||
|  |                         foreach (PropertyID propertyId in propertyIDs) | ||||||
|  |                         { | ||||||
|  |                             if (property.ID.Code == propertyId.Code) | ||||||
|  |                             { | ||||||
|  |                                 property.ID = propertyId; | ||||||
|  |                                 break; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,45 @@ | |||||||
|  | #region copyright | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||||
|  | //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||||
|  | //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||||
|  | //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||||
|  | //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||||
|  | //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||||
|  | //  QQ群:605534569 | ||||||
|  | //------------------------------------------------------------------------------ | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||||
|  | /// <summary> | ||||||
|  | /// ServerStatus | ||||||
|  | /// </summary> | ||||||
|  | public class ServerStatus | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// CurrentTime | ||||||
|  |     /// </summary> | ||||||
|  |     public DateTime CurrentTime { get; internal set; } = new DateTime(0); | ||||||
|  |     /// <summary> | ||||||
|  |     /// LastUpdateTime | ||||||
|  |     /// </summary> | ||||||
|  |     public DateTime LastUpdateTime { get; internal set; } = new DateTime(0); | ||||||
|  |     /// <summary> | ||||||
|  |     /// ServerState | ||||||
|  |     /// </summary> | ||||||
|  |     public OPCSERVERSTATE ServerState { get; internal set; } = OPCSERVERSTATE.OPC_STATUS_NOCONFIG; | ||||||
|  |     /// <summary> | ||||||
|  |     /// StartTime | ||||||
|  |     /// </summary> | ||||||
|  |     public DateTime StartTime { get; internal set; } = new DateTime(0); | ||||||
|  |     /// <summary> | ||||||
|  |     /// VendorInfo | ||||||
|  |     /// </summary> | ||||||
|  |     public string VendorInfo { get; internal set; } = "UNKOWN"; | ||||||
|  |     /// <summary> | ||||||
|  |     /// Version | ||||||
|  |     /// </summary> | ||||||
|  |     public string Version { get; internal set; } = "UNKOWN"; | ||||||
|  | } | ||||||
| @@ -0,0 +1,256 @@ | |||||||
|  | #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; | ||||||
|  | using System.Text; | ||||||
|  |  | ||||||
|  | using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Discovery; | ||||||
|  | /// <summary> | ||||||
|  | /// OpcDiscovery | ||||||
|  | /// </summary> | ||||||
|  | public class OpcDiscovery | ||||||
|  | { | ||||||
|  |     private static readonly Guid CATID_OPC_DA10 = new("63D5F430-CFE4-11d1-B2C8-0060083BA1FB"); | ||||||
|  |  | ||||||
|  |     private static readonly Guid CATID_OPC_DA20 = new("63D5F432-CFE4-11d1-B2C8-0060083BA1FB"); | ||||||
|  |  | ||||||
|  |     private static readonly Guid CATID_OPC_DA30 = new("CC603642-66D7-48f1-B69A-B625E73652D7"); | ||||||
|  |  | ||||||
|  |     private static readonly Guid OPCEnumCLSID = new("13486D51-4821-11D2-A494-3CB306C10000"); | ||||||
|  |     /// <summary> | ||||||
|  |     /// GetOpcServer | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="serverName"></param> | ||||||
|  |     /// <param name="host"></param> | ||||||
|  |     /// <returns></returns> | ||||||
|  |     internal static ServerInfo GetOpcServer(string serverName, string host) | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |         if (string.IsNullOrEmpty(serverName)) | ||||||
|  |         { | ||||||
|  |             throw new("检索失败,需提供OPCName"); | ||||||
|  |         } | ||||||
|  |         ServerInfo result = null; | ||||||
|  |         ServerInfo[] serverInfos = null; | ||||||
|  |         object o_Server = Comn.ComInterop.CreateInstance(OPCEnumCLSID, host); | ||||||
|  |         if (o_Server == null) | ||||||
|  |             throw new("检索失败,请检查是否安装OPC Runtime"); | ||||||
|  |         try | ||||||
|  |         { | ||||||
|  |             Guid catid = CATID_OPC_DA20; | ||||||
|  |  | ||||||
|  |             //两种方式,兼容国产部分OPCServer不支持IOPCServerList2的情况 | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 IOPCServerList2 m_server2 = (IOPCServerList2)o_Server; | ||||||
|  |                 GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server2, catid); | ||||||
|  |                 if (result == null) | ||||||
|  |                 { | ||||||
|  |                     IOPCServerList m_server = (IOPCServerList)o_Server; | ||||||
|  |                     GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             catch | ||||||
|  |             { | ||||||
|  |                 IOPCServerList m_server = (IOPCServerList)o_Server; | ||||||
|  |                 GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid); | ||||||
|  |             } | ||||||
|  |             if (result == null) | ||||||
|  |             { | ||||||
|  |                 StringBuilder sb = new StringBuilder(); | ||||||
|  |                 foreach (var item in serverInfos) | ||||||
|  |                 { | ||||||
|  |                     sb.AppendLine(item.ToString()); | ||||||
|  |                 } | ||||||
|  |                 throw new($"无法创建OPCServer连接,请检查OPC名称是否一致,以下为{host}中的OPC列表:" | ||||||
|  |                     + Environment.NewLine + | ||||||
|  |                   sb.ToString() | ||||||
|  |                     ); | ||||||
|  |             } | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             Comn.ComInterop.RealseComServer(o_Server); | ||||||
|  |             o_Server = null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList m_server, Guid catid) | ||||||
|  |     { | ||||||
|  |         object enumerator = null; | ||||||
|  |         //2 | ||||||
|  |         m_server.EnumClassesOfCategories( | ||||||
|  |             1, | ||||||
|  |             new Guid[] { catid }, | ||||||
|  |             0, | ||||||
|  |             null, | ||||||
|  |             out enumerator); | ||||||
|  |         Guid[] clsids = Comn.ComInterop.ReadClasses((IEnumGUID)enumerator); | ||||||
|  |         //释放 | ||||||
|  |         Comn.ComInterop.RealseComServer(enumerator); | ||||||
|  |         enumerator = null; | ||||||
|  |  | ||||||
|  |         serverInfos = GetServerDetails(clsids?.ToArray(), host, m_server); | ||||||
|  |         for (int i = 0; i < serverInfos.Length; i++) | ||||||
|  |         { | ||||||
|  |             if (serverInfos[i].CLSID.ToString().ToLower() == serverName.ToLower() || | ||||||
|  |                     serverInfos[i].ProgID.ToLower() == serverName.ToLower() || | ||||||
|  |                     serverInfos[i].VerIndProgID.ToLower() == serverName.ToLower()) | ||||||
|  |             { | ||||||
|  |                 result = serverInfos[i]; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList2 m_server, Guid catid) | ||||||
|  |     { | ||||||
|  |         //1 | ||||||
|  |         IOPCEnumGUID enumerator = null; | ||||||
|  |         m_server.EnumClassesOfCategories( | ||||||
|  |             1, | ||||||
|  |             new Guid[] { catid }, | ||||||
|  |             0, | ||||||
|  |             null, | ||||||
|  |             out enumerator); | ||||||
|  |         Guid[] clsids = Comn.ComInterop.ReadClasses(enumerator); | ||||||
|  |         //释放 | ||||||
|  |         Comn.ComInterop.RealseComServer(enumerator); | ||||||
|  |         enumerator = null; | ||||||
|  |  | ||||||
|  |         serverInfos = GetServerDetails(clsids?.ToArray(), host, m_server); | ||||||
|  |         for (int i = 0; i < serverInfos.Length; i++) | ||||||
|  |         { | ||||||
|  |             if (serverInfos[i].CLSID.ToString().ToLower() == serverName.ToLower() || | ||||||
|  |                     serverInfos[i].ProgID.ToLower() == serverName.ToLower() || | ||||||
|  |                     serverInfos[i].VerIndProgID.ToLower() == serverName.ToLower()) | ||||||
|  |             { | ||||||
|  |                 result = serverInfos[i]; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     private static ServerInfo[] GetServerDetails(Guid[] clsids, string host, IOPCServerList m_server) | ||||||
|  |     { | ||||||
|  |         ArrayList servers = new ArrayList(); | ||||||
|  |         for (int i = 0; i < clsids?.Length; i++) | ||||||
|  |         { | ||||||
|  |             Guid clsid = clsids[i]; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 string progID = null; | ||||||
|  |                 string description = null; | ||||||
|  |                 string verIndProgID = null; | ||||||
|  |                 ServerInfo server1 = new(); | ||||||
|  |  | ||||||
|  |                 server1.Host = host; | ||||||
|  |                 server1.CLSID = clsid; | ||||||
|  |  | ||||||
|  |                 m_server?.GetClassDetails( | ||||||
|  |                     ref clsid, | ||||||
|  |                     out progID, | ||||||
|  |                     out description, | ||||||
|  |                     out verIndProgID); | ||||||
|  |                 if (verIndProgID != null) | ||||||
|  |                 { | ||||||
|  |                     server1.VerIndProgID = verIndProgID; | ||||||
|  |                 } | ||||||
|  |                 else if (progID != null) | ||||||
|  |                 { | ||||||
|  |                     server1.ProgID = progID; | ||||||
|  |                 } | ||||||
|  |                 if (description != null) | ||||||
|  |                 { | ||||||
|  |                     server1.Description = description; | ||||||
|  |                 } | ||||||
|  |                 servers.Add(server1); | ||||||
|  |             } | ||||||
|  |             catch | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return (ServerInfo[])servers.ToArray(typeof(ServerInfo)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static ServerInfo[] GetServerDetails(Guid[] clsids, string host, IOPCServerList2 m_server) | ||||||
|  |     { | ||||||
|  |         ArrayList servers = new ArrayList(); | ||||||
|  |         for (int i = 0; i < clsids?.Length; i++) | ||||||
|  |         { | ||||||
|  |             Guid clsid = clsids[i]; | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 string progID = null; | ||||||
|  |                 string description = null; | ||||||
|  |                 string verIndProgID = null; | ||||||
|  |                 ServerInfo server1 = new(); | ||||||
|  |  | ||||||
|  |                 server1.Host = host; | ||||||
|  |                 server1.CLSID = clsid; | ||||||
|  |  | ||||||
|  |                 m_server?.GetClassDetails( | ||||||
|  |                     ref clsid, | ||||||
|  |                     out progID, | ||||||
|  |                     out description, | ||||||
|  |                     out verIndProgID); | ||||||
|  |                 if (verIndProgID != null) | ||||||
|  |                 { | ||||||
|  |                     server1.VerIndProgID = verIndProgID; | ||||||
|  |                 } | ||||||
|  |                 else if (progID != null) | ||||||
|  |                 { | ||||||
|  |                     server1.ProgID = progID; | ||||||
|  |                 } | ||||||
|  |                 if (description != null) | ||||||
|  |                 { | ||||||
|  |                     server1.Description = description; | ||||||
|  |                 } | ||||||
|  |                 servers.Add(server1); | ||||||
|  |             } | ||||||
|  |             catch | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return (ServerInfo[])servers.ToArray(typeof(ServerInfo)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | internal class ServerInfo | ||||||
|  | { | ||||||
|  |     internal Guid CLSID { get; set; } | ||||||
|  |     internal string Description { get; set; } = string.Empty; | ||||||
|  |     internal string Host { get; set; } = string.Empty; | ||||||
|  |     internal string ProgID { get; set; } = string.Empty; | ||||||
|  |     internal string VerIndProgID { get; set; } = string.Empty; | ||||||
|  |  | ||||||
|  |     public override string ToString() | ||||||
|  |     { | ||||||
|  |         StringBuilder stringBuilder = new StringBuilder(); | ||||||
|  |         stringBuilder.AppendLine($"{nameof(CLSID)}:{CLSID}"); | ||||||
|  |         stringBuilder.AppendLine($"{nameof(Description)}:{Description}"); | ||||||
|  |         stringBuilder.AppendLine($"{nameof(Host)}:{Host}"); | ||||||
|  |         stringBuilder.AppendLine($"{nameof(ProgID)}:{ProgID}"); | ||||||
|  |         stringBuilder.AppendLine($"{nameof(VerIndProgID)}:{VerIndProgID}"); | ||||||
|  |         return stringBuilder.ToString(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,103 @@ | |||||||
|  | #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.Rcw; | ||||||
|  | #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 | ||||||
|  |  | ||||||
|  | [Serializable] | ||||||
|  | public class BrowseElement : ICloneable | ||||||
|  | { | ||||||
|  |     private bool m_hasChildren; | ||||||
|  |     private bool m_isItem; | ||||||
|  |     private string m_itemName; | ||||||
|  |     private string m_itemPath; | ||||||
|  |     private string m_name; | ||||||
|  |     private ItemProperty[] m_properties = new ItemProperty[0]; | ||||||
|  |  | ||||||
|  |     public bool HasChildren | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return m_hasChildren; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             m_hasChildren = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public bool IsItem | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return m_isItem; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             m_isItem = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public string ItemName | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return m_itemName; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             m_itemName = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public string ItemPath | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return m_itemPath; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             m_itemPath = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public string Name | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return m_name; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             m_name = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     public ItemProperty[] Properties | ||||||
|  |     { | ||||||
|  |         get | ||||||
|  |         { | ||||||
|  |             return m_properties; | ||||||
|  |         } | ||||||
|  |         set | ||||||
|  |         { | ||||||
|  |             m_properties = value; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public virtual object Clone() | ||||||
|  |     { | ||||||
|  |         BrowseElement obj = (BrowseElement)MemberwiseClone(); | ||||||
|  |         obj.m_properties = (ItemProperty[])Comn.Convert.Clone(m_properties); | ||||||
|  |         return obj; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,333 @@ | |||||||
|  | #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; | ||||||
|  |  | ||||||
|  | namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||||
|  |  | ||||||
|  | #pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释 | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("B196B286-BAB4-101A-B69C-00AA00341D07")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IConnectionPoint | ||||||
|  | { | ||||||
|  |     void GetConnectionInterface( | ||||||
|  |         [Out] | ||||||
|  |         out Guid pIID); | ||||||
|  |  | ||||||
|  |     void GetConnectionPointContainer( | ||||||
|  |         [Out] | ||||||
|  |         out IConnectionPointContainer ppCPC); | ||||||
|  |  | ||||||
|  |     void Advise( | ||||||
|  |         [MarshalAs(UnmanagedType.IUnknown)] | ||||||
|  |         object pUnkSink, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pdwCookie); | ||||||
|  |  | ||||||
|  |     void Unadvise( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int dwCookie); | ||||||
|  |  | ||||||
|  |     void EnumConnections( | ||||||
|  |         [Out] | ||||||
|  |         out IEnumConnections ppEnum); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("B196B284-BAB4-101A-B69C-00AA00341D07")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IConnectionPointContainer | ||||||
|  | { | ||||||
|  |     void EnumConnectionPoints( | ||||||
|  |         [Out] | ||||||
|  |         out IEnumConnectionPoints ppEnum); | ||||||
|  |  | ||||||
|  |     void FindConnectionPoint( | ||||||
|  |         ref Guid riid, | ||||||
|  |         [Out] | ||||||
|  |         out IConnectionPoint ppCP); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("B196B285-BAB4-101A-B69C-00AA00341D07")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IEnumConnectionPoints | ||||||
|  | { | ||||||
|  |     void RemoteNext( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int cConnections, | ||||||
|  |         [Out] | ||||||
|  |         IntPtr ppCP, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pcFetched); | ||||||
|  |  | ||||||
|  |     void Skip( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int cConnections); | ||||||
|  |  | ||||||
|  |     void Reset(); | ||||||
|  |  | ||||||
|  |     void Clone( | ||||||
|  |         [Out] | ||||||
|  |         out IEnumConnectionPoints ppEnum); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("B196B287-BAB4-101A-B69C-00AA00341D07")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IEnumConnections | ||||||
|  | { | ||||||
|  |     void RemoteNext( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int cConnections, | ||||||
|  |         [Out] | ||||||
|  |         IntPtr rgcd, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pcFetched); | ||||||
|  |  | ||||||
|  |     void Skip( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int cConnections); | ||||||
|  |  | ||||||
|  |     void Reset(); | ||||||
|  |  | ||||||
|  |     void Clone( | ||||||
|  |         [Out] | ||||||
|  |         out IEnumConnections ppEnum); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("0002E000-0000-0000-C000-000000000046")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IEnumGUID | ||||||
|  | { | ||||||
|  |     void Next( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int celt, | ||||||
|  |         [Out] | ||||||
|  |         IntPtr rgelt, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pceltFetched); | ||||||
|  |  | ||||||
|  |     void Skip( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int celt); | ||||||
|  |  | ||||||
|  |     void Reset(); | ||||||
|  |  | ||||||
|  |     void Clone( | ||||||
|  |         [Out] | ||||||
|  |         out IEnumGUID ppenum); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("00000101-0000-0000-C000-000000000046")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IEnumString | ||||||
|  | { | ||||||
|  |     [PreserveSig] | ||||||
|  |     int RemoteNext( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int celt, | ||||||
|  |         IntPtr rgelt, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pceltFetched); | ||||||
|  |  | ||||||
|  |     void Skip( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int celt); | ||||||
|  |  | ||||||
|  |     void Reset(); | ||||||
|  |  | ||||||
|  |     void Clone( | ||||||
|  |         [Out] | ||||||
|  |         out IEnumString ppenum); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("00000100-0000-0000-C000-000000000046")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IEnumUnknown | ||||||
|  | { | ||||||
|  |     void RemoteNext( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int celt, | ||||||
|  |         [Out] | ||||||
|  |         IntPtr rgelt, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pceltFetched); | ||||||
|  |  | ||||||
|  |     void Skip( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int celt); | ||||||
|  |  | ||||||
|  |     void Reset(); | ||||||
|  |  | ||||||
|  |     void Clone( | ||||||
|  |         [Out] | ||||||
|  |         out IEnumUnknown ppenum); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("F31DFDE2-07B6-11d2-B2D8-0060083BA1FB")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IOPCCommon | ||||||
|  | { | ||||||
|  |     void SetLocaleID( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int dwLcid); | ||||||
|  |  | ||||||
|  |     void GetLocaleID( | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pdwLcid); | ||||||
|  |  | ||||||
|  |     void QueryAvailableLocaleIDs( | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pdwCount, | ||||||
|  |         [Out] | ||||||
|  |         out IntPtr pdwLcid); | ||||||
|  |  | ||||||
|  |     void GetErrorString( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int dwError, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         out String ppString); | ||||||
|  |  | ||||||
|  |     void SetClientName( | ||||||
|  |         [MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         String szName); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("55C382C8-21C7-4e88-96C1-BECFB1E3F483")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IOPCEnumGUID | ||||||
|  | { | ||||||
|  |     void Next( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int celt, | ||||||
|  |         [Out] | ||||||
|  |         IntPtr rgelt, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.I4)] | ||||||
|  |         out int pceltFetched); | ||||||
|  |  | ||||||
|  |     void Skip( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int celt); | ||||||
|  |  | ||||||
|  |     void Reset(); | ||||||
|  |  | ||||||
|  |     void Clone( | ||||||
|  |         [Out] | ||||||
|  |         out IOPCEnumGUID ppenum); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("13486D50-4821-11D2-A494-3CB306C10000")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IOPCServerList | ||||||
|  | { | ||||||
|  |     void EnumClassesOfCategories( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int cImplemented, | ||||||
|  |         [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStruct, SizeParamIndex=0)] | ||||||
|  |         Guid[] rgcatidImpl, | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int cRequired, | ||||||
|  |         [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStruct, SizeParamIndex=2)] | ||||||
|  |         Guid[] rgcatidReq, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.IUnknown)] | ||||||
|  |         out object ppenumClsid); | ||||||
|  |  | ||||||
|  |     void GetClassDetails( | ||||||
|  |         ref Guid clsid, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         out string ppszProgID, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         out string ppszUserType, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         out string ppszVerIndProgID); | ||||||
|  |  | ||||||
|  |     void CLSIDFromProgID( | ||||||
|  |         [MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         string szProgId, | ||||||
|  |         [Out] | ||||||
|  |         out Guid clsid); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("9DD0B56C-AD9E-43ee-8305-487F3188BF7A")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IOPCServerList2 | ||||||
|  | { | ||||||
|  |     void EnumClassesOfCategories( | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int cImplemented, | ||||||
|  |         [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStruct, SizeParamIndex=0)] | ||||||
|  |         Guid[] rgcatidImpl, | ||||||
|  |         [MarshalAs(UnmanagedType.I4)] | ||||||
|  |         int cRequired, | ||||||
|  |         [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStruct, SizeParamIndex=0)] | ||||||
|  |         Guid[] rgcatidReq, | ||||||
|  |         [Out] | ||||||
|  |         out IOPCEnumGUID ppenumClsid); | ||||||
|  |  | ||||||
|  |     void GetClassDetails( | ||||||
|  |         ref Guid clsid, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         out string ppszProgID, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         out string ppszUserType, | ||||||
|  |         [Out][MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         out string ppszVerIndProgID); | ||||||
|  |  | ||||||
|  |     void CLSIDFromProgID( | ||||||
|  |         [MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         string szProgId, | ||||||
|  |         [Out] | ||||||
|  |         out Guid clsid); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [ComImport] | ||||||
|  | [GuidAttribute("F31DFDE1-07B6-11d2-B2D8-0060083BA1FB")] | ||||||
|  | [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] | ||||||
|  | public interface IOPCShutdown | ||||||
|  | { | ||||||
|  |     void ShutdownRequest( | ||||||
|  |         [MarshalAs(UnmanagedType.LPWStr)] | ||||||
|  |         string szReason); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <exclude /> | ||||||
|  | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | ||||||
|  | public struct CONNECTDATA | ||||||
|  | { | ||||||
|  |     [MarshalAs(UnmanagedType.IUnknown)] | ||||||
|  |     object pUnk; | ||||||
|  |     [MarshalAs(UnmanagedType.I4)] | ||||||
|  |     int dwCookie; | ||||||
|  | } | ||||||