Compare commits
	
		
			313 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 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 | 
							
								
								
									
										63
									
								
								.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 | ||||
							
								
								
									
										13
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -360,4 +360,15 @@ MigrationBackup/ | ||||
| .ionide/ | ||||
|  | ||||
| # Fody - auto-generated XML schema | ||||
| FodyWeavers.xsd | ||||
| FodyWeavers.xsd | ||||
|  | ||||
| /src/Plugins/Other | ||||
| /src/ThingsGateway.Web.Server/*.db | ||||
| /src/PluginPro*/ | ||||
| /src/*Pro* | ||||
| /src/TestResults*/ | ||||
| /src/ThingsGateway.Web.Server/ThingsGateway.db | ||||
|  | ||||
| /handbook/ | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								Image/1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 135 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 125 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/3.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 217 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/4.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 119 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/5.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 189 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/6.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 151 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/7.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 187 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/8.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 164 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/9.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 149 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/gitLogo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 14 KiB | 
							
								
								
									
										
											BIN
										
									
								
								Image/pay.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 326 KiB | 
| @@ -1,4 +1,3 @@ | ||||
| 
 | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
| @@ -87,7 +86,7 @@ | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
| 
 | ||||
|    4. Cachetribution. You may reproduce and distribute copies of the | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
| @@ -187,16 +186,16 @@ | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
| 
 | ||||
