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
|
||||
11
.gitignore
vendored
@@ -361,3 +361,14 @@ MigrationBackup/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
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
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||