|    Copyright 2023-present Diego | ||||
|    Copyright [yyyy] [name of copyright owner] | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
							
								
								
									
										180
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -1,95 +1,129 @@ | ||||
| # ThingsGateway | ||||
|  | ||||
|  | ||||
| <div align='center'> | ||||
| <img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/gitLogo.png" height=100 /> | ||||
| </div> | ||||
|  | ||||
| ## Introduction | ||||
| #### 介绍 | ||||
|  | ||||
|  | ||||
| A cross-platform, high-performance edge data collection gateway based on net8, capable of handling millions of data points per. | ||||
|  | ||||
| 基于Net6/7+Blazor Server的跨平台边缘采集网关,支持南北端插件式开发, | ||||
| 并拥有较完善的北端Rpc权限管理。 | ||||
|  | ||||
| ## Documentation | ||||
| [Github地址](https://github.com/kimdiego2098/ThingsGateway) | ||||
|  | ||||
|  | ||||
| [Documentation](https://thingsgateway.cn/). | ||||
|  | ||||
| [NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | ||||
|  | ||||
| <div > | ||||
| 如果对您有帮助,请点击右上角⭐Star关注,感谢支持开源! | ||||
| </div> | ||||
|  | ||||
| ### Plugin List | ||||
| #### 开源说明 | ||||
|  | ||||
|  | ||||
| Apache 2.0+[附加协议](https://diego2098.gitee.io/thingsgateway-docs/docs/) | ||||
|  | ||||
| #### Data Collection Plugins | ||||
| Apache 2.0 开源协议的核心内容是以保护和尊重原作者的著作权为主要目的。对使用,复制,修改,商用不做过多限制,但必须包含原著的License信息。 | ||||
|  | ||||
| ####  功能亮点 | ||||
|  | ||||
| - Blazor Server架构,开发部署更简单 | ||||
| - 采集/上传配置完全支持Excel导入导出 | ||||
| - 插件式驱动,方便驱动二次开发 | ||||
| - 时序数据库存储 | ||||
| - 实时/历史报警(Sql转储),支持布尔/高低限值 | ||||
|  | ||||
| #### 演示 | ||||
|  | ||||
| http://120.24.62.140:5000/ | ||||
|  | ||||
| 默认账户密码:superAdmin 111111 | ||||
|  | ||||
|  | ||||
| | Plugin Name | Remarks                                                       | | ||||
| | ----------- | ------------------------------------------------------------- | | ||||
| | Modbus      | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links   | | ||||
| | SiemensS7   | Siemens PLC S7 series                                         | | ||||
| | Dlt6452007  | Supports Serial/Tcp/Udp links                                 | | ||||
| | OpcDaMaster | Compiled for 64-bit                                           | | ||||
| | OpcUaMaster | Supports certificate login, object extension, Json read/write | | ||||
| #### 社区版采集插件 | ||||
| > 支持分包解析/订阅 | ||||
| - Modbus(Rtu/Tcp/Udp) | ||||
| - OPCDAClient(支持导入节点) | ||||
| - OPCUAClient(支持导入节点) | ||||
| - 西门子S7协议 | ||||
|  | ||||
| #### Business Plugins | ||||
| #### 社区版上传插件 | ||||
| > 支持Rpc写入 | ||||
| - Modbus Server | ||||
| - OPCUA Server (支持历史查询) | ||||
| - Mqtt Server (支持自定义json) | ||||
| - Mqtt Client (支持自定义json) | ||||
| - IotSharp Client (IotSharp网关插件,Rpc待测试) | ||||
|  | ||||
| > 不支持Rpc | ||||
| - RabbitMQ (支持自定义json) | ||||
| - Kafka | ||||
|  | ||||
| | Plugin Name      | Remarks                                                                                           | | ||||
| | ---------------- | ------------------------------------------------------------------------------------------------- | | ||||
| | ModbusSlave      | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links, supports Rpc reverse writing         | | ||||
| | OpcUaServer      | OpcUa server, supports Rpc reverse writing                                                        | | ||||
| | MqttClient       | Mqtt client, supports Rpc reverse writing, script-customizable upload content                     | | ||||
| | MqttServer       | Mqtt server, supports WebSocket, supports Rpc reverse writing, script-customizable upload content | | ||||
| | KafkaProducer    | Script-customizable upload content                                                                | | ||||
| | RabbitMQProducer | Script-customizable upload content                                                                | | ||||
| | SqlDB            | Relational database storage, supports historical storage and real-time data updates               | | ||||
| | SqlHistoryAlarm      | Alarm historical data relational database storage                                                 | | ||||
| | TDengineDB       | Time-series database storage                                                                      | | ||||
| | QuestDB          | Time-series database storage                                                                      | | ||||
| #### nuget | ||||
|  | ||||
|  | ||||
|  | ||||
| ## License | ||||
|  | ||||
|  | ||||
| [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE) | ||||
|  | ||||
|  | ||||
| ## Demo | ||||
|  | ||||
|  | ||||
| [Demo](http://47.119.161.158:5000/) | ||||
|  | ||||
| Account: **SuperAdmin** | ||||
|  | ||||
| Password: **111111** | ||||
|  | ||||
| **In the upper-right corner, switch to the IoT Gateway module in the personal popup box** | ||||
|  | ||||
| ## Docker | ||||
|  | ||||
| ```shell | ||||
|  | ||||
| docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway | ||||
|  | ||||
| docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64 | ||||
| - Modbus库,支持ModbusTcp、ModbusRtu、ModbusRtuOverTcp、ModbusUdp、ModbusServer等 | ||||
| ``` 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 | ||||
| ``` | ||||
|  | ||||
|  | ||||
| - S7库 | ||||
| ``` powershell | ||||
|  dotnet add package ThingsGateway.Foundation.Adapter.Siemens | ||||
| ``` | ||||
|  | ||||
| ## Sponsorship | ||||
| ####  效果图 | ||||
|  <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> | ||||
|  | ||||
|  | ||||
| [Sponsorship Approach](https://thingsgateway.cn/docs/1000) | ||||
|  | ||||
|  | ||||
| ## Community | ||||
|  ####  文档 | ||||
|  | ||||
|  | ||||
| QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569) | ||||
|  | ||||
|  使用前请查看Gitee Pages [文档站点](https://diego2098.gitee.io/thingsgateway-docs/) | ||||
|  | ||||
| ## Pro Plugins | ||||
|  #### 特别鸣谢 | ||||
| -  Furion:[https://dotnetchina.gitee.io/furion](https://dotnetchina.gitee.io/furion) | ||||
| -  SqlSugar:[https://gitee.com/dotnetchina/SqlSugar](https://gitee.com/dotnetchina/SqlSugar) | ||||
| -  Simple.Admin:[https://gitee.com/zxzyjs/SimpleAdmin](https://gitee.com/zxzyjs/SimpleAdmin) | ||||
| -  Masa.Blazor:[https://www.masastack.com/blazor](https://www.masastack.com/blazor) | ||||
| -  MiniExcel:[https://gitee.com/dotnetchina/MiniExcel](https://gitee.com/dotnetchina/MiniExcel) | ||||
| -  TouchSocket:[https://gitee.com/rrqm_home/touchsocket](https://gitee.com/rrqm_home/touchsocket) | ||||
| -  IdGenerator:[https://github.com/yitter/idgenerator](https://github.com/yitter/idgenerator) | ||||
| -  CodingSeb.ExpressionEvaluator:[https://github.com/codingseb/ExpressionEvaluator](https://github.com/codingseb/ExpressionEvaluator) | ||||
| -  Hardware.Info:[https://github.com/Jinjinov/Hardware.Info](https://github.com/Jinjinov/Hardware.Info) | ||||
| -  UAParser:[https://github.com/ua-parser/uap-csharp](https://github.com/ua-parser/uap-csharp) | ||||
|  | ||||
| #### 补充说明 | ||||
| * 使用OPC相关插件时请遵循OPC基金会的授权规则 | ||||
| * 使用OPCDA插件时,需安装OPC核心库,[文件地址](https://gitee.com/diego2098/ThingsGateway/attach_files) | ||||
|  | ||||
|   | ||||
|  | ||||
| ####  支持作者 | ||||
|  如果对您有帮助,请点击右上角⭐Star关注,感谢支持开源! | ||||
|  | ||||
|  若希望捐赠项目,请查看以下捐赠码或使用Gitee捐赠功能 | ||||
|  | ||||
| <img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/pay.png" height=180 /> | ||||
|  | ||||
| ####  联系作者 | ||||
|  * QQ群:605534569 | ||||
|  * 邮箱:2248356998@qq.com | ||||
|  | ||||
|  | ||||
| [Plugin List](https://thingsgateway.cn/docs/1001) | ||||
|   | ||||
| @@ -1,75 +0,0 @@ | ||||
| # ThingsGateway | ||||
|  | ||||
| ## 介绍 | ||||
|  | ||||
| 基于net8的跨平台高性能边缘采集网关,单机采集数据点位可达百万 | ||||
|  | ||||
| ## 文档 | ||||
|  | ||||
| [文档](https://thingsgateway.cn/) | ||||
|  | ||||
| [NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22) | ||||
|  | ||||
| ### 插件列表 | ||||
|  | ||||
| #### 采集插件 | ||||
|  | ||||
|  | ||||
| | 插件名称    | 备注                                  | | ||||
| | ----------- | ------------------------------------- | | ||||
| | Modbus      | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路 | | ||||
| | SiemensS7   | 西门子PLC S7系列                      | | ||||
| | Dlt6452007  | 支持串口/Tcp/Udp链路                  | | ||||
| | OpcDaMaster | 64位编译                              | | ||||
| | OpcUaMaster | 支持证书登录,扩展对象,Json读写      | | ||||
|  | ||||
| #### 业务插件 | ||||
|  | ||||
|  | ||||
| | 插件名称         | 备注                                                       | | ||||
| | ---------------- | ---------------------------------------------------------- | | ||||
| | ModbusSlave      | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路,支持Rpc反写         | | ||||
| | OpcUaServer      | OpcUa服务端,支持Rpc反写                                   | | ||||
| | MqttClient       | Mqtt客户端,支持Rpc反写,脚本自定义上传内容                | | ||||
| | MqttServer       | Mqtt服务端,支持WebSocket,支持Rpc反写,脚本自定义上传内容 | | ||||
| | KafkaProducer    | 脚本自定义上传内容                                         | | ||||
| | RabbitMQProducer | 脚本自定义上传内容                                         | | ||||
| | SqlDB            | 关系数据库存储,支持历史存储和实时数据更新                 | | ||||
| | SqlHistoryAlarm      | 报警历史数据关系数据库存储                                 | | ||||
| | TDengineDB       | 时序数据库存储                                             | | ||||
| | QuestDB          | 时序数据库存储                                             | | ||||
|  | ||||
| ## 协议 | ||||
|  | ||||
| [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE) | ||||
|  | ||||
| ## 演示 | ||||
|  | ||||
| [ThingsGateway演示地址](http://47.119.161.158:5000/) | ||||
|  | ||||
| 账户	:  **SuperAdmin** | ||||
|  | ||||
| 密码 : **111111** | ||||
|  | ||||
| **右上角个人弹出框中,切换到物联网关模块** | ||||
|  | ||||
| ## Docker | ||||
|  | ||||
| ```shell | ||||
|  | ||||
| docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway | ||||
|  | ||||
| docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64 | ||||
| ``` | ||||
|  | ||||
| ## 赞助 | ||||
|  | ||||
| [赞助途径](https://thingsgateway.cn/docs/1000) | ||||
|  | ||||
| ## 社区 | ||||
|  | ||||
| QQ群:605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569) | ||||
|  | ||||
| ## Pro插件 | ||||
|  | ||||
| [插件列表](https://thingsgateway.cn/docs/1001) | ||||
| @@ -1,248 +0,0 @@ | ||||
| root = true | ||||
|  | ||||
| # To learn more about .editorconfig see https://aka.ms/editorconfigdocs | ||||
| ###############################  | ||||
| # Core EditorConfig Options   #  | ||||
| ###############################  | ||||
|  | ||||
| # All files  | ||||
| [*] | ||||
| indent_style = space | ||||
| charset = utf-8 | ||||
| trim_trailing_whitespace = true | ||||
| insert_final_newline = true | ||||
| spelling_exclusion_path = .\exclusion.dic | ||||
|  | ||||
| # Microsoft .NET properties | ||||
| csharp_new_line_before_members_in_object_initializers = false | ||||
| csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion | ||||
| csharp_style_prefer_utf8_string_literals = true:suggestion | ||||
| csharp_style_var_elsewhere = true:suggestion | ||||
| csharp_style_var_for_built_in_types = true:suggestion | ||||
| csharp_style_var_when_type_is_apparent = true:suggestion | ||||
| dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True | ||||
| dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field | ||||
| dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef | ||||
| dotnet_naming_rule.unity_serialized_field_rule.severity = warning | ||||
| dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style | ||||
| dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols | ||||
| dotnet_naming_style.lower_camel_case_style.capitalization = camel_case | ||||
| dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = * | ||||
| dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =  | ||||
| dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field | ||||
| dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance | ||||
| dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none | ||||
| dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none | ||||
| dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none | ||||
| dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion | ||||
| dotnet_style_predefined_type_for_member_access = true:suggestion | ||||
| dotnet_style_qualification_for_event = false:suggestion | ||||
| dotnet_style_qualification_for_field = false:suggestion | ||||
| dotnet_style_qualification_for_method = false:suggestion | ||||
| dotnet_style_qualification_for_property = false:suggestion | ||||
| dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion | ||||
|  | ||||
| # ReSharper properties | ||||
| resharper_autodetect_indent_settings = true | ||||
| resharper_formatter_off_tag = @formatter:off | ||||
| resharper_formatter_on_tag = @formatter:on | ||||
| resharper_formatter_tags_enabled = true | ||||
| resharper_new_line_before_while = true | ||||
| resharper_place_attribute_on_same_line = false | ||||
| resharper_show_autodetect_configure_formatting_tip = false | ||||
| resharper_use_indent_from_vs = false | ||||
|  | ||||
| # ReSharper inspection severities | ||||
| resharper_arrange_redundant_parentheses_highlighting = hint | ||||
| resharper_arrange_this_qualifier_highlighting = hint | ||||
| resharper_arrange_type_member_modifiers_highlighting = hint | ||||
| resharper_arrange_type_modifiers_highlighting = hint | ||||
| resharper_built_in_type_reference_style_for_member_access_highlighting = hint | ||||
| resharper_built_in_type_reference_style_highlighting = hint | ||||
| resharper_redundant_base_qualifier_highlighting = warning | ||||
| resharper_suggest_var_or_type_built_in_types_highlighting = hint | ||||
| resharper_suggest_var_or_type_elsewhere_highlighting = hint | ||||
| resharper_suggest_var_or_type_simple_types_highlighting = hint | ||||
| resharper_web_config_module_not_resolved_highlighting = warning | ||||
| resharper_web_config_type_not_resolved_highlighting = warning | ||||
| resharper_web_config_wrong_module_highlighting = warning | ||||
|  | ||||
| # Code files  | ||||
| [*.{cs,csx,vb,vbx}] | ||||
| indent_size = 4 | ||||
|  | ||||
| [*.{cs,css,js,json,*html,razor,txt,log}] | ||||
| charset = utf-8-bom | ||||
|  | ||||
| [*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}] | ||||
| indent_size = 2 | ||||
|  | ||||
| # Xml config files | ||||
| [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] | ||||
| indent_size = 2 | ||||
|  | ||||
| [*.json] | ||||
| indent_size = 2 | ||||
|  | ||||
| [*.{ps1,psm1}] | ||||
| indent_size = 4 | ||||
|  | ||||
| [*.sh] | ||||
| indent_size = 4 | ||||
| end_of_line = lf | ||||
|  | ||||
| ###############################  | ||||
| # .NET Coding Conventions     #  | ||||
| ###############################  | ||||
| [*.{cs,vb}] | ||||
| # Organize usings  | ||||
| dotnet_sort_system_directives_first = false | ||||
| # this. preferences  | ||||
| dotnet_style_qualification_for_field = false:silent | ||||
| dotnet_style_qualification_for_property = false:silent | ||||
| dotnet_style_qualification_for_method = false:silent | ||||
| dotnet_style_qualification_for_event = false:silent | ||||
| # Language keywords vs BCL types preferences  | ||||
| dotnet_style_predefined_type_for_locals_parameters_members = true:silent | ||||
| dotnet_style_predefined_type_for_member_access = true:silent | ||||
| # Parentheses preferences  | ||||
| dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent | ||||
| dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent | ||||
| dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent | ||||
| dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent | ||||
| # Modifier preferences  | ||||
| dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent | ||||
| dotnet_style_readonly_field = true:suggestion | ||||
| # Expression-level preferences  | ||||
| dotnet_style_object_initializer = true:suggestion | ||||
| dotnet_style_collection_initializer = true:suggestion | ||||
| dotnet_style_explicit_tuple_names = true:suggestion | ||||
| dotnet_style_null_propagation = true:suggestion | ||||
| dotnet_style_coalesce_expression = true:suggestion | ||||
| dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent | ||||
| dotnet_style_prefer_inferred_tuple_names = true:suggestion | ||||
| dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion | ||||
| dotnet_style_prefer_auto_properties = true:silent | ||||
| dotnet_style_prefer_conditional_expression_over_assignment = true:silent | ||||
| dotnet_style_prefer_conditional_expression_over_return = true:silent | ||||
| ###############################  | ||||
| # Naming Conventions          #  | ||||
| ###############################  | ||||
| # Style Definitions  | ||||
| dotnet_naming_style.pascal_case_style.capitalization = pascal_case | ||||
| # Use PascalCase for constant fields    | ||||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion | ||||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields | ||||
| dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style | ||||
| dotnet_naming_symbols.constant_fields.applicable_kinds = field | ||||
| dotnet_naming_symbols.constant_fields.applicable_accessibilities = * | ||||
| dotnet_naming_symbols.constant_fields.required_modifiers = const | ||||
| dotnet_style_operator_placement_when_wrapping = beginning_of_line | ||||
| tab_width = 4 | ||||
| end_of_line = crlf | ||||
| dotnet_style_prefer_collection_expression = when_types_exactly_match:suggestion | ||||
| dotnet_style_prefer_simplified_boolean_expressions = true:suggestion | ||||
| dotnet_style_prefer_compound_assignment = true:suggestion | ||||
| dotnet_style_prefer_simplified_interpolation = true:suggestion | ||||
| dotnet_style_namespace_match_folder = true:suggestion | ||||
| dotnet_style_allow_multiple_blank_lines_experimental = true:silent | ||||
| dotnet_style_allow_statement_immediately_after_block_experimental = true:silent | ||||
| dotnet_code_quality_unused_parameters = all:suggestion | ||||
| ###############################  | ||||
| # C# Coding Conventions       #  | ||||
| ###############################  | ||||
| [*.cs] | ||||
| # var preferences  | ||||
| csharp_style_var_for_built_in_types = true:silent | ||||
| csharp_style_var_when_type_is_apparent = true:silent | ||||
| csharp_style_var_elsewhere = true:silent | ||||
| csharp_prefer_static_local_function = true:silent | ||||
| # Expression-bodied members  | ||||
| csharp_style_expression_bodied_methods = false:silent | ||||
| csharp_style_expression_bodied_constructors = false:silent | ||||
| csharp_style_expression_bodied_operators = false:silent | ||||
| csharp_style_expression_bodied_properties = true:silent | ||||
| csharp_style_expression_bodied_indexers = true:silent | ||||
| csharp_style_expression_bodied_accessors = true:silent | ||||
| # Pattern matching preferences  | ||||
| csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion | ||||
| csharp_style_pattern_matching_over_as_with_null_check = true:suggestion | ||||
| # Null-checking preferences  | ||||
| csharp_style_throw_expression = true:suggestion | ||||
| csharp_style_conditional_delegate_call = true:suggestion | ||||
| # Modifier preferences  | ||||
| csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion | ||||
| # Expression-level preferences  | ||||
| csharp_prefer_braces = true:silent | ||||
| csharp_style_deconstructed_variable_declaration = true:suggestion | ||||
| csharp_prefer_simple_default_expression = true:suggestion | ||||
| csharp_style_pattern_local_over_anonymous_function = true:suggestion | ||||
| csharp_style_inlined_variable_declaration = true:suggestion | ||||
| ###############################  | ||||
| # C# Formatting Rules         #  | ||||
| ###############################  | ||||
| # New line preferences  | ||||
| csharp_new_line_before_open_brace = all | ||||
| csharp_new_line_before_else = true | ||||
| csharp_new_line_before_catch = true | ||||
| csharp_new_line_before_finally = true | ||||
| csharp_new_line_before_members_in_object_initializers = true | ||||
| csharp_new_line_before_members_in_anonymous_types = true | ||||
| csharp_new_line_between_query_expression_clauses = true | ||||
| # Indentation preferences  | ||||
| csharp_indent_case_contents = true | ||||
| csharp_indent_switch_labels = true | ||||
| csharp_indent_labels = flush_left | ||||
| # Space preferences  | ||||
| csharp_space_after_cast = false | ||||
| csharp_space_after_keywords_in_control_flow_statements = true | ||||
| csharp_space_between_method_call_parameter_list_parentheses = false | ||||
| csharp_space_between_method_declaration_parameter_list_parentheses = false | ||||
| csharp_space_between_parentheses = false | ||||
| csharp_space_before_colon_in_inheritance_clause = true | ||||
| csharp_space_after_colon_in_inheritance_clause = true | ||||
| csharp_space_around_binary_operators = before_and_after | ||||
| csharp_space_between_method_declaration_empty_parameter_list_parentheses = false | ||||
| csharp_space_between_method_call_name_and_opening_parenthesis = false | ||||
| csharp_space_between_method_call_empty_parameter_list_parentheses = false | ||||
| # Wrapping preferences  | ||||
| csharp_preserve_single_line_statements = true | ||||
| csharp_preserve_single_line_blocks = true | ||||
| ###############################  | ||||
| # VB Coding Conventions       #  | ||||
| ###############################  | ||||
| [*.vb] | ||||
| # Modifier preferences  | ||||
| visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion | ||||
| [*.cs] | ||||
| # Add file header | ||||
| file_header_template = ------------------------------------------------------------------------------\n此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充\n此代码版权(除特别声明外的代码)归作者本人Diego所有\n源代码使用协议遵循本仓库的开源协议及附加协议\nGitee源代码仓库:https://gitee.com/diego2098/ThingsGateway\nGithub源代码仓库:https://github.com/kimdiego2098/ThingsGateway\n使用文档:https://thingsgateway.cn/\nQQ群:605534569\n------------------------------------------------------------------------------ | ||||
|  | ||||
| csharp_style_namespace_declarations = file_scoped:suggestion | ||||
| csharp_style_expression_bodied_local_functions = true:silent | ||||
| csharp_using_directive_placement = outside_namespace:silent | ||||
| csharp_prefer_simple_using_statement = true:suggestion | ||||
| csharp_style_prefer_method_group_conversion = true:silent | ||||
| csharp_style_prefer_top_level_statements = true:silent | ||||
| csharp_style_prefer_primary_constructors = true:suggestion | ||||
| csharp_style_expression_bodied_lambdas = true:silent | ||||
| csharp_style_prefer_null_check_over_type_check = true:suggestion | ||||
| csharp_style_prefer_local_over_anonymous_function = true:suggestion | ||||
| csharp_style_prefer_index_operator = true:suggestion | ||||
| csharp_style_prefer_range_operator = true:suggestion | ||||
| csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion | ||||
| csharp_style_prefer_utf8_string_literals = true:suggestion | ||||
| csharp_style_prefer_tuple_swap = true:suggestion | ||||
| csharp_style_unused_value_expression_statement_preference = discard_variable:silent | ||||
| csharp_style_unused_value_assignment_preference = discard_variable:suggestion | ||||
| csharp_style_prefer_readonly_struct_member = true:suggestion | ||||
| csharp_style_prefer_readonly_struct = true:suggestion | ||||
| csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent | ||||
| csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent | ||||
| csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent | ||||
| csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent | ||||
| csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent | ||||
| csharp_style_prefer_switch_expression = true:suggestion | ||||
| csharp_style_prefer_pattern_matching = true:silent | ||||
| csharp_style_prefer_not_pattern = true:suggestion | ||||
| csharp_style_prefer_extended_property_pattern = true:suggestion | ||||
| @@ -1,7 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <OutputType>Exe</OutputType> | ||||
|   </PropertyGroup> | ||||
|  | ||||
| </Project> | ||||
| @@ -1,18 +0,0 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
| // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| namespace AutoUpdate; | ||||
|  | ||||
| internal class Program | ||||
| { | ||||
|     private static void Main(string[] args) | ||||
|     { | ||||
|     } | ||||
| } | ||||
| @@ -1,40 +0,0 @@ | ||||
| { | ||||
|   "$schema": "null", | ||||
|  | ||||
|   "AllowedHosts": "*", | ||||
|  | ||||
|   "AppSettings": { | ||||
|     "InjectSpecificationDocument": true, // 生产环境是否开启Swagger | ||||
|     "ExternalAssemblies": [ "Plugins" ], // 插件目录 | ||||
|  | ||||
|     // nuget动态加载的程序集 | ||||
|     "SupportPackageNamePrefixs": [ | ||||
|  | ||||
|       "ThingsGateway.Foundation.Razor", | ||||
|       "ThingsGateway.Debug.Razor", | ||||
|       "ThingsGateway.Core", | ||||
|       "ThingsGateway.Razor" | ||||
|  | ||||
|     ] | ||||
|   }, | ||||
|  | ||||
|   "DynamicApiControllerSettings": { | ||||
|     //"DefaultRoutePrefix": "api", // 默认路由前缀 | ||||
|     "CamelCaseSeparator": "", // 驼峰命名分隔符 | ||||
|     "SplitCamelCase": false, // 切割骆驼(驼峰)/帕斯卡命名 | ||||
|     "LowercaseRoute": false, // 小写路由格式 | ||||
|     "AsLowerCamelCase": true, // 小驼峰命名(首字母小写) | ||||
|     "KeepVerb": false, // 保留动作方法请求谓词 | ||||
|     "KeepName": false // 保持原有名称不处理 | ||||
|   }, | ||||
|   "FriendlyExceptionSettings": { | ||||
|     "DefaultErrorMessage": "系统异常,请联系管理员", | ||||
|     "ThrowBah": true, // 是否将 Oops.Oh 默认抛出为业务异常 | ||||
|     "LogError": false // 是否输出异常日志 | ||||
|   }, | ||||
|   "CorsAccessorSettings": { | ||||
|     "PolicyName": "ThingsGateway", | ||||
|     "WithExposedHeaders": [ "Content-Disposition", "X-Pagination", "access-token", "x-access-token" ], // 如果前端不代理且是axios请求 | ||||
|     "SignalRSupport": true // 启用 SignalR 跨域支持 | ||||
|   } | ||||
| } | ||||
| @@ -1,33 +0,0 @@ | ||||
| { | ||||
|  | ||||
|  | ||||
|   //BootstrapBlazor配置 | ||||
|   "BootstrapBlazorOptions": { | ||||
|  | ||||
|  | ||||
|     "ToastDelay": 4000, | ||||
|     "MessageDelay": 4000, | ||||
|     "SwalDelay": 4000, | ||||
|     "EnableErrorLogger": true, | ||||
|     "FallbackCulture": "zh-CN", | ||||
|     "SupportedCultures": [ | ||||
|       "zh-CN", | ||||
|       "en-US", | ||||
|       "zh-TW" | ||||
|     ], | ||||
|     "DefaultCultureInfo": "zh-CN", //修改默认语言 | ||||
|     "TableSettings": { | ||||
|       "CheckboxColumnWidth": 36 | ||||
|     }, | ||||
|     "IgnoreLocalizerMissing": true, | ||||
|     "StepSettings": { | ||||
|       "Short": 1, | ||||
|       "Int": 1, | ||||
|       "Long": 1, | ||||
|       "Float": 0.1, | ||||
|       "Double": 0.01, | ||||
|       "Decimal": 0.01 | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -1,48 +0,0 @@ | ||||
| { | ||||
|  | ||||
|  | ||||
|   "Logging": { | ||||
|     "LogLevel": { | ||||
|       "Default": "Information", | ||||
|       "Microsoft": "Warning", | ||||
|       "Microsoft.Hosting.Lifetime": "Information" | ||||
|     }, | ||||
|     "EventLog": { | ||||
|       "LogLevel": { | ||||
|         "Default": "Warning", | ||||
|         "Microsoft.Hosting.Lifetime": "Information" | ||||
|       } //windows事件输出日志等级 | ||||
|     }, | ||||
|     "Console": { | ||||
|       "LogLevel": { | ||||
|         "Default": "Information", | ||||
|         "Microsoft": "Warning", | ||||
|         "Microsoft.Hosting.Lifetime": "Information" | ||||
|       } //控制台输出日志等级 | ||||
|     }, | ||||
|     "BackendLog": { | ||||
|       "LogLevel": { | ||||
|         "Default": "Warning" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|  | ||||
|   "Monitor": { | ||||
|     "GlobalEnabled": false, // 启用全局拦截日志 | ||||
|     "IncludeOfMethods": [], // 拦截特定方法,当GlobalEnabled=false有效 | ||||
|     "ExcludeOfMethods": [], // 排除特定方法,当GlobalEnabled=true有效 | ||||
|     "BahLogLevel": "Information", // Oops.Oh 和 Oops.Bah 业务日志输出级别 | ||||
|     "WithReturnValue": true, // 是否包含返回值,默认true | ||||
|     "ReturnValueThreshold": 500, // 返回值字符串阈值,默认0全量输出 | ||||
|     "JsonBehavior": "None", // 是否输出Json,默认None(OnlyJson、All) | ||||
|     "JsonIndented": false, // 是否格式化Json | ||||
|     "UseUtcTimestamp": false // 时间格式UTC、LOCAL | ||||
|   }, | ||||
|  | ||||
|  | ||||
|   //日志配置 | ||||
|   "LogJob": { | ||||
|     "DaysAgo": 10 //清理10天前日志 | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -1,40 +0,0 @@ | ||||
| { | ||||
|   "Menu": { | ||||
|  | ||||
|     "MenuItems": [ | ||||
|       { | ||||
|         "Url": "/", | ||||
|         "Text": "首页" | ||||
|       }, | ||||
|       { | ||||
|         "Text": "Modbus", | ||||
|         "Items": [ | ||||
|           { | ||||
|             "Url": "/ModbusMaster", | ||||
|             "Text": "ModbusMaster" | ||||
|           }, | ||||
|           { | ||||
|             "Url": "/ModbusSlave", | ||||
|             "Text": "ModbusSlave" | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "Url": "/SiemensS7Master", | ||||
|         "Text": "Siemens" | ||||
|       }, | ||||
|       { | ||||
|         "Url": "/Dlt645_2007Master", | ||||
|         "Text": "Dlt645_2007Master" | ||||
|       }, | ||||
|       { | ||||
|         "Url": "/OpcUaMaster", | ||||
|         "Text": "OpcUaMaster" | ||||
|       }, | ||||
|       { | ||||
|         "Url": "/OpcDaMaster", | ||||
|         "Text": "OpcDaMaster" | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @@ -1,13 +0,0 @@ | ||||
| { | ||||
|   //网站配置 | ||||
|   "Website": { | ||||
|     "Copyright": "版权所有 © 2023-present Diego", | ||||
|     "IsShowAbout": true, //是否显示关于页面 | ||||
|     "SourceUrl": "https://gitee.com/diego2098/ThingsGateway", | ||||
|     "WikiUrl": "https://thingsgateway.cn/", | ||||
|     "QQGroup1Link": "http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569", | ||||
|     "QQGroup1Number": "605534569", | ||||
|     "Title": "ThingsGateway", | ||||
|     "Demo": false | ||||
|   } | ||||
| } | ||||
| @@ -1,7 +0,0 @@ | ||||
| { | ||||
|    | ||||
|   "ThingsGateway.Debug.MainLayout": { | ||||
|     "FullScreenButton": "Full Screen", | ||||
|     "About": "About" | ||||
|   } | ||||
| } | ||||
| @@ -1,6 +0,0 @@ | ||||
| { | ||||
|   "ThingsGateway.Debug.MainLayout": { | ||||
|     "FullScreenButton": "全屏", | ||||
|     "About": "关于" | ||||
|   } | ||||
| } | ||||
| @@ -1,6 +0,0 @@ | ||||
| { | ||||
|   "ThingsGateway.Debug.MainLayout": { | ||||
|     "FullScreenButton": "全屏", | ||||
|     "About": "關於" | ||||
|   } | ||||
| } | ||||
| @@ -1,74 +0,0 @@ | ||||
| @inherits LayoutComponentBase | ||||
| @layout BaseLayout | ||||
| @namespace ThingsGateway.Debug | ||||
| @using BootstrapBlazor.Components | ||||
| @using ThingsGateway.Extension | ||||
| @using ThingsGateway.NewLife.Extension | ||||
| @using ThingsGateway.Razor | ||||
| @inject NavigationManager NavigationManager | ||||
|  | ||||
| <div class="mainlayout"> | ||||
|  | ||||
|     <Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true" | ||||
|             ShowGotoTop="true" ShowCollapseBar="true" Menus="@MenuService.MenuItems" | ||||
|             AllowDragTab=true AdditionalAssemblies="@_assemblyList" | ||||
|             UseTabSet="false" TabDefaultUrl="/"> | ||||
|         <Header> | ||||
|  | ||||
|             <div class="flex-fill"></div> | ||||
|             @* 搜索框 *@ | ||||
|             <GlobalSearch Menus=@(MenuService.SameLevelMenuItems) /> | ||||
|             @* 语言选择 *@ | ||||
|  | ||||
|             <div class="d-none d-xl-flex "> | ||||
|                 <CultureChooser /> | ||||
|             </div> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|             @* 全屏按钮 *@ | ||||
|             <FullScreenButton class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-arrows-alt" | ||||
|                               TooltipPlacement=Placement.Bottom TooltipText="@Localizer[nameof(FullScreenButton)]" /> | ||||
|             @if (WebsiteOption.Value.IsShowAbout) | ||||
|             { | ||||
|                 <Button OnClick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" /> | ||||
|             } | ||||
|             @* 版本号 *@ | ||||
|             <div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div> | ||||
|  | ||||
|             @* 主题切换 *@ | ||||
|             @* <ThemeToggle /> *@ | ||||
|             <ThemeProvider class="layout-header-bar d-none d-lg-flex px-0"></ThemeProvider> | ||||
|  | ||||
|         </Header> | ||||
|         <Side> | ||||
|             <div class="layout-banner"> | ||||
|                 <span class="avatar"> | ||||
|                     @WebsiteOption.Value.Title?.GetNameLen2() | ||||
|                 </span> | ||||
|  | ||||
|                 <div class="layout-title d-flex align-items-center justify-content-center"> | ||||
|                     <span>@WebsiteOption.Value.Title</span> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </Side> | ||||
|         <Main> | ||||
|  | ||||
|             <Tab ClickTabToNavigation="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true | ||||
|                  Menus="@MenuService.MenuItems" AdditionalAssemblies="@_assemblyList" | ||||
|                  DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=> | ||||
|                  { | ||||
|                  return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty())); | ||||
|                  })> | ||||
|             </Tab> | ||||
|  | ||||
|         </Main> | ||||
|         <NotAuthorized> | ||||
|             <Redirect /> | ||||
|         </NotAuthorized> | ||||
|     </Layout> | ||||
|  | ||||
| </div> | ||||
|  | ||||
|  | ||||
| @@ -1,80 +0,0 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using BootstrapBlazor.Components; | ||||
|  | ||||
| using Microsoft.AspNetCore.Components; | ||||
| using Microsoft.Extensions.Localization; | ||||
| using Microsoft.Extensions.Options; | ||||
|  | ||||
| using System.Diagnostics.CodeAnalysis; | ||||
| using System.Reflection; | ||||
|  | ||||
| using ThingsGateway.Razor; | ||||
|  | ||||
| namespace ThingsGateway.Debug; | ||||
|  | ||||
| public partial class MainLayout | ||||
| { | ||||
|     private List<Assembly> _assemblyList = new(); | ||||
|  | ||||
|     private string _versionString = string.Empty; | ||||
|  | ||||
|     [Inject] | ||||
|     [NotNull] | ||||
|     private DialogService? DialogService { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     [NotNull] | ||||
|     private IStringLocalizer<MainLayout>? Localizer { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     [NotNull] | ||||
|     private IMenuService? MenuService { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     [NotNull] | ||||
|     private IAppVersionService? VersionService { get; set; } | ||||
|  | ||||
|     [Inject] | ||||
|     [NotNull] | ||||
|     private IOptions<WebsiteOptions>? WebsiteOption { get; set; } | ||||
|  | ||||
|     protected override void OnInitialized() | ||||
|     { | ||||
|         _assemblyList = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => | ||||
|  a.GetTypes()).Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass | ||||
| && u.IsDefined(typeof(Microsoft.AspNetCore.Components.RouteAttribute), true)).Select(a => a.Assembly) | ||||
| //.Where(a => a != typeof(BlazorApp).Assembly) | ||||
| .Distinct().ToList(); | ||||
|         base.OnInitialized(); | ||||
|     } | ||||
|  | ||||
|     protected override Task OnInitializedAsync() | ||||
|     { | ||||
|         _versionString = $"v{VersionService.Version}"; | ||||
|         return base.OnInitializedAsync(); | ||||
|     } | ||||
|  | ||||
|     private async Task ShowAbout() | ||||
|     { | ||||
|         DialogOption? op = null; | ||||
|  | ||||
|         op = new DialogOption() | ||||
|         { | ||||
|             IsScrolling = false, | ||||
|             Size = Size.Medium, | ||||
|             ShowFooter = false, | ||||
|             Title = Localizer["About"], | ||||
|             BodyTemplate = BootstrapDynamicComponent.CreateComponent<About>().Render(), | ||||
|         }; | ||||
|         await DialogService.Show(op); | ||||
|     } | ||||
| } | ||||
| @@ -1,138 +0,0 @@ | ||||
| ::deep .avatar { | ||||
|     border-radius: 1.5rem; | ||||
|     width: 36px; | ||||
|     height: 36px; | ||||
|     background-color: var(--bs-green); | ||||
|     color: #fff; | ||||
|     flex: 0 0 auto; | ||||
|     font-size: 1rem; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .menu-icon { | ||||
|     width: 16px; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .layout-main > .tabs > .tabs-body { | ||||
|     background-color: var(--tabs-body-bg); | ||||
| } | ||||
|  | ||||
|     .mainlayout ::deep .layout-main > .tabs > .tabs-body > .tabs-body-content { | ||||
|         height: var(--bb-layout-body-height); | ||||
|         background-color: var(--bs-body-bg); | ||||
|         padding: 4px; | ||||
|     } | ||||
|  | ||||
| .mainlayout ::deep .tabs { | ||||
|     --bb-tabs-item-height: 32px; | ||||
|     --bb-tabs-body-padding: 0.5rem; | ||||
| } | ||||
|  | ||||
|     .mainlayout ::deep .tabs.tabs-border-card { | ||||
|         box-shadow: 0 0px 0px 0 rgba(0,0,0,0),0 0 6px 0 rgba(0,0,0,0); | ||||
|     } | ||||
|  | ||||
|     .mainlayout ::deep .tabs .extend .nav-link-bar.left { | ||||
|         border-width: 0px 0px 0px 0px; | ||||
|     } | ||||
|  | ||||
| .mainlayout ::deep .tabs-nav-wrap > .nav-link-bar.dropdown { | ||||
|     border-width: 0px 0px 0px 0px; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs .extend .nav-link-bar.right { | ||||
|     border-width: 0px 0px 0px 0px; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs .tabs-item-fix { | ||||
|     border-width: 0px 0px 0px 0px; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs.tabs-card > .tabs-header .tabs-item { | ||||
|     border-width: 0px 0px 0px 0px; | ||||
|     border: none; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs.tabs-border-card > .tabs-header .tabs-item { | ||||
|     border-width: 0px 0px 0px 0px; | ||||
|     border: none; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs.tabs-card .tabs-header .tabs-item.active { | ||||
|     border-width: 0px 0px 1px 0px; | ||||
|     border-style: solid; | ||||
|     border-color: var(--bb-tabs-item-active-color); | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs.tabs-border-card .tabs-header .tabs-item.active { | ||||
|     border-width: 0px 0px 1px 0px; | ||||
|     border-style: solid; | ||||
|     border-color: var(--bb-tabs-item-active-color); | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs.tabs-card .tabs-header .tabs-item.active { | ||||
|     background-color: var(--bs-primary-bg1); | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep.tabs.tabs-card .tabs-header .tabs-item:hover { | ||||
|     background-color: var(--bs-primary-bg1); | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep.tabs.tabs-border-card .tabs-header .tabs-item:hover { | ||||
|     background-color: var(--bs-primary-bg1); | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs-nav-wrap .nav-link-bar { | ||||
|     font-size: 0.7rem; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .tabs-item .tabs-item-close { | ||||
|     top: 5px; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .table-wrapper { | ||||
|     border-radius: unset; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .layout-side { | ||||
|     box-shadow: inset -1px 0 0px 0px var(--bs-border-color); /* 下内阴影 */ | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .layout-banner { | ||||
|     box-shadow: inset -1px 0 0px 0px var(--bs-border-color); /* 下内阴影 */ | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .layout { | ||||
|     --bb-layout-header-height: 44px; | ||||
|     --bb-layout-headerbar-background: transparent; | ||||
|     --bs-navbar-color: var(--bb-layout-header-color); | ||||
|     --bs-navbar-hover-color: var(--bs-primary); | ||||
|     --bb-layout-header-background: var(--tg-nav-bg); | ||||
|     --bb-layout-sidebar-background: var(--tg-nav-bg); | ||||
|     --bb-layout-footer-background: var(--tg-nav-bg); | ||||
|     --bb-layout-sidebar-banner-background: var(--tg-nav-bg); | ||||
|     --bb-layout-banner-font-size: 1.2rem; | ||||
|     --bb-layout-banner-logo-width: 36px; | ||||
|     --bb-layout-banner-logo-height: 36px; | ||||
|     --line-chart-height: 350px; | ||||
|     --bb-layout-body-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - 20px); | ||||
|     --line-chart-table-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - var(--line-chart-height) - 30px); | ||||
|     --table-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - 30px); | ||||
|     --bs-header-height: 30px; | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .dropdown-logout { | ||||
|     --bb-logout-avatar-width: 32px; | ||||
|     --bb-logout-avatar-height: 32px; | ||||
|     --bb-logout-user-bg: rgba(52,58,64,0.7); | ||||
|     --bb-logout-menu-border-color: var(--bs-border-color); | ||||
| } | ||||
|  | ||||
| .mainlayout ::deep .layout-header-bar { | ||||
|     border-color: transparent; | ||||
|     border: 0px; | ||||
|     color: var(--bb-layout-header-color); | ||||
| } | ||||
|  | ||||
|     .mainlayout ::deep .layout-header-bar:hover { | ||||
|         color: var(--bs-navbar-hover-color); | ||||
|     } | ||||
| @@ -1,96 +0,0 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Hosting; | ||||
| using Microsoft.Extensions.Hosting; | ||||
|  | ||||
| using Photino.Blazor; | ||||
|  | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.NewLife.Log; | ||||
|  | ||||
| namespace ThingsGateway.Debug; | ||||
|  | ||||
| internal class Program | ||||
| { | ||||
|  | ||||
|     [STAThread] | ||||
|     private static void Main(string[] args) | ||||
|     { | ||||
|         //当前工作目录设为程序集的基目录 | ||||
|         System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory); | ||||
|         // 增加中文编码支持 | ||||
|         Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); | ||||
|  | ||||
|         #region 控制台输出Logo | ||||
|  | ||||
|         Console.Write(Environment.NewLine); | ||||
|         Console.ForegroundColor = ConsoleColor.Yellow; | ||||
|         XTrace.WriteLine(string.Empty); | ||||
|         Console.WriteLine( | ||||
|             """ | ||||
|  | ||||
|                _______  _      _                      _____         _ | ||||
|               |__   __|| |    (_)                    / ____|       | | | ||||
|                  | |   | |__   _  _ __    __ _  ___ | |  __   __ _ | |_  ___ __      __ __ _  _   _ | ||||
|                  | |   | '_ \ | || '_ \  / _` |/ __|| | |_ | / _` || __|/ _ \\ \ /\ / // _` || | | | | ||||
|                  | |   | | | || || | | || (_| |\__ \| |__| || (_| || |_|  __/ \ V  V /| (_| || |_| | | ||||
|                  |_|   |_| |_||_||_| |_| \__, ||___/ \_____| \__,_| \__|\___|  \_/\_/  \__,_| \__, | | ||||
|                                           __/ |                                                __/ | | ||||
|                                          |___/                                                |___/ | ||||
|  | ||||
|             """ | ||||
|          ); | ||||
|         Console.ResetColor(); | ||||
|  | ||||
|         #endregion 控制台输出Logo | ||||
|  | ||||
|         var builder = PhotinoBlazorAppBuilder.CreateDefault(args); | ||||
|         builder.RootComponents.Add<Routes>("#app"); | ||||
|  | ||||
|         var options = GenericRunOptions.DefaultSilence | ||||
|               .ConfigureServices(services => | ||||
|               { | ||||
|                   foreach (var item in builder.Services) | ||||
|                   { | ||||
|                       services.Add(item); | ||||
|                   } | ||||
|               }); | ||||
|         ; | ||||
|         Serve.BuildApplication(options, out var app); | ||||
|  | ||||
|         app.Start(); | ||||
|  | ||||
|         var hybridApp = builder.Build(app.Services); | ||||
|  | ||||
|         hybridApp.MainWindow.ContextMenuEnabled = false; | ||||
|         hybridApp.MainWindow.DevToolsEnabled = true; | ||||
|         hybridApp.MainWindow.GrantBrowserPermissions = true; | ||||
|         hybridApp.MainWindow.SetUseOsDefaultLocation(false); | ||||
|         hybridApp.MainWindow.SetUseOsDefaultSize(false); | ||||
|         hybridApp.MainWindow.SetSize(new System.Drawing.Size(1920, 1080)); | ||||
|         hybridApp.MainWindow.SetTitle("ThingsGateway"); | ||||
|         hybridApp.MainWindow.SetIconFile("wwwroot/favicon.ico"); | ||||
|         AppDomain.CurrentDomain.UnhandledException += (sender, error) => | ||||
|         { | ||||
|         }; | ||||
|  | ||||
|         hybridApp.MainWindow.WindowClosing += (sender, e) => | ||||
|         { | ||||
|             app.StopAsync(); | ||||
|             return false; | ||||
|         }; | ||||
|         hybridApp.Run(); | ||||
|         Thread.Sleep(5000); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -1,11 +0,0 @@ | ||||
| { | ||||
|   "profiles": { | ||||
|     "ThingsGateway.Debug.Photino": { | ||||
|       "commandName": "Project" | ||||
|     }, | ||||
|     "WSL": { | ||||
|       "commandName": "WSL2", | ||||
|       "distributionName": "" | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1,48 +0,0 @@ | ||||
| @using Microsoft.AspNetCore.Components.Routing | ||||
| @using ThingsGateway.Debug | ||||
| @namespace ThingsGateway | ||||
|  | ||||
| @{ | ||||
|     #if NET6_0 | ||||
| } | ||||
| <Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState> | ||||
|  | ||||
| <Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="App.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)"> | ||||
| <Found Context="routeData"> | ||||
|  | ||||
|     <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> | ||||
|  | ||||
|  | ||||
| </Found> | ||||
| <NotFound> | ||||
|     <LayoutView Layout="@typeof(Razor.BaseLayout)"> | ||||
|               <p role="alert">Sorry, there's nothing at this address.</p> | ||||
|         </LayoutView> | ||||
|     </NotFound> | ||||
| </Router> | ||||
|  | ||||
|  | ||||
| </Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState> | ||||
|  | ||||
|  | ||||
| @{ | ||||
|     #else | ||||
| } | ||||
| <Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="App.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)"> | ||||
| <Found Context="routeData"> | ||||
|  | ||||
|     <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> | ||||
|  | ||||
|  | ||||
| </Found> | ||||
| <NotFound> | ||||
|     <LayoutView Layout="@typeof(Razor.BaseLayout)"> | ||||
|               <p role="alert">Sorry, there's nothing at this address.</p> | ||||
|         </LayoutView> | ||||
|     </NotFound> | ||||
| </Router> | ||||
|  | ||||
| @{ | ||||
|     #endif | ||||
| } | ||||
|  | ||||
| @@ -1,65 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|   <Import Project="$(SolutionDir)Version.props" /> | ||||
|  | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <OutputType>WinExe</OutputType> | ||||
|     <ApplicationIcon>favicon.ico</ApplicationIcon> | ||||
|     <TargetFrameworks>net8.0;net6.0</TargetFrameworks> | ||||
|  | ||||
|     <!--动态适用GC--> | ||||
|     <GarbageCollectionAdaptationMode>true</GarbageCollectionAdaptationMode> | ||||
|     <!--使用自托管线程池--> | ||||
|     <!--<UseWindowsThreadPool>false</UseWindowsThreadPool> --> | ||||
|  | ||||
|     <!--使用工作站GC--> | ||||
|     <!--<ServerGarbageCollection>true</ServerGarbageCollection>--> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Content Update="wwwroot\**"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </Content> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Content Remove="Locales\*.json" /> | ||||
|     <EmbeddedResource Include="Locales\*.json"> | ||||
|       <CopyToOutputDirectory>Never</CopyToOutputDirectory> | ||||
|     </EmbeddedResource> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||||
|     <PackageReference Include="ThingsGateway.Razor" Version="$(AdminVersion)" /> | ||||
|     <PackageReference Include="ThingsGateway.Debug.Razor" Version="$(PluginVersion)" /> | ||||
|     <PackageReference Include="Photino.NET" Version="3.1.18" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup Condition="'$(TargetFramework)' == 'net6.0'"> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="6.0.33" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'"> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="8.0.10" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Compile Include="..\..\ThingsGateway.Photino\Photino\**" LinkBase="Photino"> | ||||
|     </Compile> | ||||
|  | ||||
|   </ItemGroup> | ||||
|  | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Content Update="appsettings.json"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|       <ExcludeFromSingleFile>true</ExcludeFromSingleFile> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
| @@ -1,6 +0,0 @@ | ||||
| { | ||||
|   "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件) | ||||
|   "IgnoreConfigurationFiles": [ "" ], | ||||
|   "ExternalAssemblies": [ "" ] | ||||
|  | ||||
| } | ||||
| @@ -1,6 +0,0 @@ | ||||
| { | ||||
|   "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件) | ||||
|   "IgnoreConfigurationFiles": [ "" ], | ||||
|   "ExternalAssemblies": [ "" ] | ||||
|  | ||||
| } | ||||
| Before Width: | Height: | Size: 4.2 KiB | 
| Before Width: | Height: | Size: 4.2 KiB | 
| Before Width: | Height: | Size: 10 KiB | 
| @@ -1,25 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> | ||||
|     <title>ThingsGateway.Debug</title> | ||||
|     <base href="/" /> | ||||
|  | ||||
|     <link rel="icon" href="favicon.ico" type="image/x-icon"> | ||||
|     <link href="_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css" rel="stylesheet"> | ||||
|     <link href="_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css" rel="stylesheet"> | ||||
|     <link href="_content/BootstrapBlazor/css/motronic.min.css" rel="stylesheet"> | ||||
|     <link href="ThingsGateway.Debug.Photino.styles.css" rel="stylesheet" /> | ||||
|     <link href="/_content/ThingsGateway.Razor/css/site.css" rel="stylesheet" /> | ||||
|     <script src="/_content/ThingsGateway.Razor/js/theme.js" type="module"></script><!-- 初始主题 --> | ||||
|     <script src="/_content/ThingsGateway.Razor/js/culture.js"></script> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     <div id="app"></div> | ||||
|  | ||||
|     <script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script> | ||||
|     <script src="_framework/blazor.webview.js" autostart="true"></script> | ||||
| </body> | ||||
| </html> | ||||
| @@ -1,51 +0,0 @@ | ||||
| using System.Drawing; | ||||
| using System.Windows.Forms; | ||||
|  | ||||
| namespace ThingsGateway.Debug | ||||
| { | ||||
|     partial class MainForm | ||||
|     { | ||||
|         /// <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() | ||||
|         { | ||||
|             var resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); | ||||
|             SuspendLayout(); | ||||
|             //  | ||||
|             // MainForm | ||||
|             //  | ||||
|             AutoScaleDimensions = new SizeF(9F, 20F); | ||||
|             AutoScaleMode = AutoScaleMode.Font; | ||||
|             ClientSize = new Size(1902, 1033); | ||||
|             Icon = (Icon)resources.GetObject("$this.Icon"); | ||||
|             Margin = new Padding(2); | ||||
|             Name = "MainForm"; | ||||
|             Text = "ThingsGateway"; | ||||
|             ResumeLayout(false); | ||||
|         } | ||||
|  | ||||
|         #endregion | ||||
|     } | ||||
| } | ||||
| @@ -1,116 +0,0 @@ | ||||
| // ------------------------------------------------------------------------------ | ||||
| // 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| // 此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| // 源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| // Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| // Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| // 使用文档:https://thingsgateway.cn/ | ||||
| // QQ群:605534569 | ||||
| // ------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.AspNetCore.Components.WebView; | ||||
| using Microsoft.AspNetCore.Components.WebView.WindowsForms; | ||||
| using Microsoft.Web.WebView2.Core; | ||||
|  | ||||
| using System.Windows.Forms; | ||||
|  | ||||
| namespace ThingsGateway.Debug | ||||
| { | ||||
|     public partial class MainForm : Form | ||||
|     { | ||||
|         protected string UploadPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "uploads"); | ||||
|         private BlazorWebView blazorWebView; | ||||
|  | ||||
|  | ||||
|         public MainForm(IServiceProvider serviceProvider) | ||||
|         { | ||||
|             InitializeComponent(); | ||||
|  | ||||
|             //默认全屏 | ||||
|             //this.WindowState = System.Windows.Forms.FormWindowState.Maximized; | ||||
|             //this.FormBorderStyle =FormBorderStyle.None; | ||||
|             //this.TopMost = true; | ||||
|             //this.KeyPreview = true; | ||||
|             KeyUp += new System.Windows.Forms.KeyEventHandler(MainForm_KeyUp); | ||||
|  | ||||
|             blazorWebView = new BlazorWebView() | ||||
|             { | ||||
|                 Dock = DockStyle.Fill, | ||||
|                 HostPage = "wwwroot/index.html", | ||||
|                 Services = serviceProvider | ||||
|             }; | ||||
|  | ||||
|             FormClosing += Program.Closing; | ||||
|  | ||||
|             blazorWebView.RootComponents.Add<Routes>("#app"); | ||||
|             Controls.Add(blazorWebView); | ||||
|             blazorWebView.BringToFront(); | ||||
|             blazorWebView.KeyUp += new System.Windows.Forms.KeyEventHandler(MainForm_KeyUp); | ||||
|  | ||||
|             blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; | ||||
|  | ||||
|             blazorWebView.UrlLoading += | ||||
|                 (sender, urlLoadingEventArgs) => | ||||
|                 { | ||||
|                     if (urlLoadingEventArgs.Url.Host != "0.0.0.0") | ||||
|                     { | ||||
|                         //外部链接WebView内打开,例如pdf浏览器 | ||||
|                         Console.WriteLine(urlLoadingEventArgs.Url); | ||||
|                         urlLoadingEventArgs.UrlLoadingStrategy = | ||||
|                             UrlLoadingStrategy.OpenInWebView; | ||||
|                     } | ||||
|                 }; | ||||
|         } | ||||
|  | ||||
|         private void BlazorWebViewInitialized(object? sender, EventArgs e) | ||||
|         { | ||||
|             //下载开始时引发 DownloadStarting,阻止默认下载 | ||||
|             blazorWebView.WebView.CoreWebView2.DownloadStarting += CoreWebView2_DownloadStarting; | ||||
|  | ||||
|             //指定下载保存位置 | ||||
|             blazorWebView.WebView.CoreWebView2.Profile.DefaultDownloadFolderPath = UploadPath; | ||||
|  | ||||
|             ////[无依赖发布webview2程序] 固定版本运行时环境的方式来实现加载网页 | ||||
|             ////设置web用户文件夹  | ||||
|             //var browserExecutableFolder = "c:\\wb2"; | ||||
|             //var userData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "BlazorWinFormsApp"); | ||||
|             //Directory.CreateDirectory(userData); | ||||
|             //var creationProperties = new CoreWebView2CreationProperties() | ||||
|             //{ | ||||
|             //    UserDataFolder = userData, | ||||
|             //    BrowserExecutableFolder = browserExecutableFolder | ||||
|             //}; | ||||
|             //mainBlazorWebView.WebView.CreationProperties = creationProperties; | ||||
|         } | ||||
|  | ||||
|         private void CoreWebView2_DownloadStarting(object? sender, CoreWebView2DownloadStartingEventArgs e) | ||||
|         { | ||||
|             var downloadOperation = e.DownloadOperation; | ||||
|             string fileName = Path.GetFileName(e.ResultFilePath); | ||||
|             var filePath = Path.Combine(UploadPath, fileName); | ||||
|  | ||||
|             //指定下载保存位置 | ||||
|             e.ResultFilePath = filePath; | ||||
|             MessageBox.Show($"下载文件完成 {fileName}", "提示"); | ||||
|         } | ||||
|  | ||||
|         private void MainForm_KeyUp(object? sender, KeyEventArgs e) | ||||
|         { | ||||
|             if (e.KeyCode == Keys.Escape) | ||||
|             { | ||||
|                 if (WindowState == System.Windows.Forms.FormWindowState.Normal) | ||||
|                 { | ||||
|                     WindowState = System.Windows.Forms.FormWindowState.Maximized; | ||||
|                     FormBorderStyle = FormBorderStyle.None; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     WindowState = System.Windows.Forms.FormWindowState.Normal; | ||||
|                     FormBorderStyle = FormBorderStyle.Sizable; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,197 +0,0 @@ | ||||
| <?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> | ||||
| @@ -1,87 +0,0 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Hosting; | ||||
|  | ||||
| using System.Text; | ||||
| using System.Windows.Forms; | ||||
|  | ||||
| using ThingsGateway.NewLife.Log; | ||||
|  | ||||
| namespace ThingsGateway.Debug; | ||||
|  | ||||
| internal class Program | ||||
| { | ||||
|     internal static void Closing(object? sender, FormClosingEventArgs e) | ||||
|     { | ||||
|         host.StopAsync(); | ||||
|     } | ||||
|  | ||||
|     [STAThread] | ||||
|     private static void Main(string[] args) | ||||
|     { | ||||
|         //当前工作目录设为程序集的基目录 | ||||
|         System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory); | ||||
|         // 增加中文编码支持 | ||||
|         Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); | ||||
|  | ||||
|         #region 控制台输出Logo | ||||
|  | ||||
|         Console.Write(Environment.NewLine); | ||||
|         Console.ForegroundColor = ConsoleColor.Yellow; | ||||
|         XTrace.WriteLine(string.Empty); | ||||
|         Console.WriteLine( | ||||
|             """ | ||||
|  | ||||
|                _______  _      _                      _____         _ | ||||
|               |__   __|| |    (_)                    / ____|       | | | ||||
|                  | |   | |__   _  _ __    __ _  ___ | |  __   __ _ | |_  ___ __      __ __ _  _   _ | ||||
|                  | |   | '_ \ | || '_ \  / _` |/ __|| | |_ | / _` || __|/ _ \\ \ /\ / // _` || | | | | ||||
|                  | |   | | | || || | | || (_| |\__ \| |__| || (_| || |_|  __/ \ V  V /| (_| || |_| | | ||||
|                  |_|   |_| |_||_||_| |_| \__, ||___/ \_____| \__,_| \__|\___|  \_/\_/  \__,_| \__, | | ||||
|                                           __/ |                                                __/ | | ||||
|                                          |___/                                                |___/ | ||||
|  | ||||
|             """ | ||||
|          ); | ||||
|         Console.ResetColor(); | ||||
|  | ||||
|         #endregion 控制台输出Logo | ||||
|  | ||||
|  | ||||
|         var options = GenericRunOptions.DefaultSilence | ||||
|               .ConfigureServices(services => | ||||
|               { | ||||
|                   services.AddWindowsFormsBlazorWebView(); | ||||
|               }); | ||||
|         ; | ||||
|  | ||||
|         Serve.BuildApplication(options, out var app); | ||||
|         host = app; | ||||
|         app.Start(); | ||||
|  | ||||
|         AppDomain.CurrentDomain.UnhandledException += (sender, error) => | ||||
|         { | ||||
|             MessageBox.Show(text: error.ExceptionObject.ToString(), caption: "Error"); | ||||
|         }; | ||||
|  | ||||
|         Application.SetHighDpiMode(HighDpiMode.SystemAware); | ||||
|         Application.EnableVisualStyles(); | ||||
|         Application.SetCompatibleTextRenderingDefault(false); | ||||
|         Application.Run(new MainForm(app.Services)); | ||||
|  | ||||
|         Thread.Sleep(5000); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private static IHost host; | ||||
|  | ||||
| } | ||||
| @@ -1,48 +0,0 @@ | ||||
| @using Microsoft.AspNetCore.Components.Routing | ||||
| @using ThingsGateway.Debug | ||||
| @namespace ThingsGateway | ||||
|  | ||||
| @{ | ||||
|     #if NET6_0 | ||||
| } | ||||
| <Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState> | ||||
|  | ||||
| <Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="App.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)"> | ||||
| <Found Context="routeData"> | ||||
|  | ||||
|     <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> | ||||
|  | ||||
|  | ||||
| </Found> | ||||
| <NotFound> | ||||
|     <LayoutView Layout="@typeof(Razor.BaseLayout)"> | ||||
|               <p role="alert">Sorry, there's nothing at this address.</p> | ||||
|         </LayoutView> | ||||
|     </NotFound> | ||||
| </Router> | ||||
|  | ||||
|  | ||||
| </Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState> | ||||
|  | ||||
|  | ||||
| @{ | ||||
|     #else | ||||
| } | ||||
| <Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="App.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)"> | ||||
| <Found Context="routeData"> | ||||
|  | ||||
|     <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> | ||||
|  | ||||
|  | ||||
| </Found> | ||||
| <NotFound> | ||||
|     <LayoutView Layout="@typeof(Razor.BaseLayout)"> | ||||
|               <p role="alert">Sorry, there's nothing at this address.</p> | ||||
|         </LayoutView> | ||||
|     </NotFound> | ||||
| </Router> | ||||
|  | ||||
| @{ | ||||
|     #endif | ||||
| } | ||||
|  | ||||
| @@ -1,61 +0,0 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk.Razor"> | ||||
|  | ||||
|   <Import Project="$(SolutionDir)Version.props" /> | ||||
|   <PropertyGroup> | ||||
|     <OutputType>WinExe</OutputType> | ||||
|     <OpenApiGenerateDocuments>false</OpenApiGenerateDocuments> | ||||
|     <SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages> | ||||
|     <PublishReadyToRunComposite>true</PublishReadyToRunComposite> | ||||
|     <ApplicationIcon>favicon.ico</ApplicationIcon> | ||||
|     <TargetFrameworks>net8.0-windows;</TargetFrameworks> | ||||
|  | ||||
|     <!--动态适用GC--> | ||||
|     <GarbageCollectionAdaptationMode>true</GarbageCollectionAdaptationMode> | ||||
|     <!--使用自托管线程池--> | ||||
|     <!--<UseWindowsThreadPool>false</UseWindowsThreadPool> --> | ||||
|  | ||||
|     <!--使用工作站GC--> | ||||
|     <!--<ServerGarbageCollection>true</ServerGarbageCollection>--> | ||||
|  | ||||
|     <!--<PlatformTarget>x86</PlatformTarget>--> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|  | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <FrameworkReference Include="Microsoft.AspNetCore.App" /> | ||||
|     <PackageReference Include="ThingsGateway.Razor" Version="$(AdminVersion)" /> | ||||
|     <PackageReference Include="ThingsGateway.Debug.Razor" Version="$(PluginVersion)" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="8.0.91" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Content Update="wwwroot\**"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </Content> | ||||
|  | ||||
|   <EmbeddedResource Include="..\ThingsGateway.Debug.Photino\Locales\en-US.json" Link="Locales\en-US.json" /> | ||||
|   <EmbeddedResource Include="..\ThingsGateway.Debug.Photino\Locales\zh-CN.json" Link="Locales\zh-CN.json" /> | ||||
|   <EmbeddedResource Include="..\ThingsGateway.Debug.Photino\Locales\zh-TW.json"> | ||||
|     <Link>Locales\zh-TW.json</Link> | ||||
|   </EmbeddedResource> | ||||
|   <EmbeddedResource Include="Locales\*.json"> | ||||
|     <CopyToOutputDirectory>Never</CopyToOutputDirectory> | ||||
|   </EmbeddedResource> | ||||
|  | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <Content Include="..\ThingsGateway.Debug.Photino\MainLayout.razor" Link="MainLayout.razor" /> | ||||
|     <Compile Include="..\ThingsGateway.Debug.Photino\MainLayout.razor.cs" Link="MainLayout.razor.cs" /> | ||||
|     <Content Include="..\ThingsGateway.Debug.Photino\MainLayout.razor.css" Link="MainLayout.razor.css" /> | ||||
|     <Content Include="..\ThingsGateway.Debug.Photino\Configuration\*" LinkBase="Configuration"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </Content> | ||||
|   </ItemGroup> | ||||
|   | ||||
|  | ||||
| </Project> | ||||
| @@ -1,6 +0,0 @@ | ||||
| { | ||||
|   "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件) | ||||
|   "IgnoreConfigurationFiles": [ "" ], | ||||
|   "ExternalAssemblies": [ "" ] | ||||
|  | ||||
| } | ||||
| @@ -1,6 +0,0 @@ | ||||
| { | ||||
|   "ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹(自动合并该文件夹里面所有json文件) | ||||
|   "IgnoreConfigurationFiles": [ "" ], | ||||
|   "ExternalAssemblies": [ "" ] | ||||
|  | ||||
| } | ||||
| Before Width: | Height: | Size: 4.2 KiB | 
| Before Width: | Height: | Size: 4.2 KiB | 
| Before Width: | Height: | Size: 10 KiB | 
| @@ -1,25 +0,0 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> | ||||
|     <title>ThingsGateway.Debug</title> | ||||
|     <base href="/" /> | ||||
|  | ||||
|     <link rel="icon" href="favicon.ico" type="image/x-icon"> | ||||
|     <link href="_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css" rel="stylesheet"> | ||||
|     <link href="_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css" rel="stylesheet"> | ||||
|     <link href="_content/BootstrapBlazor/css/motronic.min.css" rel="stylesheet"> | ||||
|     <link href="ThingsGateway.Debug.Winform.styles.css" rel="stylesheet" /> | ||||
|     <link href="/_content/ThingsGateway.Razor/css/site.css" rel="stylesheet" /> | ||||
|     <script src="/_content/ThingsGateway.Razor/js/theme.js" type="module"></script><!-- 初始主题 --> | ||||
|     <script src="/_content/ThingsGateway.Razor/js/culture.js"></script> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|     <div id="app"></div> | ||||
|  | ||||
|     <script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script> | ||||
|     <script src="_framework/blazor.webview.js" autostart="true"></script> | ||||
| </body> | ||||
| </html> | ||||
| @@ -1,34 +0,0 @@ | ||||
| <Project> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <AdminVersion>7.0.0.63</AdminVersion> | ||||
|     <PluginVersion>9.0.0.13</PluginVersion> | ||||
|     <ProPluginVersion>9.0.0.16</ProPluginVersion> | ||||
|   </PropertyGroup> | ||||
|    | ||||
| 	<PropertyGroup> | ||||
| 		<NoWarn>CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;CS8714;CS8619;CS8629;CS8765;CS8634;CS8621;CS8767;CS8633;CS8620;CS8610;CS8631;CS8605;CS8622;CS8613;NU5100;</NoWarn> | ||||
| 		<TargetFrameworks>net8.0;net6.0;</TargetFrameworks> | ||||
| 		<LangVersion>12.0</LangVersion> | ||||
| 		<ImplicitUsings>enable</ImplicitUsings> | ||||
| 		<Nullable>enable</Nullable> | ||||
| 		<Authors>Diego</Authors> | ||||
| 		<Company>Diego</Company> | ||||
| 		<Product>Diego</Product> | ||||
| 		<Copyright>版权所有 © 2023-present Diego</Copyright> | ||||
| 		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl> | ||||
| 		<RepositoryType>Gitee</RepositoryType> | ||||
| 		<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<None Include="$(SolutionDir)Directory.Build.props" Pack="true" PackagePath="\" /> | ||||
| 	</ItemGroup> | ||||
| 	 | ||||
| 	<PropertyGroup> | ||||
| 		<DebugSymbols>True</DebugSymbols> | ||||
| 		<DebugType>Embedded</DebugType> | ||||
| 		<EmbedAllSources>True</EmbedAllSources> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| </Project> | ||||
| @@ -1,14 +0,0 @@ | ||||
| <Project> | ||||
| 	<PropertyGroup> | ||||
| 		<TargetFrameworks>net462;netstandard2.0;net8.0;net6.0;</TargetFrameworks> | ||||
| 	</PropertyGroup> | ||||
| 	<ItemGroup> | ||||
| 		<Content Remove="Locales\*.json" /> | ||||
| 		<EmbeddedResource Include="Locales\*.json"> | ||||
| 			<CopyToOutputDirectory>Never</CopyToOutputDirectory> | ||||
| 		</EmbeddedResource> | ||||
| 	</ItemGroup> | ||||
| 	<ItemGroup> | ||||
| 		<None Include="$(SolutionDir)Foundation.props" Pack="true" PackagePath="\" /> | ||||
| 	</ItemGroup> | ||||
| </Project> | ||||
| @@ -1,26 +0,0 @@ | ||||
| <Project> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
|  | ||||
| 		<GenerateDocumentationFile>True</GenerateDocumentationFile> | ||||
| 		<GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||||
| 		<PackageOutputPath>$(SolutionDir)..\..\nupkgs</PackageOutputPath> | ||||
| 		<PackageVersion>$(Version)</PackageVersion> | ||||
| 		<PackageTags>ThingsGateway;Diego;Blazor;IOT;设备采集;边缘网关;物联网</PackageTags> | ||||
| 		<PackageProjectUrl>https://gitee.com/diego2098/ThingsGateway</PackageProjectUrl> | ||||
| 		<PackageIcon>icon.png</PackageIcon> | ||||
| 		<PackageReadmeFile>README.zh-CN.md</PackageReadmeFile> | ||||
| 		<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> | ||||
|  | ||||
| 	</PropertyGroup> | ||||
| 	<ItemGroup> | ||||
| 		<None Include="$(SolutionDir)PackNuget.props" Pack="true" PackagePath="\" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 		<None Include="$(SolutionDir)..\README.md" Pack="true" PackagePath="\" /> | ||||
| 		<None Include="$(SolutionDir)..\README.zh-CN.md" Pack="true" PackagePath="\" /> | ||||
| 		<None Include="$(SolutionDir)..\LICENSE" Pack="true" PackagePath="\" /> | ||||
| 		<None Include="$(SolutionDir)..\icon.png" Pack="true" PackagePath="\" /> | ||||
| 	</ItemGroup> | ||||
| </Project> | ||||
| @@ -1,32 +0,0 @@ | ||||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # 17 | ||||
| VisualStudioVersion = 17.9.34622.214 | ||||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{1A92A426-94A8-444D-83EB-5760150384D0}" | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Razor", "ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj", "{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
| 		Release|Any CPU = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
| 		{1A92A426-94A8-444D-83EB-5760150384D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{1A92A426-94A8-444D-83EB-5760150384D0}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{1A92A426-94A8-444D-83EB-5760150384D0}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{1A92A426-94A8-444D-83EB-5760150384D0}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| 		SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282} | ||||
| 		RESX_Rules = {"EnabledRules":[]} | ||||
| 		RESX_NeutralResourcesLanguage = zh-Hans | ||||
| 	EndGlobalSection | ||||
| EndGlobal | ||||
| @@ -1,14 +1,16 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| global using Microsoft.Extensions.DependencyInjection; | ||||
| global using System; | ||||
| 
 | ||||
| global using ThingsGateway.Admin.Application; | ||||
| global using ThingsGateway.Foundation; | ||||
| global using TouchSocket.Core; | ||||
| global using TouchSocket.Sockets; | ||||
| @@ -0,0 +1,147 @@ | ||||
| #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; | ||||
|  | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Modbus协议地址 | ||||
|     /// </summary> | ||||
|     public class ModbusAddress : DeviceAddressBase | ||||
|     { | ||||
|         public ModbusAddress() | ||||
|         { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public ModbusAddress(string address, ushort len) | ||||
|         { | ||||
|             Station = -1; | ||||
|             AddressStart = 0; | ||||
|             Parse(address, len); | ||||
|         } | ||||
|  | ||||
|         public ModbusAddress(string address, byte station) | ||||
|         { | ||||
|             Station = station; | ||||
|             AddressStart = 0; | ||||
|             Parse(address, 0); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 读取功能码 | ||||
|         /// </summary> | ||||
|         public int ReadFunction { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 站号信息 | ||||
|         /// </summary> | ||||
|         public int Station { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 写入功能码 | ||||
|         /// </summary> | ||||
|         public int WriteFunction { get; set; } | ||||
|  | ||||
|         public override void Parse(string address, int length) | ||||
|         { | ||||
|             Length = length; | ||||
|             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) | ||||
|                             Station = byte.Parse(strArray[index].Substring(2)); | ||||
|                     } | ||||
|                     else if (strArray[index].ToUpper().StartsWith("W=")) | ||||
|                     { | ||||
|                         if (Convert.ToInt16(strArray[index].Substring(2)) > 0) | ||||
|                             this.WriteFunction = (int)byte.Parse(strArray[index].Substring(2)); | ||||
|                     } | ||||
|                     else if (!strArray[index].Contains("=")) | ||||
|                     { | ||||
|                         Address(strArray[index]); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             void Address(string address) | ||||
|             { | ||||
|                 var readF = ushort.Parse(address.Substring(0, 1)); | ||||
|                 if (readF > 4) | ||||
|                     throw new("功能码错误"); | ||||
|                 GetFunction(readF); | ||||
|                 AddressStart = int.Parse(address.Substring(1)) - 1; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public override string ToString() | ||||
|         { | ||||
|             StringBuilder stringGeter = new StringBuilder(); | ||||
|             if (Station > 0) | ||||
|             { | ||||
|                 stringGeter.Append("s=" + Station.ToString() + ";"); | ||||
|             } | ||||
|             if (WriteFunction > 0) | ||||
|             { | ||||
|                 stringGeter.Append("w=" + WriteFunction.ToString() + ";"); | ||||
|             } | ||||
|             stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString()); | ||||
|             return stringGeter.ToString(); | ||||
|         } | ||||
|  | ||||
|         private void GetFunction(ushort readF) | ||||
|         { | ||||
|             switch (readF) | ||||
|             { | ||||
|                 case 0: | ||||
|                     ReadFunction = 1; | ||||
|                     break; | ||||
|                 case 1: | ||||
|                     ReadFunction = 2; | ||||
|                     break; | ||||
|                 case 3: | ||||
|                     ReadFunction = 4; | ||||
|                     break; | ||||
|                 case 4: | ||||
|                     ReadFunction = 3; | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         private string GetFunctionString(int readF) | ||||
|         { | ||||
|             switch (readF) | ||||
|             { | ||||
|                 case 1: | ||||
|                     return "0"; | ||||
|                 case 2: | ||||
|                     return "1"; | ||||
|                 case 3: | ||||
|                     return "4"; | ||||
|                 case 4: | ||||
|                     return "3"; | ||||
|             } | ||||
|             return "4"; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,311 @@ | ||||
| #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.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     internal class ModbusHelper | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// modbus地址格式说明 | ||||
|         /// </summary> | ||||
|         /// <returns></returns> | ||||
|         internal static string GetAddressDescription() | ||||
|         { | ||||
|             StringBuilder stringBuilder = new StringBuilder(); | ||||
|             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> | ||||
|         /// 添加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> | ||||
|         /// 通过错误码来获取到对应的文本消息 | ||||
|         /// </summary> | ||||
|         internal static string GetDescriptionByErrorCode(byte code) | ||||
|         { | ||||
|             switch (code) | ||||
|             { | ||||
|                 case 1: | ||||
|                     return "不支持的功能码"; | ||||
|                 case 2: | ||||
|                     return "读取寄存器越界"; | ||||
|                 case 3: | ||||
|                     return "读取长度超限"; | ||||
|                 case 4: | ||||
|                     return "读写异常"; | ||||
|                 default: | ||||
|                     return "未知错误"; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头 | ||||
|         /// </summary> | ||||
|         /// <param name="send">发送数据</param> | ||||
|         /// <param name="response">返回数据</param> | ||||
|         /// <returns></returns> | ||||
|         internal static OperResult<byte[]> GetModbusData(byte[] send, byte[] response) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|  | ||||
|                 if (response[1] >= 0x80)//错误码 | ||||
|                     return new OperResult<byte[]>(GetDescriptionByErrorCode(response[2])); | ||||
|                 if (send.Length == 0) | ||||
|                 { | ||||
|                     var result = OperResult.CreateSuccessResult(GenericHelpers.ArrayRemoveBegin(response, 3)); | ||||
|                     result.Message = "接收数据正确,但主机并没有主动请求数据"; | ||||
|                     result.ResultCode = ResultCode.Canceled; | ||||
|                     return result; | ||||
|                 } | ||||
|                 if (send[0] != response[0]) | ||||
|                     return new OperResult<byte[]>(string.Format("站号不一致", send[0], response[0])); | ||||
|                 if (send[1] != response[1]) | ||||
|                     return new OperResult<byte[]>(response) { Message = "功能码不一致" }; | ||||
|                 if (response.Length > 3) | ||||
|                     return OperResult.CreateSuccessResult(GenericHelpers.ArrayRemoveBegin(response, 3)); | ||||
|                 else | ||||
|                     return new OperResult<byte[]>(response) { Message = "数据长度为0" }; | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex.Message); | ||||
|             } | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 去除Crc,返回modbus数据区 | ||||
|         /// </summary> | ||||
|         /// <param name="send"></param> | ||||
|         /// <param name="response"></param> | ||||
|         /// <param name="crcCheck"></param> | ||||
|         /// <returns></returns> | ||||
|         internal static OperResult<byte[]> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true) | ||||
|         { | ||||
|             if (response.Length < 5) | ||||
|                 return new OperResult<byte[]>("数据长度不足" + response.ToHexString()); | ||||
|             if (crcCheck && !EasyCRC16.CheckCRC16(response)) | ||||
|                 return new OperResult<byte[]>("Crc校验失败" + DataHelper.ByteToHexString(response, ' ')); | ||||
|             return GetModbusData(send, response.RemoveLast(2)); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 获取读取报文 | ||||
|         /// </summary> | ||||
|         internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 ModbusAddress mAddress = new ModbusAddress(address, station); | ||||
|                 return GetReadModbusCommand(mAddress, length); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex.Message); | ||||
|             } | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 获取写入布尔量报文,根据地址识别功能码 | ||||
|         /// </summary> | ||||
|         internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 ModbusAddress mAddress = new ModbusAddress(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.Message); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取写入字报文,根据地址识别功能码 | ||||
|         /// </summary> | ||||
|         internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 ModbusAddress mAddress = new ModbusAddress(address, station); | ||||
|                 //功能码或实际长度 | ||||
|                 if (value?.Length > 2 || mAddress.WriteFunction == 16) | ||||
|                     return GetWriteModbusCommand(mAddress, value); | ||||
|                 else | ||||
|                     return GetWriteOneModbusCommand(mAddress, value); | ||||
|  | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex.Message); | ||||
|             } | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 获取读取报文 | ||||
|         /// </summary> | ||||
|         private static OperResult<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 OperResult.CreateSuccessResult(array); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取05写入布尔量报文 | ||||
|         /// </summary> | ||||
|         private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (address.IndexOf('.') <= 0) | ||||
|                 { | ||||
|                     ModbusAddress mAddress = new ModbusAddress(address, station); | ||||
|                     return GetWriteBoolModbusCommand(mAddress, value); | ||||
|                 } | ||||
|                 return new("不支持写入字寄存器的某一位"); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex.Message); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取05写入布尔量报文 | ||||
|         /// </summary> | ||||
|         private static OperResult<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 OperResult.CreateSuccessResult(array); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取15写入布尔量报文 | ||||
|         /// </summary> | ||||
|         private 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.Message); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取16写入字报文 | ||||
|         /// </summary> | ||||
|         private static OperResult<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 OperResult.CreateSuccessResult(numArray); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取6写入字报文 | ||||
|         /// </summary> | ||||
|         private static OperResult<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 OperResult.CreateSuccessResult(numArray); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,155 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.ComponentModel; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| using ThingsGateway.Foundation.Serial; | ||||
|  | ||||
| using TouchSocket.Resources; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     public class ModbusRtu : ReadWriteDevicesSerialBase | ||||
|     { | ||||
|         public ModbusRtuDataHandleAdapter DataHandleAdapter = new(); | ||||
|         private IWaitingClient<SerialClient> waitingClient; | ||||
|  | ||||
|         public ModbusRtu(SerialClient serialClient) : base(serialClient) | ||||
|         { | ||||
|             ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|             RegisterByteLength = 2; | ||||
|             waitingClient = SerialClient.GetTGWaitingClient(new()); | ||||
|         } | ||||
|  | ||||
|         [Description("组包缓存时间")] | ||||
|         public double CacheTimeout { get; set; } = 1; | ||||
|  | ||||
|         [Description("Crc校验")] | ||||
|         public bool Crc16CheckEnable { get; set; } | ||||
|  | ||||
|         [Description("帧前时间")] | ||||
|         public int FrameTime { get; set; } | ||||
|  | ||||
|         [Description("站号")] | ||||
|         public byte Station { get; set; } = 1; | ||||
|  | ||||
|         public override string GetAddressDescription() | ||||
|         { | ||||
|             return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|         } | ||||
|         public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<byte[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         public override void SetDataAdapter() | ||||
|         { | ||||
|             DataHandleAdapter = new(); | ||||
|             DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable; | ||||
|             DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout); | ||||
|             SerialClient.SetDataHandlingAdapter(DataHandleAdapter); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var item = commandResult.Content; | ||||
|                 await SerialClient.EasyLock.LockAsync(); | ||||
|                 await Task.Delay(FrameTime, token); | ||||
|  | ||||
|                 var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token); | ||||
|                 return result; | ||||
|  | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 SerialClient.EasyLock.UnLock(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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 | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     public class ModbusRtuDataHandleAdapter : ReadWriteDevicesSerialDataHandleAdapter<ModbusRtuMessage> | ||||
|     { | ||||
|         public bool Crc16CheckEnable { get; set; } = true; | ||||
|  | ||||
|         public override byte[] PackCommand(byte[] command) | ||||
|         { | ||||
|             return ModbusHelper.AddCrc(command); | ||||
|         } | ||||
|  | ||||
|         protected override ModbusRtuMessage GetInstance() | ||||
|         { | ||||
|             return new ModbusRtuMessage(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 解包获取实际数据包 | ||||
|         /// </summary> | ||||
|         protected override FilterResult GetResponse(ByteBlock byteBlock, ModbusRtuMessage request, byte[] allBytes, byte[] bytes) | ||||
|         { | ||||
|             var unpackbytes = UnpackResponse(request.SendBytes, allBytes); | ||||
|             request.Message = unpackbytes.Message; | ||||
|             request.ResultCode = unpackbytes.ResultCode; | ||||
|             if (unpackbytes.IsSuccess) | ||||
|             { | ||||
|                 request.Content = unpackbytes.Content; | ||||
|                 request.ReceivedBytes = allBytes; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 byteBlock.Pos = byteBlock.Len; | ||||
|                 request.ReceivedBytes = allBytes; | ||||
|                 request.Message = unpackbytes.Message; | ||||
|                 if (allBytes.Length <= 1) | ||||
|                 { | ||||
|                     return FilterResult.Cache; | ||||
|                 } | ||||
|                 if (!(allBytes[1] <= 0x10)) | ||||
|                 { | ||||
|                     return FilterResult.Success; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if ((allBytes.Length > allBytes[2] + 4)) | ||||
|                     { | ||||
|                         return FilterResult.Success; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         return FilterResult.Cache; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response) | ||||
|         { | ||||
|             return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,30 @@ | ||||
| #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 | ||||
| { | ||||
|     public class ModbusRtuMessage : MessageBase, IMessage | ||||
|     { | ||||
|         public override int HeadBytesLength => -1; | ||||
|  | ||||
|         public override bool CheckHeadBytes(byte[] head) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         protected override void SendBytesThen() | ||||
|         { | ||||
|             BodyLength = -1; | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,146 @@ | ||||
| #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.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| using TouchSocket.Resources; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase | ||||
|     { | ||||
|         public ModbusRtuOverTcpDataHandleAdapter DataHandleAdapter = new(); | ||||
|  | ||||
|         private IWaitingClient<TGTcpClient> waitingClient; | ||||
|  | ||||
|         public ModbusRtuOverTcp(TGTcpClient tcpClient) : base(tcpClient) | ||||
|         { | ||||
|             ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|             RegisterByteLength = 2; | ||||
|             waitingClient = TGTcpClient.GetTGWaitingClient(new()); | ||||
|         } | ||||
|         public double CacheTimeout { get; set; } = 1; | ||||
|         public bool Crc16CheckEnable { get; set; } | ||||
|  | ||||
|         public int FrameTime { get; set; } | ||||
|         public byte Station { get; set; } = 1; | ||||
|         public override string GetAddressDescription() | ||||
|         { | ||||
|             return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<byte[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         public override void SetDataAdapter() | ||||
|         { | ||||
|             DataHandleAdapter = new(); | ||||
|             DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable; | ||||
|             DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout); | ||||
|             TGTcpClient.SetDataHandlingAdapter(DataHandleAdapter); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var item = commandResult.Content; | ||||
|                 await TGTcpClient.EasyLock.LockAsync(); | ||||
|                 await Task.Delay(FrameTime, token); | ||||
|  | ||||
|                 var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token); | ||||
|                 return result; | ||||
|  | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 TGTcpClient.EasyLock.UnLock(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,76 @@ | ||||
| #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 | ||||
| { | ||||
|     public class ModbusRtuOverTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusRtuMessage> | ||||
|     { | ||||
|         public bool Crc16CheckEnable { get; set; } = true; | ||||
|  | ||||
|         public override byte[] PackCommand(byte[] command) | ||||
|         { | ||||
|             return ModbusHelper.AddCrc(command); | ||||
|         } | ||||
|  | ||||
|         protected override ModbusRtuMessage GetInstance() | ||||
|         { | ||||
|             return new ModbusRtuMessage(); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 解包获取实际数据包 | ||||
|         /// </summary> | ||||
|         protected override FilterResult GetResponse(ByteBlock byteBlock, ModbusRtuMessage request, byte[] allBytes, byte[] bytes) | ||||
|         { | ||||
|             var unpackbytes = UnpackResponse(request.SendBytes, allBytes); | ||||
|             request.Message = unpackbytes.Message; | ||||
|             request.ResultCode = unpackbytes.ResultCode; | ||||
|             if (unpackbytes.IsSuccess) | ||||
|             { | ||||
|                 request.Content = unpackbytes.Content; | ||||
|                 request.ReceivedBytes = allBytes; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 byteBlock.Pos = byteBlock.Len; | ||||
|                 request.ReceivedBytes = allBytes; | ||||
|                 request.Message = unpackbytes.Message; | ||||
|                 if (allBytes.Length <= 4) | ||||
|                 { | ||||
|                     return FilterResult.Cache; | ||||
|                 } | ||||
|                 if (!(allBytes[1] <= 0x10)) | ||||
|                 { | ||||
|                     return FilterResult.Success; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if ((allBytes.Length > allBytes[2] + 4)) | ||||
|                     { | ||||
|                         return FilterResult.Success; | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         return FilterResult.Cache; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         protected override OperResult<byte[]> UnpackResponse( | ||||
|                           byte[] send, byte[] response) | ||||
|         { | ||||
|             return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,145 @@ | ||||
| #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.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| using TouchSocket.Resources; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     public class ModbusRtuOverUdp : ReadWriteDevicesUdpBase | ||||
|     { | ||||
|         public ModbusRtuOverUdpDataHandleAdapter DataHandleAdapter = new(); | ||||
|         private IWaitingClient<TGUdpSession> waitingClient; | ||||
|  | ||||
|         public ModbusRtuOverUdp(TGUdpSession udpSession) : base(udpSession) | ||||
|         { | ||||
|             ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|             RegisterByteLength = 2; | ||||
|             waitingClient = TGUdpSession.GetTGWaitingClient(new()); | ||||
|         } | ||||
|  | ||||
|         public bool Crc16CheckEnable { get => DataHandleAdapter.Crc16CheckEnable; set => DataHandleAdapter.Crc16CheckEnable = value; } | ||||
|  | ||||
|         public int FrameTime { get; set; } | ||||
|  | ||||
|         public byte Station { get; set; } = 1; | ||||
|  | ||||
|         public override string GetAddressDescription() | ||||
|         { | ||||
|             return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|         } | ||||
|         public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<byte[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         public override void SetDataAdapter() | ||||
|         { | ||||
|             DataHandleAdapter = new(); | ||||
|             DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable; | ||||
|             TGUdpSession.SetDataHandlingAdapter(DataHandleAdapter); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|         } | ||||
|  | ||||
|         private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var item = commandResult.Content; | ||||
|                 await TGUdpSession.EasyLock.LockAsync(); | ||||
|                 await Task.Delay(FrameTime, token); | ||||
|  | ||||
|                 var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token); | ||||
|                 return result; | ||||
|  | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 TGUdpSession.EasyLock.UnLock(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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 | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusRtuMessage> | ||||
|     { | ||||
|         public bool Crc16CheckEnable { get; set; } = true; | ||||
|  | ||||
|         public override byte[] PackCommand(byte[] command) | ||||
|         { | ||||
|             return ModbusHelper.AddCrc(command); | ||||
|         } | ||||
|  | ||||
|         protected override OperResult<byte[]> UnpackResponse( | ||||
|                   byte[] send, byte[] response) | ||||
|         { | ||||
|             return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); | ||||
|         } | ||||
|  | ||||
|         protected override ModbusRtuMessage GetInstance() | ||||
|         { | ||||
|             return new ModbusRtuMessage(); | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,402 @@ | ||||
| #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; | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     public class ModbusServer : ReadWriteDevicesTcpServerBase | ||||
|     { | ||||
|         public ModbusServerDataHandleAdapter DataHandleAdapter = new(); | ||||
|         /// <summary> | ||||
|         /// 继电器 | ||||
|         /// </summary> | ||||
|         public Dictionary<int, ByteBlock> ModbusServer01ByteBlocks = new(); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 开关输入 | ||||
|         /// </summary> | ||||
|         public Dictionary<int, ByteBlock> ModbusServer02ByteBlocks = new(); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 输入寄存器 | ||||
|         /// </summary> | ||||
|         public Dictionary<int, ByteBlock> ModbusServer03ByteBlocks = new(); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 保持寄存器 | ||||
|         /// </summary> | ||||
|         public Dictionary<int, ByteBlock> ModbusServer04ByteBlocks = new(); | ||||
|  | ||||
|         public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<OperResult>> Write; | ||||
|  | ||||
|         public ModbusServer(TcpService tcpService) : base(tcpService) | ||||
|         { | ||||
|             ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|             RegisterByteLength = 2; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 多站点 | ||||
|         /// </summary> | ||||
|         public bool MulStation { get; set; } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 默认站点 | ||||
|         /// </summary> | ||||
|         public byte Station { get; set; } = 1; | ||||
|  | ||||
|         public override string GetAddressDescription() | ||||
|         { | ||||
|             return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|         } | ||||
|         public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default) | ||||
|         { | ||||
|             ModbusAddress mAddress = null; | ||||
|             try | ||||
|             { | ||||
|                 mAddress = new ModbusAddress(address, Station); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return Task.FromResult(new OperResult<byte[]>(ex)); | ||||
|             } | ||||
|             if (MulStation) | ||||
|             { | ||||
|                 ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)); | ||||
|                 ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)); | ||||
|                 ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)); | ||||
|                 ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)); | ||||
|                 ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|                 ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|                 ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|                 ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (Station != mAddress.Station) | ||||
|                 { | ||||
|                     return Task.FromResult(new OperResult<byte[]>("地址错误")); | ||||
|                 } | ||||
|                 ModbusServer01ByteBlocks.TryAdd(Station, new(1024 * 128)); | ||||
|                 ModbusServer02ByteBlocks.TryAdd(Station, new(1024 * 128)); | ||||
|                 ModbusServer03ByteBlocks.TryAdd(Station, new(1024 * 128)); | ||||
|                 ModbusServer04ByteBlocks.TryAdd(Station, new(1024 * 128)); | ||||
|                 ModbusServer01ByteBlocks[Station].SetLength(1024 * 128); | ||||
|                 ModbusServer02ByteBlocks[Station].SetLength(1024 * 128); | ||||
|                 ModbusServer03ByteBlocks[Station].SetLength(1024 * 128); | ||||
|                 ModbusServer04ByteBlocks[Station].SetLength(1024 * 128); | ||||
|             } | ||||
|  | ||||
|             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 Task.FromResult(OperResult.CreateSuccessResult(bytes0)); | ||||
|                 case 2: | ||||
|                     byte[] bytes1 = new byte[len]; | ||||
|                     ModbusServer02ByteBlock.Pos = mAddress.AddressStart; | ||||
|                     ModbusServer02ByteBlock.Read(bytes1); | ||||
|                     return Task.FromResult(OperResult.CreateSuccessResult(bytes1)); | ||||
|                 case 3: | ||||
|  | ||||
|                     byte[] bytes3 = new byte[len]; | ||||
|                     ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||
|                     ModbusServer03ByteBlock.Read(bytes3); | ||||
|                     return Task.FromResult(OperResult.CreateSuccessResult(bytes3)); | ||||
|                 case 4: | ||||
|                     byte[] bytes4 = new byte[len]; | ||||
|                     ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||
|                     ModbusServer04ByteBlock.Read(bytes4); | ||||
|                     return Task.FromResult(OperResult.CreateSuccessResult(bytes4)); | ||||
|             } | ||||
|             return Task.FromResult(new OperResult<byte[]>("功能码错误")); | ||||
|         } | ||||
|  | ||||
|         public override void SetDataAdapter(SocketClient client) | ||||
|         { | ||||
|             DataHandleAdapter = new(); | ||||
|             client.SetDataHandlingAdapter(DataHandleAdapter); | ||||
|         } | ||||
|         public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) | ||||
|         { | ||||
|             ModbusAddress mAddress = null; | ||||
|             try | ||||
|             { | ||||
|                 mAddress = new ModbusAddress(address, Station); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return Task.FromResult(new OperResult(ex)); | ||||
|             } | ||||
|             if (MulStation) | ||||
|             { | ||||
|                 ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)); | ||||
|                 ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)); | ||||
|                 ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|                 ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (Station != mAddress.Station) | ||||
|                 { | ||||
|                     return Task.FromResult(new OperResult("地址错误")); | ||||
|                 } | ||||
|                 ModbusServer03ByteBlocks.TryAdd(Station, new(1024 * 128)); | ||||
|                 ModbusServer04ByteBlocks.TryAdd(Station, new(1024 * 128)); | ||||
|                 ModbusServer03ByteBlocks[Station].SetLength(1024 * 128); | ||||
|                 ModbusServer04ByteBlocks[Station].SetLength(1024 * 128); | ||||
|             } | ||||
|  | ||||
|  | ||||
|             var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station]; | ||||
|             var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station]; | ||||
|             switch (mAddress.ReadFunction) | ||||
|             { | ||||
|                 case 3: | ||||
|                     ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||
|                     ModbusServer03ByteBlock.Write(value); | ||||
|                     return Task.FromResult(OperResult.CreateSuccessResult()); | ||||
|                 case 4: | ||||
|                     ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength; | ||||
|                     ModbusServer04ByteBlock.Write(value); | ||||
|                     return Task.FromResult(OperResult.CreateSuccessResult()); | ||||
|             } | ||||
|             return Task.FromResult(new OperResult("功能码错误")); | ||||
|         } | ||||
|  | ||||
|         public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) | ||||
|         { | ||||
|             ModbusAddress mAddress = null; | ||||
|             try | ||||
|             { | ||||
|                 mAddress = new ModbusAddress(address, Station); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return Task.FromResult(new OperResult(ex)); | ||||
|             } | ||||
|             if (MulStation) | ||||
|             { | ||||
|                 ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)); | ||||
|                 ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)); | ||||
|                 ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|                 ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (Station != mAddress.Station) | ||||
|                 { | ||||
|                     return Task.FromResult(new OperResult("地址错误")); | ||||
|                 } | ||||
|                 ModbusServer01ByteBlocks.TryAdd(Station, new(1024 * 128)); | ||||
|                 ModbusServer02ByteBlocks.TryAdd(Station, new(1024 * 128)); | ||||
|                 ModbusServer01ByteBlocks[Station].SetLength(1024 * 128); | ||||
|                 ModbusServer02ByteBlocks[Station].SetLength(1024 * 128); | ||||
|             } | ||||
|  | ||||
|             var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station]; | ||||
|             var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station]; | ||||
|             switch (mAddress.ReadFunction) | ||||
|             { | ||||
|                 case 1: | ||||
|                     ModbusServer01ByteBlock.Pos = mAddress.AddressStart; | ||||
|                     ModbusServer01ByteBlock.Write(value.BoolArrayToByte()); | ||||
|                     return Task.FromResult(OperResult.CreateSuccessResult()); | ||||
|                 case 2: | ||||
|                     ModbusServer02ByteBlock.Pos = mAddress.AddressStart; | ||||
|                     ModbusServer02ByteBlock.Write(value.BoolArrayToByte()); | ||||
|                     return Task.FromResult(OperResult.CreateSuccessResult()); | ||||
|             } | ||||
|             return Task.FromResult(new OperResult("功能码错误")); | ||||
|         } | ||||
|  | ||||
|         protected override void Dispose(bool disposing) | ||||
|         { | ||||
|             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(); | ||||
|             Disconnect(); | ||||
|             base.Dispose(disposing); | ||||
|         } | ||||
|  | ||||
|         protected override async Task ReceivedAsync(SocketClient client, IRequestInfo requestInfo) | ||||
|         { | ||||
|  | ||||
|             if (requestInfo is ModbusServerMessage modbusServerMessage) | ||||
|             { | ||||
|                 if (!modbusServerMessage.IsSuccess) | ||||
|                 { | ||||
|                     return; | ||||
|                 } | ||||
|                 if (modbusServerMessage.CurModbusAddress == null) | ||||
|                 { | ||||
|                     WriteError(client, modbusServerMessage); | ||||
|                 } | ||||
|                 if (modbusServerMessage.CurModbusAddress.WriteFunction == 0) | ||||
|                 { | ||||
|                     var data = await ReadAsync(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.CurModbusAddress.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((double)modbusServerMessage.CurModbusAddress.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 | ||||
|                     { | ||||
|  | ||||
|                         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); | ||||
|  | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     var coreData = modbusServerMessage.Content; | ||||
|                     if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2) | ||||
|                     { | ||||
|                         //写入继电器 | ||||
|                         if (Write != null) | ||||
|                         { | ||||
|  | ||||
|                             if ((await Write(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) | ||||
|                             { | ||||
|                                 var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.CurModbusAddress.Length)); | ||||
|                                 if (result.IsSuccess) | ||||
|                                 { | ||||
|                                     WriteSuccess03(client, modbusServerMessage); | ||||
|  | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     WriteError(client, modbusServerMessage); | ||||
|  | ||||
|                                 } | ||||
|  | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.CurModbusAddress.Length)); | ||||
|                             if (result.IsSuccess) | ||||
|                             { | ||||
|                                 WriteSuccess03(client, modbusServerMessage); | ||||
|  | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
|  | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         //写入寄存器 | ||||
|                         if (Write != null) | ||||
|                         { | ||||
|  | ||||
|                             if ((await Write(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess) | ||||
|                             { | ||||
|                                 var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||
|                                 if (result.IsSuccess) | ||||
|                                 { | ||||
|                                     WriteSuccess03(client, modbusServerMessage); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     WriteError(client, modbusServerMessage); | ||||
|                                 } | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
|                             } | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData); | ||||
|                             if (result.IsSuccess) | ||||
|                             { | ||||
|                                 WriteSuccess03(client, modbusServerMessage); | ||||
|  | ||||
|                             } | ||||
|                             else | ||||
|                             { | ||||
|                                 WriteError(client, modbusServerMessage); | ||||
|  | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|  | ||||
|             static void WriteError(SocketClient client, ModbusServerMessage 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, ModbusServerMessage modbusServerMessage) | ||||
|         { | ||||
|             var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12); | ||||
|             sendData[5] = (byte)(sendData.Length - 6); | ||||
|             client.Send(sendData); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,144 @@ | ||||
| #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 | ||||
| { | ||||
|     public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusServerMessage> | ||||
|     { | ||||
|  | ||||
|         public override byte[] PackCommand(byte[] command) | ||||
|         { | ||||
|             return command; | ||||
|         } | ||||
|  | ||||
|         protected override ModbusServerMessage GetInstance() | ||||
|         { | ||||
|             return new ModbusServerMessage(); | ||||
|         } | ||||
|  | ||||
|         protected override OperResult<byte[]> UnpackResponse( | ||||
|                           byte[] send, | ||||
|           byte[] response) | ||||
|         { | ||||
|             return GetModbusData(response.RemoveBegin(6)); | ||||
|         } | ||||
|         public ThingsGatewayBitConverter ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|         protected override FilterResult GetResponse(ByteBlock byteBlock, ModbusServerMessage request, byte[] allBytes, byte[] bytes) | ||||
|         { | ||||
|             var unpackbytes = UnpackResponse(request.SendBytes, allBytes); | ||||
|             request.Message = unpackbytes.Message; | ||||
|             request.ResultCode = unpackbytes.ResultCode; | ||||
|             if (unpackbytes.IsSuccess) | ||||
|             { | ||||
|                 request.ReceivedBytes = allBytes; | ||||
|                 //解析01 03 00 00 00 0A | ||||
|                 var station = ThingsGatewayBitConverter.ToByte(bytes, 6); | ||||
|                 var function = ThingsGatewayBitConverter.ToByte(bytes, 7); | ||||
|                 int addressStart = ThingsGatewayBitConverter.ToInt16(bytes, 8); | ||||
|                 if (addressStart == -1) | ||||
|                 { | ||||
|                     addressStart = 65535; | ||||
|                 } | ||||
|                 if (function > 4) | ||||
|                 { | ||||
|                     if (function > 6) | ||||
|                     { | ||||
|                         request.CurModbusAddress = new ModbusAddress() | ||||
|                         { | ||||
|                             Station = station, | ||||
|                             AddressStart = addressStart, | ||||
|                             WriteFunction = function, | ||||
|                             ReadFunction = function == 16 ? 3 : function == 15 ? 1 : 3, | ||||
|                             Length = ThingsGatewayBitConverter.ToByte(bytes, 11), | ||||
|                         }; | ||||
|                         request.Content = unpackbytes.Content.RemoveBegin(7); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         request.CurModbusAddress = new ModbusAddress() | ||||
|                         { | ||||
|                             Station = station, | ||||
|                             AddressStart = addressStart, | ||||
|                             WriteFunction = function, | ||||
|                             ReadFunction = function == 6 ? 3 : function == 5 ? 1 : 3, | ||||
|                             Length = 1, | ||||
|                         }; | ||||
|                         request.Content = unpackbytes.Content.RemoveBegin(4); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     request.CurModbusAddress = new ModbusAddress() | ||||
|                     { | ||||
|                         Station = station, | ||||
|                         AddressStart = addressStart, | ||||
|                         ReadFunction = function, | ||||
|                         Length = ThingsGatewayBitConverter.ToByte(bytes, 11), | ||||
|                     }; | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 byteBlock.Pos = byteBlock.Len; | ||||
|                 request.ReceivedBytes = allBytes; | ||||
|                 return FilterResult.Success; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取modbus写入数据区内容 | ||||
|         /// </summary> | ||||
|         /// <param name="send">发送数据</param> | ||||
|         /// <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[]>(response) { Message = $"数据长度{response.Length}错误" }; | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex.Message); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| #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 | ||||
| { | ||||
|     public class ModbusServerMessage : MessageBase, IMessage | ||||
|     { | ||||
|         public override int HeadBytesLength => 6; | ||||
|         public override bool CheckHeadBytes(byte[] head) | ||||
|         { | ||||
|             if (head == null || head.Length != 6) return false; | ||||
|             HeadBytes = head; | ||||
|  | ||||
|             int num = (HeadBytes[4] * 256) + HeadBytes[5]; | ||||
|             BodyLength = num; | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         public ModbusAddress CurModbusAddress { get; set; } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,156 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using System.ComponentModel; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| using TouchSocket.Resources; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     public class ModbusTcp : ReadWriteDevicesTcpClientBase | ||||
|     { | ||||
|         public ModbusTcpDataHandleAdapter DataHandleAdapter = new(); | ||||
|         private IWaitingClient<TGTcpClient> waitingClient; | ||||
|  | ||||
|         public ModbusTcp(TGTcpClient tcpClient) : base(tcpClient) | ||||
|         { | ||||
|             ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|             RegisterByteLength = 2; | ||||
|             waitingClient = TGTcpClient.GetTGWaitingClient(new()); | ||||
|         } | ||||
|  | ||||
|         [Description("组包缓存时间")] | ||||
|         public double CacheTimeout { get; set; } = 1; | ||||
|  | ||||
|         [Description("帧前时间")] | ||||
|         public int FrameTime { get; set; } | ||||
|  | ||||
|         [Description("检测事务标识符")] | ||||
|         public bool IsCheckMessageId { get; set; } | ||||
|  | ||||
|         [Description("站号")] | ||||
|         public byte Station { get; set; } = 1; | ||||
|  | ||||
|         public override string GetAddressDescription() | ||||
|         { | ||||
|             return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|         } | ||||
|         public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<byte[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public override void SetDataAdapter() | ||||
|         { | ||||
|             DataHandleAdapter = new(); | ||||
|             DataHandleAdapter.IsCheckMessageId = IsCheckMessageId; | ||||
|             DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout); | ||||
|             TGTcpClient.SetDataHandlingAdapter(DataHandleAdapter); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var item = commandResult.Content; | ||||
|                 await TGTcpClient.EasyLock.LockAsync(); | ||||
|                 await Task.Delay(FrameTime, token); | ||||
|  | ||||
|                 var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token); | ||||
|                 return result; | ||||
|  | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 TGTcpClient.EasyLock.UnLock(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,54 @@ | ||||
| #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 | ||||
| { | ||||
|     public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage> | ||||
|     { | ||||
|         private readonly EasyIncrementCount easyIncrementCount = new EasyIncrementCount(ushort.MaxValue); | ||||
|  | ||||
|         public bool IsCheckMessageId | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return Request?.IsCheckMessageId ?? false; | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 Request.IsCheckMessageId = value; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public EasyIncrementCount MessageId => easyIncrementCount; | ||||
|         public override byte[] PackCommand(byte[] command) | ||||
|         { | ||||
|             return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue()); | ||||
|         } | ||||
|  | ||||
|         protected override ModbusTcpMessage GetInstance() | ||||
|         { | ||||
|             return new ModbusTcpMessage(); | ||||
|         } | ||||
|  | ||||
|         protected override OperResult<byte[]> UnpackResponse( | ||||
|                           byte[] send, | ||||
|           byte[] response) | ||||
|         { | ||||
|             return ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| #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 | ||||
| { | ||||
|     public class ModbusTcpMessage : MessageBase, IMessage | ||||
|     { | ||||
|         public override int HeadBytesLength => 6; | ||||
|  | ||||
|         public bool IsCheckMessageId { get; set; } = false; | ||||
|         public override bool CheckHeadBytes(byte[] head) | ||||
|         { | ||||
|             if (head == null || head.Length <= 0) return false; | ||||
|             HeadBytes = head; | ||||
|  | ||||
|             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,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 System.Threading; | ||||
| using System.Threading.Tasks; | ||||
|  | ||||
| using TouchSocket.Resources; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.Modbus | ||||
| { | ||||
|     public class ModbusUdp : ReadWriteDevicesUdpBase | ||||
|     { | ||||
|         public ModbusUdpDataHandleAdapter DataHandleAdapter = new(); | ||||
|         private IWaitingClient<TGUdpSession> waitingClient; | ||||
|  | ||||
|         public ModbusUdp(TGUdpSession udpSession) : base(udpSession) | ||||
|         { | ||||
|             ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big); | ||||
|             RegisterByteLength = 2; | ||||
|             waitingClient = TGUdpSession.GetTGWaitingClient(new()); | ||||
|         } | ||||
|  | ||||
|         public int FrameTime { get; set; } | ||||
|  | ||||
|         public bool IsCheckMessageId { get => DataHandleAdapter.IsCheckMessageId; set => DataHandleAdapter.IsCheckMessageId = value; } | ||||
|  | ||||
|         public byte Station { get; set; } = 1; | ||||
|  | ||||
|         public override string GetAddressDescription() | ||||
|         { | ||||
|             return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription(); | ||||
|         } | ||||
|         public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<byte[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<byte[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public override void SetDataAdapter() | ||||
|         { | ||||
|             DataHandleAdapter = new(); | ||||
|             DataHandleAdapter.IsCheckMessageId = IsCheckMessageId; | ||||
|             TGUdpSession.SetDataHandlingAdapter(DataHandleAdapter); | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await ConnectAsync(token); | ||||
|                 var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station); | ||||
|                 if (commandResult.IsSuccess) | ||||
|                 { | ||||
|                     ResponsedData result = await SendThenReturnAsync(commandResult, token); | ||||
|                     if (result.RequestInfo is MessageBase collectMessage) | ||||
|                     { | ||||
|                         return collectMessage; | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return OperResult.CreateFailedResult<bool[]>(commandResult); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<bool[]>(ex); | ||||
|             } | ||||
|             return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription()); | ||||
|  | ||||
|         } | ||||
|  | ||||
|         private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var item = commandResult.Content; | ||||
|                 await TGUdpSession.EasyLock.LockAsync(); | ||||
|                 await Task.Delay(FrameTime, token); | ||||
|  | ||||
|                 var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token); | ||||
|                 return result; | ||||
|  | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 TGUdpSession.EasyLock.UnLock(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,51 @@ | ||||
| #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 | ||||
| { | ||||
|     public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusTcpMessage> | ||||
|     { | ||||
|         private readonly EasyIncrementCount easyIncrementCount = new EasyIncrementCount(ushort.MaxValue); | ||||
|  | ||||
|         public bool IsCheckMessageId | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return Request?.IsCheckMessageId ?? false; | ||||
|             } | ||||
|             set | ||||
|             { | ||||
|                 Request.IsCheckMessageId = value; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public EasyIncrementCount MessageId => easyIncrementCount; | ||||
|         public override byte[] PackCommand(byte[] command) | ||||
|         { | ||||
|             return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue()); | ||||
|         } | ||||
|  | ||||
|         protected override ModbusTcpMessage GetInstance() | ||||
|         { | ||||
|             return new ModbusTcpMessage(); | ||||
|         } | ||||
|  | ||||
|         protected override OperResult<byte[]> UnpackResponse( | ||||
|                           byte[] send, | ||||
|           byte[] response) | ||||
|         { | ||||
|             return ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,33 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<LangVersion>latestMajor</LangVersion> | ||||
| 		<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>--> | ||||
| 		<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks> | ||||
| 		<Version>1.7.0</Version> | ||||
| 		<Title>ThingsGateway.Foundation.Adapter.Modbus</Title> | ||||
| 		<GeneratePackageOnBuild>True</GeneratePackageOnBuild> | ||||
| 		<Authors>Diego</Authors> | ||||
| 		<Description>Modbus通讯类库,支持MdobusTcp;ModbusRtu;ModbusRtuOverTcp;ModbusTcpServer</Description> | ||||
| 		<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> | ||||
| 		<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance> | ||||
| 		<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild> | ||||
| 		<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl> | ||||
| 		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl> | ||||
| 		<PackageOutputPath>$(SolutionDir)</PackageOutputPath> | ||||
| 		<PackageReadmeFile>README.md</PackageReadmeFile> | ||||
| 		<Platforms>AnyCPU;x86</Platforms> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <None Include="..\..\..\README.md"> | ||||
| 	    <Pack>True</Pack> | ||||
| 	    <PackagePath>\</PackagePath> | ||||
| 	  </None> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| </Project> | ||||
| @@ -0,0 +1,367 @@ | ||||
| #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; | ||||
| using System.Runtime.InteropServices; | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn; | ||||
|  | ||||
| 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(); | ||||
|         coserverInfo.pwszName = hostName; | ||||
|         coserverInfo.pAuthInfo = IntPtr.Zero; | ||||
|         coserverInfo.dwReserved1 = 0; | ||||
|         coserverInfo.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); | ||||
|  | ||||
|         int result = 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,30 @@ | ||||
| #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; | ||||
|  | ||||
| public delegate void OnDataChangedHandler(ItemReadResult[] opcItems); | ||||
| public delegate void OnReadCompletedHandler(ItemReadResult[] opcItems); | ||||
|  | ||||
| public delegate void OnWriteCompletedHandler(ItemWriteResult[] opcItems); | ||||
| public class ItemReadResult | ||||
| { | ||||
|     public string Name { get; set; } = ""; | ||||
|     public short Quality { get; set; } | ||||
|     public DateTime TimeStamp { get; set; } | ||||
|     public object Value { get; set; } = 0; | ||||
| } | ||||
| public class ItemWriteResult | ||||
| { | ||||
|     public int Exception { get; set; } = 0; | ||||
|     public string Name { get; set; } = ""; | ||||
| } | ||||
| @@ -0,0 +1,489 @@ | ||||
| #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; | ||||
| using System.Linq; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Text; | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||
|  | ||||
| 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 List<ItemReadResult>(); | ||||
|         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.ToArray()); | ||||
|     } | ||||
|  | ||||
|     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 List<ItemReadResult>(); | ||||
|         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.ToArray()); | ||||
|     } | ||||
|  | ||||
|     public void OnWriteComplete(int dwTransid, | ||||
|                                 int hGroup, | ||||
|                                 int hrMastererr, | ||||
|                                 int dwCount, | ||||
|                                 int[] pClienthandles, | ||||
|                                 int[] pErrors) | ||||
|     { | ||||
|         List<ItemWriteResult> itemwrite = new List<ItemWriteResult>(); | ||||
|         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.ToArray()); | ||||
|     } | ||||
|  | ||||
|     internal OperResult 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); | ||||
|             StringBuilder stringBuilder = 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 | ||||
|                 { | ||||
|                     stringBuilder.AppendLine(items[j].ItemID + "添加失败,错误代码:" + errors[j]); | ||||
|                 } | ||||
|             } | ||||
|             if (stringBuilder.Length > 0) | ||||
|             { | ||||
|                 return new OperResult(stringBuilder.ToString()); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|         } | ||||
|         catch (COMException ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|         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 OperResult 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; | ||||
|                 } | ||||
|                 int cancelId = 0; | ||||
|                 m_Async2IO.Read(OpcItems.Count, serverHandle, 2, out cancelId, out pErrors); | ||||
|                 Marshal.Copy(pErrors, PErrors, 0, OpcItems.Count); | ||||
|                 if (PErrors.Any(a => a > 0)) | ||||
|                 { | ||||
|                     return new OperResult("读取错误,错误代码为" + pErrors); | ||||
|                 } | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|             else | ||||
|                 return new OperResult("连接无效"); | ||||
|         } | ||||
|         catch (COMException ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             if (pErrors != IntPtr.Zero) | ||||
|             { | ||||
|                 Marshal.FreeCoTaskMem(pErrors); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal OperResult RemoveItem(OpcItem[] items) | ||||
|     { | ||||
|         IntPtr pErrors = IntPtr.Zero; | ||||
|         bool[] result = new bool[items.Length]; | ||||
|         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); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         StringBuilder stringBuilder = new(); | ||||
|         for (int i = 0; i < errors.Length; i++) | ||||
|         { | ||||
|             if (errors[i] != 0) | ||||
|             { | ||||
|                 stringBuilder.AppendLine(items[i].ItemID + "移除失败,错误代码:" + errors[i]); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 OpcItems.Remove(items[i]); | ||||
|             } | ||||
|         } | ||||
|         if (stringBuilder.Length > 0) | ||||
|         { | ||||
|             return new OperResult(stringBuilder.ToString()); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         } | ||||
|     } | ||||
|     internal OperResult WriteAsync(object[] values, int[] serverHandle, out int[] errors) | ||||
|     { | ||||
|         IntPtr pErrors = IntPtr.Zero; | ||||
|         errors = new int[values.Length]; | ||||
|         if (m_Async2IO != null) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 int cancelId, transactionID = 0; | ||||
|                 m_Async2IO.Write(values.Length, serverHandle, values, transactionID, out cancelId, out pErrors); | ||||
|                 Marshal.Copy(pErrors, errors, 0, values.Length); | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|             catch (COMException ex) | ||||
|             { | ||||
|                 return new OperResult(ex); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 if (pErrors != IntPtr.Zero) | ||||
|                 { | ||||
|                     Marshal.FreeCoTaskMem(pErrors); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|             return new OperResult("连接无效"); | ||||
|     } | ||||
|     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 OperResult 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 | ||||
|         { | ||||
|             int nRevUpdateRate = 0; | ||||
|             m_StateManagement?.SetState(pRequestedUpdateRate, | ||||
|                                         out nRevUpdateRate, | ||||
|                                         hActive.AddrOfPinnedObject(), | ||||
|                                         pTimeBias, | ||||
|                                         pDeadband, | ||||
|                                         pLCID, | ||||
|                                         hClientGroup); | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         } | ||||
|         catch (COMException ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|         } | ||||
|         finally | ||||
|         { | ||||
|             hActive.Free(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,49 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||
|  | ||||
| public class OpcItem | ||||
| { | ||||
|     private static int _hanle = 0; | ||||
|     public OpcItem(string itemId) | ||||
|     { | ||||
|         ItemID = itemId; | ||||
|         ClientHandle = ++_hanle; | ||||
|     } | ||||
|  | ||||
|     public string AccessPath { get; private set; } = ""; | ||||
|  | ||||
|     public IntPtr Blob { get; set; } = IntPtr.Zero; | ||||
|  | ||||
|     public int BlobSize { get; set; } = 0; | ||||
|  | ||||
|     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; | ||||
|  | ||||
|     public int Quality { get; set; } = Qualities.OPC_QUALITY_BAD; | ||||
|     public short RunTimeDataType { get; set; } = 0; | ||||
|     public int ServerHandle { get; set; } | ||||
|     public DateTime TimeStamp { get; set; } = new DateTime(0); | ||||
|     public object Value { get; set; } | ||||
| } | ||||
| @@ -0,0 +1,284 @@ | ||||
| #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; | ||||
| using System.Linq; | ||||
| using System.Runtime.InteropServices; | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||
|  | ||||
| internal class OpcServer : IDisposable | ||||
| { | ||||
|  | ||||
|     private bool disposedValue; | ||||
|  | ||||
|     private IOPCServer m_OpcServer = null; | ||||
|  | ||||
|     internal OpcServer(string name, string host = "localhost") | ||||
|     { | ||||
|         Name = name; | ||||
|         if (host.IsNullOrEmpty()) | ||||
|         { | ||||
|             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 OperResult<OpcGroup> AddGroup(string groupName) | ||||
|     { | ||||
|         return AddGroup(groupName, true, 1000, 0); | ||||
|     } | ||||
|  | ||||
|     /// <returns></returns> | ||||
|     internal OperResult<OpcGroup> AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand) | ||||
|     { | ||||
|         if (null == m_OpcServer || IsConnected == false) | ||||
|             return new OperResult<OpcGroup>("未初始化连接!"); | ||||
|         OpcGroup group = new OpcGroup(groupName, active, reqUpdateRate, deadBand); | ||||
|         Guid riid = typeof(IOPCItemMgt).GUID; | ||||
|         try | ||||
|         { | ||||
|             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 | ||||
|             { | ||||
|                 return new OperResult<OpcGroup>("添加OPC组错误,OPC服务器返回null"); | ||||
|             } | ||||
|             return OperResult.CreateSuccessResult(group); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<OpcGroup>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取节点 | ||||
|     /// </summary> | ||||
|     internal OperResult<List<BrowseElement>> Browse(string itemId = null) | ||||
|     { | ||||
|         lock (this) | ||||
|         { | ||||
|             if (null == m_OpcServer || IsConnected == false) | ||||
|                 return new OperResult<List<BrowseElement>>("未初始化连接!"); | ||||
|  | ||||
|  | ||||
|             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), | ||||
|                          }; | ||||
|             try | ||||
|             { | ||||
|  | ||||
|                 var server = m_OpcServer as IOPCBrowse; | ||||
|                 server.Browse( | ||||
|                          itemId.IsNullOrEmpty() ? "" : itemId, | ||||
|                      ref pContinuationPoint, | ||||
|                      int.MaxValue, | ||||
|                         OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL, | ||||
|                           "", | ||||
|                          "", | ||||
|                          0, | ||||
|                          1, | ||||
|                          filterId.Length, | ||||
|                          Interop.GetPropertyIDs(filterId), | ||||
|                      out moreElements, | ||||
|                      out count, | ||||
|                      out pElements); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult<List<BrowseElement>>(ex); | ||||
|             } | ||||
|             BrowseElement[] browseElements = Interop.GetBrowseElements(ref pElements, count, true); | ||||
|             string stringUni = Marshal.PtrToStringUni(pContinuationPoint); | ||||
|             Marshal.FreeCoTaskMem(pContinuationPoint); | ||||
|             this.ProcessResults(browseElements, filterId); | ||||
|             return OperResult.CreateSuccessResult(browseElements?.ToList()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal OperResult Connect() | ||||
|     { | ||||
|         if (!string.IsNullOrEmpty(Host) && !string.IsNullOrEmpty(Name)) | ||||
|         { | ||||
|             var info = Discovery.OpcDiscovery.GetOpcServer(Name, Host); | ||||
|             if (!info.IsSuccess) | ||||
|             { | ||||
|                 return info; | ||||
|             } | ||||
|             object o = Comn.ComInterop.CreateInstance(info.Content.CLSID, Host); | ||||
|             if (o == null) | ||||
|             { | ||||
|                 return new(string.Format("{0}{1}无法创建com对象", info.Content.CLSID, Host)); | ||||
|             } | ||||
|             m_OpcServer = (IOPCServer)o; | ||||
|             IsConnected = true; | ||||
|             return OperResult.CreateSuccessResult(); | ||||
|         } | ||||
|         return new("应初始化Host与Name"); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 服务器状态 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     internal OperResult<ServerStatus> GetServerStatus() | ||||
|     { | ||||
|         if (null == m_OpcServer || IsConnected == false) | ||||
|             return new OperResult<ServerStatus>("未初始化连接!"); | ||||
|         IntPtr statusPtr = IntPtr.Zero; | ||||
|         try | ||||
|         { | ||||
|             m_OpcServer?.GetStatus(out statusPtr); | ||||
|             OPCSERVERSTATUS status; | ||||
|             ServerStatus = new ServerStatus(); | ||||
|             if (statusPtr != IntPtr.Zero) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS)); | ||||
|                     if (null != o) | ||||
|                     { | ||||
|                         status = (OPCSERVERSTATUS)o; | ||||
|                         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 OperResult.CreateSuccessResult(ServerStatus); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     IsConnected = false; | ||||
|                     return new OperResult<ServerStatus>(ex); | ||||
|                 } | ||||
|  | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 IsConnected = false; | ||||
|                 return new OperResult<ServerStatus>("获取状态失败"); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             IsConnected = false; | ||||
|             return new OperResult<ServerStatus>(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     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,25 @@ | ||||
| #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; | ||||
|  | ||||
| public class ServerStatus | ||||
| { | ||||
|     public DateTime CurrentTime { get; internal set; } = new DateTime(0); | ||||
|     public DateTime LastUpdateTime { get; internal set; } = new DateTime(0); | ||||
|     public OPCSERVERSTATE ServerState { get; internal set; } = OPCSERVERSTATE.OPC_STATUS_NOCONFIG; | ||||
|     public DateTime StartTime { get; internal set; } = new DateTime(0); | ||||
|     public string VendorInfo { get; internal set; } = "UNKOWN"; | ||||
|     public string Version { get; internal set; } = "UNKOWN"; | ||||
| } | ||||
| @@ -0,0 +1,237 @@ | ||||
| #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.Linq; | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
| using ThingsGateway.Foundation.Extension.Json; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Discovery; | ||||
|  | ||||
| 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"); | ||||
|  | ||||
|     public static OperResult<ServerInfo> GetOpcServer(string serverName, string host) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (serverName.IsNullOrEmpty()) | ||||
|             { | ||||
|                 return new OperResult<ServerInfo>("检索失败,需提供OPCName"); | ||||
|             } | ||||
|             ServerInfo result = null; | ||||
|             ServerInfo[] serverInfos = null; | ||||
|             object o_Server = Comn.ComInterop.CreateInstance(OPCEnumCLSID, host); | ||||
|             if (o_Server == null) | ||||
|                 return new OperResult<ServerInfo>("检索失败,请检查是否安装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 (Exception) | ||||
|                 { | ||||
|                     IOPCServerList m_server = (IOPCServerList)o_Server; | ||||
|                     GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid); | ||||
|                 } | ||||
|                 if (result == null) | ||||
|                 { | ||||
|                     return new OperResult<ServerInfo>($"无法创建OPCServer连接,请检查OPC名称是否一致,以下为Host{host}中的OPC列表:" | ||||
|                         + Environment.NewLine + | ||||
|                         serverInfos.ToJson().FormatJson() | ||||
|                         ); | ||||
|                 } | ||||
|                 return OperResult.CreateSuccessResult(result); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 Comn.ComInterop.RealseComServer(o_Server); | ||||
|                 o_Server = null; | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult<ServerInfo>(ex); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     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)); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| public class ServerInfo | ||||
| { | ||||
|     public Guid CLSID { get; set; } | ||||
|     public string Description { get; set; } = string.Empty; | ||||
|     public string Host { get; set; } = string.Empty; | ||||
|     public string ProgID { get; set; } = string.Empty; | ||||
|     public string VerIndProgID { get; set; } = string.Empty; | ||||
| } | ||||
| @@ -0,0 +1,102 @@ | ||||
| #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; | ||||
|  | ||||
| [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,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. | ||||
|  | ||||
| Runtime.InteropServices; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
|  | ||||
| /// <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; | ||||
| } | ||||
| @@ -0,0 +1,869 @@ | ||||
| #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.Reflection; | ||||
| using System.Runtime.InteropServices; | ||||
| using System.Xml; | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
|  | ||||
|  | ||||
| public class Interop | ||||
| { | ||||
|     public static PropertyID GetPropertyID(int input) | ||||
|     { | ||||
|         foreach (FieldInfo field in typeof(Property).GetFields(BindingFlags.Static | BindingFlags.Public)) | ||||
|         { | ||||
|             PropertyID propertyId = (PropertyID)field.GetValue((object)typeof(PropertyID)); | ||||
|             if (input == propertyId.Code) | ||||
|                 return propertyId; | ||||
|         } | ||||
|         return new PropertyID(input); | ||||
|     } | ||||
|  | ||||
|     internal static BrowseElement GetBrowseElement(IntPtr pInput, bool deallocate) | ||||
|     { | ||||
|         BrowseElement browseElement = (BrowseElement)null; | ||||
|         if (pInput != IntPtr.Zero) | ||||
|         { | ||||
|             OPCBROWSEELEMENT structure = (OPCBROWSEELEMENT)Marshal.PtrToStructure(pInput, typeof(OPCBROWSEELEMENT)); | ||||
|             browseElement = new BrowseElement(); | ||||
|             browseElement.Name = structure.szName; | ||||
|             browseElement.ItemPath = (string)null; | ||||
|             browseElement.ItemName = structure.szItemID; | ||||
|             browseElement.IsItem = (structure.dwFlagValue & 2) != 0; | ||||
|             browseElement.HasChildren = (structure.dwFlagValue & 1) != 0; | ||||
|             browseElement.Properties = Interop.GetItemProperties(ref structure.ItemProperties, deallocate); | ||||
|             if (deallocate) | ||||
|                 Marshal.DestroyStructure(pInput, typeof(OPCBROWSEELEMENT)); | ||||
|         } | ||||
|         return browseElement; | ||||
|     } | ||||
|  | ||||
|     internal static OPCBROWSEELEMENT GetBrowseElement( | ||||
|       BrowseElement input, | ||||
|       bool propertiesRequested) | ||||
|     { | ||||
|         OPCBROWSEELEMENT browseElement = new OPCBROWSEELEMENT(); | ||||
|         if (input != null) | ||||
|         { | ||||
|             browseElement.szName = input.Name; | ||||
|             browseElement.szItemID = input.ItemName; | ||||
|             browseElement.dwFlagValue = 0; | ||||
|             browseElement.ItemProperties = Interop.GetItemProperties(input.Properties); | ||||
|             if (input.IsItem) | ||||
|                 browseElement.dwFlagValue |= 2; | ||||
|             if (input.HasChildren) | ||||
|                 browseElement.dwFlagValue |= 1; | ||||
|         } | ||||
|         return browseElement; | ||||
|     } | ||||
|  | ||||
|     internal static BrowseElement[] GetBrowseElements( | ||||
|                   ref IntPtr pInput, | ||||
|       int count, | ||||
|       bool deallocate) | ||||
|     { | ||||
|         BrowseElement[] browseElements = (BrowseElement[])null; | ||||
|         if (pInput != IntPtr.Zero && count > 0) | ||||
|         { | ||||
|             browseElements = new BrowseElement[count]; | ||||
|             IntPtr pInput1 = pInput; | ||||
|             for (int index = 0; index < count; ++index) | ||||
|             { | ||||
|                 browseElements[index] = Interop.GetBrowseElement(pInput1, deallocate); | ||||
|                 pInput1 = (IntPtr)(pInput1.ToInt64() + (long)Marshal.SizeOf(typeof(OPCBROWSEELEMENT))); | ||||
|             } | ||||
|             if (deallocate) | ||||
|             { | ||||
|                 Marshal.FreeCoTaskMem(pInput); | ||||
|                 pInput = IntPtr.Zero; | ||||
|             } | ||||
|         } | ||||
|         return browseElements; | ||||
|     } | ||||
|  | ||||
|     internal static IntPtr GetBrowseElements(BrowseElement[] input, bool propertiesRequested) | ||||
|     { | ||||
|         IntPtr browseElements = IntPtr.Zero; | ||||
|         if (input != null && input.Length != 0) | ||||
|         { | ||||
|             browseElements = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OPCBROWSEELEMENT)) * input.Length); | ||||
|             IntPtr ptr = browseElements; | ||||
|             for (int index = 0; index < input.Length; ++index) | ||||
|             { | ||||
|                 Marshal.StructureToPtr((object)Interop.GetBrowseElement(input[index], propertiesRequested), ptr, false); | ||||
|                 ptr = (IntPtr)(ptr.ToInt64() + (long)Marshal.SizeOf(typeof(OPCBROWSEELEMENT))); | ||||
|             } | ||||
|         } | ||||
|         return browseElements; | ||||
|     } | ||||
|     internal static ItemProperty[] GetItemProperties( | ||||
|       ref OPCITEMPROPERTIES input, | ||||
|       bool deallocate) | ||||
|     { | ||||
|         ItemProperty[] itemProperties = (ItemProperty[])null; | ||||
|         if (input.dwNumProperties > 0) | ||||
|         { | ||||
|             itemProperties = new ItemProperty[input.dwNumProperties]; | ||||
|             IntPtr pInput = input.pItemProperties; | ||||
|             for (int index = 0; index < itemProperties.Length; ++index) | ||||
|             { | ||||
|                 try | ||||
|                 { | ||||
|                     itemProperties[index] = Interop.GetItemProperty(pInput, deallocate); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     itemProperties[index] = new ItemProperty(); | ||||
|                     itemProperties[index].Description = ex.Message; | ||||
|                     itemProperties[index].ResultID = ResultID.E_FAIL; | ||||
|                 } | ||||
|                 pInput = (IntPtr)(pInput.ToInt64() + (long)Marshal.SizeOf(typeof(OPCITEMPROPERTY))); | ||||
|             } | ||||
|             if (deallocate) | ||||
|             { | ||||
|                 Marshal.FreeCoTaskMem(input.pItemProperties); | ||||
|                 input.pItemProperties = IntPtr.Zero; | ||||
|             } | ||||
|         } | ||||
|         return itemProperties; | ||||
|     } | ||||
|  | ||||
|     internal static OPCITEMPROPERTIES GetItemProperties(ItemProperty[] input) | ||||
|     { | ||||
|         OPCITEMPROPERTIES itemProperties = new OPCITEMPROPERTIES(); | ||||
|         if (input != null && input.Length != 0) | ||||
|         { | ||||
|             itemProperties.hrErrorID = 0; | ||||
|             itemProperties.dwReserved = 0; | ||||
|             itemProperties.dwNumProperties = input.Length; | ||||
|             itemProperties.pItemProperties = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OPCITEMPROPERTY)) * input.Length); | ||||
|             bool flag = false; | ||||
|             IntPtr ptr = itemProperties.pItemProperties; | ||||
|             for (int index = 0; index < input.Length; ++index) | ||||
|             { | ||||
|                 Marshal.StructureToPtr((object)Interop.GetItemProperty(input[index]), ptr, false); | ||||
|                 ptr = (IntPtr)(ptr.ToInt64() + (long)Marshal.SizeOf(typeof(OPCITEMPROPERTY))); | ||||
|                 if (input[index].ResultID.Failed()) | ||||
|                     flag = true; | ||||
|             } | ||||
|             if (flag) | ||||
|                 itemProperties.hrErrorID = 1; | ||||
|         } | ||||
|         return itemProperties; | ||||
|     } | ||||
|  | ||||
|     internal static ItemProperty GetItemProperty(IntPtr pInput, bool deallocate) | ||||
|     { | ||||
|         ItemProperty itemProperty = (ItemProperty)null; | ||||
|         if (pInput != IntPtr.Zero) | ||||
|         { | ||||
|             OPCITEMPROPERTY structure = (OPCITEMPROPERTY)Marshal.PtrToStructure(pInput, typeof(OPCITEMPROPERTY)); | ||||
|             itemProperty = new ItemProperty() | ||||
|             { | ||||
|                 ID = Interop.GetPropertyID(structure.dwPropertyID), | ||||
|                 Description = structure.szDescription, | ||||
|                 DataType = Interop.GetType((VarEnum)structure.vtDataType), | ||||
|                 ItemPath = (string)null, | ||||
|                 ItemName = structure.szItemID | ||||
|             }; | ||||
|             itemProperty.Value = Interop.UnmarshalPropertyValue(itemProperty.ID, structure.vValue); | ||||
|             itemProperty.ResultID = Interop.GetResultID(structure.hrErrorID); | ||||
|             if (structure.hrErrorID == -1073479674) | ||||
|                 itemProperty.ResultID = new ResultID(ResultID.Da.E_WRITEONLY, -1073479674L); | ||||
|             if (deallocate) | ||||
|                 Marshal.DestroyStructure(pInput, typeof(OPCITEMPROPERTY)); | ||||
|         } | ||||
|         return itemProperty; | ||||
|     } | ||||
|     internal static OPCITEMPROPERTY GetItemProperty(ItemProperty input) | ||||
|     { | ||||
|         OPCITEMPROPERTY itemProperty = new OPCITEMPROPERTY(); | ||||
|         if (input != null) | ||||
|         { | ||||
|             itemProperty.dwPropertyID = input.ID.Code; | ||||
|             itemProperty.szDescription = input.Description; | ||||
|             itemProperty.vtDataType = (short)Interop.GetType(input.DataType); | ||||
|             itemProperty.vValue = Interop.MarshalPropertyValue(input.ID, input.Value); | ||||
|             itemProperty.wReserved = (short)0; | ||||
|             itemProperty.hrErrorID = Interop.GetResultID(input.ResultID); | ||||
|             PropertyDescription propertyDescription = PropertyDescription.Find(input.ID); | ||||
|             if (propertyDescription != null) | ||||
|                 itemProperty.vtDataType = (short)Interop.GetType(propertyDescription.Type); | ||||
|             if (input.ResultID == ResultID.Da.E_WRITEONLY) | ||||
|                 itemProperty.hrErrorID = -1073479674; | ||||
|         } | ||||
|         return itemProperty; | ||||
|     } | ||||
|  | ||||
|     internal static int[] GetPropertyIDs(PropertyID[] propertyIDs) | ||||
|     { | ||||
|         ArrayList arrayList = new ArrayList(); | ||||
|         if (propertyIDs != null) | ||||
|         { | ||||
|             foreach (PropertyID propertyId in propertyIDs) | ||||
|                 arrayList.Add((object)propertyId.Code); | ||||
|         } | ||||
|         return (int[])arrayList.ToArray(typeof(int)); | ||||
|     } | ||||
|  | ||||
|     internal static ResultID GetResultID(int input) | ||||
|     { | ||||
|         switch (input) | ||||
|         { | ||||
|             case -2147467262: | ||||
|                 return new ResultID(ResultID.E_NOTSUPPORTED, (long)input); | ||||
|             case -2147467259: | ||||
|                 return new ResultID(ResultID.E_FAIL, (long)input); | ||||
|             case -2147352571: | ||||
|                 return new ResultID(ResultID.Da.E_BADTYPE, (long)input); | ||||
|             case -2147352566: | ||||
|                 return new ResultID(ResultID.Da.E_RANGE, (long)input); | ||||
|             case -2147217401: | ||||
|                 return new ResultID(ResultID.Hda.W_NOFILTER, (long)input); | ||||
|             case -2147024882: | ||||
|                 return new ResultID(ResultID.E_OUTOFMEMORY, (long)input); | ||||
|             case -2147024809: | ||||
|                 return new ResultID(ResultID.E_INVALIDARG, (long)input); | ||||
|             case -1073479679: | ||||
|                 return new ResultID(ResultID.Da.E_INVALIDHANDLE, (long)input); | ||||
|             case -1073479676: | ||||
|                 return new ResultID(ResultID.Da.E_BADTYPE, (long)input); | ||||
|             case -1073479673: | ||||
|                 return new ResultID(ResultID.Da.E_UNKNOWN_ITEM_NAME, (long)input); | ||||
|             case -1073479672: | ||||
|                 return new ResultID(ResultID.Da.E_INVALID_ITEM_NAME, (long)input); | ||||
|             case -1073479671: | ||||
|                 return new ResultID(ResultID.Da.E_INVALID_FILTER, (long)input); | ||||
|             case -1073479670: | ||||
|                 return new ResultID(ResultID.Da.E_UNKNOWN_ITEM_PATH, (long)input); | ||||
|             case -1073479669: | ||||
|                 return new ResultID(ResultID.Da.E_RANGE, (long)input); | ||||
|             case -1073479165: | ||||
|                 return new ResultID(ResultID.Da.E_INVALID_PID, (long)input); | ||||
|             case -1073479164: | ||||
|                 return new ResultID(ResultID.Ae.E_INVALIDTIME, (long)input); | ||||
|             case -1073479163: | ||||
|                 return new ResultID(ResultID.Ae.E_BUSY, (long)input); | ||||
|             case -1073479162: | ||||
|                 return new ResultID(ResultID.Ae.E_NOINFO, (long)input); | ||||
|             case -1073478655: | ||||
|                 return new ResultID(ResultID.Da.E_NO_ITEM_DEADBAND, (long)input); | ||||
|             case -1073478654: | ||||
|                 return new ResultID(ResultID.Da.E_NO_ITEM_BUFFERING, (long)input); | ||||
|             case -1073478653: | ||||
|                 return new ResultID(ResultID.Da.E_INVALIDCONTINUATIONPOINT, (long)input); | ||||
|             case -1073478650: | ||||
|                 return new ResultID(ResultID.Da.E_NO_WRITEQT, (long)input); | ||||
|             case -1073478649: | ||||
|                 return new ResultID(ResultID.Cpx.E_TYPE_CHANGED, (long)input); | ||||
|             case -1073478648: | ||||
|                 return new ResultID(ResultID.Cpx.E_FILTER_DUPLICATE, (long)input); | ||||
|             case -1073478647: | ||||
|                 return new ResultID(ResultID.Cpx.E_FILTER_INVALID, (long)input); | ||||
|             case -1073478646: | ||||
|                 return new ResultID(ResultID.Cpx.E_FILTER_ERROR, (long)input); | ||||
|             case -1073477888: | ||||
|                 return new ResultID(ResultID.Dx.E_PERSISTING, (long)input); | ||||
|             case -1073477887: | ||||
|                 return new ResultID(ResultID.Dx.E_NOITEMLIST, (long)input); | ||||
|             case -1073477886: | ||||
|                 return new ResultID(ResultID.Dx.E_VERSION_MISMATCH, (long)input); | ||||
|             case -1073477885: | ||||
|                 return new ResultID(ResultID.Dx.E_VERSION_MISMATCH, (long)input); | ||||
|             case -1073477884: | ||||
|                 return new ResultID(ResultID.Dx.E_UNKNOWN_ITEM_PATH, (long)input); | ||||
|             case -1073477883: | ||||
|                 return new ResultID(ResultID.Dx.E_UNKNOWN_ITEM_NAME, (long)input); | ||||
|             case -1073477882: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_ITEM_PATH, (long)input); | ||||
|             case -1073477881: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_ITEM_NAME, (long)input); | ||||
|             case -1073477880: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_NAME, (long)input); | ||||
|             case -1073477879: | ||||
|                 return new ResultID(ResultID.Dx.E_DUPLICATE_NAME, (long)input); | ||||
|             case -1073477878: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_BROWSE_PATH, (long)input); | ||||
|             case -1073477877: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_SERVER_URL, (long)input); | ||||
|             case -1073477876: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_SERVER_TYPE, (long)input); | ||||
|             case -1073477875: | ||||
|                 return new ResultID(ResultID.Dx.E_UNSUPPORTED_SERVER_TYPE, (long)input); | ||||
|             case -1073477874: | ||||
|                 return new ResultID(ResultID.Dx.E_CONNECTIONS_EXIST, (long)input); | ||||
|             case -1073477873: | ||||
|                 return new ResultID(ResultID.Dx.E_TOO_MANY_CONNECTIONS, (long)input); | ||||
|             case -1073477872: | ||||
|                 return new ResultID(ResultID.Dx.E_OVERRIDE_BADTYPE, (long)input); | ||||
|             case -1073477871: | ||||
|                 return new ResultID(ResultID.Dx.E_OVERRIDE_RANGE, (long)input); | ||||
|             case -1073477870: | ||||
|                 return new ResultID(ResultID.Dx.E_SUBSTITUTE_BADTYPE, (long)input); | ||||
|             case -1073477869: | ||||
|                 return new ResultID(ResultID.Dx.E_SUBSTITUTE_RANGE, (long)input); | ||||
|             case -1073477868: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_TARGET_ITEM, (long)input); | ||||
|             case -1073477867: | ||||
|                 return new ResultID(ResultID.Dx.E_UNKNOWN_TARGET_ITEM, (long)input); | ||||
|             case -1073477866: | ||||
|                 return new ResultID(ResultID.Dx.E_TARGET_ALREADY_CONNECTED, (long)input); | ||||
|             case -1073477865: | ||||
|                 return new ResultID(ResultID.Dx.E_UNKNOWN_SERVER_NAME, (long)input); | ||||
|             case -1073477864: | ||||
|                 return new ResultID(ResultID.Dx.E_UNKNOWN_SOURCE_ITEM, (long)input); | ||||
|             case -1073477863: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_SOURCE_ITEM, (long)input); | ||||
|             case -1073477862: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_QUEUE_SIZE, (long)input); | ||||
|             case -1073477861: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_DEADBAND, (long)input); | ||||
|             case -1073477860: | ||||
|                 return new ResultID(ResultID.Dx.E_INVALID_CONFIG_FILE, (long)input); | ||||
|             case -1073477859: | ||||
|                 return new ResultID(ResultID.Dx.E_PERSIST_FAILED, (long)input); | ||||
|             case -1073477858: | ||||
|                 return new ResultID(ResultID.Dx.E_TARGET_FAULT, (long)input); | ||||
|             case -1073477857: | ||||
|                 return new ResultID(ResultID.Dx.E_TARGET_NO_ACCESSS, (long)input); | ||||
|             case -1073477856: | ||||
|                 return new ResultID(ResultID.Dx.E_SOURCE_SERVER_FAULT, (long)input); | ||||
|             case -1073477855: | ||||
|                 return new ResultID(ResultID.Dx.E_SOURCE_SERVER_NO_ACCESSS, (long)input); | ||||
|             case -1073477854: | ||||
|                 return new ResultID(ResultID.Dx.E_SUBSCRIPTION_FAULT, (long)input); | ||||
|             case -1073477853: | ||||
|                 return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BADRIGHTS, (long)input); | ||||
|             case -1073477852: | ||||
|                 return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BAD_QUALITY, (long)input); | ||||
|             case -1073477851: | ||||
|                 return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BADTYPE, (long)input); | ||||
|             case -1073477850: | ||||
|                 return new ResultID(ResultID.Dx.E_SOURCE_ITEM_RANGE, (long)input); | ||||
|             case -1073477849: | ||||
|                 return new ResultID(ResultID.Dx.E_SOURCE_SERVER_NOT_CONNECTED, (long)input); | ||||
|             case -1073477848: | ||||
|                 return new ResultID(ResultID.Dx.E_SOURCE_SERVER_TIMEOUT, (long)input); | ||||
|             case -1073477847: | ||||
|                 return new ResultID(ResultID.Dx.E_TARGET_ITEM_DISCONNECTED, (long)input); | ||||
|             case -1073477846: | ||||
|                 return new ResultID(ResultID.Dx.E_TARGET_NO_WRITES_ATTEMPTED, (long)input); | ||||
|             case -1073477845: | ||||
|                 return new ResultID(ResultID.Dx.E_TARGET_ITEM_BADTYPE, (long)input); | ||||
|             case -1073477844: | ||||
|                 return new ResultID(ResultID.Dx.E_TARGET_ITEM_RANGE, (long)input); | ||||
|             case -1073475583: | ||||
|                 return new ResultID(ResultID.Hda.E_MAXEXCEEDED, (long)input); | ||||
|             case -1073475580: | ||||
|                 return new ResultID(ResultID.Hda.E_INVALIDAGGREGATE, (long)input); | ||||
|             case -1073475576: | ||||
|                 return new ResultID(ResultID.Hda.E_UNKNOWNATTRID, (long)input); | ||||
|             case -1073475575: | ||||
|                 return new ResultID(ResultID.Hda.E_NOT_AVAIL, (long)input); | ||||
|             case -1073475574: | ||||
|                 return new ResultID(ResultID.Hda.E_INVALIDDATATYPE, (long)input); | ||||
|             case -1073475573: | ||||
|                 return new ResultID(ResultID.Hda.E_DATAEXISTS, (long)input); | ||||
|             case -1073475572: | ||||
|                 return new ResultID(ResultID.Hda.E_INVALIDATTRID, (long)input); | ||||
|             case -1073475571: | ||||
|                 return new ResultID(ResultID.Hda.E_NODATAEXISTS, (long)input); | ||||
|             case 0: | ||||
|                 return new ResultID(ResultID.S_OK, (long)input); | ||||
|             case 262157: | ||||
|                 return new ResultID(ResultID.Da.S_UNSUPPORTEDRATE, (long)input); | ||||
|             case 262158: | ||||
|                 return new ResultID(ResultID.Da.S_CLAMP, (long)input); | ||||
|             case 262656: | ||||
|                 return new ResultID(ResultID.Ae.S_ALREADYACKED, (long)input); | ||||
|             case 262657: | ||||
|                 return new ResultID(ResultID.Ae.S_INVALIDBUFFERTIME, (long)input); | ||||
|             case 262658: | ||||
|                 return new ResultID(ResultID.Ae.S_INVALIDMAXSIZE, (long)input); | ||||
|             case 262659: | ||||
|                 return new ResultID(ResultID.Ae.S_INVALIDKEEPALIVETIME, (long)input); | ||||
|             case 263172: | ||||
|                 return new ResultID(ResultID.Da.S_DATAQUEUEOVERFLOW, (long)input); | ||||
|             case 263179: | ||||
|                 return new ResultID(ResultID.Cpx.S_FILTER_NO_DATA, (long)input); | ||||
|             case 264064: | ||||
|                 return new ResultID(ResultID.Dx.S_TARGET_SUBSTITUTED, (long)input); | ||||
|             case 264065: | ||||
|                 return new ResultID(ResultID.Dx.S_TARGET_OVERRIDEN, (long)input); | ||||
|             case 264066: | ||||
|                 return new ResultID(ResultID.Dx.S_CLAMP, (long)input); | ||||
|             case 1074008066: | ||||
|                 return new ResultID(ResultID.Hda.S_NODATA, (long)input); | ||||
|             case 1074008067: | ||||
|                 return new ResultID(ResultID.Hda.S_MOREDATA, (long)input); | ||||
|             case 1074008069: | ||||
|                 return new ResultID(ResultID.Hda.S_CURRENTVALUE, (long)input); | ||||
|             case 1074008070: | ||||
|                 return new ResultID(ResultID.Hda.S_EXTRADATA, (long)input); | ||||
|             case 1074008078: | ||||
|                 return new ResultID(ResultID.Hda.S_INSERTED, (long)input); | ||||
|             case 1074008079: | ||||
|                 return new ResultID(ResultID.Hda.S_REPLACED, (long)input); | ||||
|             default: | ||||
|                 if ((input & 2147418112) == 65536) | ||||
|                     return new ResultID(ResultID.E_NETWORK_ERROR, (long)input); | ||||
|                 return input >= 0 ? new ResultID(ResultID.S_FALSE, (long)input) : new ResultID(ResultID.E_FAIL, (long)input); | ||||
|         } | ||||
|     } | ||||
|     internal static int GetResultID(ResultID input) | ||||
|     { | ||||
|         if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/DataAccess/") | ||||
|         { | ||||
|             if (input == ResultID.S_OK) | ||||
|                 return 0; | ||||
|             if (input == ResultID.E_FAIL) | ||||
|                 return -2147467259; | ||||
|             if (input == ResultID.E_INVALIDARG) | ||||
|                 return -2147024809; | ||||
|             if (input == ResultID.Da.E_BADTYPE) | ||||
|                 return -1073479676; | ||||
|             if (input == ResultID.Da.E_READONLY || input == ResultID.Da.E_WRITEONLY) | ||||
|                 return -1073479674; | ||||
|             if (input == ResultID.Da.E_RANGE) | ||||
|                 return -1073479669; | ||||
|             if (input == ResultID.E_OUTOFMEMORY) | ||||
|                 return -2147024882; | ||||
|             if (input == ResultID.E_NOTSUPPORTED) | ||||
|                 return -2147467262; | ||||
|             if (input == ResultID.Da.E_INVALIDHANDLE) | ||||
|                 return -1073479679; | ||||
|             if (input == ResultID.Da.E_UNKNOWN_ITEM_NAME) | ||||
|                 return -1073479673; | ||||
|             if (input == ResultID.Da.E_INVALID_ITEM_NAME || input == ResultID.Da.E_INVALID_ITEM_PATH) | ||||
|                 return -1073479672; | ||||
|             if (input == ResultID.Da.E_UNKNOWN_ITEM_PATH) | ||||
|                 return -1073479670; | ||||
|             if (input == ResultID.Da.E_INVALID_FILTER) | ||||
|                 return -1073479671; | ||||
|             if (input == ResultID.Da.S_UNSUPPORTEDRATE) | ||||
|                 return 262157; | ||||
|             if (input == ResultID.Da.S_CLAMP) | ||||
|                 return 262158; | ||||
|             if (input == ResultID.Da.E_INVALID_PID) | ||||
|                 return -1073479165; | ||||
|             if (input == ResultID.Da.E_NO_ITEM_DEADBAND) | ||||
|                 return -1073478655; | ||||
|             if (input == ResultID.Da.E_NO_ITEM_BUFFERING) | ||||
|                 return -1073478654; | ||||
|             if (input == ResultID.Da.E_NO_WRITEQT) | ||||
|                 return -1073478650; | ||||
|             if (input == ResultID.Da.E_INVALIDCONTINUATIONPOINT) | ||||
|                 return -1073478653; | ||||
|             if (input == ResultID.Da.S_DATAQUEUEOVERFLOW) | ||||
|                 return 263172; | ||||
|         } | ||||
|         else if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/ComplexData/") | ||||
|         { | ||||
|             if (input == ResultID.Cpx.E_TYPE_CHANGED) | ||||
|                 return -1073478649; | ||||
|             if (input == ResultID.Cpx.E_FILTER_DUPLICATE) | ||||
|                 return -1073478648; | ||||
|             if (input == ResultID.Cpx.E_FILTER_INVALID) | ||||
|                 return -1073478647; | ||||
|             if (input == ResultID.Cpx.E_FILTER_ERROR) | ||||
|                 return -1073478646; | ||||
|             if (input == ResultID.Cpx.S_FILTER_NO_DATA) | ||||
|                 return 263179; | ||||
|         } | ||||
|         else if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/HistoricalDataAccess/") | ||||
|         { | ||||
|             if (input == ResultID.Hda.E_MAXEXCEEDED) | ||||
|                 return -1073475583; | ||||
|             if (input == ResultID.Hda.S_NODATA) | ||||
|                 return 1074008066; | ||||
|             if (input == ResultID.Hda.S_MOREDATA) | ||||
|                 return 1074008067; | ||||
|             if (input == ResultID.Hda.E_INVALIDAGGREGATE) | ||||
|                 return -1073475580; | ||||
|             if (input == ResultID.Hda.S_CURRENTVALUE) | ||||
|                 return 1074008069; | ||||
|             if (input == ResultID.Hda.S_EXTRADATA) | ||||
|                 return 1074008070; | ||||
|             if (input == ResultID.Hda.E_UNKNOWNATTRID) | ||||
|                 return -1073475576; | ||||
|             if (input == ResultID.Hda.E_NOT_AVAIL) | ||||
|                 return -1073475575; | ||||
|             if (input == ResultID.Hda.E_INVALIDDATATYPE) | ||||
|                 return -1073475574; | ||||
|             if (input == ResultID.Hda.E_DATAEXISTS) | ||||
|                 return -1073475573; | ||||
|             if (input == ResultID.Hda.E_INVALIDATTRID) | ||||
|                 return -1073475572; | ||||
|             if (input == ResultID.Hda.E_NODATAEXISTS) | ||||
|                 return -1073475571; | ||||
|             if (input == ResultID.Hda.S_INSERTED) | ||||
|                 return 1074008078; | ||||
|             if (input == ResultID.Hda.S_REPLACED) | ||||
|                 return 1074008079; | ||||
|         } | ||||
|         if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/DataExchange/") | ||||
|         { | ||||
|             if (input == ResultID.Dx.E_PERSISTING) | ||||
|                 return -1073477888; | ||||
|             if (input == ResultID.Dx.E_NOITEMLIST) | ||||
|                 return -1073477887; | ||||
|             if (input == ResultID.Dx.E_SERVER_STATE || input == ResultID.Dx.E_VERSION_MISMATCH) | ||||
|                 return -1073477885; | ||||
|             if (input == ResultID.Dx.E_UNKNOWN_ITEM_PATH) | ||||
|                 return -1073477884; | ||||
|             if (input == ResultID.Dx.E_UNKNOWN_ITEM_NAME) | ||||
|                 return -1073477883; | ||||
|             if (input == ResultID.Dx.E_INVALID_ITEM_PATH) | ||||
|                 return -1073477882; | ||||
|             if (input == ResultID.Dx.E_INVALID_ITEM_NAME) | ||||
|                 return -1073477881; | ||||
|             if (input == ResultID.Dx.E_INVALID_NAME) | ||||
|                 return -1073477880; | ||||
|             if (input == ResultID.Dx.E_DUPLICATE_NAME) | ||||
|                 return -1073477879; | ||||
|             if (input == ResultID.Dx.E_INVALID_BROWSE_PATH) | ||||
|                 return -1073477878; | ||||
|             if (input == ResultID.Dx.E_INVALID_SERVER_URL) | ||||
|                 return -1073477877; | ||||
|             if (input == ResultID.Dx.E_INVALID_SERVER_TYPE) | ||||
|                 return -1073477876; | ||||
|             if (input == ResultID.Dx.E_UNSUPPORTED_SERVER_TYPE) | ||||
|                 return -1073477875; | ||||
|             if (input == ResultID.Dx.E_CONNECTIONS_EXIST) | ||||
|                 return -1073477874; | ||||
|             if (input == ResultID.Dx.E_TOO_MANY_CONNECTIONS) | ||||
|                 return -1073477873; | ||||
|             if (input == ResultID.Dx.E_OVERRIDE_BADTYPE) | ||||
|                 return -1073477872; | ||||
|             if (input == ResultID.Dx.E_OVERRIDE_RANGE) | ||||
|                 return -1073477871; | ||||
|             if (input == ResultID.Dx.E_SUBSTITUTE_BADTYPE) | ||||
|                 return -1073477870; | ||||
|             if (input == ResultID.Dx.E_SUBSTITUTE_RANGE) | ||||
|                 return -1073477869; | ||||
|             if (input == ResultID.Dx.E_INVALID_TARGET_ITEM) | ||||
|                 return -1073477868; | ||||
|             if (input == ResultID.Dx.E_UNKNOWN_TARGET_ITEM) | ||||
|                 return -1073477867; | ||||
|             if (input == ResultID.Dx.E_TARGET_ALREADY_CONNECTED) | ||||
|                 return -1073477866; | ||||
|             if (input == ResultID.Dx.E_UNKNOWN_SERVER_NAME) | ||||
|                 return -1073477865; | ||||
|             if (input == ResultID.Dx.E_UNKNOWN_SOURCE_ITEM) | ||||
|                 return -1073477864; | ||||
|             if (input == ResultID.Dx.E_INVALID_SOURCE_ITEM) | ||||
|                 return -1073477863; | ||||
|             if (input == ResultID.Dx.E_INVALID_QUEUE_SIZE) | ||||
|                 return -1073477862; | ||||
|             if (input == ResultID.Dx.E_INVALID_DEADBAND) | ||||
|                 return -1073477861; | ||||
|             if (input == ResultID.Dx.E_INVALID_CONFIG_FILE) | ||||
|                 return -1073477860; | ||||
|             if (input == ResultID.Dx.E_PERSIST_FAILED) | ||||
|                 return -1073477859; | ||||
|             if (input == ResultID.Dx.E_TARGET_FAULT) | ||||
|                 return -1073477858; | ||||
|             if (input == ResultID.Dx.E_TARGET_NO_ACCESSS) | ||||
|                 return -1073477857; | ||||
|             if (input == ResultID.Dx.E_SOURCE_SERVER_FAULT) | ||||
|                 return -1073477856; | ||||
|             if (input == ResultID.Dx.E_SOURCE_SERVER_NO_ACCESSS) | ||||
|                 return -1073477855; | ||||
|             if (input == ResultID.Dx.E_SUBSCRIPTION_FAULT) | ||||
|                 return -1073477854; | ||||
|             if (input == ResultID.Dx.E_SOURCE_ITEM_BADRIGHTS) | ||||
|                 return -1073477853; | ||||
|             if (input == ResultID.Dx.E_SOURCE_ITEM_BAD_QUALITY) | ||||
|                 return -1073477852; | ||||
|             if (input == ResultID.Dx.E_SOURCE_ITEM_BADTYPE) | ||||
|                 return -1073477851; | ||||
|             if (input == ResultID.Dx.E_SOURCE_ITEM_RANGE) | ||||
|                 return -1073477850; | ||||
|             if (input == ResultID.Dx.E_SOURCE_SERVER_NOT_CONNECTED) | ||||
|                 return -1073477849; | ||||
|             if (input == ResultID.Dx.E_SOURCE_SERVER_TIMEOUT) | ||||
|                 return -1073477848; | ||||
|             if (input == ResultID.Dx.E_TARGET_ITEM_DISCONNECTED) | ||||
|                 return -1073477847; | ||||
|             if (input == ResultID.Dx.E_TARGET_NO_WRITES_ATTEMPTED) | ||||
|                 return -1073477846; | ||||
|             if (input == ResultID.Dx.E_TARGET_ITEM_BADTYPE) | ||||
|                 return -1073477845; | ||||
|             if (input == ResultID.Dx.E_TARGET_ITEM_RANGE) | ||||
|                 return -1073477844; | ||||
|             if (input == ResultID.Dx.S_TARGET_SUBSTITUTED) | ||||
|                 return 264064; | ||||
|             if (input == ResultID.Dx.S_TARGET_OVERRIDEN) | ||||
|                 return 264065; | ||||
|             if (input == ResultID.Dx.S_CLAMP) | ||||
|                 return 264066; | ||||
|         } | ||||
|         else if (input.Code == -1) | ||||
|             return input.Succeeded() ? 1 : -2147467259; | ||||
|         return input.Code; | ||||
|     } | ||||
|  | ||||
|     internal static VarEnum GetType(System.Type input) | ||||
|     { | ||||
|         if (input == (System.Type)null) | ||||
|             return VarEnum.VT_EMPTY; | ||||
|         if (input == typeof(sbyte)) | ||||
|             return VarEnum.VT_I1; | ||||
|         if (input == typeof(byte)) | ||||
|             return VarEnum.VT_UI1; | ||||
|         if (input == typeof(short)) | ||||
|             return VarEnum.VT_I2; | ||||
|         if (input == typeof(ushort)) | ||||
|             return VarEnum.VT_UI2; | ||||
|         if (input == typeof(int)) | ||||
|             return VarEnum.VT_I4; | ||||
|         if (input == typeof(uint)) | ||||
|             return VarEnum.VT_UI4; | ||||
|         if (input == typeof(long)) | ||||
|             return VarEnum.VT_I8; | ||||
|         if (input == typeof(ulong)) | ||||
|             return VarEnum.VT_UI8; | ||||
|         if (input == typeof(float)) | ||||
|             return VarEnum.VT_R4; | ||||
|         if (input == typeof(double)) | ||||
|             return VarEnum.VT_R8; | ||||
|         if (input == typeof(Decimal)) | ||||
|             return VarEnum.VT_CY; | ||||
|         if (input == typeof(bool)) | ||||
|             return VarEnum.VT_BOOL; | ||||
|         if (input == typeof(DateTime)) | ||||
|             return VarEnum.VT_DATE; | ||||
|         if (input == typeof(string)) | ||||
|             return VarEnum.VT_BSTR; | ||||
|         if (input == typeof(object)) | ||||
|             return VarEnum.VT_EMPTY; | ||||
|         if (input == typeof(sbyte[])) | ||||
|             return VarEnum.VT_I1 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(byte[])) | ||||
|             return VarEnum.VT_UI1 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(short[])) | ||||
|             return VarEnum.VT_I2 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(ushort[])) | ||||
|             return VarEnum.VT_UI2 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(int[])) | ||||
|             return VarEnum.VT_I4 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(uint[])) | ||||
|             return VarEnum.VT_UI4 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(long[])) | ||||
|             return VarEnum.VT_I8 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(ulong[])) | ||||
|             return VarEnum.VT_UI8 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(float[])) | ||||
|             return VarEnum.VT_R4 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(double[])) | ||||
|             return VarEnum.VT_R8 | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(Decimal[])) | ||||
|             return VarEnum.VT_CY | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(bool[])) | ||||
|             return VarEnum.VT_BOOL | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(DateTime[])) | ||||
|             return VarEnum.VT_DATE | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(string[])) | ||||
|             return VarEnum.VT_BSTR | VarEnum.VT_ARRAY; | ||||
|         if (input == typeof(object[])) | ||||
|             return VarEnum.VT_VARIANT | VarEnum.VT_ARRAY; | ||||
|         if (input == Type.ILLEGAL_TYPE) | ||||
|             return (VarEnum)System.Enum.ToObject(typeof(VarEnum), (int)short.MaxValue); | ||||
|         if (input == typeof(System.Type) || input == typeof(Quality)) | ||||
|             return VarEnum.VT_I2; | ||||
|         return input == typeof(accessRights) || input == typeof(euType) ? VarEnum.VT_I4 : VarEnum.VT_EMPTY; | ||||
|     } | ||||
|     internal static System.Type GetType(VarEnum input) | ||||
|     { | ||||
|         switch (input) | ||||
|         { | ||||
|             case VarEnum.VT_EMPTY: | ||||
|                 return (System.Type)null; | ||||
|             case VarEnum.VT_I2: | ||||
|                 return typeof(short); | ||||
|             case VarEnum.VT_I4: | ||||
|                 return typeof(int); | ||||
|             case VarEnum.VT_R4: | ||||
|                 return typeof(float); | ||||
|             case VarEnum.VT_R8: | ||||
|                 return typeof(double); | ||||
|             case VarEnum.VT_CY: | ||||
|                 return typeof(Decimal); | ||||
|             case VarEnum.VT_DATE: | ||||
|                 return typeof(DateTime); | ||||
|             case VarEnum.VT_BSTR: | ||||
|                 return typeof(string); | ||||
|             case VarEnum.VT_BOOL: | ||||
|                 return typeof(bool); | ||||
|             case VarEnum.VT_I1: | ||||
|                 return typeof(sbyte); | ||||
|             case VarEnum.VT_UI1: | ||||
|                 return typeof(byte); | ||||
|             case VarEnum.VT_UI2: | ||||
|                 return typeof(ushort); | ||||
|             case VarEnum.VT_UI4: | ||||
|                 return typeof(uint); | ||||
|             case VarEnum.VT_I8: | ||||
|                 return typeof(long); | ||||
|             case VarEnum.VT_UI8: | ||||
|                 return typeof(ulong); | ||||
|             case VarEnum.VT_I2 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(short[]); | ||||
|             case VarEnum.VT_I4 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(int[]); | ||||
|             case VarEnum.VT_R4 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(float[]); | ||||
|             case VarEnum.VT_R8 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(double[]); | ||||
|             case VarEnum.VT_CY | VarEnum.VT_ARRAY: | ||||
|                 return typeof(Decimal[]); | ||||
|             case VarEnum.VT_DATE | VarEnum.VT_ARRAY: | ||||
|                 return typeof(DateTime[]); | ||||
|             case VarEnum.VT_BSTR | VarEnum.VT_ARRAY: | ||||
|                 return typeof(string[]); | ||||
|             case VarEnum.VT_BOOL | VarEnum.VT_ARRAY: | ||||
|                 return typeof(bool[]); | ||||
|             case VarEnum.VT_VARIANT | VarEnum.VT_ARRAY: | ||||
|                 return typeof(object[]); | ||||
|             case VarEnum.VT_I1 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(sbyte[]); | ||||
|             case VarEnum.VT_UI1 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(byte[]); | ||||
|             case VarEnum.VT_UI2 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(ushort[]); | ||||
|             case VarEnum.VT_UI4 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(uint[]); | ||||
|             case VarEnum.VT_I8 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(long[]); | ||||
|             case VarEnum.VT_UI8 | VarEnum.VT_ARRAY: | ||||
|                 return typeof(ulong[]); | ||||
|             default: | ||||
|                 return Type.ILLEGAL_TYPE; | ||||
|         } | ||||
|     } | ||||
|     internal static object MarshalPropertyValue(PropertyID propertyID, object input) | ||||
|     { | ||||
|         if (input == null) | ||||
|             return (object)null; | ||||
|         try | ||||
|         { | ||||
|             if (propertyID == Property.DATATYPE) | ||||
|                 return (object)(short)Interop.GetType((System.Type)input); | ||||
|             if (propertyID == Property.ACCESSRIGHTS) | ||||
|             { | ||||
|                 switch ((accessRights)input) | ||||
|                 { | ||||
|                     case accessRights.readable: | ||||
|                         return (object)1; | ||||
|                     case accessRights.writable: | ||||
|                         return (object)2; | ||||
|                     case accessRights.readWritable: | ||||
|                         return (object)3; | ||||
|                     default: | ||||
|                         return (object)null; | ||||
|                 } | ||||
|             } | ||||
|             else if (propertyID == Property.EUTYPE) | ||||
|             { | ||||
|                 switch ((euType)input) | ||||
|                 { | ||||
|                     case euType.noEnum: | ||||
|                         return (object)OPCEUTYPE.OPC_NOENUM; | ||||
|                     case euType.analog: | ||||
|                         return (object)OPCEUTYPE.OPC_ANALOG; | ||||
|                     case euType.enumerated: | ||||
|                         return (object)OPCEUTYPE.OPC_ENUMERATED; | ||||
|                     default: | ||||
|                         return (object)null; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (propertyID == Property.QUALITY) | ||||
|                     return (object)((Quality)input).GetCode(); | ||||
|                 if (propertyID == Property.TIMESTAMP) | ||||
|                 { | ||||
|                     if (input.GetType() == typeof(DateTime)) | ||||
|                     { | ||||
|                         DateTime dateTime = (DateTime)input; | ||||
|                         return dateTime != DateTime.MinValue ? (object)dateTime.ToUniversalTime() : (object)dateTime; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|         } | ||||
|         return input; | ||||
|     } | ||||
|  | ||||
|     internal static object UnmarshalPropertyValue(PropertyID propertyID, object input) | ||||
|     { | ||||
|         if (input == null) | ||||
|             return (object)null; | ||||
|         try | ||||
|         { | ||||
|             if (propertyID == Property.DATATYPE) | ||||
|                 return (object)Interop.GetType((VarEnum)System.Convert.ToUInt16(input)); | ||||
|             if (propertyID == Property.ACCESSRIGHTS) | ||||
|             { | ||||
|                 switch (System.Convert.ToInt32(input)) | ||||
|                 { | ||||
|                     case 1: | ||||
|                         return (object)accessRights.readable; | ||||
|                     case 2: | ||||
|                         return (object)accessRights.writable; | ||||
|                     case 3: | ||||
|                         return (object)accessRights.readWritable; | ||||
|                     default: | ||||
|                         return (object)null; | ||||
|                 } | ||||
|             } | ||||
|             else if (propertyID == Property.EUTYPE) | ||||
|             { | ||||
|                 switch ((OPCEUTYPE)input) | ||||
|                 { | ||||
|                     case OPCEUTYPE.OPC_NOENUM: | ||||
|                         return (object)euType.noEnum; | ||||
|                     case OPCEUTYPE.OPC_ANALOG: | ||||
|                         return (object)euType.analog; | ||||
|                     case OPCEUTYPE.OPC_ENUMERATED: | ||||
|                         return (object)euType.enumerated; | ||||
|                     default: | ||||
|                         return (object)null; | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 if (propertyID == Property.QUALITY) | ||||
|                     return (object)new Quality(System.Convert.ToInt16(input)); | ||||
|                 if (propertyID == Property.TIMESTAMP) | ||||
|                 { | ||||
|                     if (input.GetType() == typeof(DateTime)) | ||||
|                     { | ||||
|                         DateTime dateTime = (DateTime)input; | ||||
|                         return dateTime != DateTime.MinValue ? (object)dateTime.ToLocalTime() : (object)dateTime; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         catch | ||||
|         { | ||||
|         } | ||||
|         return input; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,960 @@ | ||||
| #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.Reflection; | ||||
| using System.Runtime.Serialization; | ||||
| using System.Xml; | ||||
|  | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
|  | ||||
| public enum accessRights | ||||
| { | ||||
|     readable = 1, | ||||
|     writable, | ||||
|     readWritable | ||||
| } | ||||
|  | ||||
| public enum euType | ||||
| { | ||||
|     noEnum = 1, | ||||
|     analog, | ||||
|     enumerated | ||||
| } | ||||
|  | ||||
| public enum limitBits | ||||
| { | ||||
|     none, | ||||
|     low, | ||||
|     high, | ||||
|     constant | ||||
| } | ||||
|  | ||||
| public enum qualityBits | ||||
| { | ||||
|     good = 192, | ||||
|     goodLocalOverride = 216, | ||||
|     bad = 0, | ||||
|     badConfigurationError = 4, | ||||
|     badNotConnected = 8, | ||||
|     badDeviceFailure = 12, | ||||
|     badSensorFailure = 0x10, | ||||
|     badLastKnownValue = 20, | ||||
|     badCommFailure = 24, | ||||
|     badOutOfService = 28, | ||||
|     badWaitingForInitialData = 0x20, | ||||
|     uncertain = 0x40, | ||||
|     uncertainLastUsableValue = 68, | ||||
|     uncertainSensorNotAccurate = 80, | ||||
|     uncertainEUExceeded = 84, | ||||
|     uncertainSubNormal = 88 | ||||
| } | ||||
|  | ||||
| public interface IResult | ||||
| { | ||||
|     string DiagnosticInfo { get; set; } | ||||
|     ResultID ResultID { get; set; } | ||||
| } | ||||
|  | ||||
| [Serializable] | ||||
| public struct PropertyID : ISerializable | ||||
| { | ||||
|     private int m_code; | ||||
|  | ||||
|     private XmlQualifiedName m_name; | ||||
|  | ||||
|     public PropertyID(XmlQualifiedName name) | ||||
|     { | ||||
|         m_name = name; | ||||
|         m_code = 0; | ||||
|     } | ||||
|  | ||||
|     public PropertyID(int code) | ||||
|     { | ||||
|         m_name = null; | ||||
|         m_code = code; | ||||
|     } | ||||
|  | ||||
|     public PropertyID(string name, int code, string ns) | ||||
|     { | ||||
|         m_name = new XmlQualifiedName(name, ns); | ||||
|         m_code = code; | ||||
|     } | ||||
|  | ||||
|     private PropertyID(SerializationInfo info, StreamingContext context) | ||||
|     { | ||||
|         SerializationInfoEnumerator enumerator = info.GetEnumerator(); | ||||
|         string name = ""; | ||||
|         string ns = ""; | ||||
|         enumerator.Reset(); | ||||
|         while (enumerator.MoveNext()) | ||||
|         { | ||||
|             if (enumerator.Current.Name.Equals("NA")) | ||||
|             { | ||||
|                 name = (string)enumerator.Current.Value; | ||||
|             } | ||||
|             else if (enumerator.Current.Name.Equals("NS")) | ||||
|             { | ||||
|                 ns = (string)enumerator.Current.Value; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         m_name = new XmlQualifiedName(name, ns); | ||||
|         m_code = (int)info.GetValue("CO", typeof(int)); | ||||
|     } | ||||
|  | ||||
|     public int Code => m_code; | ||||
|  | ||||
|     public XmlQualifiedName Name => m_name; | ||||
|  | ||||
|     public static bool operator !=(PropertyID a, PropertyID b) | ||||
|     { | ||||
|         return !a.Equals(b); | ||||
|     } | ||||
|  | ||||
|     public static bool operator ==(PropertyID a, PropertyID b) | ||||
|     { | ||||
|         return a.Equals(b); | ||||
|     } | ||||
|  | ||||
|     public override bool Equals(object target) | ||||
|     { | ||||
|         if (target != null && target.GetType() == typeof(PropertyID)) | ||||
|         { | ||||
|             PropertyID propertyID = (PropertyID)target; | ||||
|             if (propertyID.Code != 0 && Code != 0) | ||||
|             { | ||||
|                 return propertyID.Code == Code; | ||||
|             } | ||||
|  | ||||
|             if (propertyID.Name != null && Name != null) | ||||
|             { | ||||
|                 return propertyID.Name == Name; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public override int GetHashCode() | ||||
|     { | ||||
|         if (Code != 0) | ||||
|         { | ||||
|             return Code.GetHashCode(); | ||||
|         } | ||||
|  | ||||
|         if (Name != null) | ||||
|         { | ||||
|             return Name.GetHashCode(); | ||||
|         } | ||||
|  | ||||
|         return base.GetHashCode(); | ||||
|     } | ||||
|  | ||||
|     public void GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|     { | ||||
|         if (m_name != null) | ||||
|         { | ||||
|             info.AddValue("NA", m_name.Name); | ||||
|             info.AddValue("NS", m_name.Namespace); | ||||
|         } | ||||
|  | ||||
|         info.AddValue("CO", m_code); | ||||
|     } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         if (Name != null && Code != 0) | ||||
|         { | ||||
|             return $"{Name.Name} ({Code})"; | ||||
|         } | ||||
|  | ||||
|         if (Name != null) | ||||
|         { | ||||
|             return Name.Name; | ||||
|         } | ||||
|  | ||||
|         if (Code != 0) | ||||
|         { | ||||
|             return $"{Code}"; | ||||
|         } | ||||
|  | ||||
|         return ""; | ||||
|     } | ||||
|  | ||||
|     private class Names | ||||
|     { | ||||
|         internal const string CODE = "CO"; | ||||
|         internal const string NAME = "NA"; | ||||
|  | ||||
|         internal const string NAMESPACE = "NS"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| [Serializable] | ||||
| public struct Quality | ||||
| { | ||||
|     public static readonly Quality Bad = new Quality(qualityBits.bad); | ||||
|     public static readonly Quality Good = new Quality(qualityBits.good); | ||||
|     private limitBits m_limitBits; | ||||
|     private qualityBits m_qualityBits; | ||||
|     private byte m_vendorBits; | ||||
|     public Quality(qualityBits quality) | ||||
|     { | ||||
|         m_qualityBits = quality; | ||||
|         m_limitBits = limitBits.none; | ||||
|         m_vendorBits = 0; | ||||
|     } | ||||
|  | ||||
|     public Quality(short code) | ||||
|     { | ||||
|         m_qualityBits = (qualityBits)(code & 0xFC); | ||||
|         m_limitBits = (limitBits)(code & 3); | ||||
|         m_vendorBits = (byte)((code & -253) >> 8); | ||||
|     } | ||||
|  | ||||
|     public limitBits LimitBits | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_limitBits; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_limitBits = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public qualityBits QualityBits | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_qualityBits; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_qualityBits = value; | ||||
|         } | ||||
|     } | ||||
|     public byte VendorBits | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_vendorBits; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_vendorBits = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static bool operator !=(Quality a, Quality b) | ||||
|     { | ||||
|         return !a.Equals(b); | ||||
|     } | ||||
|  | ||||
|     public static bool operator ==(Quality a, Quality b) | ||||
|     { | ||||
|         return a.Equals(b); | ||||
|     } | ||||
|  | ||||
|     public override bool Equals(object target) | ||||
|     { | ||||
|         if (target == null || target.GetType() != typeof(Quality)) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         Quality quality = (Quality)target; | ||||
|         if (QualityBits != quality.QualityBits) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (LimitBits != quality.LimitBits) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (VendorBits != quality.VendorBits) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public short GetCode() | ||||
|     { | ||||
|         ushort num = 0; | ||||
|         num = (ushort)(num | (ushort)QualityBits); | ||||
|         num = (ushort)(num | (ushort)LimitBits); | ||||
|         num = (ushort)(num | (ushort)(VendorBits << 8)); | ||||
|         if (num > 32767) | ||||
|         { | ||||
|             return (short)(-(65536 - num)); | ||||
|         } | ||||
|  | ||||
|         return (short)num; | ||||
|     } | ||||
|  | ||||
|     public override int GetHashCode() | ||||
|     { | ||||
|         return GetCode(); | ||||
|     } | ||||
|  | ||||
|     public void SetCode(short code) | ||||
|     { | ||||
|         m_qualityBits = (qualityBits)(code & 0xFC); | ||||
|         m_limitBits = (limitBits)(code & 3); | ||||
|         m_vendorBits = (byte)((code & -253) >> 8); | ||||
|     } | ||||
|     public override string ToString() | ||||
|     { | ||||
|         string text = QualityBits.ToString(); | ||||
|         if (LimitBits != 0) | ||||
|         { | ||||
|             text += $"[{LimitBits.ToString()}]"; | ||||
|         } | ||||
|  | ||||
|         if (VendorBits != 0) | ||||
|         { | ||||
|             text += string.Format(":{0,0:X}", VendorBits); | ||||
|         } | ||||
|  | ||||
|         return text; | ||||
|     } | ||||
| } | ||||
|  | ||||
| [Serializable] | ||||
| public struct ResultID : ISerializable | ||||
| { | ||||
|     public static readonly ResultID E_ACCESS_DENIED = new ResultID("E_ACCESS_DENIED", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly ResultID E_FAIL = new ResultID("E_FAIL", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly ResultID E_INVALIDARG = new ResultID("E_INVALIDARG", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly ResultID E_NETWORK_ERROR = new ResultID("E_NETWORK_ERROR", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly ResultID E_NOTSUPPORTED = new ResultID("E_NOTSUPPORTED", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly ResultID E_OUTOFMEMORY = new ResultID("E_OUTOFMEMORY", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly ResultID E_TIMEDOUT = new ResultID("E_TIMEDOUT", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly ResultID S_FALSE = new ResultID("S_FALSE", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly ResultID S_OK = new ResultID("S_OK", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     private int m_code; | ||||
|  | ||||
|     private XmlQualifiedName m_name; | ||||
|  | ||||
|     public ResultID(XmlQualifiedName name) | ||||
|     { | ||||
|         m_name = name; | ||||
|         m_code = -1; | ||||
|     } | ||||
|  | ||||
|     public ResultID(long code) | ||||
|     { | ||||
|         m_name = null; | ||||
|         if (code > int.MaxValue) | ||||
|         { | ||||
|             code = -(4294967296L - code); | ||||
|         } | ||||
|  | ||||
|         m_code = (int)code; | ||||
|     } | ||||
|  | ||||
|     public ResultID(string name, string ns) | ||||
|     { | ||||
|         m_name = new XmlQualifiedName(name, ns); | ||||
|         m_code = -1; | ||||
|     } | ||||
|  | ||||
|     public ResultID(ResultID resultID, long code) | ||||
|     { | ||||
|         m_name = resultID.Name; | ||||
|         if (code > int.MaxValue) | ||||
|         { | ||||
|             code = -(4294967296L - code); | ||||
|         } | ||||
|  | ||||
|         m_code = (int)code; | ||||
|     } | ||||
|  | ||||
|     private ResultID(SerializationInfo info, StreamingContext context) | ||||
|     { | ||||
|         string name = (string)info.GetValue("NA", typeof(string)); | ||||
|         string ns = (string)info.GetValue("NS", typeof(string)); | ||||
|         m_name = new XmlQualifiedName(name, ns); | ||||
|         m_code = (int)info.GetValue("CO", typeof(int)); | ||||
|     } | ||||
|  | ||||
|     public int Code => m_code; | ||||
|  | ||||
|     public XmlQualifiedName Name => m_name; | ||||
|  | ||||
|     public static bool operator !=(ResultID a, ResultID b) | ||||
|     { | ||||
|         return !a.Equals(b); | ||||
|     } | ||||
|  | ||||
|     public static bool operator ==(ResultID a, ResultID b) | ||||
|     { | ||||
|         return a.Equals(b); | ||||
|     } | ||||
|  | ||||
|     public override bool Equals(object target) | ||||
|     { | ||||
|         if (target != null && target.GetType() == typeof(ResultID)) | ||||
|         { | ||||
|             ResultID resultID = (ResultID)target; | ||||
|             if (resultID.Code != -1 && Code != -1) | ||||
|             { | ||||
|                 if (resultID.Code == Code) | ||||
|                 { | ||||
|                     return resultID.Name == Name; | ||||
|                 } | ||||
|  | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             if (resultID.Name != null && Name != null) | ||||
|             { | ||||
|                 return resultID.Name == Name; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public bool Failed() | ||||
|     { | ||||
|         if (Code != -1) | ||||
|         { | ||||
|             return Code < 0; | ||||
|         } | ||||
|  | ||||
|         if (Name != null) | ||||
|         { | ||||
|             return Name.Name.StartsWith("E_"); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public override int GetHashCode() | ||||
|     { | ||||
|         return base.GetHashCode(); | ||||
|     } | ||||
|  | ||||
|     public void GetObjectData(SerializationInfo info, StreamingContext context) | ||||
|     { | ||||
|         if (m_name != null) | ||||
|         { | ||||
|             info.AddValue("NA", m_name.Name); | ||||
|             info.AddValue("NS", m_name.Namespace); | ||||
|         } | ||||
|  | ||||
|         info.AddValue("CO", m_code); | ||||
|     } | ||||
|  | ||||
|     public bool Succeeded() | ||||
|     { | ||||
|         if (Code != -1) | ||||
|         { | ||||
|             return Code >= 0; | ||||
|         } | ||||
|  | ||||
|         if (Name != null) | ||||
|         { | ||||
|             return Name.Name.StartsWith("S_"); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         if (Name != null) | ||||
|         { | ||||
|             return Name.Name; | ||||
|         } | ||||
|  | ||||
|         return string.Format("0x{0,0:X}", Code); | ||||
|     } | ||||
|  | ||||
|     public class Ae | ||||
|     { | ||||
|         public static readonly ResultID E_BUSY = new ResultID("E_BUSY", "http://opcfoundation.org/AlarmAndEvents/"); | ||||
|         public static readonly ResultID E_INVALIDBRANCHNAME = new ResultID("E_INVALIDBRANCHNAME", "http://opcfoundation.org/AlarmAndEvents/"); | ||||
|         public static readonly ResultID E_INVALIDTIME = new ResultID("E_INVALIDTIME", "http://opcfoundation.org/AlarmAndEvents/"); | ||||
|         public static readonly ResultID E_NOINFO = new ResultID("E_NOINFO", "http://opcfoundation.org/AlarmAndEvents/"); | ||||
|         public static readonly ResultID S_ALREADYACKED = new ResultID("S_ALREADYACKED", "http://opcfoundation.org/AlarmAndEvents/"); | ||||
|  | ||||
|         public static readonly ResultID S_INVALIDBUFFERTIME = new ResultID("S_INVALIDBUFFERTIME", "http://opcfoundation.org/AlarmAndEvents/"); | ||||
|  | ||||
|         public static readonly ResultID S_INVALIDKEEPALIVETIME = new ResultID("S_INVALIDKEEPALIVETIME", "http://opcfoundation.org/AlarmAndEvents/"); | ||||
|         public static readonly ResultID S_INVALIDMAXSIZE = new ResultID("S_INVALIDMAXSIZE", "http://opcfoundation.org/AlarmAndEvents/"); | ||||
|     } | ||||
|  | ||||
|     public class Cpx | ||||
|     { | ||||
|         public static readonly ResultID E_FILTER_DUPLICATE = new ResultID("E_FILTER_DUPLICATE", "http://opcfoundation.org/ComplexData/"); | ||||
|         public static readonly ResultID E_FILTER_ERROR = new ResultID("E_FILTER_ERROR", "http://opcfoundation.org/ComplexData/"); | ||||
|         public static readonly ResultID E_FILTER_INVALID = new ResultID("E_FILTER_INVALID", "http://opcfoundation.org/ComplexData/"); | ||||
|         public static readonly ResultID E_TYPE_CHANGED = new ResultID("E_TYPE_CHANGED", "http://opcfoundation.org/ComplexData/"); | ||||
|         public static readonly ResultID S_FILTER_NO_DATA = new ResultID("S_FILTER_NO_DATA", "http://opcfoundation.org/ComplexData/"); | ||||
|     } | ||||
|  | ||||
|     public class Da | ||||
|     { | ||||
|         public static readonly ResultID E_BADTYPE = new ResultID("E_BADTYPE", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_INVALID_FILTER = new ResultID("E_INVALID_FILTER", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_INVALID_ITEM_NAME = new ResultID("E_INVALID_ITEM_NAME", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_INVALID_ITEM_PATH = new ResultID("E_INVALID_ITEM_PATH", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_INVALID_PID = new ResultID("E_INVALID_PID", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_INVALIDCONTINUATIONPOINT = new ResultID("E_INVALIDCONTINUATIONPOINT", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_INVALIDHANDLE = new ResultID("E_INVALIDHANDLE", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_NO_ITEM_BUFFERING = new ResultID("E_NO_ITEM_BUFFERING", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_NO_ITEM_DEADBAND = new ResultID("E_NO_ITEM_DEADBAND", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_NO_ITEM_SAMPLING = new ResultID("E_NO_ITEM_SAMPLING", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_NO_WRITEQT = new ResultID("E_NO_WRITEQT", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_RANGE = new ResultID("E_RANGE", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_READONLY = new ResultID("E_READONLY", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_UNKNOWN_ITEM_NAME = new ResultID("E_UNKNOWN_ITEM_NAME", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_UNKNOWN_ITEM_PATH = new ResultID("E_UNKNOWN_ITEM_PATH", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID E_WRITEONLY = new ResultID("E_WRITEONLY", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID S_CLAMP = new ResultID("S_CLAMP", "http://opcfoundation.org/DataAccess/"); | ||||
|         public static readonly ResultID S_DATAQUEUEOVERFLOW = new ResultID("S_DATAQUEUEOVERFLOW", "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|         public static readonly ResultID S_UNSUPPORTEDRATE = new ResultID("S_UNSUPPORTEDRATE", "http://opcfoundation.org/DataAccess/"); | ||||
|     } | ||||
|  | ||||
|     public class Dx | ||||
|     { | ||||
|         public static readonly ResultID E_CONNECTIONS_EXIST = new ResultID("E_CONNECTIONS_EXIST", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_DUPLICATE_NAME = new ResultID("E_DUPLICATE_NAME", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_BROWSE_PATH = new ResultID("E_INVALID_BROWSE_PATH", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_CONFIG_FILE = new ResultID("E_INVALID_CONFIG_FILE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_DEADBAND = new ResultID("E_INVALID_DEADBAND", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_ITEM_NAME = new ResultID("E_INVALID_ITEM_NAME", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_ITEM_PATH = new ResultID("E_INVALID_ITEM_PATH", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_NAME = new ResultID("E_INVALID_NAME", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_QUEUE_SIZE = new ResultID("E_INVALID_QUEUE_SIZE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_SERVER_TYPE = new ResultID("E_INVALID_SERVER_TYPE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_SERVER_URL = new ResultID("E_INVALID_SERVER_URL", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_SOURCE_ITEM = new ResultID("E_INVALID_SOURCE_ITEM", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_INVALID_TARGET_ITEM = new ResultID("E_INVALID_TARGET_ITEM", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_NOITEMLIST = new ResultID("E_NOITEMLIST", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_OVERRIDE_BADTYPE = new ResultID("E_OVERRIDE_BADTYPE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_OVERRIDE_RANGE = new ResultID("E_OVERRIDE_RANGE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_PERSIST_FAILED = new ResultID("E_PERSIST_FAILED", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_PERSISTING = new ResultID("E_PERSISTING", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SERVER_STATE = new ResultID("E_SERVER_STATE", "http://opcfoundation.org/DataExchange/"); | ||||
|  | ||||
|         public static readonly ResultID E_SOURCE_ITEM_BAD_QUALITY = new ResultID("E_SOURCE_ITEM_BAD_QUALITY", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SOURCE_ITEM_BADRIGHTS = new ResultID("E_SOURCE_ITEM_BADRIGHTS", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SOURCE_ITEM_BADTYPE = new ResultID("E_SOURCE_ITEM_BADTYPE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SOURCE_ITEM_RANGE = new ResultID("E_SOURCE_ITEM_RANGE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SOURCE_SERVER_FAULT = new ResultID("E_SOURCE_SERVER_FAULT", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SOURCE_SERVER_NO_ACCESSS = new ResultID("E_SOURCE_SERVER_NO_ACCESSS", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SOURCE_SERVER_NOT_CONNECTED = new ResultID("E_SOURCE_SERVER_NOT_CONNECTED", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SOURCE_SERVER_TIMEOUT = new ResultID("E_SOURCE_SERVER_TIMEOUT", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SUBSCRIPTION_FAULT = new ResultID("E_SUBSCRIPTION_FAULT", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SUBSTITUTE_BADTYPE = new ResultID("E_SUBSTITUTE_BADTYPE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_SUBSTITUTE_RANGE = new ResultID("E_SUBSTITUTE_RANGE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_TARGET_ALREADY_CONNECTED = new ResultID("E_TARGET_ALREADY_CONNECTED", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_TARGET_FAULT = new ResultID("E_TARGET_FAULT", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_TARGET_ITEM_BADTYPE = new ResultID("E_TARGET_ITEM_BADTYPE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_TARGET_ITEM_DISCONNECTED = new ResultID("E_TARGET_ITEM_DISCONNECTED", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_TARGET_ITEM_RANGE = new ResultID("E_TARGET_ITEM_RANGE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_TARGET_NO_ACCESSS = new ResultID("E_TARGET_NO_ACCESSS", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_TARGET_NO_WRITES_ATTEMPTED = new ResultID("E_TARGET_NO_WRITES_ATTEMPTED", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_TOO_MANY_CONNECTIONS = new ResultID("E_TOO_MANY_CONNECTIONS", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_UNKNOWN_ITEM_NAME = new ResultID("E_UNKNOWN_ITEM_NAME", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_UNKNOWN_ITEM_PATH = new ResultID("E_UNKNOWN_ITEM_PATH", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_UNKNOWN_SERVER_NAME = new ResultID("E_UNKNOWN_SERVER_NAME", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_UNKNOWN_SOURCE_ITEM = new ResultID("E_UNKNOWN_SOURCE_ITEM", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_UNKNOWN_TARGET_ITEM = new ResultID("E_UNKNOWN_TARGET_ITEM", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_UNSUPPORTED_SERVER_TYPE = new ResultID("E_UNSUPPORTED_SERVER_TYPE", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID E_VERSION_MISMATCH = new ResultID("E_VERSION_MISMATCH", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID S_CLAMP = new ResultID("S_CLAMP", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID S_TARGET_OVERRIDEN = new ResultID("S_TARGET_OVERRIDEN", "http://opcfoundation.org/DataExchange/"); | ||||
|         public static readonly ResultID S_TARGET_SUBSTITUTED = new ResultID("S_TARGET_SUBSTITUTED", "http://opcfoundation.org/DataExchange/"); | ||||
|     } | ||||
|  | ||||
|     public class Hda | ||||
|     { | ||||
|         public static readonly ResultID E_DATAEXISTS = new ResultID("E_DATAEXISTS", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID E_INVALIDAGGREGATE = new ResultID("E_INVALIDAGGREGATE", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID E_INVALIDATTRID = new ResultID("E_INVALIDATTRID", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID E_INVALIDDATATYPE = new ResultID("E_INVALIDDATATYPE", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID E_MAXEXCEEDED = new ResultID("E_MAXEXCEEDED", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|  | ||||
|         public static readonly ResultID E_NODATAEXISTS = new ResultID("E_NODATAEXISTS", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID E_NOT_AVAIL = new ResultID("E_NOT_AVAIL", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID E_UNKNOWNATTRID = new ResultID("E_UNKNOWNATTRID", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID S_CURRENTVALUE = new ResultID("S_CURRENTVALUE", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID S_EXTRADATA = new ResultID("S_EXTRADATA", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID S_INSERTED = new ResultID("S_INSERTED", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID S_MOREDATA = new ResultID("S_MOREDATA", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID S_NODATA = new ResultID("S_NODATA", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID S_REPLACED = new ResultID("S_REPLACED", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|         public static readonly ResultID W_NOFILTER = new ResultID("W_NOFILTER", "http://opcfoundation.org/HistoricalDataAccess/"); | ||||
|     } | ||||
|  | ||||
|     private class Names | ||||
|     { | ||||
|         internal const string CODE = "CO"; | ||||
|         internal const string NAME = "NA"; | ||||
|  | ||||
|         internal const string NAMESPACE = "NS"; | ||||
|     } | ||||
| } | ||||
| [Serializable] | ||||
| public class ItemProperty : ICloneable, IResult | ||||
| { | ||||
|     private System.Type m_datatype; | ||||
|     private string m_description; | ||||
|     private string m_diagnosticInfo; | ||||
|     private PropertyID m_id; | ||||
|     private string m_itemName; | ||||
|     private string m_itemPath; | ||||
|     private ResultID m_resultID = ResultID.S_OK; | ||||
|     private object m_value; | ||||
|     public System.Type DataType | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_datatype; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_datatype = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public string Description | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_description; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_description = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public string DiagnosticInfo | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_diagnosticInfo; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_diagnosticInfo = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public PropertyID ID | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_id; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_id = value; | ||||
|         } | ||||
|     } | ||||
|     public string ItemName | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_itemName; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_itemName = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public string ItemPath | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_itemPath; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_itemPath = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public ResultID ResultID | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_resultID; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_resultID = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public object Value | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_value; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_value = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public virtual object Clone() | ||||
|     { | ||||
|         ItemProperty obj = (ItemProperty)MemberwiseClone(); | ||||
|  | ||||
|         obj.Value = Comn.Convert.Clone(Value); | ||||
|         return obj; | ||||
|     } | ||||
| } | ||||
| public class Property | ||||
| { | ||||
|     public static readonly PropertyID ACCESSRIGHTS = new PropertyID("accessRights", 5, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID ALARM_AREA_LIST = new PropertyID("alarmAreaList", 302, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID ALARM_QUICK_HELP = new PropertyID("alarmQuickHelp", 301, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID CLOSELABEL = new PropertyID("closeLabel", 106, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID CONDITION_LOGIC = new PropertyID("conditionLogic", 304, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID CONDITION_STATUS = new PropertyID("conditionStatus", 300, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID CONSISTENCY_WINDOW = new PropertyID("consistencyWindow", 605, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID DATA_FILTER_VALUE = new PropertyID("dataFilterValue", 609, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID DATATYPE = new PropertyID("dataType", 1, "http://opcfoundation.org/DataAccess/"); | ||||
|  | ||||
|     public static readonly PropertyID DEADBAND = new PropertyID("deadband", 306, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID DESCRIPTION = new PropertyID("description", 101, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID DEVIATION_LIMIT = new PropertyID("deviationLimit", 312, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID DICTIONARY = new PropertyID("dictionary", 603, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID DICTIONARY_ID = new PropertyID("dictionaryID", 601, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID ENGINEERINGUINTS = new PropertyID("engineeringUnits", 100, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID EUINFO = new PropertyID("euInfo", 8, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID EUTYPE = new PropertyID("euType", 7, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID HI_LIMIT = new PropertyID("hiLimit", 308, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID HIGHEU = new PropertyID("highEU", 102, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID HIGHIR = new PropertyID("highIR", 104, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID HIHI_LIMIT = new PropertyID("hihiLimit", 307, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID LIMIT_EXCEEDED = new PropertyID("limitExceeded", 305, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID LO_LIMIT = new PropertyID("loLimit", 309, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID LOLO_LIMIT = new PropertyID("loloLimit", 310, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID LOWEU = new PropertyID("lowEU", 103, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID LOWIR = new PropertyID("lowIR", 105, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID MAXIMUM_VALUE = new PropertyID("maximumValue", 110, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID MINIMUM_VALUE = new PropertyID("minimumValue", 109, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID OPENLABEL = new PropertyID("openLabel", 107, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID PRIMARY_ALARM_AREA = new PropertyID("primaryAlarmArea", 303, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID QUALITY = new PropertyID("quality", 3, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID RATE_CHANGE_LIMIT = new PropertyID("rangeOfChangeLimit", 311, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID SCANRATE = new PropertyID("scanRate", 6, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID SOUNDFILE = new PropertyID("soundFile", 313, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID TIMESTAMP = new PropertyID("timestamp", 4, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID TIMEZONE = new PropertyID("timeZone", 108, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID TYPE_DESCRIPTION = new PropertyID("typeDescription", 604, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID TYPE_ID = new PropertyID("typeID", 602, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID TYPE_SYSTEM_ID = new PropertyID("typeSystemID", 600, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID UNCONVERTED_ITEM_ID = new PropertyID("unconvertedItemID", 607, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID UNFILTERED_ITEM_ID = new PropertyID("unfilteredItemID", 608, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID VALUE = new PropertyID("value", 2, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID VALUE_PRECISION = new PropertyID("valuePrecision", 111, "http://opcfoundation.org/DataAccess/"); | ||||
|     public static readonly PropertyID WRITE_BEHAVIOR = new PropertyID("writeBehavior", 606, "http://opcfoundation.org/DataAccess/"); | ||||
| } | ||||
| public class Type | ||||
| { | ||||
|     public static System.Type ANY_TYPE = typeof(object); | ||||
|     public static System.Type ARRAY_ANY_TYPE = typeof(object[]); | ||||
|     public static System.Type ARRAY_BOOLEAN = typeof(bool[]); | ||||
|     public static System.Type ARRAY_DATETIME = typeof(DateTime[]); | ||||
|     public static System.Type ARRAY_DECIMAL = typeof(decimal[]); | ||||
|     public static System.Type ARRAY_DOUBLE = typeof(double[]); | ||||
|     public static System.Type ARRAY_FLOAT = typeof(float[]); | ||||
|     public static System.Type ARRAY_INT = typeof(int[]); | ||||
|     public static System.Type ARRAY_LONG = typeof(long[]); | ||||
|     public static System.Type ARRAY_SHORT = typeof(short[]); | ||||
|     public static System.Type ARRAY_STRING = typeof(string[]); | ||||
|     public static System.Type ARRAY_UINT = typeof(uint[]); | ||||
|     public static System.Type ARRAY_ULONG = typeof(ulong[]); | ||||
|     public static System.Type ARRAY_USHORT = typeof(ushort[]); | ||||
|     public static System.Type BINARY = typeof(byte[]); | ||||
|     public static System.Type BOOLEAN = typeof(bool); | ||||
|     public static System.Type BYTE = typeof(byte); | ||||
|     public static System.Type DATETIME = typeof(DateTime); | ||||
|     public static System.Type DECIMAL = typeof(decimal); | ||||
|     public static System.Type DOUBLE = typeof(double); | ||||
|     public static System.Type DURATION = typeof(TimeSpan); | ||||
|     public static System.Type FLOAT = typeof(float); | ||||
|     public static System.Type ILLEGAL_TYPE = typeof(Type); | ||||
|     public static System.Type INT = typeof(int); | ||||
|     public static System.Type LONG = typeof(long); | ||||
|     public static System.Type SBYTE = typeof(sbyte); | ||||
|     public static System.Type SHORT = typeof(short); | ||||
|  | ||||
|     public static System.Type STRING = typeof(string); | ||||
|     public static System.Type UINT = typeof(uint); | ||||
|     public static System.Type ULONG = typeof(ulong); | ||||
|     public static System.Type USHORT = typeof(ushort); | ||||
|     public static System.Type[] Enumerate() | ||||
|     { | ||||
|         ArrayList arrayList = new ArrayList(); | ||||
|         FieldInfo[] fields = typeof(Type).GetFields(BindingFlags.Static | BindingFlags.Public); | ||||
|         foreach (FieldInfo fieldInfo in fields) | ||||
|         { | ||||
|             arrayList.Add(fieldInfo.GetValue(typeof(System.Type))); | ||||
|         } | ||||
|  | ||||
|         return (System.Type[])arrayList.ToArray(typeof(System.Type)); | ||||
|     } | ||||
| } | ||||
| [Serializable] | ||||
| public class PropertyDescription | ||||
| { | ||||
|     public static readonly PropertyDescription ACCESSRIGHTS = new PropertyDescription(Property.ACCESSRIGHTS, typeof(accessRights), "Item Access Rights"); | ||||
|     public static readonly PropertyDescription ALARM_AREA_LIST = new PropertyDescription(Property.ALARM_AREA_LIST, typeof(string), "Alarm Area List"); | ||||
|     public static readonly PropertyDescription ALARM_QUICK_HELP = new PropertyDescription(Property.ALARM_QUICK_HELP, typeof(string), "Alarm Quick Help"); | ||||
|     public static readonly PropertyDescription CLOSELABEL = new PropertyDescription(Property.CLOSELABEL, typeof(string), "Contact Close Label"); | ||||
|     public static readonly PropertyDescription CONDITION_LOGIC = new PropertyDescription(Property.CONDITION_LOGIC, typeof(string), "Condition Logic"); | ||||
|     public static readonly PropertyDescription CONDITION_STATUS = new PropertyDescription(Property.CONDITION_STATUS, typeof(string), "Condition Status"); | ||||
|     public static readonly PropertyDescription CONSISTENCY_WINDOW = new PropertyDescription(Property.CONSISTENCY_WINDOW, typeof(string), "Consistency Window"); | ||||
|     public static readonly PropertyDescription DATA_FILTER_VALUE = new PropertyDescription(Property.DATA_FILTER_VALUE, typeof(string), "Data Filter Value"); | ||||
|     public static readonly PropertyDescription DATATYPE = new PropertyDescription(Property.DATATYPE, typeof(System.Type), "Item Canonical DataType"); | ||||
|     public static readonly PropertyDescription DEADBAND = new PropertyDescription(Property.DEADBAND, typeof(double), "Deadband"); | ||||
|     public static readonly PropertyDescription DESCRIPTION = new PropertyDescription(Property.DESCRIPTION, typeof(string), "Item Description"); | ||||
|     public static readonly PropertyDescription DEVIATION_LIMIT = new PropertyDescription(Property.DEVIATION_LIMIT, typeof(double), "Deviation Limit"); | ||||
|     public static readonly PropertyDescription DICTIONARY = new PropertyDescription(Property.DICTIONARY, typeof(object), "Dictionary"); | ||||
|     public static readonly PropertyDescription DICTIONARY_ID = new PropertyDescription(Property.DICTIONARY_ID, typeof(string), "Dictionary ID"); | ||||
|     public static readonly PropertyDescription ENGINEERINGUINTS = new PropertyDescription(Property.ENGINEERINGUINTS, typeof(string), "EU Units"); | ||||
|     public static readonly PropertyDescription EUINFO = new PropertyDescription(Property.EUINFO, typeof(string[]), "Item EU Info"); | ||||
|     public static readonly PropertyDescription EUTYPE = new PropertyDescription(Property.EUTYPE, typeof(euType), "Item EU Type"); | ||||
|     public static readonly PropertyDescription HI_LIMIT = new PropertyDescription(Property.HI_LIMIT, typeof(double), "Hi Limit"); | ||||
|     public static readonly PropertyDescription HIGHEU = new PropertyDescription(Property.HIGHEU, typeof(double), "High EU"); | ||||
|     public static readonly PropertyDescription HIGHIR = new PropertyDescription(Property.HIGHIR, typeof(double), "High Instrument Range"); | ||||
|     public static readonly PropertyDescription HIHI_LIMIT = new PropertyDescription(Property.HIHI_LIMIT, typeof(double), "HiHi Limit"); | ||||
|     public static readonly PropertyDescription LIMIT_EXCEEDED = new PropertyDescription(Property.LIMIT_EXCEEDED, typeof(string), "Limit Exceeded"); | ||||
|     public static readonly PropertyDescription LO_LIMIT = new PropertyDescription(Property.LO_LIMIT, typeof(double), "Lo Limit"); | ||||
|     public static readonly PropertyDescription LOLO_LIMIT = new PropertyDescription(Property.LOLO_LIMIT, typeof(double), "LoLo Limit"); | ||||
|     public static readonly PropertyDescription LOWEU = new PropertyDescription(Property.LOWEU, typeof(double), "Low EU"); | ||||
|     public static readonly PropertyDescription LOWIR = new PropertyDescription(Property.LOWIR, typeof(double), "Low Instrument Range"); | ||||
|     public static readonly PropertyDescription MAXIMUM_VALUE = new PropertyDescription(Property.MAXIMUM_VALUE, typeof(object), "Maximum Value"); | ||||
|     public static readonly PropertyDescription MINIMUM_VALUE = new PropertyDescription(Property.MINIMUM_VALUE, typeof(object), "Minimum Value"); | ||||
|     public static readonly PropertyDescription OPENLABEL = new PropertyDescription(Property.OPENLABEL, typeof(string), "Contact Open Label"); | ||||
|     public static readonly PropertyDescription PRIMARY_ALARM_AREA = new PropertyDescription(Property.PRIMARY_ALARM_AREA, typeof(string), "Primary Alarm Area"); | ||||
|     public static readonly PropertyDescription QUALITY = new PropertyDescription(Property.QUALITY, typeof(Quality), "Item Quality"); | ||||
|     public static readonly PropertyDescription RATE_CHANGE_LIMIT = new PropertyDescription(Property.RATE_CHANGE_LIMIT, typeof(double), "Rate of Change Limit"); | ||||
|     public static readonly PropertyDescription SCANRATE = new PropertyDescription(Property.SCANRATE, typeof(float), "Server Scan Rate"); | ||||
|     public static readonly PropertyDescription SOUNDFILE = new PropertyDescription(Property.SOUNDFILE, typeof(string), "Sound File"); | ||||
|     public static readonly PropertyDescription TIMESTAMP = new PropertyDescription(Property.TIMESTAMP, typeof(DateTime), "Item Timestamp"); | ||||
|     public static readonly PropertyDescription TIMEZONE = new PropertyDescription(Property.TIMEZONE, typeof(int), "Timezone"); | ||||
|     public static readonly PropertyDescription TYPE_DESCRIPTION = new PropertyDescription(Property.TYPE_DESCRIPTION, typeof(string), "Type Description"); | ||||
|     public static readonly PropertyDescription TYPE_ID = new PropertyDescription(Property.TYPE_ID, typeof(string), "Type ID"); | ||||
|     public static readonly PropertyDescription TYPE_SYSTEM_ID = new PropertyDescription(Property.TYPE_SYSTEM_ID, typeof(string), "Type System ID"); | ||||
|     public static readonly PropertyDescription UNCONVERTED_ITEM_ID = new PropertyDescription(Property.UNCONVERTED_ITEM_ID, typeof(string), "Unconverted Item ID"); | ||||
|     public static readonly PropertyDescription UNFILTERED_ITEM_ID = new PropertyDescription(Property.UNFILTERED_ITEM_ID, typeof(string), "Unfiltered Item ID"); | ||||
|     public static readonly PropertyDescription VALUE = new PropertyDescription(Property.VALUE, typeof(object), "Item Value"); | ||||
|     public static readonly PropertyDescription VALUE_PRECISION = new PropertyDescription(Property.VALUE_PRECISION, typeof(object), "Value Precision"); | ||||
|     public static readonly PropertyDescription WRITE_BEHAVIOR = new PropertyDescription(Property.WRITE_BEHAVIOR, typeof(string), "Write Behavior"); | ||||
|     private PropertyID m_id; | ||||
|  | ||||
|     private string m_name; | ||||
|     private System.Type m_type; | ||||
|     public PropertyDescription(PropertyID id, System.Type type, string name) | ||||
|     { | ||||
|         ID = id; | ||||
|         Type = type; | ||||
|         Name = name; | ||||
|     } | ||||
|  | ||||
|     public PropertyID ID | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_id; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_id = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public string Name | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_name; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_name = value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public System.Type Type | ||||
|     { | ||||
|         get | ||||
|         { | ||||
|             return m_type; | ||||
|         } | ||||
|         set | ||||
|         { | ||||
|             m_type = value; | ||||
|         } | ||||
|     } | ||||
|     public static PropertyDescription[] Enumerate() | ||||
|     { | ||||
|         ArrayList arrayList = new ArrayList(); | ||||
|         FieldInfo[] fields = typeof(PropertyDescription).GetFields(BindingFlags.Static | BindingFlags.Public); | ||||
|         foreach (FieldInfo fieldInfo in fields) | ||||
|         { | ||||
|             arrayList.Add(fieldInfo.GetValue(typeof(PropertyDescription))); | ||||
|         } | ||||
|  | ||||
|         return (PropertyDescription[])arrayList.ToArray(typeof(PropertyDescription)); | ||||
|     } | ||||
|  | ||||
|     public static PropertyDescription Find(PropertyID id) | ||||
|     { | ||||
|         FieldInfo[] fields = typeof(PropertyDescription).GetFields(BindingFlags.Static | BindingFlags.Public); | ||||
|         for (int i = 0; i < fields.Length; i++) | ||||
|         { | ||||
|             PropertyDescription propertyDescription = (PropertyDescription)fields[i].GetValue(typeof(PropertyDescription)); | ||||
|             if (propertyDescription.ID == id) | ||||
|             { | ||||
|                 return propertyDescription; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return Name; | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +1,15 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| global using System; | ||||
| 
 | ||||
| global using TouchSocket.Core; | ||||
| @@ -0,0 +1,477 @@ | ||||
| #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.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using System.Timers; | ||||
|  | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Da; | ||||
| using ThingsGateway.Foundation.Adapter.OPCDA.Rcw; | ||||
| using ThingsGateway.Foundation.Extension; | ||||
| using ThingsGateway.Foundation.Extension.Enumerator; | ||||
| using ThingsGateway.Foundation.Extension.Generic; | ||||
|  | ||||
| using Timer = System.Timers.Timer; | ||||
|  | ||||
| //部分非托管交互代码来自https://gitee.com/Zer0Day/opc-client与OPC基金会opcnet库,更改部分逻辑 | ||||
|  | ||||
| namespace ThingsGateway.Foundation.Adapter.OPCDA; | ||||
|  | ||||
| public delegate void DataChangedEventHandler(List<ItemReadResult> values); | ||||
|  | ||||
| public class OPCDAClient : DisposableObject | ||||
| { | ||||
|     /// <summary> | ||||
|     /// 当前配置 | ||||
|     /// </summary> | ||||
|     public OPCNode OPCNode; | ||||
|     private ILog _logger; | ||||
|     private EasyLock checkLock = new(); | ||||
|     private Timer checkTimer; | ||||
|     private bool FirstConnect; | ||||
|     private int IsExit = 1; | ||||
|     /// <summary> | ||||
|     /// 当前保存的需订阅列表 | ||||
|     /// </summary> | ||||
|     private Dictionary<string, List<OpcItem>> ItemDicts = new(); | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 订阅返回值队列 | ||||
|     /// </summary> | ||||
|     private ConcurrentQueue<ItemReadResult> ItemReadResults = new ConcurrentQueue<ItemReadResult>(); | ||||
|  | ||||
|     private OpcServer m_server; | ||||
|     //定义组对象(订阅者) | ||||
|     public OPCDAClient(ILog logger) | ||||
|     { | ||||
|         _logger = logger; | ||||
|         Task.Run(dataChangedHandlerInvoke); | ||||
|     } | ||||
|     public event DataChangedEventHandler DataChangedHandler; | ||||
|     public bool IsConnected => m_server?.IsConnected == true; | ||||
|     private List<OpcGroup> Groups => m_server.OpcGroups; | ||||
|     /// <summary> | ||||
|     /// 添加节点,需要在连接成功后执行 | ||||
|     /// </summary> | ||||
|     /// <param name="tags">组名称/变量节点,注意每次添加的组名称不能相同</param> | ||||
|     public OperResult AddTags(Dictionary<string, List<OpcItem>> tags) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             StringBuilder stringBuilder = new StringBuilder(); | ||||
|             if (IsExit == 1) return new("对象已释放"); | ||||
|             foreach (var item in tags) | ||||
|             { | ||||
|                 if (IsExit == 1) return new("对象已释放"); | ||||
|                 var subscription = m_server.AddGroup(item.Key, true, OPCNode.UpdateRate, OPCNode.DeadBand); | ||||
|                 if (subscription.IsSuccess) | ||||
|                 { | ||||
|                     subscription.Content.ActiveSubscribe = OPCNode.ActiveSubscribe; | ||||
|                     subscription.Content.OnDataChanged += Subscription_OnDataChanged; | ||||
|                     subscription.Content.OnReadCompleted += Subscription_OnDataChanged; | ||||
|  | ||||
|                     var result = subscription.Content.AddOpcItem(item.Value.ToArray()); | ||||
|                     if (!result.IsSuccess) | ||||
|                     { | ||||
|                         stringBuilder.AppendLine("添加变量失败" + result.Message); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         ItemDicts.AddOrUpdate(item.Key, item.Value); | ||||
|                         _logger?.Debug($"添加变量{item.Value.Select(a => a.ItemID).ToList().ToJson()}成功"); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     stringBuilder.AppendLine("添加组失败" + subscription.Message); | ||||
|                 } | ||||
|             } | ||||
|             for (int i = 0; i < Groups?.Count; i++) | ||||
|             { | ||||
|                 var group = Groups[i]; | ||||
|                 if (group != null) | ||||
|                 { | ||||
|                     if (group.OpcItems.Count == 0) | ||||
|                     { | ||||
|                         ItemDicts.Remove(group.Name); | ||||
|                         m_server.RemoveGroup(group); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (stringBuilder.Length > 0) | ||||
|             { | ||||
|                 return new(stringBuilder.ToString()); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return OperResult.CreateSuccessResult(); | ||||
|             } | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new(ex); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 连接服务器 | ||||
|     /// </summary> | ||||
|     public void Connect() | ||||
|     { | ||||
|         Interlocked.CompareExchange(ref IsExit, 0, 1); | ||||
|         connect(); | ||||
|         FirstConnect = true; | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 断开连接 | ||||
|     /// </summary> | ||||
|     public void Disconnect() | ||||
|     { | ||||
|         Interlocked.CompareExchange(ref IsExit, 1, 0); | ||||
|         disconnect(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 浏览节点 | ||||
|     /// </summary> | ||||
|     /// <param name="itemId"></param> | ||||
|     /// <returns></returns> | ||||
|     public OperResult<List<BrowseElement>> GetBrowse(string itemId = null) | ||||
|     { | ||||
|         return this.m_server?.Browse(itemId); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 获取服务状态 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     public OperResult<ServerStatus> GetStatus() | ||||
|     { | ||||
|         return this.m_server?.GetServerStatus(); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 初始化设置 | ||||
|     /// </summary> | ||||
|     /// <param name="node"></param> | ||||
|     public void Init(OPCNode node = null) | ||||
|     { | ||||
|         if (node != null) | ||||
|             OPCNode = node; | ||||
|         checkTimer?.Stop(); | ||||
|         checkTimer?.SafeDispose(); | ||||
|         checkTimer = new Timer(OPCNode.CheckRate); | ||||
|         checkTimer.Elapsed += checkTimer_Elapsed; | ||||
|         checkTimer.Start(); | ||||
|         m_server?.SafeDispose(); | ||||
|         m_server = new OpcServer(OPCNode.OPCName, OPCNode.OPCIP); | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 手动读取变量,结果会在订阅事件中返回 | ||||
|     /// </summary> | ||||
|     /// <param name="groupName"></param> | ||||
|     /// <returns></returns> | ||||
|     public OperResult ReadGroup(string groupName = null) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             if (connect()) | ||||
|             { | ||||
|                 var groups = groupName != null ? Groups.Where(a => a.Name == groupName) : Groups; | ||||
|                 foreach (var group in groups) | ||||
|                 { | ||||
|                     if (group.OpcItems.Count > 0) | ||||
|                     { | ||||
|                         return group.ReadAsync(); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         return new OperResult("不存在任何变量"); | ||||
|                     } | ||||
|                 } | ||||
|                 return new OperResult("不存在任何变量"); | ||||
|             } | ||||
|             return new OperResult("未初始化连接"); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             return new OperResult(ex); | ||||
|  | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 移除节点 | ||||
|     /// </summary> | ||||
|     /// <param name="tags"></param> | ||||
|     public void RemoveTags(List<string> tags) | ||||
|     { | ||||
|         foreach (var item in tags) | ||||
|         { | ||||
|             if (IsExit == 1) return; | ||||
|             var opcGroup = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == item)); | ||||
|             if (opcGroup == null) | ||||
|             { | ||||
|                 _logger.Warning("找不到变量" + item); | ||||
|                 continue; | ||||
|             } | ||||
|             var tag = opcGroup.OpcItems.Where(a => item == a.ItemID); | ||||
|             var result = opcGroup.RemoveItem(tag.ToArray()); | ||||
|             if (!result.IsSuccess) | ||||
|             { | ||||
|                 _logger.Warning($"移除变量{item}-" + result.Message); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 _logger?.Debug($"移除变量{item}成功"); | ||||
|             } | ||||
|             if (opcGroup.OpcItems.Count == 0) | ||||
|             { | ||||
|                 ItemDicts.Remove(opcGroup.Name); | ||||
|                 m_server.RemoveGroup(opcGroup); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 ItemDicts[opcGroup.Name].RemoveWhere(a => tag.Contains(a)); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 设置节点并保存 | ||||
|     /// </summary> | ||||
|     /// <param name="tags"></param> | ||||
|     /// <returns></returns> | ||||
|     public Dictionary<string, List<OpcItem>> SetTags(List<string> tags) | ||||
|     { | ||||
|         int i = 0; | ||||
|         ItemDicts = tags.ToList().ConvertAll(o => new OpcItem(o) | ||||
|           ).ChunkTrivialBetter(OPCNode.GroupSize).ToDictionary(a => "default" + (i++)); | ||||
|         return ItemDicts; | ||||
|     } | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return OPCNode.ToString(); | ||||
|     } | ||||
|     /// <summary> | ||||
|     /// 写入值 | ||||
|     /// </summary> | ||||
|     /// <param name="valueName">写入</param> | ||||
|     /// <param name="value"></param> | ||||
|     /// <returns></returns> | ||||
|     public OperResult Write(string valueName, object value) | ||||
|     { | ||||
|         if (connect()) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 var group = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == valueName)); | ||||
|                 if (group == null) | ||||
|                     return new OperResult("不存在该变量" + valueName); | ||||
|                 var item = group.OpcItems.Where(it => it.ItemID == valueName).FirstOrDefault(); | ||||
|                 int[] serverHandle = new int[1] { item.ServerHandle }; | ||||
|                 object[] Value = new object[1] { value }; | ||||
|                 int[] PErrors = new int[1]; | ||||
|                 group.WriteAsync(Value, serverHandle, out PErrors); | ||||
|                 //itemvalue.Value = Convert.ChangeType(value, item.RunTimeDataType); | ||||
|                 if (PErrors != null && PErrors.First() == 0) | ||||
|                 { | ||||
|                     return OperResult.CreateSuccessResult(); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     return new OperResult(); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 return new OperResult(ex.Message); | ||||
|             } | ||||
|         } | ||||
|         return new OperResult(); | ||||
|     } | ||||
|  | ||||
|     protected override void Dispose(bool disposing) | ||||
|     { | ||||
|         disconnect(); | ||||
|         base.Dispose(disposing); | ||||
|     } | ||||
|  | ||||
|     private void AddTags() | ||||
|     { | ||||
|         var result = AddTags(ItemDicts); | ||||
|         if (!result.IsSuccess) | ||||
|         { | ||||
|             _logger.Warning(result.Message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void checkTimer_Elapsed(object sender, ElapsedEventArgs e) | ||||
|     { | ||||
|         if (checkLock.IsWaitting) return; | ||||
|         checkLock.Lock(); | ||||
|         try | ||||
|         { | ||||
|             if (IsExit == 0) | ||||
|             { | ||||
|                 var status = m_server.GetServerStatus(); | ||||
|                 if (status.IsSuccess) | ||||
|                 { | ||||
|                     _logger?.Trace(OPCNode.ToString() + "OPC状态检查正常!"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     if (IsExit == 0 && FirstConnect) | ||||
|                     { | ||||
|                         if (connect()) | ||||
|                         { | ||||
|                             _logger?.Warning(OPCNode.ToString() + "OPC重新链接成功!"); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|  | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var timeer = sender as Timer; | ||||
|                 timeer.Enabled = false; | ||||
|                 timeer.Stop(); | ||||
|             } | ||||
|         } | ||||
|         finally { checkLock.UnLock(); } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private bool connect() | ||||
|     { | ||||
|         lock (this) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 if (m_server?.IsConnected == true) | ||||
|                 { | ||||
|                     var status = m_server.GetServerStatus(); | ||||
|                     if (!status.IsSuccess) | ||||
|                     { | ||||
|                         var status1 = m_server.GetServerStatus(); | ||||
|                         if (!status1.IsSuccess) | ||||
|                         { | ||||
|                             _logger?.Error(status1.Message); | ||||
|                             //失败重新连接 | ||||
|                             try | ||||
|                             { | ||||
|                                 //disconnect(); | ||||
|                                 Init(OPCNode); | ||||
|                                 _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 正在连接"); | ||||
|                                 var result = m_server?.Connect(); | ||||
|                                 if (result.IsSuccess) | ||||
|                                 { | ||||
|                                     _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 连接成功"); | ||||
|                                     AddTags(); | ||||
|                                 } | ||||
|                                 else | ||||
|                                 { | ||||
|                                     _logger?.Error(result.Message); | ||||
|                                     return IsConnected; | ||||
|                                 } | ||||
|                             } | ||||
|                             catch (Exception ex2) | ||||
|                             { | ||||
|                                 _logger?.Exception(ex2); | ||||
|                                 return IsConnected; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //disconnect(); | ||||
|                     Init(OPCNode); | ||||
|                     _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 正在连接"); | ||||
|                     var result = m_server?.Connect(); | ||||
|                     if (result.IsSuccess) | ||||
|                     { | ||||
|                         _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 连接成功"); | ||||
|                         AddTags(); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         _logger?.Error(result.Message); | ||||
|                         return IsConnected; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 _logger?.Exception(OPCNode.ToString(), ex); | ||||
|                 return false; | ||||
|             } | ||||
|             return IsConnected; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// 订阅通知线程 | ||||
|     /// </summary> | ||||
|     /// <returns></returns> | ||||
|     private async Task dataChangedHandlerInvoke() | ||||
|     { | ||||
|         while (!DisposedValue) | ||||
|         { | ||||
|             if (ItemReadResults.Count > 0) | ||||
|                 DataChangedHandler?.Invoke(ItemReadResults.ToListWithDequeue()); | ||||
|             if (OPCNode == null) | ||||
|                 await Task.Delay(1000); | ||||
|             else | ||||
|                 await Task.Delay(OPCNode.UpdateRate == 0 ? 1000 : OPCNode.UpdateRate); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void disconnect() | ||||
|     { | ||||
|         if (IsConnected) | ||||
|             _logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 断开连接"); | ||||
|         checkTimer.Enabled = false; | ||||
|         checkTimer.Stop(); | ||||
|         try | ||||
|         { | ||||
|             m_server?.SafeDispose(); | ||||
|             m_server = null; | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             _logger?.Exception(ToString(), ex); | ||||
|         } | ||||
|     } | ||||
|     private void Subscription_OnDataChanged(ItemReadResult[] values) | ||||
|     { | ||||
|         for (int i = 0; i < values.Length; i++) | ||||
|         { | ||||
|             ItemReadResults.Enqueue(values[i]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| #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; | ||||
| public class OPCNode | ||||
| { | ||||
|     public bool ActiveSubscribe { get; set; } = true; | ||||
|     public int CheckRate { get; set; } = 600000; | ||||
|     public float DeadBand { get; set; } = 0; | ||||
|     public int GroupSize { get; set; } = 500; | ||||
|     public string OPCIP { get; set; } = "localhost"; | ||||
|     public string OPCName { get; set; } = "Kepware.KEPServerEX.V6"; | ||||
|     public int UpdateRate { get; set; } = 1000; | ||||
|     public override string ToString() | ||||
|     { | ||||
|         return $"{(OPCIP.IsNullOrEmpty() ? "localhost" : OPCIP)}:{OPCName}"; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,37 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
| 	<PropertyGroup> | ||||
| 		<LangVersion>latestMajor</LangVersion> | ||||
| 		<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks> | ||||
| 		<NoWarn>CA1416;CS8625;1591;</NoWarn> | ||||
| 		<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>--> | ||||
| 		<Version>1.7.0</Version> | ||||
| 		<Platforms>AnyCPU</Platforms> | ||||
| 		<OutputType>Library</OutputType> | ||||
| 		 | ||||
| 		<Title>ThingsGateway.Foundation.Adapter.Modbus</Title> | ||||
| 		<GeneratePackageOnBuild>True</GeneratePackageOnBuild> | ||||
| 		<Authors>Diego</Authors> | ||||
| 		<Description>OPCDA通讯类库,支持OPCDAClient</Description> | ||||
| 		<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> | ||||
| 		<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance> | ||||
| 		<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild> | ||||
| 		<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl> | ||||
| 		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl> | ||||
| 		<PackageOutputPath>$(SolutionDir)</PackageOutputPath> | ||||
| 		<PackageReadmeFile>README.md</PackageReadmeFile> | ||||
| 		<Platforms>AnyCPU;x86</Platforms> | ||||
| 	</PropertyGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <None Include="..\..\..\README.md"> | ||||
| 	    <Pack>True</Pack> | ||||
| 	    <PackagePath>\</PackagePath> | ||||
| 	  </None> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| 	<ItemGroup> | ||||
| 	  <ProjectReference Include="..\..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" /> | ||||
| 	</ItemGroup> | ||||
|  | ||||
| </Project> | ||||
| @@ -1,16 +1,15 @@ | ||||
| //------------------------------------------------------------------------------ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://thingsgateway.cn/ | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway-docs/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
| 
 | ||||
| namespace ThingsGateway.Gateway.Application; | ||||
| global using System; | ||||
| 
 | ||||
| public interface IDriverUIBase | ||||
| { | ||||
|     public object Driver { get; set; } | ||||
| } | ||||
| global using TouchSocket.Core; | ||||
| @@ -0,0 +1,171 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Opc.Ua; | ||||
| using Opc.Ua.Client; | ||||
|  | ||||
| namespace WebPlatform.OPCUALayer | ||||
| { | ||||
|     public class DataTypeAnalyzer | ||||
|     { | ||||
|         private Session m_session; | ||||
|  | ||||
|         public DataTypeAnalyzer(Session session) | ||||
|         { | ||||
|             this.m_session = session; | ||||
|         } | ||||
|  | ||||
|         public static BuiltInType GetBuiltinTypeFromTypeName(string nameSpace, string type) | ||||
|         { | ||||
|             switch (nameSpace) | ||||
|             { | ||||
|                 case "opc": | ||||
|                     return GetBuiltinTypeFromBinaryTypeName(type); | ||||
|                 case "ua": | ||||
|                     return GetBuiltinTypeFromUaTypeName(type); | ||||
|                 default: | ||||
|                     return GetBuiltinTypeFromUaTypeName(type); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private static BuiltInType GetBuiltinTypeFromUaTypeName(string type) | ||||
|         { | ||||
|             Type mType = Type.GetType("Opc.Ua." + type + ", Opc.Ua.Core"); | ||||
|             BuiltInType builtInType = TypeInfo.GetBuiltInType(TypeInfo.GetDataTypeId(mType)); | ||||
|             return builtInType; | ||||
|         } | ||||
|  | ||||
|         private static BuiltInType GetBuiltinTypeFromBinaryTypeName(string type) | ||||
|         { | ||||
|             switch (type) | ||||
|             { | ||||
|                 case "Bit": | ||||
|                 case "Boolean": | ||||
|                     return BuiltInType.Boolean; | ||||
|                 case "SByte": | ||||
|                     return BuiltInType.SByte; | ||||
|                 case "Byte": | ||||
|                     return BuiltInType.Byte; | ||||
|                 case "Int16": | ||||
|                     return BuiltInType.Int16; | ||||
|                 case "UInt16": | ||||
|                     return BuiltInType.UInt16; | ||||
|                 case "Int32": | ||||
|                     return BuiltInType.Int32; | ||||
|                 case "UInt32": | ||||
|                     return BuiltInType.UInt32; | ||||
|                 case "Int64": | ||||
|                     return BuiltInType.Int64; | ||||
|                 case "UInt64": | ||||
|                     return BuiltInType.UInt64; | ||||
|                 case "Float": | ||||
|                     return BuiltInType.Float; | ||||
|                 case "Double": | ||||
|                     return BuiltInType.Double; | ||||
|                 case "Char": | ||||
|                 case "WideChar": | ||||
|                 case "String": | ||||
|                 case "CharArray": | ||||
|                 case "WideString": | ||||
|                 case "WideCharArray": | ||||
|                     return BuiltInType.String; | ||||
|                 case "DateTime": | ||||
|                     return BuiltInType.DateTime; | ||||
|                 case "ByteString": | ||||
|                     return BuiltInType.ByteString; | ||||
|                 case "Guid": | ||||
|                     return BuiltInType.Guid; | ||||
|                 default: | ||||
|                     return BuiltInType.Null; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         internal NodeId GetDataTypeEncodingNodeId(NodeId dataTypeNodeId) | ||||
|         { | ||||
|             m_session.Browse( | ||||
|                 null, | ||||
|                 null, | ||||
|                 dataTypeNodeId, | ||||
|                 0u, | ||||
|                 BrowseDirection.Forward, | ||||
|                 ReferenceTypeIds.HasEncoding, | ||||
|                 true, | ||||
|                 (uint)NodeClass.Object, | ||||
|                 out var continuationPoint, | ||||
|                 out var refDescriptionCollection); | ||||
|  | ||||
|             //Choose always first encoding | ||||
|             return (NodeId)refDescriptionCollection[0].NodeId; | ||||
|         } | ||||
|  | ||||
|         internal NodeId GetDataTypeDescriptionNodeId(NodeId dataTypeEncodingNodeId) | ||||
|         { | ||||
|             m_session.Browse( | ||||
|                 null, | ||||
|                 null, | ||||
|                 dataTypeEncodingNodeId, //starting node is always an EncodingNode | ||||
|                 0u, | ||||
|                 BrowseDirection.Forward, | ||||
|                 ReferenceTypeIds.HasDescription,  //HasDescription reference | ||||
|                 true, | ||||
|                 (uint)NodeClass.Variable, | ||||
|                 out var continuationPoint, | ||||
|                 out var refDescriptionCollection); | ||||
|  | ||||
|             return (NodeId)refDescriptionCollection[0].NodeId; | ||||
|         } | ||||
|  | ||||
|         internal string GetDictionary(NodeId dataTypeDescriptionNodeId) | ||||
|         { | ||||
|             m_session.Browse( | ||||
|                 null, | ||||
|                 null, | ||||
|                 dataTypeDescriptionNodeId, //the starting node is a DataTypeDescription | ||||
|                 0u, | ||||
|                 BrowseDirection.Inverse, //It is an inverse Reference  | ||||
|                 ReferenceTypeIds.HasComponent, //So it is ComponentOf | ||||
|                 true, | ||||
|                 (uint)NodeClass.Variable, | ||||
|                 out var continuationPoint, | ||||
|                 out var refDescriptionCollection); | ||||
|  | ||||
|             var dataTypeDictionaryNodeId = (NodeId)refDescriptionCollection[0].NodeId; | ||||
|  | ||||
|             var dataValueCollection = Read(dataTypeDictionaryNodeId, Attributes.Value); | ||||
|  | ||||
|             return System.Text.Encoding.UTF8.GetString((byte[])dataValueCollection[0].Value); | ||||
|         } | ||||
|  | ||||
|         private DataValueCollection Read(NodeId nodeId, uint attributeId) | ||||
|         { | ||||
|             ReadValueIdCollection nodeToRead = new ReadValueIdCollection(1); | ||||
|  | ||||
|             ReadValueId vId = new ReadValueId() | ||||
|             { | ||||
|                 NodeId = nodeId, | ||||
|                 AttributeId = attributeId | ||||
|             }; | ||||
|  | ||||
|             nodeToRead.Add(vId); | ||||
|  | ||||
|             var responseRead = m_session.Read(null, | ||||
|                 0, | ||||
|                 TimestampsToReturn.Both, | ||||
|                 nodeToRead, | ||||
|                 out var dataValueCollection, | ||||
|                 out var diagnCollection | ||||
|             ); | ||||
|  | ||||
|             return dataValueCollection; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,147 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Opc.Ua; | ||||
|  | ||||
| namespace WebPlatform.OPCUALayer | ||||
| { | ||||
|     public static class DataTypeSchemaGenerator | ||||
|     { | ||||
|         public static JSchema GenerateSchemaForArray(int[] dimensions, JSchema innermostSchema) | ||||
|         { | ||||
|             var dimLen = dimensions.Length; | ||||
|  | ||||
|             if (dimLen == 1) | ||||
|             { | ||||
|                 var schema = new JSchema | ||||
|                 { | ||||
|                     Type = JSchemaType.Array, | ||||
|                     Items = { innermostSchema }, | ||||
|                     MinimumItems = dimensions[0], | ||||
|                     MaximumItems = dimensions[0] | ||||
|                 }; | ||||
|  | ||||
|                 return schema; | ||||
|             } | ||||
|  | ||||
|             JSchema innerSchema = new JSchema(); | ||||
|             JSchema outerSchema = new JSchema(); | ||||
|             for (int dim = dimLen - 1; dim >= 0; dim--) | ||||
|             { | ||||
|                 if (dim == dimLen - 1) | ||||
|                 { | ||||
|                     innerSchema = new JSchema | ||||
|                     { | ||||
|                         Type = JSchemaType.Array, | ||||
|                         Items = { innermostSchema }, | ||||
|                         MinimumItems = dimensions[dim], | ||||
|                         MaximumItems = dimensions[dim] | ||||
|                     }; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     outerSchema = new JSchema | ||||
|                     { | ||||
|                         Type = JSchemaType.Array, | ||||
|                         Items = { innerSchema }, | ||||
|                         MinimumItems = dimensions[dim], | ||||
|                         MaximumItems = dimensions[dim] | ||||
|                     }; | ||||
|                     innerSchema = outerSchema; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return outerSchema; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Used for generating schema based on standard types used in DataTypeDictionary. | ||||
|         /// They are defined in Part 3 - Table C.9 | ||||
|         /// </summary> | ||||
|         /// <param name="type"></param> | ||||
|         /// <returns></returns> | ||||
|         public static JSchema GenerateSchemaForStandardTypeDescription(BuiltInType type) | ||||
|         { | ||||
|             switch (type) | ||||
|             { | ||||
|                 case BuiltInType.Boolean: | ||||
|                     return new JSchema { Type = JSchemaType.Boolean }; | ||||
|                 case BuiltInType.SByte: | ||||
|                 case BuiltInType.Byte: | ||||
|                 case BuiltInType.Int16: | ||||
|                 case BuiltInType.UInt16: | ||||
|                 case BuiltInType.Int32: | ||||
|                 case BuiltInType.UInt32: | ||||
|                 case BuiltInType.Int64: | ||||
|                 case BuiltInType.UInt64: | ||||
|                 case BuiltInType.Float: | ||||
|                 case BuiltInType.Double: | ||||
|                     return new JSchema { Type = JSchemaType.Number }; | ||||
|                 case BuiltInType.String: | ||||
|                 case BuiltInType.DateTime: | ||||
|                 case BuiltInType.Guid: | ||||
|                 case BuiltInType.DiagnosticInfo: | ||||
|                 case BuiltInType.NodeId: | ||||
|                 case BuiltInType.ExpandedNodeId: | ||||
|                 case BuiltInType.XmlElement: | ||||
|                 case BuiltInType.ByteString: | ||||
|                     return new JSchema { Type = JSchemaType.String }; | ||||
|                 case BuiltInType.LocalizedText: | ||||
|                     return new JSchema() | ||||
|                     { | ||||
|                         Type = JSchemaType.Object, | ||||
|                         Properties = { | ||||
|                                 { "Locale", new JSchema { Type = JSchemaType.String } }, | ||||
|                                 { "Text", new JSchema { Type = JSchemaType.String } } | ||||
|                             } | ||||
|                     }; ; | ||||
|                 case BuiltInType.StatusCode: | ||||
|                     return new JSchema | ||||
|                     { | ||||
|                         Type = JSchemaType.Object, | ||||
|                         Properties = | ||||
|                             { | ||||
|                                 { "code", new JSchema | ||||
|                                     { | ||||
|                                         Type = JSchemaType.String, | ||||
|                                         Enum = { "Good", "Uncertain", "Bad" } | ||||
|                                     } | ||||
|                                 }, | ||||
|                                 { "structureChanged", new JSchema{ Type = JSchemaType.Boolean } } | ||||
|                             } | ||||
|                     }; ; | ||||
|                 case BuiltInType.QualifiedName: | ||||
|                     return new JSchema | ||||
|                     { | ||||
|                         Type = JSchemaType.Object, | ||||
|                         Properties = | ||||
|                             { | ||||
|                                 { "NamespaceIndex", new JSchema{ Type = JSchemaType.Integer} }, | ||||
|                                 { "Name", new JSchema{ Type = JSchemaType.String } } | ||||
|                             } | ||||
|                     }; | ||||
|                 case BuiltInType.Enumeration: | ||||
|                     return new JSchema | ||||
|                     { | ||||
|                         Type = JSchemaType.Object, | ||||
|                         Properties = | ||||
|                             { | ||||
|                                 { "EnumValue", new JSchema { Type = JSchemaType.Integer } }, | ||||
|                                 { "EnumLabel", new JSchema { Type = JSchemaType.String } } | ||||
|                             } | ||||
|                     }; ; | ||||
|                 default: | ||||
|                     return new JSchema(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| #region copyright | ||||
| //------------------------------------------------------------------------------ | ||||
| //  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充 | ||||
| //  此代码版权(除特别声明外的代码)归作者本人Diego所有 | ||||
| //  源代码使用协议遵循本仓库的开源协议及附加协议 | ||||
| //  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway | ||||
| //  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway | ||||
| //  使用文档:https://diego2098.gitee.io/thingsgateway/ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| namespace WebPlatform.OPCUALayer | ||||
| { | ||||
|     public class EmptyClass | ||||
|     { | ||||
|         public EmptyClass() | ||||
|         { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,381 @@ | ||||
| #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/ | ||||
| //  QQȺ<51><C8BA>605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
| #endregion | ||||
|  | ||||
| using Newtonsoft.Json.Linq; | ||||
|  | ||||
| using Opc.Ua; | ||||
| using Opc.Ua.Client; | ||||
|  | ||||
| using System.Linq; | ||||
| using System.Text.RegularExpressions; | ||||
|  | ||||
| namespace WebPlatform.Extensions | ||||
| { | ||||
|     public static class ExpandedNodeIdExtensionMethods | ||||
|     { | ||||
|         public static string ToStringId(this ExpandedNodeId expandedNodeId, NamespaceTable namespaceTable) | ||||
|         { | ||||
|             var nodeId = ExpandedNodeId.ToNodeId(expandedNodeId, namespaceTable); | ||||
|             return $"{nodeId.NamespaceIndex}-{nodeId.Identifier}"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static class CollectionInitializerExtensionMethods | ||||
|     { | ||||
|         public static void Add(this IList<JToken> list, IList<JToken> toAdd) | ||||
|         { | ||||
|             foreach (var a in toAdd) | ||||
|             { | ||||
|                 list.Add(a); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static class JTokenExtensionMethods | ||||
|     { | ||||
|         public static int[] GetJsonArrayDimensions(this JToken jToken) | ||||
|         { | ||||
|             if (jToken.Type != JTokenType.Array) | ||||
|                 throw new ValueToWriteTypeException("Expected a JSON Array but received a " + jToken.Type); | ||||
|             while (jToken.HasValues) | ||||
|             { | ||||
|                 var children = jToken.Children(); | ||||
|                 var count = children.First().Count(); | ||||
|  | ||||
|                 //if(children.All(x => x.Count() == count)) throw new ValueToWriteTypeException("The array sent must have the same number of element in each dimension"); | ||||
|  | ||||
|                 foreach (var child in children) | ||||
|                 { | ||||
|                     if (child.Count() != count) | ||||
|                         throw new ValueToWriteTypeException("The array sent must have the same number of element in each dimension"); | ||||
|                 } | ||||
|                 jToken = jToken.Last; | ||||
|             } | ||||
|  | ||||
|             const string pattern = @"\[(\d+)\]"; | ||||
|             var regex = new Regex(pattern); | ||||
|             var matchColl = regex.Matches(jToken.Path); | ||||
|             var dimensions = new int[matchColl.Count]; | ||||
|             for (var i = 0; i < matchColl.Count; i++) | ||||
|             { | ||||
|                 dimensions[i] = int.Parse(matchColl[i].Groups[1].Value) + 1; | ||||
|             } | ||||
|             return dimensions; | ||||
|         } | ||||
|  | ||||
|         public static JArray ToOneDimensionJArray(this JToken jToken) | ||||
|         { | ||||
|             var dimensions = jToken.GetJsonArrayDimensions(); | ||||
|             return jToken.ToOneDimensionJArray(dimensions); | ||||
|         } | ||||
|  | ||||
|         public static JArray ToOneDimensionJArray(this JToken jToken, int[] dimensions) | ||||
|         { | ||||
|             var flatValuesToWrite = jToken.Children().ToArray(); | ||||
|             for (var i = 0; i < dimensions.Length - 1; i++) | ||||
|                 flatValuesToWrite = flatValuesToWrite.SelectMany(a => a).ToArray(); | ||||
|  | ||||
|             return new JArray(flatValuesToWrite); | ||||
|         } | ||||
|  | ||||
|         public static int CalculateActualValueRank(this JToken jToken) | ||||
|         { | ||||
|             if (jToken.Type != JTokenType.Array) | ||||
|                 return -1; | ||||
|  | ||||
|             var jArray = jToken.ToArray(); | ||||
|             int numDimensions = 1; | ||||
|  | ||||
|             while (jArray.GetElementsType() == JTokenType.Array) | ||||
|             { | ||||
|                 jArray = jArray.Children().ToArray(); | ||||
|                 numDimensions++; | ||||
|             } | ||||
|  | ||||
|             return numDimensions; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         public static JTokenType GetElementsType(this JToken[] jTokens) | ||||
|         { | ||||
|             if (!jTokens.ElementsHasSameType()) | ||||
|                 throw new ValueToWriteTypeException("The array sent must have the same type of element in each dimension"); | ||||
|             return jTokens.First().Type; | ||||
|         } | ||||
|  | ||||
|         private static bool ElementsHasSameType(this JToken[] jTokens) | ||||
|         { | ||||
|             var checkType = jTokens[0].Type == JTokenType.Integer ? JTokenType.Float : jTokens[0].Type; | ||||
|             return jTokens | ||||
|                 .Select(x => (x.Type == JTokenType.Integer) ? JTokenType.Float : x.Type) | ||||
|                 .All(t => t == checkType); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static class BuiltInExtensionMethods | ||||
|     { | ||||
|         public static Func<Variant> GetDecodeDelegate(this BuiltInType builtIn, PlatformJsonDecoder decoder) | ||||
|         { | ||||
|             switch (builtIn) | ||||
|             { | ||||
|                 case BuiltInType.Boolean: | ||||
|                     return () => new Variant(decoder.ReadBoolean("Value")); | ||||
|                 case BuiltInType.SByte: | ||||
|                     return () => new Variant(decoder.ReadBoolean("Value")); | ||||
|                 case BuiltInType.Byte: | ||||
|                     return () => new Variant(decoder.ReadSByte("Value")); | ||||
|                 case BuiltInType.Int16: | ||||
|                     return () => new Variant(decoder.ReadInt16("Value")); | ||||
|                 case BuiltInType.UInt16: | ||||
|                     return () => new Variant(decoder.ReadUInt16("Value")); | ||||
|                 case BuiltInType.Int32: | ||||
|                     return () => new Variant(decoder.ReadInt32("Value")); | ||||
|                 case BuiltInType.UInt32: | ||||
|                     return () => new Variant(decoder.ReadUInt32("Value")); | ||||
|                 case BuiltInType.Int64: | ||||
|                     return () => new Variant(decoder.ReadInt64("Value")); | ||||
|                 case BuiltInType.UInt64: | ||||
|                     return () => new Variant(decoder.ReadUInt64("Value")); | ||||
|                 case BuiltInType.Float: | ||||
|                     return () => new Variant(decoder.ReadFloat("Value")); | ||||
|                 case BuiltInType.Double: | ||||
|                     return () => new Variant(decoder.ReadDouble("Value")); | ||||
|                 case BuiltInType.String: | ||||
|                     return () => new Variant(decoder.ReadString("Value")); | ||||
|                 case BuiltInType.DateTime: | ||||
|                     return () => new Variant(decoder.ReadDateTime("Value")); | ||||
|                 case BuiltInType.Guid: | ||||
|                     return () => new Variant(decoder.ReadGuid("Value")); | ||||
|                 case BuiltInType.ByteString: | ||||
|                     return () => new Variant(decoder.ReadByteString("Value")); | ||||
|                 case BuiltInType.XmlElement: | ||||
|                     return () => new Variant(decoder.ReadXmlElement("Value")); | ||||
|                 case BuiltInType.NodeId: | ||||
|                     return () => new Variant(decoder.ReadNodeId("Value")); | ||||
|                 case BuiltInType.ExpandedNodeId: | ||||
|                     return () => new Variant(decoder.ReadExpandedNodeId("Value")); | ||||
|                 case BuiltInType.StatusCode: | ||||
|                     return () => new Variant(decoder.ReadStatusCode("Value")); | ||||
|                 case BuiltInType.QualifiedName: | ||||
|                     return () => new Variant(decoder.ReadQualifiedName("Value")); | ||||
|                 case BuiltInType.LocalizedText: | ||||
|                     return () => new Variant(decoder.ReadLocalizedText("Value")); | ||||
|                 case BuiltInType.ExtensionObject: | ||||
|                     return () => new Variant(decoder.ReadExtensionObject("Value")); | ||||
|                 case BuiltInType.DiagnosticInfo: | ||||
|                     return () => new Variant(decoder.ReadDiagnosticInfo("Value")); | ||||
|                 case BuiltInType.Enumeration: | ||||
|                     return () => new Variant(decoder.ReadEnumeration("Value")); | ||||
|                 default: | ||||
|                     throw new NotImplementedException(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public static Func<Variant> GetDecodeArrayDelegate(this BuiltInType builtIn, PlatformJsonDecoder decoder) | ||||
|         { | ||||
|             switch (builtIn) | ||||
|             { | ||||
|                 case BuiltInType.Boolean: | ||||
|                     return () => new Variant(decoder.ReadBooleanArray("Value").ToArray()); | ||||
|                 case BuiltInType.SByte: | ||||
|                     return () => new Variant(decoder.ReadSByteArray("Value").ToArray()); | ||||
|                 case BuiltInType.Byte: | ||||
|                     return () => new Variant(decoder.ReadByteArray("Value").ToArray()); | ||||
|                 case BuiltInType.Int16: | ||||
|                     return () => new Variant(decoder.ReadInt16Array("Value").ToArray()); | ||||
|                 case BuiltInType.UInt16: | ||||
|                     return () => new Variant(decoder.ReadUInt16Array("Value").ToArray()); | ||||
|                 case BuiltInType.Int32: | ||||
|                     return () => new Variant(decoder.ReadInt32Array("Value").ToArray()); | ||||
|                 case BuiltInType.UInt32: | ||||
|                     return () => new Variant(decoder.ReadUInt32Array("Value").ToArray()); | ||||
|                 case BuiltInType.Int64: | ||||
|                     return () => new Variant(decoder.ReadInt64Array("Value").ToArray()); | ||||
|                 case BuiltInType.UInt64: | ||||
|                     return () => new Variant(decoder.ReadUInt64Array("Value").ToArray()); | ||||
|                 case BuiltInType.Float: | ||||
|                     return () => new Variant(decoder.ReadFloatArray("Value").ToArray()); | ||||
|                 case BuiltInType.Double: | ||||
|                     return () => new Variant(decoder.ReadDoubleArray("Value").ToArray()); | ||||
|                 case BuiltInType.String: | ||||
|                     return () => new Variant(decoder.ReadStringArray("Value").ToArray()); | ||||
|                 case BuiltInType.DateTime: | ||||
|                     return () => new Variant(decoder.ReadDateTimeArray("Value").ToArray()); | ||||
|                 case BuiltInType.Guid: | ||||
|                     return () => new Variant(decoder.ReadGuidArray("Value").ToArray()); | ||||
|                 case BuiltInType.ByteString: | ||||
|                     return () => new Variant(decoder.ReadByteStringArray("Value").ToArray()); | ||||
|                 case BuiltInType.XmlElement: | ||||
|                     return () => new Variant(decoder.ReadXmlElementArray("Value").ToArray()); | ||||
|                 case BuiltInType.NodeId: | ||||
|                     return () => new Variant(decoder.ReadNodeIdArray("Value").ToArray()); | ||||
|                 case BuiltInType.ExpandedNodeId: | ||||
|                     return () => new Variant(decoder.ReadExpandedNodeIdArray("Value").ToArray()); | ||||
|                 case BuiltInType.StatusCode: | ||||
|                     return () => new Variant(decoder.ReadStatusCodeArray("Value").ToArray()); | ||||
|                 case BuiltInType.QualifiedName: | ||||
|                     return () => new Variant(decoder.ReadQualifiedNameArray("Value").ToArray()); | ||||
|                 case BuiltInType.LocalizedText: | ||||
|                     return () => new Variant(decoder.ReadLocalizedTextArray("Value").ToArray()); | ||||
|                 case BuiltInType.ExtensionObject: | ||||
|                     return () => new Variant(decoder.ReadExtensionObjectArray("Value").ToArray()); | ||||
|                 case BuiltInType.DiagnosticInfo: | ||||
|                     return () => new Variant(decoder.ReadDiagnosticInfoArray("Value").ToArray()); | ||||
|                 case BuiltInType.Enumeration: | ||||
|                     return () => new Variant(decoder.ReadEnumerationArray("Value").ToArray()); | ||||
|                 default: | ||||
|                     throw new NotImplementedException(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public static Func<Variant> GetDecodeMatrixDelegate(this BuiltInType builtIn, PlatformJsonDecoder decoder) | ||||
|         { | ||||
|             switch (builtIn) | ||||
|             { | ||||
|                 case BuiltInType.Boolean: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadBooleanArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.SByte: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadSByteArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.Byte: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadSByteArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.Int16: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadInt16Array("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.UInt16: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadUInt16Array("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.Int32: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadInt32Array("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.UInt32: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadUInt32Array("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.Int64: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadInt64Array("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.UInt64: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadUInt64Array("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.Float: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadFloatArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.Double: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadDoubleArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.String: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadStringArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.DateTime: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadDateTimeArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.Guid: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadGuidArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.ByteString: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadByteStringArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.XmlElement: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadXmlElementArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.NodeId: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadNodeIdArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.ExpandedNodeId: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadExpandedNodeIdArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.StatusCode: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadStatusCodeArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.QualifiedName: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadQualifiedNameArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.LocalizedText: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadLocalizedTextArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.ExtensionObject: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadExtensionObjectArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.DiagnosticInfo: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadDiagnosticInfoArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 case BuiltInType.Enumeration: | ||||
|                     return () => new Variant(new Matrix(decoder.ReadEnumerationArray("Value").ToArray(), builtIn, decoder.Dimensions)); | ||||
|                 default: | ||||
|                     throw new NotImplementedException(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static class SessionExtensionMethods | ||||
|     { | ||||
|         public static (LocalizedText[] enumStrings, EnumValueType[] enumValues) GetEnumStrings(this Session session, NodeId dataTypeId) | ||||
|         { | ||||
|             var referenceCollection = session.GetPropertiesReferenceCollection(dataTypeId); | ||||
|  | ||||
|             foreach (var dataValueCollection in referenceCollection | ||||
|                                                      .Where(referenceDescription => referenceDescription.BrowseName.Name.Equals("EnumStrings")) | ||||
|                                                      .Select(descr => ExpandedNodeId.ToNodeId(descr.NodeId, session.MessageContext.NamespaceUris)) | ||||
|                                                      .Select(enid => session.ReadNodeAttribute(enid, Attributes.Value))) | ||||
|             { | ||||
|                 return (enumStrings: (LocalizedText[])dataValueCollection[0].Value, enumValues: null); | ||||
|             } | ||||
|  | ||||
|             foreach (var dataValueCollection in referenceCollection | ||||
|                 .Where(referenceDescription => referenceDescription.BrowseName.Name.Equals("EnumValues")) | ||||
|                 .Select(descr => ExpandedNodeId.ToNodeId(descr.NodeId, session.MessageContext.NamespaceUris)) | ||||
|                 .Select(enid => session.ReadNodeAttribute(enid, Attributes.Value))) | ||||
|             { | ||||
|                 var evs = ((ExtensionObject[])dataValueCollection[0].Value).Select(eo => eo.Body) | ||||
|                     .Cast<EnumValueType>().ToArray(); | ||||
|                 return (enumStrings: null, enumValues: evs); | ||||
|             } | ||||
|  | ||||
|             return (null, null); | ||||
|         } | ||||
|  | ||||
|         public static ReferenceDescriptionCollection GetPropertiesReferenceCollection(this Session session, NodeId dataTypeId) | ||||
|         { | ||||
|             session.Browse( | ||||
|                 null, | ||||
|                 null, | ||||
|                 dataTypeId, | ||||
|                 0u, | ||||
|                 BrowseDirection.Forward, | ||||
|                 ReferenceTypeIds.HasProperty, | ||||
|                 true, | ||||
|                 (uint)NodeClass.Variable, | ||||
|                 out _, | ||||
|                 out var refDescriptionCollection); | ||||
|  | ||||
|             return refDescriptionCollection; | ||||
|         } | ||||
|  | ||||
|         public static DataValueCollection ReadNodeAttribute(this Session session, NodeId nodeId, uint attributeId) | ||||
|         { | ||||
|             var nodeToRead = new ReadValueIdCollection(); | ||||
|  | ||||
|             var vId = new ReadValueId() | ||||
|             { | ||||
|                 NodeId = nodeId, | ||||
|                 AttributeId = attributeId | ||||
|             }; | ||||
|  | ||||
|             nodeToRead.Add(vId); | ||||
|  | ||||
|             session.Read(null, | ||||
|                 0, | ||||
|                 TimestampsToReturn.Both, | ||||
|                 nodeToRead, | ||||
|                 out var dataValueCollection, | ||||
|                 out _ | ||||
|             ); | ||||
|  | ||||
|             return dataValueCollection; | ||||
|         } | ||||
|  | ||||
|         public static bool IsServerStatusGood(this Session session) | ||||
|         { | ||||
|             DataValue serverStatus; | ||||
|             try | ||||
|             { | ||||
|                 serverStatus = session.ReadValue(new NodeId(2259, 0)); | ||||
|             } | ||||
|             catch (Exception) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|             return DataValue.IsGood(serverStatus) && (int)serverStatus.Value == 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||