Compare commits
291 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0230d614e7 | ||
|
|
252d99ad78 | ||
|
|
1ffc200350 | ||
|
|
807d89b2b2 | ||
|
|
4013afa1f1 | ||
|
|
a580927ceb | ||
|
|
bf2cf52034 | ||
|
|
81bb8b7c31 | ||
|
|
a825007fb5 | ||
|
|
988124d96a | ||
|
|
f0de815296 | ||
|
|
0e2d58c887 | ||
|
|
b155382626 | ||
|
|
f362d740af | ||
|
|
4a85e31a4f | ||
|
|
302c270ad5 | ||
|
|
3c1517d0f3 | ||
|
|
f9fb222044 | ||
|
|
e8edc02ba3 | ||
|
|
95a44e3053 | ||
|
|
74a9fe9a87 | ||
|
|
4d03f9ea1a | ||
|
|
67c96ca991 | ||
|
|
88fb793c68 | ||
|
|
d6d02d8cc5 | ||
|
|
c5a3f8e2e3 | ||
|
|
27e8653a1a | ||
|
|
863beda82c | ||
|
|
bac84c3ecd | ||
|
|
2fca2ad9f8 | ||
|
|
dd75286fe0 | ||
|
|
7f91792cf1 | ||
|
|
0e0ccad311 | ||
|
|
0691f72e67 | ||
|
|
7e38a51720 | ||
|
|
34ca8243a3 | ||
|
|
112fea7632 | ||
|
|
378763e4ee | ||
|
|
517bd0394d | ||
|
|
70adb97fb5 | ||
|
|
623d44cabe | ||
|
|
0d479ca00b | ||
|
|
8bc49ef437 | ||
|
|
f83fcec786 | ||
|
|
93690ce40d | ||
|
|
f82c5f2f27 | ||
|
|
a83c1c3899 | ||
|
|
91d6aed109 | ||
|
|
db8f8fe51d | ||
|
|
4596004b17 | ||
|
|
d5540906cb | ||
|
|
90796a979d | ||
|
|
2190a87772 | ||
|
|
c5953b83f8 | ||
|
|
24bc60abf0 | ||
|
|
31eee6b009 | ||
|
|
c5da565a8f | ||
|
|
947cd712e1 | ||
|
|
edc208f96b | ||
|
|
1fb0296ee7 | ||
|
|
6488d3df87 | ||
|
|
56189d78e0 | ||
|
|
bff18127b8 | ||
|
|
363206e0ba | ||
|
|
fd3e378501 | ||
|
|
4ba2fe4c9d | ||
|
|
2c499626ad | ||
|
|
2b581a03c3 | ||
|
|
450c15210a | ||
|
|
65fed8cc93 | ||
|
|
4b64771ea2 | ||
|
|
f39977a6ff | ||
|
|
933b535caa | ||
|
|
8abc5d2f20 | ||
|
|
d8783cd994 | ||
|
|
d5d087feb5 | ||
|
|
6ba3399df7 | ||
|
|
65124b3aa8 | ||
|
|
98597f4726 | ||
|
|
e7981f0d8e | ||
|
|
cf654427c3 | ||
|
|
ff2f628282 | ||
|
|
ae818ca265 | ||
|
|
0f2aed458e | ||
|
|
d486c44ff6 | ||
|
|
ca7b9980bf | ||
|
|
3c71e6a8e3 | ||
|
|
542442864c | ||
|
|
5edb64fa85 | ||
|
|
8dc1c898a3 | ||
|
|
1ed35726b0 | ||
|
|
27fae9ebaa | ||
|
|
b103f25c94 | ||
|
|
abff450274 | ||
|
|
c260736a11 | ||
|
|
166ac2307a | ||
|
|
b21a4e1a4d | ||
|
|
f7dc943fa3 | ||
|
|
bfbd2693ec | ||
|
|
819e71c993 | ||
|
|
9fd0b489a2 | ||
|
|
f5fe9f8dae | ||
|
|
f9ffc18145 | ||
|
|
08db5b983a | ||
|
|
5b3b4c8c50 | ||
|
|
73f914ffc4 | ||
|
|
d6bdd73ed6 | ||
|
|
7370ee7349 | ||
|
|
4574596bac | ||
|
|
4d16855e36 | ||
|
|
13a0d4d282 | ||
|
|
b9cd06b829 | ||
|
|
5b460e8fa2 | ||
|
|
41087edf17 | ||
|
|
2afcc38e38 | ||
|
|
e59ccce25f | ||
|
|
d7425890e8 | ||
|
|
a989a837fb | ||
|
|
db1221da50 | ||
|
|
cf794569ed | ||
|
|
51e5bbab0d | ||
|
|
2c197ed2b2 | ||
|
|
d8fc6665b3 | ||
|
|
c671a79822 | ||
|
|
9d93ce4c41 | ||
|
|
a6d99fe227 | ||
|
|
923b8bca31 | ||
|
|
e2c30d1c88 | ||
|
|
b6d9f2a04e | ||
|
|
57306ea664 | ||
|
|
cd7f3fd02f | ||
|
|
0482e077a8 | ||
|
|
5f986a45ca | ||
|
|
ca7b49c0d5 | ||
|
|
52dd555e6c | ||
|
|
579b1a59f9 | ||
|
|
5299c5c4be | ||
|
|
f7756bccef | ||
|
|
a6b874d160 | ||
|
|
3e5fb3ddcf | ||
|
|
5e6bcb12d3 | ||
|
|
14303f1429 | ||
|
|
96711ba022 | ||
|
|
cbfc0fdbdc | ||
|
|
6e81886c0e | ||
|
|
2d976bc132 | ||
|
|
57f6a476af | ||
|
|
8491ed296e | ||
|
|
cd1288afdc | ||
|
|
ec6c830cb0 | ||
|
|
2f86ccc4bf | ||
|
|
8ca445aec0 | ||
|
|
1e1f27c8a5 | ||
|
|
2b84bde367 | ||
|
|
b52e58551d | ||
|
|
9aceed00bf | ||
|
|
58814f7f74 | ||
|
|
6a70ef9f31 | ||
|
|
82cc4ca500 | ||
|
|
4567fa04ed | ||
|
|
8b98b5d818 | ||
|
|
176d0351af | ||
|
|
d63dc3384b | ||
|
|
1ccd704e30 | ||
|
|
f5d23dbe79 | ||
|
|
75bfe53ac3 | ||
|
|
3308f916dd | ||
|
|
e7140279ca | ||
|
|
1034719f5e | ||
|
|
2c00043a7f | ||
|
|
65c695d9ce | ||
|
|
57253fe46a | ||
|
|
4e5c443440 | ||
|
|
0b3b73d8ec | ||
|
|
921eabc134 | ||
|
|
0faa428751 | ||
|
|
f71a2fdd63 | ||
|
|
4eb9ed8aba | ||
|
|
d7b549abb8 | ||
|
|
95d723c578 | ||
|
|
2fcd853e86 | ||
|
|
07eef7c812 | ||
|
|
b01e0757fa | ||
|
|
32844a20c6 | ||
|
|
5b6532c601 | ||
|
|
2c5b4b4027 | ||
|
|
72d7ecf195 | ||
|
|
2cfa6b4306 | ||
|
|
6f6ffde0ab | ||
|
|
1694739a16 | ||
|
|
95d1e8bfca | ||
|
|
60dec08e3c | ||
|
|
a99d71be93 | ||
|
|
f1331b6a0c | ||
|
|
10d66b642b | ||
|
|
cd2310e4a8 | ||
|
|
1b399cf6b0 | ||
|
|
877445bc0a | ||
|
|
9a5b345bde | ||
|
|
fc9e8ea7b3 | ||
|
|
32be6fcfc1 | ||
|
|
49847236c2 | ||
|
|
d8424443e6 | ||
|
|
f3b571ec3f | ||
|
|
99318bb5d7 | ||
|
|
1aa154c9aa | ||
|
|
c65d8a445b | ||
|
|
80f4f85570 | ||
|
|
5beee43a6b | ||
|
|
8d6ae203a0 | ||
|
|
4353479a5c | ||
|
|
34d7687f9e | ||
|
|
b1dc3cf4af | ||
|
|
6a58b95933 | ||
|
|
d3badfd02b | ||
|
|
0098be057b | ||
|
|
6f972aa515 | ||
|
|
7407ba6313 | ||
|
|
1c79de207b | ||
|
|
257c79db92 | ||
|
|
9d1934a308 | ||
|
|
d70f959902 | ||
|
|
e4d810222f | ||
|
|
bc1af4ae07 | ||
|
|
6e688ef43f | ||
|
|
f0fe1b23dc | ||
|
|
aaf2006401 | ||
|
|
b821e26935 | ||
|
|
7ae4287157 | ||
|
|
c6fcc38a65 | ||
|
|
ab2d5c8853 | ||
|
|
5e557ff0bc | ||
|
|
918ca449a1 | ||
|
|
8e73368008 | ||
|
|
f3c1faf672 | ||
|
|
d6df04dd6a | ||
|
|
b1b9e51ab6 | ||
|
|
e49d4770ac | ||
|
|
8fa1075511 | ||
|
|
9a70169b94 | ||
|
|
fefb928237 | ||
|
|
ad7e700d0d | ||
|
|
1699c69147 | ||
|
|
1695f7cece | ||
|
|
052c27f907 | ||
|
|
dc46c32b30 | ||
|
|
fa63349bb2 | ||
|
|
ffe26448a6 | ||
|
|
4af51e8a84 | ||
|
|
1e453cf5a5 | ||
|
|
591282b87d | ||
|
|
e87528d520 | ||
|
|
d79eb0411d | ||
|
|
ac1e0a4cf7 | ||
|
|
9525eab130 | ||
|
|
89b317496c | ||
|
|
13be91e78b | ||
|
|
f68c1437f3 | ||
|
|
4c64c969bb | ||
|
|
b4bf3b5138 | ||
|
|
083bc4b400 | ||
|
|
e8683c5bcc | ||
|
|
80e0d1de91 | ||
|
|
dbe841037e | ||
|
|
bdd537c33c | ||
|
|
c0c3846094 | ||
|
|
9e8710e7d2 | ||
|
|
475553fdf6 | ||
|
|
9d570f5b45 | ||
|
|
af7fafd34f | ||
|
|
d43130f4fc | ||
|
|
7500194620 | ||
|
|
eb27c29144 | ||
|
|
43260b3e24 | ||
|
|
f80713f0aa | ||
|
|
0c4bdc7ad1 | ||
|
|
811cff7bd0 | ||
|
|
30269aa75c | ||
|
|
e345ef7083 | ||
|
|
f559c9b8f7 | ||
|
|
f4af0916b2 | ||
|
|
f15f14f28d | ||
|
|
834f44f58d | ||
|
|
b36f45dcf4 | ||
|
|
11ba21c9a8 | ||
|
|
b045557ce1 | ||
|
|
0dd251a3f6 | ||
|
|
793acb1725 | ||
|
|
921243e8bd | ||
|
|
bd9d7a90d9 | ||
|
|
cc444a4cea |
7
.gitignore
vendored
@@ -362,10 +362,5 @@ MigrationBackup/
|
|||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
/src/Plugins/Other
|
|
||||||
/src/ThingsGateway.Web.Server/*.db
|
|
||||||
/src/PluginPro*/
|
|
||||||
/src/*Pro*
|
|
||||||
/src/TestResults*/
|
|
||||||
/src/ThingsGateway.Web.Server/ThingsGateway.db
|
|
||||||
|
|
||||||
|
/framework/*Pro*
|
||||||
|
|||||||
BIN
Image/1.png
|
Before Width: | Height: | Size: 135 KiB |
BIN
Image/2.png
|
Before Width: | Height: | Size: 125 KiB |
BIN
Image/3.png
|
Before Width: | Height: | Size: 217 KiB |
BIN
Image/4.png
|
Before Width: | Height: | Size: 119 KiB |
BIN
Image/5.png
|
Before Width: | Height: | Size: 189 KiB |
BIN
Image/6.png
|
Before Width: | Height: | Size: 151 KiB |
BIN
Image/7.png
|
Before Width: | Height: | Size: 187 KiB |
BIN
Image/8.png
|
Before Width: | Height: | Size: 164 KiB |
BIN
Image/9.png
|
Before Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 14 KiB |
BIN
Image/pay.png
|
Before Width: | Height: | Size: 326 KiB |
21
LICENSE.txt
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
http://www.apache.org/licenses/
|
http://www.apache.org/licenses/
|
||||||
@@ -186,16 +187,16 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
Copyright 2023-present Diego
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
147
README.md
@@ -1,141 +1,38 @@
|
|||||||
|
|
||||||
<div align='center'>
|
|
||||||
<img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/gitLogo.png" height=100 />
|
|
||||||
|
|
||||||
<div align="center">
|
# ThingsGateway
|
||||||
|
|
||||||
[](https://www.nuget.org/packages/kimdiego/)
|
## 介绍
|
||||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
|
||||||
[](https://gitee.com/diego2098/ThingsGateway/stargazers)
|
|
||||||
[](https://gitee.com/diego2098/ThingsGateway/members)
|
|
||||||

|
|
||||||
|
|
||||||
</div>
|
**NetCore** 跨平台边缘采集网关(工业设备采集)
|
||||||
</div>
|
|
||||||
|
|
||||||
#### 介绍
|
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
|
||||||
|
|
||||||
基于Net6/7+Blazor Server的跨平台边缘采集网关,支持南北端插件式开发,
|
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
|
||||||
并拥有较完善的北端Rpc权限管理。
|
|
||||||
|
|
||||||
[Github地址](https://github.com/kimdiego2098/ThingsGateway)
|
|
||||||
|
|
||||||
<div >
|
|
||||||
如果对您有帮助,请点击右上角⭐Star关注,感谢支持开源!
|
|
||||||
</div>
|
|
||||||
|
|
||||||
#### 开源说明
|
|
||||||
|
|
||||||
Apache 2.0+[附加协议](https://diego2098.gitee.io/thingsgateway-docs/docs/)
|
|
||||||
|
|
||||||
Apache 2.0 开源协议的核心内容是以保护和尊重原作者的著作权为主要目的。对使用,复制,修改,商用不做过多限制,但必须包含原著的License信息。
|
|
||||||
|
|
||||||
#### 功能亮点
|
|
||||||
|
|
||||||
- Blazor Server架构,开发部署更简单
|
|
||||||
- 采集/上传配置完全支持Excel导入导出
|
|
||||||
- 插件式驱动,方便驱动二次开发
|
|
||||||
- 支持采集通道冗余,上传离线缓存
|
|
||||||
- 时序数据库存储
|
|
||||||
- 实时/历史报警(Sql转储),支持布尔/高低限值
|
|
||||||
|
|
||||||
#### 演示
|
|
||||||
|
|
||||||
http://120.24.62.140:5000/
|
|
||||||
|
|
||||||
默认账户密码:superAdmin 111111
|
|
||||||
|
|
||||||
|
|
||||||
#### 社区版采集插件
|
## 文档
|
||||||
> 支持分包解析/订阅
|
|
||||||
- Modbus(Rtu/Tcp/Udp)
|
|
||||||
- OPCDAClient(支持导入节点)
|
|
||||||
- OPCUAClient(支持导入节点,动态类型)
|
|
||||||
- 西门子S7协议
|
|
||||||
|
|
||||||
#### 社区版上传插件
|
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
|
||||||
> 支持Rpc写入
|
|
||||||
- Modbus Server
|
|
||||||
- OPCUA Server (支持历史查询)
|
|
||||||
- Mqtt Server (支持自定义json)
|
|
||||||
- Mqtt Client (支持自定义json)
|
|
||||||
- IotSharp Client (IotSharp网关插件,Rpc待测试)
|
|
||||||
|
|
||||||
> 不支持Rpc
|
## 协议
|
||||||
- RabbitMQ (支持自定义json)
|
|
||||||
- Kafka
|
|
||||||
|
|
||||||
#### nuget
|
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。
|
||||||
|
|
||||||
- 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库
|
[ThingsGateway演示地址](http://120.24.62.140:5000/)
|
||||||
``` powershell
|
|
||||||
dotnet add package ThingsGateway.Foundation.Adapter.Siemens
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 效果图
|
账户 : **superAdmin**
|
||||||
<table>
|
|
||||||
<tr>
|
密码 : **111111**
|
||||||
<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>
|
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
|
||||||
<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>
|
QQ群:605534569
|
||||||
</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>
|
|
||||||
|
|
||||||
|
|
||||||
#### 文档
|
|
||||||
|
|
||||||
使用前请查看Gitee Pages [文档站点](https://diego2098.gitee.io/thingsgateway-docs/)
|
|
||||||
|
|
||||||
#### 特别鸣谢
|
|
||||||
- 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)
|
|
||||||
- OPCUAWebPlatformUniCT:[https://github.com/OPCUAUniCT/OPCUAWebPlatformUniCT](https://github.com/OPCUAUniCT/OPCUAWebPlatformUniCT)
|
|
||||||
|
|
||||||
#### 补充说明
|
|
||||||
* 使用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
|
|
||||||
|
|
||||||
|
|||||||
103
framework/.editorconfig
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# CA1848: 使用 LoggerMessage 委托
|
||||||
|
dotnet_diagnostic.CA1848.severity = none
|
||||||
|
|
||||||
|
# CA2254: 模板应为静态表达式
|
||||||
|
dotnet_diagnostic.CA2254.severity = suggestion
|
||||||
|
|
||||||
|
[*.cs]
|
||||||
|
#### 命名样式 ####
|
||||||
|
|
||||||
|
# 命名规则
|
||||||
|
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
|
||||||
|
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
|
||||||
|
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
|
||||||
|
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||||
|
|
||||||
|
# 符号规范
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.types.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||||
|
|
||||||
|
# 命名样式
|
||||||
|
|
||||||
|
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||||
|
dotnet_naming_style.begins_with_i.required_suffix =
|
||||||
|
dotnet_naming_style.begins_with_i.word_separator =
|
||||||
|
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.pascal_case.required_prefix =
|
||||||
|
dotnet_naming_style.pascal_case.required_suffix =
|
||||||
|
dotnet_naming_style.pascal_case.word_separator =
|
||||||
|
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||||
|
|
||||||
|
[*.vb]
|
||||||
|
#### 命名样式 ####
|
||||||
|
|
||||||
|
# 命名规则
|
||||||
|
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
|
||||||
|
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
|
||||||
|
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
|
||||||
|
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||||
|
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
|
||||||
|
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
|
||||||
|
|
||||||
|
# 符号规范
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.interface.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.类型.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
|
||||||
|
dotnet_naming_symbols.非字段成员.required_modifiers =
|
||||||
|
|
||||||
|
# 命名样式
|
||||||
|
|
||||||
|
dotnet_naming_style.以_i_开始.required_prefix = I
|
||||||
|
dotnet_naming_style.以_i_开始.required_suffix =
|
||||||
|
dotnet_naming_style.以_i_开始.word_separator =
|
||||||
|
dotnet_naming_style.以_i_开始.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.word_separator =
|
||||||
|
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
|
||||||
63
framework/.gitattributes
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
||||||
364
framework/.gitignore
vendored
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Oo]ut/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
15
framework/Demo/Directory.Build.props
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Version>3.0.0.18</Version>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
|
||||||
|
<Authors>Diego</Authors>
|
||||||
|
<Product>ThingsGateway</Product>
|
||||||
|
<Copyright>© 2023-present Diego</Copyright>
|
||||||
|
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||||
|
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
|
||||||
|
<GenerateDocumentationFile>False</GenerateDocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
global using System;
|
||||||
|
|
||||||
|
global using ThingsGateway.Components;
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using Photino.Blazor;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
private static void Main(string[] args)
|
||||||
|
{
|
||||||
|
System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory);
|
||||||
|
|
||||||
|
var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args);
|
||||||
|
|
||||||
|
appBuilder.RootComponents.Add<App>("#app");
|
||||||
|
|
||||||
|
appBuilder.Services.ThingsGatewayComponentsConfigureServices();
|
||||||
|
var app = appBuilder.Build();
|
||||||
|
app.MainWindow.SetTitle("ThingsGateway.Foundation.Demo");
|
||||||
|
app.MainWindow.SetIconFile("wwwroot/favicon.ico");
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<ApplicationIcon>favicon.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="favicon.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="favicon.ico">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Photino.Blazor" Version="2.6.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
||||||
BIN
framework/Demo/ThingsGateway.Foundation.Demo.Photino/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
26
framework/Demo/ThingsGateway.Foundation.Demo.Rcl/App.razor
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
|
||||||
|
<Router AppAssembly="@typeof(App).Assembly">
|
||||||
|
<Found Context="routeData">
|
||||||
|
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||||
|
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
<NotFound>
|
||||||
|
<LayoutView Layout="@typeof(MainLayout)">
|
||||||
|
<p role="alert">Sorry, there's nothing at this address.</p>
|
||||||
|
</LayoutView>
|
||||||
|
</NotFound>
|
||||||
|
</Router>
|
||||||
|
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
using ThingsGateway.Components;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||||
|
/// <summary>
|
||||||
|
/// 调试UI
|
||||||
|
/// </summary>
|
||||||
|
public abstract class DriverDebugUIBase : ComponentBase, IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志缓存
|
||||||
|
/// </summary>
|
||||||
|
public ConcurrentLinkedList<(LogLevel level, string message)> Messages = new();
|
||||||
|
|
||||||
|
private PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(1));
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
~DriverDebugUIBase()
|
||||||
|
{
|
||||||
|
this.SafeDispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 变量地址
|
||||||
|
/// </summary>
|
||||||
|
public virtual string Address { get; set; } = "40001";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 长度
|
||||||
|
/// </summary>
|
||||||
|
public virtual int Length { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认读写设备
|
||||||
|
/// </summary>
|
||||||
|
public virtual IReadWrite Plc { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 写入值
|
||||||
|
/// </summary>
|
||||||
|
public virtual string WriteValue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据类型
|
||||||
|
/// </summary>
|
||||||
|
protected virtual DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Int16;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
[Inject]
|
||||||
|
public InitTimezone InitTimezone { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
_periodicTimer?.Dispose();
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void LogOut(ThingsGateway.Foundation.Core.LogLevel logLevel, object source, string message, Exception exception)
|
||||||
|
{
|
||||||
|
Messages.Add(((LogLevel)logLevel,
|
||||||
|
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {message} {exception}"));
|
||||||
|
if (Messages.Count > 2500)
|
||||||
|
{
|
||||||
|
Messages.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public virtual async Task ReadAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = await Plc.ReadAsync(Address, Length, DataTypeEnum);
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
Messages.Add((LogLevel.Information,
|
||||||
|
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 对应类型值:{Environment.NewLine}{data.Content.ToJsonString(true)} "));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Messages.Add((LogLevel.Warning,
|
||||||
|
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Messages.Add((LogLevel.Error,
|
||||||
|
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public virtual async Task WriteAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = await Plc.WriteAsync(Address, WriteValue, Length, DataTypeEnum);
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
Messages.Add((LogLevel.Information,
|
||||||
|
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Messages.Add((LogLevel.Warning,
|
||||||
|
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Messages.Add((LogLevel.Error,
|
||||||
|
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
_ = RunTimerAsync();
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
private async Task RunTimerAsync()
|
||||||
|
{
|
||||||
|
while (await _periodicTimer.WaitForNextTickAsync())
|
||||||
|
{
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@using BlazorComponent;
|
||||||
|
@using Microsoft.AspNetCore.Components.Web;
|
||||||
|
@using Microsoft.JSInterop;
|
||||||
|
@using ThingsGateway.Foundation.Core;
|
||||||
|
@using Masa.Blazor;
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
@inherits DriverDebugUIBase
|
||||||
|
|
||||||
|
<MCard Elevation="1" Rounded="false" Class=" pa-2" Style="width:100%">
|
||||||
|
<MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters>
|
||||||
|
|
||||||
|
<MCol Md="5">
|
||||||
|
<MTabs @bind-Value="tab" Class="ma-2">
|
||||||
|
<MTab Value=1> 读写测试 </MTab>
|
||||||
|
<MTab Value=2> 特殊功能 </MTab>
|
||||||
|
<MTab Value=3> 代码示例 </MTab>
|
||||||
|
</MTabs>
|
||||||
|
|
||||||
|
<MTabsItems Value="tab">
|
||||||
|
<MTabItem Value="1">
|
||||||
|
@if (tab == 1)
|
||||||
|
{
|
||||||
|
@if (ReadWriteContent == null)
|
||||||
|
{
|
||||||
|
|
||||||
|
<div class="my-1 py-1">
|
||||||
|
<MTooltip Bottom Context="tip">
|
||||||
|
<ActivatorContent>
|
||||||
|
<MTextarea Class="mx-1 my-1" Label="变量地址" @attributes="@tip.Attrs" Dense Outlined HideDetails="@("auto")" @bind-Value=@Address />
|
||||||
|
</ActivatorContent>
|
||||||
|
<ChildContent>
|
||||||
|
<span style="white-space: pre-wrap;">@Plc?.GetAddressDescription()</span>
|
||||||
|
</ChildContent>
|
||||||
|
</MTooltip>
|
||||||
|
<MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters>
|
||||||
|
<MTextField Class="mx-1 my-1" Style="max-width:200px" Label="读取长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@Length />
|
||||||
|
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="DataTypeEnum" Outlined Label="数据类型"
|
||||||
|
Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar())
|
||||||
|
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||||
|
ItemText=@((u) =>u.Description)
|
||||||
|
ItemValue=@(u =>(DataTypeEnum)u.Value)
|
||||||
|
HideDetails=@("auto") Height="30"
|
||||||
|
Dense>
|
||||||
|
</MSelect>
|
||||||
|
</MRow>
|
||||||
|
|
||||||
|
<MButton Class="mx-1 my-1" Color="primary" OnClick="ReadAsync">
|
||||||
|
读取
|
||||||
|
</MButton>
|
||||||
|
<MTextarea Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@WriteValue />
|
||||||
|
<MButton Class="mx-1 my-1" Color="primary" OnClick="WriteAsync">
|
||||||
|
写入
|
||||||
|
</MButton>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@ReadWriteContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</MTabItem>
|
||||||
|
|
||||||
|
<MTabItem Value="2">
|
||||||
|
@if (tab == 2)
|
||||||
|
{
|
||||||
|
@if (ShowDefaultOtherContent)
|
||||||
|
{
|
||||||
|
<MSubheader>
|
||||||
|
连读打包
|
||||||
|
</MSubheader>
|
||||||
|
<MContainer>
|
||||||
|
|
||||||
|
@foreach (var item in DeviceVariableRunTimes)
|
||||||
|
{
|
||||||
|
<MRow Dense Align="AlignTypes.Center">
|
||||||
|
<MTextField Class="ma-1" Outlined Style="min-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.VariableAddress)) Dense HideDetails="@("auto")" @bind-Value=@item.VariableAddress></MTextField>
|
||||||
|
<MSelect Class="mx-1 my-1" Style="max-width:120px" @bind-Value="item.DataTypeEnum" Outlined Label=@(item.DescriptionWithOutSugar(x => x.DataTypeEnum))
|
||||||
|
Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar())
|
||||||
|
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||||
|
ItemText=@((u) =>u.Description)
|
||||||
|
ItemValue=@(u =>(DataTypeEnum)u.Value)
|
||||||
|
HideDetails=@("auto") Height="30"
|
||||||
|
Dense>
|
||||||
|
</MSelect>
|
||||||
|
|
||||||
|
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.IntervalTime)) Dense HideDetails="@("auto")" @bind-Value=@item.IntervalTime></MTextField>
|
||||||
|
|
||||||
|
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=实时值 Readonly ClearIcon="" Dense HideDetails="@("auto")" Value=item.Value?.ToJsonString()></MTextField>
|
||||||
|
|
||||||
|
</MRow>
|
||||||
|
}
|
||||||
|
<MRow Dense>
|
||||||
|
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label="打包长度" Dense HideDetails="@("auto")" @bind-Value=@MaxPack></MTextField>
|
||||||
|
<MButton Class="ma-1" Color="primary" OnClick="MulReadAsync">
|
||||||
|
读取
|
||||||
|
</MButton>
|
||||||
|
</MRow>
|
||||||
|
</MContainer>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (OtherContent != null)
|
||||||
|
{
|
||||||
|
<MSheet Style="height:100%;overflow-y:auto">
|
||||||
|
@OtherContent
|
||||||
|
</MSheet>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</MTabItem>
|
||||||
|
|
||||||
|
|
||||||
|
<MTabItem Value="3">
|
||||||
|
@if (tab == 3)
|
||||||
|
{
|
||||||
|
@if (CodeContent != null)
|
||||||
|
@CodeContent
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
<MRow Align="AlignTypes.Center">
|
||||||
|
<MContainer>
|
||||||
|
|
||||||
|
<MItemGroup @bind-Value="_selected" Class="shrink mr-6" Mandatory>
|
||||||
|
@{
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
<MRow>
|
||||||
|
|
||||||
|
@foreach (var item in Sections)
|
||||||
|
{
|
||||||
|
<MItem Value="@(index++)">
|
||||||
|
<div>
|
||||||
|
<MButton IsActive="@context.Active" Icon OnClick="@context.Toggle">
|
||||||
|
<MIcon>mdi-record</MIcon>
|
||||||
|
</MButton>
|
||||||
|
</div>
|
||||||
|
</MItem>
|
||||||
|
}
|
||||||
|
</MRow>
|
||||||
|
</MItemGroup>
|
||||||
|
</MContainer>
|
||||||
|
|
||||||
|
<MCol>
|
||||||
|
<MWindow Value="_selected" Vertical Class="elevation-1 grey lighten-5 rounded-b" Style=@($"height:450px;overflow:auto")>
|
||||||
|
@{
|
||||||
|
int index = 0;
|
||||||
|
}
|
||||||
|
@foreach (var item in Sections)
|
||||||
|
{
|
||||||
|
<MWindowItem Value="@(index++)">
|
||||||
|
<AppCode RoundedTop0 Code="@item.Code" Language="@item.Language" />
|
||||||
|
</MWindowItem>
|
||||||
|
}
|
||||||
|
</MWindow>
|
||||||
|
</MCol>
|
||||||
|
</MRow>
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</MTabItem>
|
||||||
|
</MTabsItems>
|
||||||
|
|
||||||
|
</MCol>
|
||||||
|
|
||||||
|
<MCol Md="7">
|
||||||
|
<MCard Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||||
|
<ConsoleTxt Messages="Messages" Height=500></ConsoleTxt>
|
||||||
|
</MCard>
|
||||||
|
</MCol>
|
||||||
|
|
||||||
|
</MRow>
|
||||||
|
|
||||||
|
</MCard>
|
||||||
|
|
||||||
|
|
||||||
|
@code {
|
||||||
|
StringNumber tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using BlazorComponent;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public partial class DriverDebugUIPage : DriverDebugUIBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DeviceVariableRunTimes
|
||||||
|
/// </summary>
|
||||||
|
public List<DeviceVariableRunTime> DeviceVariableRunTimes;
|
||||||
|
/// <summary>
|
||||||
|
/// MaxPack
|
||||||
|
/// </summary>
|
||||||
|
public int MaxPack = 100;
|
||||||
|
/// <summary>
|
||||||
|
/// MulReadAsync
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task MulReadAsync()
|
||||||
|
{
|
||||||
|
var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack);
|
||||||
|
foreach (var item in deviceVariableSourceReads)
|
||||||
|
{
|
||||||
|
var result = await Plc.ReadAsync(item.VariableAddress, item.Length);
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
item.DeviceVariableRunTimes.PraseStructContent(Plc, result.Content);
|
||||||
|
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Content.ToHexString(' ')));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringNumber _selected = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// Sections
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public List<(string Code, string Language)> Sections { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ShowDefaultOtherContent
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowDefaultOtherContent { get; set; } = true;
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
DeviceVariableRunTimes = new()
|
||||||
|
{
|
||||||
|
new DeviceVariableRunTime()
|
||||||
|
{
|
||||||
|
DataTypeEnum=DataTypeEnum.Int16,
|
||||||
|
VariableAddress="40001",
|
||||||
|
IntervalTime=1000,
|
||||||
|
},
|
||||||
|
new DeviceVariableRunTime()
|
||||||
|
{
|
||||||
|
DataTypeEnum=DataTypeEnum.Int16,
|
||||||
|
VariableAddress="40011",
|
||||||
|
IntervalTime=1000,
|
||||||
|
},
|
||||||
|
new DeviceVariableRunTime()
|
||||||
|
{
|
||||||
|
DataTypeEnum=DataTypeEnum.Int16,
|
||||||
|
VariableAddress="40031",
|
||||||
|
IntervalTime=1000,
|
||||||
|
},
|
||||||
|
new DeviceVariableRunTime()
|
||||||
|
{
|
||||||
|
DataTypeEnum=DataTypeEnum.Int16,
|
||||||
|
VariableAddress="40101",
|
||||||
|
IntervalTime=1000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Sections.Add((
|
||||||
|
"""
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class DeviceVariableSourceRead : IDeviceVariableSourceRead<DeviceVariableRunTime>
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public TimerTick TimerTick { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string VariableAddress { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int Length { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public List<DeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<DeviceVariableRunTime>();
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class DeviceVariableRunTime : IDeviceVariableRunTime
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Description("读取间隔")]
|
||||||
|
public int IntervalTime { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Description("变量地址")]
|
||||||
|
public string VariableAddress { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int Index { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Description("数据类型")]
|
||||||
|
public DataTypeEnum DataTypeEnum { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Description("实时值")]
|
||||||
|
public object Value { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public OperResult SetValue(object value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<DeviceVariableRunTime> DeviceVariableRunTimes;
|
||||||
|
|
||||||
|
private static async Task ModbusClientAsync(IReadWrite plc)
|
||||||
|
{
|
||||||
|
DeviceVariableRunTimes = new()
|
||||||
|
{
|
||||||
|
new DeviceVariableRunTime()
|
||||||
|
{
|
||||||
|
DataTypeEnum=DataTypeEnum.Int16,
|
||||||
|
VariableAddress="40001",
|
||||||
|
IntervalTime=1000,
|
||||||
|
},
|
||||||
|
new DeviceVariableRunTime()
|
||||||
|
{
|
||||||
|
DataTypeEnum=DataTypeEnum.Int16,
|
||||||
|
VariableAddress="40011",
|
||||||
|
IntervalTime=1000,
|
||||||
|
},
|
||||||
|
new DeviceVariableRunTime()
|
||||||
|
{
|
||||||
|
DataTypeEnum=DataTypeEnum.Int16,
|
||||||
|
VariableAddress="40031",
|
||||||
|
IntervalTime=1000,
|
||||||
|
},
|
||||||
|
new DeviceVariableRunTime()
|
||||||
|
{
|
||||||
|
DataTypeEnum=DataTypeEnum.Int16,
|
||||||
|
VariableAddress="40101",
|
||||||
|
IntervalTime=1000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#region 连读
|
||||||
|
var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack);
|
||||||
|
foreach (var item in deviceVariableSourceReads)
|
||||||
|
{
|
||||||
|
var result = await Plc.ReadAsync(item.VariableAddress, item.Length);
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
item.DeviceVariableRunTimes.PraseStructContent(result.Content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
""", "csharp"));
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义模板
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ReadWriteContent { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义模板
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment OtherContent { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义模板
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment CodeContent { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
~DriverDebugUIPage()
|
||||||
|
{
|
||||||
|
this.SafeDispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public override IReadWrite Plc { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
Plc?.SafeDispose();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
base.OnAfterRender(firstRender);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
@using BlazorComponent;
|
||||||
|
@using Microsoft.AspNetCore.Components.Web;
|
||||||
|
@using System.IO.Ports;
|
||||||
|
@using Masa.Blazor
|
||||||
|
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
|
||||||
|
<div class="mb-4">通道配置</div>
|
||||||
|
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||||
|
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.PortName)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.PortName />
|
||||||
|
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.BaudRate)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.BaudRate />
|
||||||
|
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.DataBits)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.DataBits />
|
||||||
|
<MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.Parity" Label="@(serialProperty.DescriptionWithOutSugar(x => x.Parity))"
|
||||||
|
Items=@(typeof(Parity).GetEnumListWithOutSugar())
|
||||||
|
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||||
|
ItemText=@((u) =>u.Description)
|
||||||
|
ItemValue=@(u =>(Parity)u.Value)
|
||||||
|
HideDetails=@("auto") Height="30"
|
||||||
|
Dense>
|
||||||
|
</MSelect>
|
||||||
|
<MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.StopBits" Label="@(serialProperty.DescriptionWithOutSugar(x => x.StopBits))"
|
||||||
|
Items=@(typeof(StopBits).GetEnumListWithOutSugar())
|
||||||
|
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||||
|
ItemText=@((u) =>u.Description)
|
||||||
|
ItemValue=@(u =>(StopBits)u.Value)
|
||||||
|
HideDetails=@("auto") Height="30"
|
||||||
|
Dense>
|
||||||
|
</MSelect>
|
||||||
|
<MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
|
||||||
|
连接
|
||||||
|
</MButton>
|
||||||
|
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
|
||||||
|
断开
|
||||||
|
</MButton>
|
||||||
|
</MRow>
|
||||||
|
</MCard>
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public partial class SerialSessionPage : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志输出
|
||||||
|
/// </summary>
|
||||||
|
public Action<LogLevel, object, string, Exception> LogAction;
|
||||||
|
|
||||||
|
private TouchSocketConfig config;
|
||||||
|
|
||||||
|
private readonly SerialProperty serialProperty = new();
|
||||||
|
|
||||||
|
private SerialSession SerialSession { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取对象
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public SerialSession GetSerialSession()
|
||||||
|
{
|
||||||
|
config ??= new TouchSocketConfig();
|
||||||
|
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||||
|
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||||
|
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||||
|
config.SetSerialProperty(serialProperty);
|
||||||
|
//载入配置
|
||||||
|
SerialSession.Setup(config);
|
||||||
|
return SerialSession;
|
||||||
|
}
|
||||||
|
private async Task ConnectAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SerialSession.Close();
|
||||||
|
await GetSerialSession().ConnectAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private void DisConnect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SerialSession.Close();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
config ??= new TouchSocketConfig();
|
||||||
|
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||||
|
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||||
|
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||||
|
SerialSession.Setup(config);
|
||||||
|
}
|
||||||
|
base.OnAfterRender(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
SerialSession.SafeDispose();
|
||||||
|
}
|
||||||
|
internal void StateHasChangedAsync()
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
@using BlazorComponent;
|
||||||
|
@using Microsoft.AspNetCore.Components.Web;
|
||||||
|
@using System.IO.Ports;
|
||||||
|
@using System.Collections.Concurrent;
|
||||||
|
@using ThingsGateway.Foundation.Core;
|
||||||
|
@using Masa.Blazor
|
||||||
|
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
|
||||||
|
<div class="mb-4">通道配置</div>
|
||||||
|
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||||
|
|
||||||
|
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP />
|
||||||
|
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port />
|
||||||
|
|
||||||
|
<MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
|
||||||
|
连接
|
||||||
|
</MButton>
|
||||||
|
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
|
||||||
|
断开
|
||||||
|
</MButton>
|
||||||
|
</MRow>
|
||||||
|
</MCard>
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public partial class TcpClientPage : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志输出
|
||||||
|
/// </summary>
|
||||||
|
public Action<LogLevel, object, string, Exception> LogAction;
|
||||||
|
|
||||||
|
private TouchSocketConfig config;
|
||||||
|
/// <summary>
|
||||||
|
/// IP
|
||||||
|
/// </summary>
|
||||||
|
private string IP = "127.0.0.1";
|
||||||
|
/// <summary>
|
||||||
|
/// Port
|
||||||
|
/// </summary>
|
||||||
|
public int Port { get; set; } = 502;
|
||||||
|
|
||||||
|
private TcpClient TcpClient { get; set; } = new();
|
||||||
|
|
||||||
|
private async Task ConnectAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TcpClient.Close();
|
||||||
|
await GetTcpClient().ConnectAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private void DisConnect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TcpClient.Close();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取对象
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public TcpClient GetTcpClient()
|
||||||
|
{
|
||||||
|
config ??= new TouchSocketConfig();
|
||||||
|
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||||
|
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||||
|
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||||
|
config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
|
||||||
|
//载入配置
|
||||||
|
TcpClient.Setup(config);
|
||||||
|
return TcpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
config ??= new TouchSocketConfig();
|
||||||
|
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||||
|
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||||
|
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||||
|
config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
|
||||||
|
TcpClient.Setup(config);
|
||||||
|
}
|
||||||
|
base.OnAfterRender(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
TcpClient.SafeDispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void StateHasChangedAsync()
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
@using BlazorComponent;
|
||||||
|
@using Microsoft.AspNetCore.Components.Web;
|
||||||
|
@using System.IO.Ports;
|
||||||
|
@using System.Collections.Concurrent;
|
||||||
|
@using ThingsGateway.Foundation.Core;
|
||||||
|
@using Masa.Blazor
|
||||||
|
|
||||||
|
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
|
||||||
|
<div class="mb-4">通道配置</div>
|
||||||
|
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||||
|
|
||||||
|
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
|
||||||
|
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
|
||||||
|
|
||||||
|
<MButton Class="ma-1" OnClick=@Connect Color="primary">
|
||||||
|
连接
|
||||||
|
</MButton>
|
||||||
|
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
|
||||||
|
断开
|
||||||
|
</MButton>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</MRow>
|
||||||
|
</MCard>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public partial class TcpServerPage : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志输出
|
||||||
|
/// </summary>
|
||||||
|
public Action<LogLevel, object, string, Exception> LogAction;
|
||||||
|
|
||||||
|
private TouchSocketConfig config;
|
||||||
|
|
||||||
|
private string ip = "127.0.0.1";
|
||||||
|
|
||||||
|
private int port = 502;
|
||||||
|
|
||||||
|
private TcpService TcpServer { get; set; } = new();
|
||||||
|
|
||||||
|
private void Connect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TcpServer.Stop();
|
||||||
|
GetTcpServer().Start();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private void DisConnect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TcpServer.Stop();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取对象
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public TcpService GetTcpServer()
|
||||||
|
{
|
||||||
|
config ??= new TouchSocketConfig();
|
||||||
|
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||||
|
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||||
|
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||||
|
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
|
||||||
|
//载入配置
|
||||||
|
TcpServer.Setup(config);
|
||||||
|
return TcpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
config ??= new TouchSocketConfig();
|
||||||
|
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||||
|
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||||
|
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||||
|
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
|
||||||
|
TcpServer.Setup(config);
|
||||||
|
}
|
||||||
|
base.OnAfterRender(firstRender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
TcpServer.SafeDispose();
|
||||||
|
}
|
||||||
|
internal void StateHasChangedAsync()
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
@using BlazorComponent;
|
||||||
|
@using Microsoft.AspNetCore.Components.Web;
|
||||||
|
@using System.IO.Ports;
|
||||||
|
@using System.Collections.Concurrent;
|
||||||
|
@using ThingsGateway.Foundation.Core;
|
||||||
|
@using Masa.Blazor
|
||||||
|
|
||||||
|
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
|
||||||
|
<div class="mb-4">通道配置</div>
|
||||||
|
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
|
||||||
|
|
||||||
|
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP />
|
||||||
|
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port />
|
||||||
|
|
||||||
|
<MButton Class="ma-1" OnClick=Connect Color="primary">
|
||||||
|
连接
|
||||||
|
</MButton>
|
||||||
|
<MButton Class="ma-1" OnClick=DisConnect Color="red">
|
||||||
|
断开
|
||||||
|
</MButton>
|
||||||
|
|
||||||
|
|
||||||
|
</MRow>
|
||||||
|
</MCard>
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public partial class UdpSessionPage : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志输出
|
||||||
|
/// </summary>
|
||||||
|
public Action<LogLevel, object, string, Exception> LogAction;
|
||||||
|
|
||||||
|
private TouchSocketConfig config;
|
||||||
|
/// <summary>
|
||||||
|
/// IP
|
||||||
|
/// </summary>
|
||||||
|
public string IP = "127.0.0.1";
|
||||||
|
/// <summary>
|
||||||
|
/// Port
|
||||||
|
/// </summary>
|
||||||
|
public int Port = 502;
|
||||||
|
|
||||||
|
private UdpSession UdpSession { get; set; } = new();
|
||||||
|
|
||||||
|
private void Connect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UdpSession.Stop();
|
||||||
|
GetUdpSession().Start();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
private void DisConnect()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UdpSession.Stop();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
LogAction?.Invoke(LogLevel.Error, null, null, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取对象
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public UdpSession GetUdpSession()
|
||||||
|
{
|
||||||
|
config ??= new TouchSocketConfig();
|
||||||
|
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||||
|
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||||
|
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||||
|
config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
|
||||||
|
config.SetBindIPHost(new IPHost(0));
|
||||||
|
//载入配置
|
||||||
|
UdpSession.Setup(config);
|
||||||
|
return UdpSession;
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
config ??= new TouchSocketConfig();
|
||||||
|
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
|
||||||
|
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
|
||||||
|
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
|
||||||
|
config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
|
||||||
|
config.SetBindIPHost(new IPHost(0));
|
||||||
|
UdpSession.Setup(config);
|
||||||
|
}
|
||||||
|
base.OnAfterRender(firstRender);
|
||||||
|
}
|
||||||
|
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
UdpSession.SafeDispose();
|
||||||
|
}
|
||||||
|
internal void StateHasChangedAsync()
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class DeviceVariableRunTime : IDeviceVariableRunTime
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Description("读取间隔")]
|
||||||
|
public int IntervalTime { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Description("变量地址")]
|
||||||
|
public string VariableAddress { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int Index { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Description("数据类型")]
|
||||||
|
public DataTypeEnum DataTypeEnum { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[Description("实时值")]
|
||||||
|
public object Value { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public OperResult SetValue(object value, DateTime dateTime = default, bool isOnline = true)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class DeviceVariableSourceRead : IDeviceVariableSourceRead<IDeviceVariableRunTime>
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public TimerTick TimerTick { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public string VariableAddress { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public int Length { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public List<IDeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<IDeviceVariableRunTime>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
global using System;
|
||||||
|
global using System.Threading;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
|
|
||||||
|
global using ThingsGateway.Components;
|
||||||
|
global using ThingsGateway.Foundation.Core;
|
||||||
|
global using ThingsGateway.Foundation.Serial;
|
||||||
|
global using ThingsGateway.Foundation.Sockets;
|
||||||
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@page "/"
|
||||||
|
@layout BaseLayout
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
@using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo("index");
|
||||||
|
}
|
||||||
|
|
||||||
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@page "/index"
|
||||||
|
|
||||||
|
<div class="ml-2">
|
||||||
|
<div class="my-6 ">
|
||||||
|
<MLabel Class="text-h3" Color="primary">ThingsGateway</MLabel>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong class="text--lighten-1 text-h5 my-1">文档</strong>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 ml-4">
|
||||||
|
<PCopyableText>
|
||||||
|
https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
</PCopyableText>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong class="text--lighten-1 text-h5 my-1">协议</strong>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 ml-4">
|
||||||
|
<PCopyableText Text="https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh">
|
||||||
|
Apache-2.0开源协议
|
||||||
|
</PCopyableText>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<strong class="text--lighten-1 text-h5 my-1">赞助</strong>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 ml-4">
|
||||||
|
<PCopyableText Text="https://diego2098.gitee.io/thingsgateway-docs/docs/donate">
|
||||||
|
ThingsGateway赞助途径
|
||||||
|
</PCopyableText>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<strong class="text--lighten-1 text-h5 my-1">社区</strong>
|
||||||
|
</div>
|
||||||
|
<div class="my-2 ml-4">
|
||||||
|
<PCopyableText Text="605534569">
|
||||||
|
QQ群:605534569
|
||||||
|
</PCopyableText>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
|
<CascadingValue Value="IsMobile" Name="IsMobile">
|
||||||
|
<MApp>
|
||||||
|
<MErrorHandler>
|
||||||
|
@Body
|
||||||
|
</MErrorHandler>
|
||||||
|
</MApp>
|
||||||
|
</CascadingValue>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
public bool IsMobile { get; set; }
|
||||||
|
[Inject]
|
||||||
|
public MasaBlazor MasaBlazor { get; set; }
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
MasaBlazor.BreakpointChanged += BreakpointOnOnUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BreakpointOnOnUpdate(object sender, BreakpointChangedEventArgs e)
|
||||||
|
{
|
||||||
|
IsMobile = MasaBlazor.Breakpoint.Mobile;
|
||||||
|
if (e.MobileChanged)
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@namespace ThingsGateway.Foundation.Demo
|
||||||
|
@using System.Text;
|
||||||
|
@inherits LayoutComponentBase
|
||||||
|
@layout BaseLayout
|
||||||
|
|
||||||
|
<PPageTabsProvider>
|
||||||
|
|
||||||
|
<CascadingValue Value="@this" IsFixed>
|
||||||
|
<CascadingValue Value="@Changed" Name="Changed">
|
||||||
|
|
||||||
|
<MNavigationDrawer Color="barcolor" @bind-Value="_drawerOpen" App Width="200">
|
||||||
|
<Logo CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_TITLE=@CONFIG_TITLE HeightInt=@(IsMobile?BlazorResourceConst.AppBarHeight:BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight) />
|
||||||
|
<AppList ClassString="overflow-y-auto" Routable
|
||||||
|
StyleString=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight}px);")
|
||||||
|
Items="Navs" />
|
||||||
|
</MNavigationDrawer>
|
||||||
|
|
||||||
|
<MAppBar Color="barcolor" Style=@($"{(!(IsMobile||_drawerOpen!=true)? "left:200px;":"")}") Elevation="1" App Flat ClippedRight Dense ElevateOnScroll
|
||||||
|
MaxHeight="@(BlazorResourceConst.AppBarHeight)" Height="@(BlazorResourceConst.AppBarHeight)">
|
||||||
|
<MButton Class="mr-0" Icon Small=IsMobile OnClick=@(() => _drawerOpen = !_drawerOpen)>
|
||||||
|
<MIcon>mdi-menu</MIcon>
|
||||||
|
</MButton>
|
||||||
|
|
||||||
|
</MAppBar>
|
||||||
|
|
||||||
|
<MMain Style=@($"{(!(IsMobile||_drawerOpen!=true)? "padding-left:200px;":"")}")>
|
||||||
|
<div class="full-width">
|
||||||
|
<PageTabs @ref="_pageTabs" PageTabItems="pageTabItems" />
|
||||||
|
</div>
|
||||||
|
<MDivider Center></MDivider>
|
||||||
|
<MCard Flat Class="overflow-y-auto overflow-x-hidden ma-auto pa-0 rounded-0" Style=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight+BlazorResourceConst.FooterHeight}px);")>
|
||||||
|
<PPageContainer PageTabs="@_pageTabs?.PPageTabs">
|
||||||
|
@Body
|
||||||
|
</PPageContainer>
|
||||||
|
</MCard>
|
||||||
|
<MSheet Class="d-flex justify-center align-center rounded-0" Style=@($"height: {BlazorResourceConst.FooterHeight}px;")>
|
||||||
|
<Foter CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE></Foter>
|
||||||
|
</MSheet>
|
||||||
|
</MMain>
|
||||||
|
</CascadingValue>
|
||||||
|
</CascadingValue>
|
||||||
|
|
||||||
|
</PPageTabsProvider>
|
||||||
|
@code {
|
||||||
|
bool Changed { get; set; }
|
||||||
|
private bool? _drawerOpen = true;
|
||||||
|
|
||||||
|
private PageTabs _pageTabs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IsMobile
|
||||||
|
/// </summary>
|
||||||
|
[CascadingParameter(Name = "IsMobile")]
|
||||||
|
public bool IsMobile { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@code{
|
||||||
|
private string CONFIG_COPYRIGHT = "Diego";
|
||||||
|
private string CONFIG_COPYRIGHT_URL = "https://gitee.com/diego2098/ThingsGateway";
|
||||||
|
private string CONFIG_TITLE = "ThingsGateway";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||||
|
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||||
|
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQȺ<51><C8BA>605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Demo;
|
||||||
|
|
||||||
|
public partial class MainLayout
|
||||||
|
{
|
||||||
|
|
||||||
|
private List<NavItem> Navs { get; set; } = new();
|
||||||
|
private List<PageTabItem> pageTabItems { get; set; } = new();
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
var dataString =
|
||||||
|
"""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Href": "/index",
|
||||||
|
"Title": "<22><>ҳ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "Modbus",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/ModbusRtu",
|
||||||
|
"Title": "ModbusRtu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/ModbusTcp",
|
||||||
|
"Title": "ModbusTcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/ModbusRtuOverTcp",
|
||||||
|
"Title": "ModbusRtuOverTcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/ModbusRtuOverUdp",
|
||||||
|
"Title": "ModbusRtuOverUdp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/ModbusUdp",
|
||||||
|
"Title": "ModbusUdp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/ModbusTcpDtu",
|
||||||
|
"Title": "ModbusTcpDtu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/ModbusTcpServer",
|
||||||
|
"Title": "ModbusTcpServer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/ModbusSerialServer",
|
||||||
|
"Title": "ModbusSerialServer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "Siemens",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/S7_1500",
|
||||||
|
"Title": "S7_1500"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/S7_1200",
|
||||||
|
"Title": "S7_1200"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/S7_200",
|
||||||
|
"Title": "S7_200"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/S7_200SMART",
|
||||||
|
"Title": "S7_200SMART"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/S7_300",
|
||||||
|
"Title": "S7_400"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/S7_400",
|
||||||
|
"Title": "S7_400"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "DLT645",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/DLT645_2007",
|
||||||
|
"Title": "DLT645_2007"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/DLT645_2007OverTcp",
|
||||||
|
"Title": "DLT645_2007OverTcp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "OPCDA",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/OPCDAClient",
|
||||||
|
"Title": "OPCDAClient"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "OPCUA",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/OPCUAClient",
|
||||||
|
"Title": "OPCUAClient"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "Mqtt",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/MqttClient",
|
||||||
|
"Title": "MqttClient"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
""";
|
||||||
|
Navs = dataString.FromJsonString<List<NavItem>>();
|
||||||
|
|
||||||
|
#if Pro
|
||||||
|
var dataStringPro =
|
||||||
|
"""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Title": "Melsec",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/QnA3E_Binary",
|
||||||
|
"Title": "QnA3E_Binary"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "ABCIP",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/ABCIPTCP",
|
||||||
|
"Title": "ABCIPTCP"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "Omron",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/OmronFinsTcp",
|
||||||
|
"Title": "OmronFinsTcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/OmronFinsUdp",
|
||||||
|
"Title": "OmronFinsUdp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "Secs",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/SecsTcp",
|
||||||
|
"Title": "SecsTcp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "TS550",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/TS550",
|
||||||
|
"Title": "TS550"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "Vigor",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/VigorSerial",
|
||||||
|
"Title": "VigorSerial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/VigorSerialOverTcp",
|
||||||
|
"Title": "VigorSerialOverTcp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Title": "GasCustom",
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"Href": "/GasCustomSerial",
|
||||||
|
"Title": "GasCustomSerial"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Href": "/GasCustomSerialOverTcp",
|
||||||
|
"Title": "GasCustomSerialOverTcp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
""";
|
||||||
|
Navs.AddRange(dataStringPro.FromJsonString<List<NavItem>>());
|
||||||
|
#endif
|
||||||
|
pageTabItems = Navs.PasePageTabItem();
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
|
||||||
|
<DefineConstants>Pro</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Melsec\Page\QnA3E_BinaryDebugPage.razor.cs" Link="Pages\Melsec\QnA3E_BinaryDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Melsec\Page\QnA3E_BinaryDebugPage.razor" Link="Pages\Melsec\QnA3E_BinaryDebugPage.razor" />
|
||||||
|
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Melsec\ThingsGateway.Foundation.Adapter.Melsec.csproj" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor.cs" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor" />
|
||||||
|
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.AllenBradleyCip\ThingsGateway.Foundation.Adapter.AllenBradleyCip.csproj" />
|
||||||
|
|
||||||
|
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor" />
|
||||||
|
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor" />
|
||||||
|
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Omron\ThingsGateway.Foundation.Adapter.Omron.csproj" />
|
||||||
|
|
||||||
|
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor.cs" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor" />
|
||||||
|
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Secs\ThingsGateway.Foundation.Adapter.Secs.csproj" />
|
||||||
|
|
||||||
|
|
||||||
|
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor.cs" Link="Pages\TS550\TS550DebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor" Link="Pages\TS550\TS550DebugPage.razor" />
|
||||||
|
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.TS550\ThingsGateway.Foundation.Adapter.TS550.csproj" />
|
||||||
|
|
||||||
|
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialDebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor" Link="Pages\Vigor\VigorSerialDebugPage.razor" />
|
||||||
|
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor" />
|
||||||
|
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Vigor\ThingsGateway.Foundation.Adapter.Vigor.csproj" />
|
||||||
|
|
||||||
|
|
||||||
|
<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor" />
|
||||||
|
<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor" />
|
||||||
|
<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.HZW_QTJC_01\ThingsGateway.Foundation.Adapter.HZW_QTJC_01.csproj" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007DebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\MqttRpcNameVaueWithId.cs" Link="Pages\Mqtt\MqttRpcNameVaueWithId.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor.cs" Link="Pages\Mqtt\MqttClientDebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor.cs" Link="Pages\Mqtt\MqttClientPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\PrivateLogger.cs" Link="Pages\Mqtt\PrivateLogger.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClient.cs" Link="Pages\Mqtt\MqttRpcClient.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClientExtensions.cs" Link="Pages\Mqtt\MqttRpcClientExtensions.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcTopicPair.cs" Link="Pages\Mqtt\MqttRpcTopicPair.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor" Link="Pages\DLT645\DLT645_2007DebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor" />
|
||||||
|
|
||||||
|
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor" Link="Pages\Modbus\ModbusRtuDebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor" Link="Pages\Modbus\ModbusTcpDebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusUdpDebugPage.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor" Link="Pages\Modbus\ModbusUdpDebugPage.razor" />
|
||||||
|
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor.cs" Link="Pages\OPCDA\OPCDAClientDebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor.cs" Link="Pages\OPCDA\OPCDAClientPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor.cs" Link="Pages\OPCDA\OPCDAImportVariable.razor.cs" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor" Link="Pages\OPCDA\OPCDAClientDebugPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor" Link="Pages\OPCDA\OPCDAClientPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor" Link="Pages\OPCDA\OPCDAImportVariable.razor" />
|
||||||
|
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor" Link="Pages\OPCUA\OPCUAClientDebugPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor" Link="Pages\OPCUA\OPCUAClientPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor" Link="Pages\OPCUA\OPCUAImportVariable.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor.cs" Link="Pages\OPCUA\OPCUAClientDebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor.cs" Link="Pages\OPCUA\OPCUAClientPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor.cs" Link="Pages\OPCUA\OPCUAImportVariable.razor.cs" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor" Link="Pages\Siemens\S7_1200DebugPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor" Link="Pages\Siemens\S7_1500DebugPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor" Link="Pages\Siemens\S7_200DebugPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor" Link="Pages\Siemens\S7_200SMARTDebugPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor" Link="Pages\Siemens\S7_300DebugPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor" Link="Pages\Siemens\S7_400DebugPage.razor" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor.cs" Link="Pages\Siemens\S7_1200DebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor.cs" Link="Pages\Siemens\S7_1500DebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor.cs" Link="Pages\Siemens\S7_200DebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor.cs" Link="Pages\Siemens\S7_200SMARTDebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor.cs" Link="Pages\Siemens\S7_300DebugPage.razor.cs" />
|
||||||
|
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor.cs" Link="Pages\Siemens\S7_400DebugPage.razor.cs" />
|
||||||
|
|
||||||
|
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup >
|
||||||
|
<!--<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" />
|
||||||
|
<PackageReference Include="ThingsGateway.Foundation.Adapter.Modbus" Version="*" />
|
||||||
|
<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCDA" Version="*" />
|
||||||
|
<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCUA" Version="*" />
|
||||||
|
<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />-->
|
||||||
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
|
||||||
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
|
||||||
|
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup >
|
||||||
|
<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="wwwroot\**">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor" Link="Pages\Mqtt\MqttClientDebugPage.razor" />
|
||||||
|
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor" Link="Pages\Mqtt\MqttClientPage.razor" />
|
||||||
|
<PackageReference Include="MQTTnet" Version="4.3.1.873" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
@*
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
*@
|
||||||
|
|
||||||
|
@using System.Net.Http
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.JSInterop
|
||||||
|
@using BlazorComponent
|
||||||
|
@using Masa.Blazor
|
||||||
|
@using Masa.Blazor.Presets
|
||||||
|
@using ThingsGateway.Foundation.Core;
|
||||||
|
@using ThingsGateway.Components;
|
||||||
|
@using ThingsGateway.Core;
|
||||||
|
@using System.Net.Http.Json
|
||||||
|
@using System.IO;
|
||||||
|
@using System.Text.Json;
|
||||||
|
@using ThingsGateway.Foundation.Serial;
|
||||||
|
@using ThingsGateway.Foundation.Sockets;
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,41 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||||
|
<title>ThingsGateway.Foundation.Demo</title>
|
||||||
|
<base href="/" />
|
||||||
|
|
||||||
|
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||||
|
|
||||||
|
|
||||||
|
<link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet" />
|
||||||
|
<link href="_content/ThingsGateway.Components/css/materialdesign/v7.1.96/css/materialdesignicons.min.css" rel="stylesheet">
|
||||||
|
<link href="_content/ThingsGateway.Components/css/material/icons.css" rel="stylesheet">
|
||||||
|
<link href="_content/ThingsGateway.Components/css/fontawesome/v6.4.0/css/all.min.css" rel="stylesheet">
|
||||||
|
<link href="_content/ThingsGateway.Components/style/custom.css" rel="stylesheet">
|
||||||
|
<link href="_content/ThingsGateway.Components/prism/prism-material-dark-for-masa.css" rel="stylesheet">
|
||||||
|
<link href="_content/ThingsGateway.Components/prism/prism-line-highlight.min.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
|
<div id="blazor-error-ui">
|
||||||
|
<span>
|
||||||
|
<environment include="Staging,Production">
|
||||||
|
An error has occurred. This application may no longer respond until reloaded.
|
||||||
|
</environment>
|
||||||
|
<environment include="Development">
|
||||||
|
An unhandled exception has occurred. See browser dev tools for details.
|
||||||
|
</environment>
|
||||||
|
</span>
|
||||||
|
<a href="" class="reload">Reload</a>
|
||||||
|
<a class="dismiss">🗙</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="_framework/blazor.webview.js" autostart="true"></script>
|
||||||
|
<script src="_content/ThingsGateway.Components/prism/prism.min.js"></script>
|
||||||
|
<script src="_content/BlazorComponent/js/blazor-component.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
53
framework/Foundation/Directory.Build.props
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<Version>3.0.0.19</Version>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks>
|
||||||
|
<Description>
|
||||||
|
ThingsGateway.Foundation是工业设备通讯类库,归属于ThingsGateway边缘网关项目,说明文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
</Description>
|
||||||
|
<Authors>Diego</Authors>
|
||||||
|
<Product>ThingsGateway</Product>
|
||||||
|
<Copyright>© 2023-present Diego</Copyright>
|
||||||
|
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<EmbedUntrackedSource>true</EmbedUntrackedSource>
|
||||||
|
<EmbedAllSources>true</EmbedAllSources>
|
||||||
|
<RepositoryType>Gitee</RepositoryType>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageIcon>icon.png</PackageIcon>
|
||||||
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
|
<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl>
|
||||||
|
<PackageTags>ThingsGateway;Diego;dotNET China;Blazor;设备采集;边缘网关</PackageTags>
|
||||||
|
<SignAssembly>True</SignAssembly>
|
||||||
|
<DelaySign>False</DelaySign>
|
||||||
|
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
|
||||||
|
<PackageOutputPath>../../nupkgs</PackageOutputPath>
|
||||||
|
<AssemblyOriginatorKeyFile>../../../snks/ThingsGateway.snk</AssemblyOriginatorKeyFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\..\..\README.md" Pack="true" PackagePath="\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="..\..\..\icon.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath></PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||||
|
<DebugSymbols>True</DebugSymbols>
|
||||||
|
<DebugType>Embedded</DebugType>
|
||||||
|
<EmbedAllSources>True</EmbedAllSources>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控制码
|
||||||
|
/// </summary>
|
||||||
|
public enum ControlCode : byte
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 读数据
|
||||||
|
/// </summary>
|
||||||
|
Read = 0x11,
|
||||||
|
/// <summary>
|
||||||
|
/// 读后续数据
|
||||||
|
/// </summary>
|
||||||
|
ReadSub = 0x12,
|
||||||
|
/// <summary>
|
||||||
|
/// 读站号
|
||||||
|
/// </summary>
|
||||||
|
ReadStation = 0x13,
|
||||||
|
/// <summary>
|
||||||
|
/// 写数据
|
||||||
|
/// </summary>
|
||||||
|
Write = 0x14,
|
||||||
|
/// <summary>
|
||||||
|
/// 写站号
|
||||||
|
/// </summary>
|
||||||
|
WriteStation = 0x15,
|
||||||
|
/// <summary>
|
||||||
|
/// 广播校时
|
||||||
|
/// </summary>
|
||||||
|
BroadcastTime = 0x08,
|
||||||
|
/// <summary>
|
||||||
|
/// 冻结
|
||||||
|
/// </summary>
|
||||||
|
Freeze = 0x16,
|
||||||
|
/// <summary>
|
||||||
|
/// 更新波特率
|
||||||
|
/// </summary>
|
||||||
|
WriteBaudRate = 0x17,
|
||||||
|
/// <summary>
|
||||||
|
/// 更新密码
|
||||||
|
/// </summary>
|
||||||
|
WritePassword = 0x18,
|
||||||
|
}
|
||||||
@@ -0,0 +1,480 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645_2007
|
||||||
|
/// </summary>
|
||||||
|
public class DLT645_2007 : ReadWriteDevicesSerialSessionBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645_2007
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serialSession"></param>
|
||||||
|
public DLT645_2007(SerialSession serialSession) : base(serialSession)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增加FE FE FE FE的报文头部
|
||||||
|
/// </summary>
|
||||||
|
[Description("前导符报文头")]
|
||||||
|
public bool EnableFEHead { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写入需操作员代码
|
||||||
|
/// </summary>
|
||||||
|
[Description("操作员代码")]
|
||||||
|
public string OperCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写入密码
|
||||||
|
/// </summary>
|
||||||
|
[Description("写入密码")]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通讯地址BCD码,一般应该是12个字符
|
||||||
|
/// </summary>
|
||||||
|
[Description("通讯地址")]
|
||||||
|
public string Station { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
|
||||||
|
var str = """
|
||||||
|
查看附带文档或者相关资料,下面列举一下常见的数据标识地址
|
||||||
|
|
||||||
|
地址 说明
|
||||||
|
-----------------------------------------
|
||||||
|
02010100 A相电压
|
||||||
|
02020100 A相电流
|
||||||
|
02030000 瞬时总有功功率
|
||||||
|
00000000 (当前)组合有功总电能
|
||||||
|
00010000 (当前)正向有功总电能
|
||||||
|
|
||||||
|
""";
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient = null)
|
||||||
|
{
|
||||||
|
var dataHandleAdapter = new DLT645_2007DataHandleAdapter
|
||||||
|
{
|
||||||
|
EnableFEHead = EnableFEHead
|
||||||
|
};
|
||||||
|
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, string value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
Password ??= string.Empty;
|
||||||
|
OperCode ??= string.Empty;
|
||||||
|
if (Password.Length < 8)
|
||||||
|
Password = Password.PadLeft(8, '0');
|
||||||
|
if (OperCode.Length < 8)
|
||||||
|
OperCode = OperCode.PadLeft(8, '0');
|
||||||
|
var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
|
||||||
|
string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
Password ??= string.Empty;
|
||||||
|
OperCode ??= string.Empty;
|
||||||
|
if (Password.Length < 8)
|
||||||
|
Password = Password.PadLeft(8, '0');
|
||||||
|
if (OperCode.Length < 8)
|
||||||
|
OperCode = OperCode.PadLeft(8, '0');
|
||||||
|
var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
|
||||||
|
string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
|
#region 其他方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 广播校时
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dateTime"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}";
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().ToArray(), "999999999999".ByHexStringToBytes());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
SerialSession.Send(commandResult.Content);
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冻结
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dateTime"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}";
|
||||||
|
if (Station.IsNullOrEmpty()) Station = string.Empty;
|
||||||
|
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取通信地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
var buffer = result1.Content.SelectMiddle(0, 6).BytesAdd(-0x33);
|
||||||
|
return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<string>(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<string>(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 修改波特率
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baudRate"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
byte baudRateByte;
|
||||||
|
switch (baudRate)
|
||||||
|
{
|
||||||
|
case 600: baudRateByte = 0x02; break;
|
||||||
|
case 1200: baudRateByte = 0x04; break;
|
||||||
|
case 2400: baudRateByte = 0x08; break;
|
||||||
|
case 4800: baudRateByte = 0x10; break;
|
||||||
|
case 9600: baudRateByte = 0x20; break;
|
||||||
|
case 19200: baudRateByte = 0x40; break;
|
||||||
|
default: return new OperResult<string>($"不支持此波特率:{baudRate}");
|
||||||
|
}
|
||||||
|
if (Station.IsNullOrEmpty()) Station = string.Empty;
|
||||||
|
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新通信地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 修改密码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level"></param>
|
||||||
|
/// <param name="oldPassword"></param>
|
||||||
|
/// <param name="newPassword"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (Station.IsNullOrEmpty()) Station = string.Empty;
|
||||||
|
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
|
||||||
|
string str = $"04000C{level + 1:D2}";
|
||||||
|
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword,
|
||||||
|
str.ByHexStringToBytes().Reverse().ToArray()
|
||||||
|
.SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray())
|
||||||
|
.SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray())
|
||||||
|
, Station.ByHexStringToBytes().Reverse().ToArray());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.String;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645_2007Address
|
||||||
|
/// </summary>
|
||||||
|
public class DLT645_2007Address : DeviceAddressBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public DLT645_2007Address()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据标识
|
||||||
|
/// </summary>
|
||||||
|
public byte[] DataId { get; set; } = new byte[0];
|
||||||
|
/// <summary>
|
||||||
|
/// 反转解析
|
||||||
|
/// </summary>
|
||||||
|
public bool Reverse { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// 站号信息
|
||||||
|
/// </summary>
|
||||||
|
public byte[] Station { get; set; } = new byte[0];
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static DLT645_2007Address ParseFrom(string address)
|
||||||
|
{
|
||||||
|
DLT645_2007Address dLT645_2007Address = new();
|
||||||
|
byte[] array;
|
||||||
|
array = new byte[0];
|
||||||
|
if (address.IndexOf(';') < 0)
|
||||||
|
{
|
||||||
|
array = address.ByHexStringToBytes().Reverse().ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
for (int index = 0; index < strArray.Length; ++index)
|
||||||
|
{
|
||||||
|
if (strArray[index].ToUpper().StartsWith("S="))
|
||||||
|
{
|
||||||
|
var station = strArray[index].Substring(2);
|
||||||
|
if (station.IsNullOrEmpty()) station = string.Empty;
|
||||||
|
if (station.Length < 12)
|
||||||
|
station = station.PadLeft(12, '0');
|
||||||
|
dLT645_2007Address.Station = station.ByHexStringToBytes().Reverse().ToArray();
|
||||||
|
}
|
||||||
|
else if (strArray[index].Contains("r="))
|
||||||
|
{
|
||||||
|
dLT645_2007Address.Reverse = strArray[index].Substring(2).GetBoolValue();
|
||||||
|
}
|
||||||
|
else if (!strArray[index].Contains("="))
|
||||||
|
{
|
||||||
|
array = strArray[index].ByHexStringToBytes().Reverse().ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dLT645_2007Address.DataId = array;
|
||||||
|
return dLT645_2007Address;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder stringGeter = new();
|
||||||
|
if (Station.Length > 0)
|
||||||
|
{
|
||||||
|
stringGeter.Append($"s={Station.Reverse().ToArray().ToHexString()};");
|
||||||
|
}
|
||||||
|
if (DataId.Length > 0)
|
||||||
|
{
|
||||||
|
stringGeter.Append($"{DataId.Reverse().ToArray().ToHexString()};");
|
||||||
|
}
|
||||||
|
if (!Reverse)
|
||||||
|
{
|
||||||
|
stringGeter.Append($"s={Reverse.ToString()};");
|
||||||
|
}
|
||||||
|
return stringGeter.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645_2007
|
||||||
|
/// </summary>
|
||||||
|
public class DLT645_2007BitConverter : ThingsGatewayBitConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645_2007
|
||||||
|
/// </summary>
|
||||||
|
public DLT645_2007BitConverter(EndianType endianType) : base(endianType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645协议转换double
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">带数据项标识</param>
|
||||||
|
/// <param name="offset"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override double ToDouble(byte[] buffer, int offset)
|
||||||
|
{
|
||||||
|
return Convert.ToDouble(this.ToString(buffer, offset, buffer.Length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override IThingsGatewayBitConverter CopyNew()
|
||||||
|
{
|
||||||
|
return new DLT645_2007BitConverter(EndianType)
|
||||||
|
{
|
||||||
|
DataFormat = DataFormat,
|
||||||
|
BcdFormat = BcdFormat,
|
||||||
|
Encoding = Encoding,
|
||||||
|
IsStringReverseByteWord = IsStringReverseByteWord,
|
||||||
|
Length = Length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString(byte[] buffer)
|
||||||
|
{
|
||||||
|
return this.ToString(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString(byte[] buffer, int offset, int length)
|
||||||
|
{
|
||||||
|
buffer = buffer.RemoveBegin(offset);
|
||||||
|
buffer = buffer.BytesAdd(-0x33);
|
||||||
|
var dataInfos = DLT645Helper.GetDataInfos(buffer);
|
||||||
|
StringBuilder stringBuilder = new();
|
||||||
|
foreach (var dataInfo in dataInfos)
|
||||||
|
{
|
||||||
|
//实际数据
|
||||||
|
var content = buffer.SelectMiddle(4, dataInfo.ByteLength).Reverse().ToArray();
|
||||||
|
if (dataInfo.IsSigned)//可能为负数
|
||||||
|
{
|
||||||
|
if (content[0] > 0x80)//最高位是表示正负
|
||||||
|
{
|
||||||
|
content[0] = (byte)(content[0] - 0x80);
|
||||||
|
if (dataInfo.Digtal == 0)//无小数点
|
||||||
|
{
|
||||||
|
stringBuilder.Append($"-{content.ToHexString()}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stringBuilder.Append((-(Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ToString(stringBuilder, dataInfo, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ToString(stringBuilder, dataInfo, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
|
||||||
|
static void ToString(StringBuilder stringBuilder, DataInfo dataInfo, byte[] content)
|
||||||
|
{
|
||||||
|
if (dataInfo.Digtal < 0)
|
||||||
|
{
|
||||||
|
stringBuilder.Append($"{Encoding.ASCII.GetString(content)}");
|
||||||
|
}
|
||||||
|
else if (dataInfo.Digtal == 0)//无小数点
|
||||||
|
{
|
||||||
|
stringBuilder.Append($"{content.ToHexString()}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stringBuilder.Append(((Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645_2007DataHandleAdapter
|
||||||
|
/// </summary>
|
||||||
|
public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<DLT645_2007Message>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 增加FE FE FE FE的报文头部
|
||||||
|
/// </summary>
|
||||||
|
[Description("前导符报文头")]
|
||||||
|
public bool EnableFEHead { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override byte[] PackCommand(byte[] command)
|
||||||
|
{
|
||||||
|
//打包时加上4个FE字节
|
||||||
|
if (EnableFEHead)
|
||||||
|
{
|
||||||
|
return DataTransUtil.SpliceArray(new byte[4] { 0xFE, 0xFE, 0xFE, 0xFE }, command);
|
||||||
|
}
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override DLT645_2007Message GetInstance()
|
||||||
|
{
|
||||||
|
return new DLT645_2007Message();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override FilterResult UnpackResponse(DLT645_2007Message request, byte[] send, byte[] body, byte[] response)
|
||||||
|
{
|
||||||
|
//因为设备可能带有FE前导符开头,这里找到0x68的位置
|
||||||
|
int headCodeIndex = -1;
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < response.Length; index++)
|
||||||
|
{
|
||||||
|
if (response[index] == 0x68)
|
||||||
|
{
|
||||||
|
headCodeIndex = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int sendHeadCodeIndex = 0;
|
||||||
|
if (send != null)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < send.Length; index++)
|
||||||
|
{
|
||||||
|
if (send[index] == 0x68)
|
||||||
|
{
|
||||||
|
sendHeadCodeIndex = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//帧起始符 地址域 帧起始符 控制码 数据域长度共10个字节
|
||||||
|
if (headCodeIndex < 0 || headCodeIndex + 10 > response.Length)
|
||||||
|
return FilterResult.Cache;
|
||||||
|
|
||||||
|
|
||||||
|
var len = 10 + response[headCodeIndex + 9] + 2;
|
||||||
|
|
||||||
|
if (response.Length - headCodeIndex < len)
|
||||||
|
{
|
||||||
|
return FilterResult.Cache;
|
||||||
|
}
|
||||||
|
if (response.Length - headCodeIndex >= len && response[len + headCodeIndex - 1] == 0x16)
|
||||||
|
{
|
||||||
|
|
||||||
|
//检查校验码
|
||||||
|
int sumCheck = 0;
|
||||||
|
for (int i = headCodeIndex; i < len + headCodeIndex - 2; i++)
|
||||||
|
sumCheck += response[i];
|
||||||
|
if ((byte)sumCheck != response[len + headCodeIndex - 2])
|
||||||
|
{
|
||||||
|
//校验错误
|
||||||
|
request.Message = "和校验错误";
|
||||||
|
request.ErrorCode = 999;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(response[headCodeIndex + 1] != send[sendHeadCodeIndex + 1]) ||
|
||||||
|
(response[headCodeIndex + 2] != send[sendHeadCodeIndex + 2]) ||
|
||||||
|
(response[headCodeIndex + 3] != send[sendHeadCodeIndex + 3]) ||
|
||||||
|
(response[headCodeIndex + 4] != send[sendHeadCodeIndex + 4]) ||
|
||||||
|
(response[headCodeIndex + 5] != send[sendHeadCodeIndex + 5]) ||
|
||||||
|
(response[headCodeIndex + 6] != send[sendHeadCodeIndex + 6])
|
||||||
|
)//设备地址不符合时,返回错误
|
||||||
|
{
|
||||||
|
|
||||||
|
if (
|
||||||
|
(send[sendHeadCodeIndex + 1] == 0xAA) &&
|
||||||
|
(send[sendHeadCodeIndex + 2] == 0xAA) &&
|
||||||
|
(send[sendHeadCodeIndex + 3] == 0xAA) &&
|
||||||
|
(send[sendHeadCodeIndex + 4] == 0xAA) &&
|
||||||
|
(send[sendHeadCodeIndex + 5] == 0xAA) &&
|
||||||
|
(send[sendHeadCodeIndex + 6] == 0xAA)
|
||||||
|
)//读写通讯地址例外
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.Message = "返回地址不符合规则";
|
||||||
|
request.ErrorCode = 999;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ((response[headCodeIndex + 8] != send[sendHeadCodeIndex + 8] + 0x80))//控制码不符合时,返回错误
|
||||||
|
{
|
||||||
|
request.Message = $"返回控制码:0x{response[headCodeIndex + 8]:X2},请求控制码:0x{send[sendHeadCodeIndex + 8]:X2},不符合规则";
|
||||||
|
request.ErrorCode = 999;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if ((response[headCodeIndex + 8] & 0x40) == 0x40)//控制码bit6为1时,返回错误
|
||||||
|
{
|
||||||
|
byte byte1 = (byte)(response[headCodeIndex + 10] - 0x33);
|
||||||
|
var error = DLT645Helper.Get2007ErrorMessage(byte1);
|
||||||
|
request.Message = $"异常控制码:0x{response[headCodeIndex + 8]:X2},错误信息:{error}";
|
||||||
|
request.ErrorCode = 999;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send[sendHeadCodeIndex + 8] == (byte)ControlCode.Read ||
|
||||||
|
send[sendHeadCodeIndex + 8] == (byte)ControlCode.Write
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//数据标识不符合时,返回错误
|
||||||
|
if (
|
||||||
|
(response[headCodeIndex + 10] == send[sendHeadCodeIndex + 10]) &&
|
||||||
|
(response[headCodeIndex + 11] == send[sendHeadCodeIndex + 11]) &&
|
||||||
|
(response[headCodeIndex + 12] == send[sendHeadCodeIndex + 12]) &&
|
||||||
|
(response[headCodeIndex + 13] == send[sendHeadCodeIndex + 13])
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.Message = "返回数据标识不符合规则";
|
||||||
|
request.ErrorCode = 999;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
request.Content = response.RemoveBegin(headCodeIndex + 10).RemoveLast(response.Length + 2 - len - headCodeIndex);
|
||||||
|
request.ErrorCode = 0;
|
||||||
|
return FilterResult.Success;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.ErrorCode = 999;
|
||||||
|
return FilterResult.Success;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class DLT645_2007Message : MessageBase, IMessage
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override int HeadBytesLength => -1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CheckHeadBytes(byte[] heads)
|
||||||
|
{
|
||||||
|
BodyLength = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,480 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645_2007
|
||||||
|
/// </summary>
|
||||||
|
public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// DLT645_2007
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tcpClient"></param>
|
||||||
|
public DLT645_2007OverTcp(TcpClient tcpClient) : base(tcpClient)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增加FE FE FE FE的报文头部
|
||||||
|
/// </summary>
|
||||||
|
[Description("前导符报文头")]
|
||||||
|
public bool EnableFEHead { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写入需操作员代码
|
||||||
|
/// </summary>
|
||||||
|
[Description("操作员代码")]
|
||||||
|
public string OperCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写入密码
|
||||||
|
/// </summary>
|
||||||
|
[Description("写入密码")]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通讯地址BCD码,一般应该是12个字符
|
||||||
|
/// </summary>
|
||||||
|
[Description("通讯地址")]
|
||||||
|
public string Station { get; set; }
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
|
||||||
|
var str = """
|
||||||
|
查看附带文档或者相关资料,下面列举一下常见的数据标识地址
|
||||||
|
|
||||||
|
地址 说明
|
||||||
|
-----------------------------------------
|
||||||
|
02010100 A相电压
|
||||||
|
02020100 A相电流
|
||||||
|
02030000 瞬时总有功功率
|
||||||
|
00000000 (当前)组合有功总电能
|
||||||
|
00010000 (当前)正向有功总电能
|
||||||
|
|
||||||
|
""";
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient = null)
|
||||||
|
{
|
||||||
|
var dataHandleAdapter = new DLT645_2007DataHandleAdapter
|
||||||
|
{
|
||||||
|
EnableFEHead = EnableFEHead
|
||||||
|
};
|
||||||
|
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, string value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
Password ??= string.Empty;
|
||||||
|
OperCode ??= string.Empty;
|
||||||
|
if (Password.Length < 8)
|
||||||
|
Password = Password.PadLeft(8, '0');
|
||||||
|
if (OperCode.Length < 8)
|
||||||
|
OperCode = OperCode.PadLeft(8, '0');
|
||||||
|
var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
|
||||||
|
string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
Password ??= string.Empty;
|
||||||
|
OperCode ??= string.Empty;
|
||||||
|
if (Password.Length < 8)
|
||||||
|
Password = Password.PadLeft(8, '0');
|
||||||
|
if (OperCode.Length < 8)
|
||||||
|
OperCode = OperCode.PadLeft(8, '0');
|
||||||
|
var data = DataTransUtil.SpliceArray(Password.ByHexStringToBytes(), OperCode.ByHexStringToBytes());
|
||||||
|
string[] strArray = value.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, double value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, float value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, long value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, short value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, int value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default) => WriteAsync(address, value.ToString(), cancellationToken);
|
||||||
|
|
||||||
|
|
||||||
|
#region 其他方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 广播校时
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dateTime"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
string str = $"{dateTime.Second:D2}{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}{dateTime.Year % 100:D2}";
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().Reverse().ToArray(), "999999999999".ByHexStringToBytes());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
TcpClient.Send(commandResult.Content);
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 冻结
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dateTime"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
string str = $"{dateTime.Minute:D2}{dateTime.Hour:D2}{dateTime.Day:D2}{dateTime.Month:D2}";
|
||||||
|
if (Station.IsNullOrEmpty()) Station = string.Empty;
|
||||||
|
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.Freeze, str.ByHexStringToBytes().Reverse().ToArray(), Station.ByHexStringToBytes().Reverse().ToArray());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取通信地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.ReadStation, null, "AAAAAAAAAAAA".ByHexStringToBytes());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
var buffer = result1.Content.SelectMiddle(0, 6).BytesAdd(-0x33);
|
||||||
|
return OperResult.CreateSuccessResult(buffer.Reverse().ToArray().ToHexString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<string>(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<string>(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 修改波特率
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baudRate"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
byte baudRateByte;
|
||||||
|
switch (baudRate)
|
||||||
|
{
|
||||||
|
case 600: baudRateByte = 0x02; break;
|
||||||
|
case 1200: baudRateByte = 0x04; break;
|
||||||
|
case 2400: baudRateByte = 0x08; break;
|
||||||
|
case 4800: baudRateByte = 0x10; break;
|
||||||
|
case 9600: baudRateByte = 0x20; break;
|
||||||
|
case 19200: baudRateByte = 0x40; break;
|
||||||
|
default: return new OperResult<string>($"不支持此波特率:{baudRate}");
|
||||||
|
}
|
||||||
|
if (Station.IsNullOrEmpty()) Station = string.Empty;
|
||||||
|
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteBaudRate, new byte[] { baudRateByte }, Station.ByHexStringToBytes().Reverse().ToArray());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新通信地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="station"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WriteStation, station.ByHexStringToBytes().Reverse().ToArray(), "AAAAAAAAAAAA".ByHexStringToBytes());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 修改密码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level"></param>
|
||||||
|
/// <param name="oldPassword"></param>
|
||||||
|
/// <param name="newPassword"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
|
||||||
|
if (Station.IsNullOrEmpty()) Station = string.Empty;
|
||||||
|
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
|
||||||
|
string str = $"04000C{level:D2}";
|
||||||
|
|
||||||
|
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword,
|
||||||
|
str.ByHexStringToBytes().Reverse().ToArray()
|
||||||
|
.SpliceArray(oldPassword.ByHexStringToBytes().Reverse().ToArray())
|
||||||
|
.SpliceArray(newPassword.ByHexStringToBytes().Reverse().ToArray())
|
||||||
|
, Station.ByHexStringToBytes().Reverse().ToArray());
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||||||
|
var result1 = ((MessageBase)result.RequestInfo);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(result1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult(commandResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<string>(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.DLT645;
|
||||||
|
|
||||||
|
internal static class PackHelper
|
||||||
|
{
|
||||||
|
public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
|
||||||
|
{
|
||||||
|
var byteConverter = device.ThingsGatewayBitConverter;
|
||||||
|
var result = new List<T>();
|
||||||
|
//需要先剔除额外信息,比如dataformat等
|
||||||
|
foreach (var item in deviceVariables)
|
||||||
|
{
|
||||||
|
var address = item.VariableAddress;
|
||||||
|
|
||||||
|
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
|
||||||
|
item.ThingsGatewayBitConverter = transformParameter;
|
||||||
|
//item.VariableAddress = address;
|
||||||
|
item.Index = device.GetBitOffset(item.VariableAddress);
|
||||||
|
|
||||||
|
result.Add(new()
|
||||||
|
{
|
||||||
|
DeviceVariableRunTimes = new() { item },
|
||||||
|
VariableAddress = address,
|
||||||
|
Length = 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
global using System;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.Linq;
|
||||||
|
global using System.Threading;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
|
|
||||||
|
global using ThingsGateway.Foundation.Core;
|
||||||
|
global using ThingsGateway.Foundation.Serial;
|
||||||
|
global using ThingsGateway.Foundation.Sockets;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
global using System;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.Linq;
|
||||||
|
global using System.Threading;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
|
|
||||||
|
global using ThingsGateway.Foundation.Core;
|
||||||
|
global using ThingsGateway.Foundation.Serial;
|
||||||
|
global using ThingsGateway.Foundation.Sockets;
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension;
|
||||||
|
using ThingsGateway.Foundation.Extension.String;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Modbus协议地址
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusAddress : DeviceAddressBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public ModbusAddress()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 读取功能码
|
||||||
|
/// </summary>
|
||||||
|
public ushort AddressStart => Address.ToUShort();
|
||||||
|
/// <summary>
|
||||||
|
/// 读取功能码
|
||||||
|
/// </summary>
|
||||||
|
public byte ReadFunction { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 站号信息
|
||||||
|
/// </summary>
|
||||||
|
public byte Station { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 写入功能码
|
||||||
|
/// </summary>
|
||||||
|
public byte WriteFunction { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 打包临时写入,需要读取的字节长度
|
||||||
|
/// </summary>
|
||||||
|
public int ByteLength { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// BitIndex
|
||||||
|
/// </summary>
|
||||||
|
public int BitIndex => (int)(Address.SplitDot().LastOrDefault().ToInt());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取功能码
|
||||||
|
/// </summary>
|
||||||
|
public ushort AddressEnd => (ushort)(AddressStart + (Math.Ceiling(ByteLength / 2.0) > 0 ? Math.Ceiling(ByteLength / 2.0) : 1));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 作为Slave时需提供的SocketId,用于分辨Socket客户端,通常对比的是初始链接时的注册包
|
||||||
|
/// </summary>
|
||||||
|
public string SocketId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 解析地址
|
||||||
|
/// </summary>
|
||||||
|
public static ModbusAddress ParseFrom(string address, byte station)
|
||||||
|
{
|
||||||
|
ModbusAddress modbusAddress = new()
|
||||||
|
{
|
||||||
|
Station = station
|
||||||
|
};
|
||||||
|
return ParseFrom(address, modbusAddress);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 解析地址
|
||||||
|
/// </summary>
|
||||||
|
public static ModbusAddress ParseFrom(string address, ModbusAddress modbusAddress = null)
|
||||||
|
{
|
||||||
|
modbusAddress ??= new();
|
||||||
|
if (address.IndexOf(';') < 0)
|
||||||
|
{
|
||||||
|
Address(address);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
for (int index = 0; index < strArray.Length; ++index)
|
||||||
|
{
|
||||||
|
if (strArray[index].ToUpper().StartsWith("S="))
|
||||||
|
{
|
||||||
|
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
|
||||||
|
modbusAddress.Station = byte.Parse(strArray[index].Substring(2));
|
||||||
|
}
|
||||||
|
else if (strArray[index].ToUpper().StartsWith("W="))
|
||||||
|
{
|
||||||
|
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
|
||||||
|
modbusAddress.WriteFunction = byte.Parse(strArray[index].Substring(2));
|
||||||
|
}
|
||||||
|
else if (strArray[index].ToUpper().StartsWith("ID="))
|
||||||
|
{
|
||||||
|
modbusAddress.SocketId = strArray[index].Substring(3);
|
||||||
|
}
|
||||||
|
else if (!strArray[index].Contains("="))
|
||||||
|
{
|
||||||
|
Address(strArray[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modbusAddress;
|
||||||
|
|
||||||
|
void Address(string address)
|
||||||
|
{
|
||||||
|
var readF = ushort.Parse(address.Substring(0, 1));
|
||||||
|
if (readF > 4)
|
||||||
|
throw new("功能码错误");
|
||||||
|
switch (readF)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
modbusAddress.ReadFunction = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
modbusAddress.ReadFunction = 2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
modbusAddress.ReadFunction = 4;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
modbusAddress.ReadFunction = 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
modbusAddress.Address = (double.Parse(address.Substring(1)) - 1).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder stringGeter = new();
|
||||||
|
if (Station > 0)
|
||||||
|
{
|
||||||
|
stringGeter.Append($"s={Station.ToString()};");
|
||||||
|
}
|
||||||
|
if (WriteFunction > 0)
|
||||||
|
{
|
||||||
|
stringGeter.Append($"w={WriteFunction.ToString()};");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(SocketId))
|
||||||
|
{
|
||||||
|
stringGeter.Append($"id={SocketId};");
|
||||||
|
}
|
||||||
|
stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString());
|
||||||
|
return stringGeter.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFunctionString(int readF)
|
||||||
|
{
|
||||||
|
return readF switch
|
||||||
|
{
|
||||||
|
1 => "0",
|
||||||
|
2 => "1",
|
||||||
|
3 => "4",
|
||||||
|
4 => "3",
|
||||||
|
_ => "4",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Bool;
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
internal class ModbusHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加Crc16
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] AddCrc(byte[] command)
|
||||||
|
{
|
||||||
|
return EasyCRC16.CRC16(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加ModbusTcp报文头
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
|
||||||
|
{
|
||||||
|
byte[] tcp = new byte[modbus.Length + 6];
|
||||||
|
tcp[0] = BitConverter.GetBytes(id)[1];
|
||||||
|
tcp[1] = BitConverter.GetBytes(id)[0];
|
||||||
|
tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
|
||||||
|
tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
|
||||||
|
modbus.CopyTo(tcp, 6);
|
||||||
|
return tcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// modbus地址格式说明
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static string GetAddressDescription()
|
||||||
|
{
|
||||||
|
StringBuilder stringBuilder = new();
|
||||||
|
stringBuilder.AppendLine("Modbus寄存器");
|
||||||
|
stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。");
|
||||||
|
stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。");
|
||||||
|
stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。");
|
||||||
|
stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。");
|
||||||
|
stringBuilder.AppendLine("举例:");
|
||||||
|
stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
|
||||||
|
stringBuilder.AppendLine("额外格式:");
|
||||||
|
stringBuilder.AppendLine("设备站号 ,比如40001;s=2; ,代表设备地址为2的保持寄存器第一个寄存器");
|
||||||
|
stringBuilder.AppendLine("写入功能码 ,比如40001;w=16; ,代表保持寄存器第一个寄存器,写入值时采用0x10功能码,而不是默认的0x06功能码");
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 通过错误码来获取到对应的文本消息
|
||||||
|
/// </summary>
|
||||||
|
internal static string GetDescriptionByErrorCode(byte code)
|
||||||
|
{
|
||||||
|
return code switch
|
||||||
|
{
|
||||||
|
1 => "不支持的功能码",
|
||||||
|
2 => "读取寄存器越界",
|
||||||
|
3 => "读取长度超限",
|
||||||
|
4 => "读写异常",
|
||||||
|
_ => "未知错误",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="send">发送数据</param>
|
||||||
|
/// <param name="response">返回数据</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static OperResult<byte[], FilterResult> GetModbusData(byte[] send, byte[] response)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (response.Length < 3)
|
||||||
|
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||||
|
|
||||||
|
|
||||||
|
if (response[1] >= 0x80)//错误码
|
||||||
|
return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
|
||||||
|
if (response[1] <= 0x05)
|
||||||
|
{
|
||||||
|
if ((response.Length < response[2] + 3))
|
||||||
|
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((response.Length < 6))
|
||||||
|
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (send.Length == 0)
|
||||||
|
{
|
||||||
|
var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
|
||||||
|
result.Message = "接收数据正确,但主机并没有主动请求数据";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (send[0] != response[0])
|
||||||
|
return new OperResult<byte[], FilterResult>(string.Format("站号不一致", send[0], response[0])) { Content2 = FilterResult.Success };
|
||||||
|
if (send[1] != response[1])
|
||||||
|
return new OperResult<byte[], FilterResult>() { Message = "功能码不一致", Content2 = FilterResult.Success };
|
||||||
|
return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[], FilterResult>(ex) { Content2 = FilterResult.Success };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 去除Crc,返回modbus数据区
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="send"></param>
|
||||||
|
/// <param name="response"></param>
|
||||||
|
/// <param name="crcCheck"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static OperResult<byte[], FilterResult> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
|
||||||
|
{
|
||||||
|
if (response.Length < 3)
|
||||||
|
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||||
|
|
||||||
|
if (response[1] >= 0x80)//错误码
|
||||||
|
return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
|
||||||
|
if (response[1] <= 0x05)
|
||||||
|
{
|
||||||
|
if ((response.Length < response[2] + 5))
|
||||||
|
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((response.Length < 8))
|
||||||
|
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var data = response.SelectMiddle(0, response[2] != 0 ? response[2] + 5 : 8);
|
||||||
|
if (crcCheck && !EasyCRC16.CheckCRC16(data))
|
||||||
|
return new OperResult<byte[], FilterResult>("Crc校验失败" + DataTransUtil.ByteToHexString(data, ' ')) { Content2 = FilterResult.Success };
|
||||||
|
return GetModbusData(send, data.RemoveLast(2));
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取读取报文
|
||||||
|
/// </summary>
|
||||||
|
internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, station);
|
||||||
|
return OperResult.CreateSuccessResult(GetReadModbusCommand(mAddress, length));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取写入布尔量报文,根据地址识别功能码
|
||||||
|
/// </summary>
|
||||||
|
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, station);
|
||||||
|
|
||||||
|
//功能码或实际长度
|
||||||
|
if (values?.Length > 1 || mAddress.WriteFunction == 15)
|
||||||
|
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
|
||||||
|
else
|
||||||
|
return GetWriteBoolModbusCommand(address, values[0], station);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取写入字报文,根据地址识别功能码
|
||||||
|
/// </summary>
|
||||||
|
internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, station);
|
||||||
|
|
||||||
|
//功能码或实际长度
|
||||||
|
if (value?.Length > 2 || mAddress.WriteFunction == 16)
|
||||||
|
return OperResult.CreateSuccessResult(GetWriteModbusCommand(mAddress, value));
|
||||||
|
else
|
||||||
|
return OperResult.CreateSuccessResult(GetWriteOneModbusCommand(mAddress, value));
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取读取报文
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] GetReadModbusCommand(ModbusAddress mAddress, int length)
|
||||||
|
{
|
||||||
|
byte[] array = new byte[6]
|
||||||
|
{
|
||||||
|
(byte) mAddress.Station,
|
||||||
|
(byte) mAddress.ReadFunction,
|
||||||
|
BitConverter.GetBytes(mAddress.AddressStart)[1],
|
||||||
|
BitConverter.GetBytes(mAddress.AddressStart)[0],
|
||||||
|
BitConverter.GetBytes(length)[1],
|
||||||
|
BitConverter.GetBytes(length)[0]
|
||||||
|
};
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取05写入布尔量报文
|
||||||
|
/// </summary>
|
||||||
|
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (address.IndexOf('.') <= 0)
|
||||||
|
{
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, station);
|
||||||
|
|
||||||
|
return OperResult.CreateSuccessResult(GetWriteBoolModbusCommand(mAddress, value));
|
||||||
|
}
|
||||||
|
return new("不支持写入字寄存器的某一位");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取05写入布尔量报文
|
||||||
|
/// </summary>
|
||||||
|
private static byte[] GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
|
||||||
|
{
|
||||||
|
byte[] array = new byte[6]
|
||||||
|
{
|
||||||
|
(byte) mAddress.Station,
|
||||||
|
(byte)5,
|
||||||
|
BitConverter.GetBytes(mAddress.AddressStart)[1],
|
||||||
|
BitConverter.GetBytes(mAddress.AddressStart)[0],
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
array[4] = 0xFF;
|
||||||
|
array[5] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
array[4] = 0;
|
||||||
|
array[5] = 0;
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取15写入布尔量报文
|
||||||
|
/// </summary>
|
||||||
|
internal static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] numArray1 = values.BoolArrayToByte();
|
||||||
|
byte[] numArray2 = new byte[7 + numArray1.Length];
|
||||||
|
numArray2[0] = (byte)mAddress.Station;
|
||||||
|
numArray2[1] = (byte)15;
|
||||||
|
numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||||
|
numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||||
|
numArray2[4] = (byte)(length / 256);
|
||||||
|
numArray2[5] = (byte)(length % 256);
|
||||||
|
numArray2[6] = (byte)numArray1.Length;
|
||||||
|
numArray1.CopyTo(numArray2, 7);
|
||||||
|
return OperResult.CreateSuccessResult(numArray2);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取16写入字报文
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
|
||||||
|
{
|
||||||
|
byte[] numArray = new byte[7 + values.Length];
|
||||||
|
numArray[0] = (byte)mAddress.Station;
|
||||||
|
numArray[1] = (byte)16;
|
||||||
|
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||||
|
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||||
|
numArray[4] = (byte)(values.Length / 2 / 256);
|
||||||
|
numArray[5] = (byte)(values.Length / 2 % 256);
|
||||||
|
numArray[6] = (byte)values.Length;
|
||||||
|
values.CopyTo(numArray, 7);
|
||||||
|
return numArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取6写入字报文
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
|
||||||
|
{
|
||||||
|
byte[] numArray = new byte[4 + values.Length];
|
||||||
|
numArray[0] = (byte)mAddress.Station;
|
||||||
|
numArray[1] = (byte)6;
|
||||||
|
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
|
||||||
|
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
|
||||||
|
values.CopyTo(numArray, 4);
|
||||||
|
return numArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <summary>
|
||||||
|
/// ModbusRtu
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusRtu : ReadWriteDevicesSerialSessionBase
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ModbusRtu
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serialSession"></param>
|
||||||
|
public ModbusRtu(SerialSession serialSession) : base(serialSession)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Crc校验
|
||||||
|
/// </summary>
|
||||||
|
[Description("Crc校验")]
|
||||||
|
public bool Crc16CheckEnable { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 站号
|
||||||
|
/// </summary>
|
||||||
|
[Description("站号")]
|
||||||
|
public byte Station { get; set; } = 1;
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient = null)
|
||||||
|
{
|
||||||
|
ModbusRtuDataHandleAdapter dataHandleAdapter = new()
|
||||||
|
{
|
||||||
|
Crc16CheckEnable = Crc16CheckEnable,
|
||||||
|
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||||
|
};
|
||||||
|
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
if (FrameTime != 0)
|
||||||
|
Thread.Sleep(FrameTime);
|
||||||
|
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
await Task.Delay(FrameTime, cancellationToken);
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rtu适配器
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusRtuDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusRtuMessage>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 检测CRC
|
||||||
|
/// </summary>
|
||||||
|
public bool Crc16CheckEnable { get; set; } = true;
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override byte[] PackCommand(byte[] command)
|
||||||
|
{
|
||||||
|
return ModbusHelper.AddCrc(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ModbusRtuMessage GetInstance()
|
||||||
|
{
|
||||||
|
return new ModbusRtuMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override FilterResult UnpackResponse(ModbusRtuMessage request, byte[] send, byte[] body, byte[] response)
|
||||||
|
{
|
||||||
|
//链路干扰时需剔除前缀中的多于字节,初步按站号+功能码找寻初始字节
|
||||||
|
if (send?.Length > 0)
|
||||||
|
{
|
||||||
|
int index = -1;
|
||||||
|
for (int i = 0; i < response.Length - 1; i++)
|
||||||
|
{
|
||||||
|
if (response[i] == send[0] && (response[i + 1] == send[1] || response[i + 1] == (send[1] + 0x80)))
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
response = response.RemoveBegin(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
//理想状态检测
|
||||||
|
var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
request.ErrorCode = result.ErrorCode;
|
||||||
|
request.Message = result.Message;
|
||||||
|
request.Content = result.Content;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.ErrorCode = result.ErrorCode;
|
||||||
|
request.Message = result.Message;
|
||||||
|
}
|
||||||
|
return result.Content2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusRtuMessage : MessageBase, IMessage
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override int HeadBytesLength => -1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CheckHeadBytes(byte[] heads)
|
||||||
|
{
|
||||||
|
BodyLength = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModbusRtuOverTcp(TcpClient tcpClient) : base(tcpClient)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Crc校验
|
||||||
|
/// </summary>
|
||||||
|
[Description("Crc校验")]
|
||||||
|
public bool Crc16CheckEnable { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 站号
|
||||||
|
/// </summary>
|
||||||
|
[Description("站号")]
|
||||||
|
public byte Station { get; set; } = 1;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient = null)
|
||||||
|
{
|
||||||
|
ModbusRtuDataHandleAdapter dataHandleAdapter = new()
|
||||||
|
{
|
||||||
|
Crc16CheckEnable = Crc16CheckEnable,
|
||||||
|
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||||
|
};
|
||||||
|
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
if (FrameTime != 0)
|
||||||
|
Thread.Sleep(FrameTime);
|
||||||
|
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
await Task.Delay(FrameTime, cancellationToken);
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class ModbusRtuOverUdp : ReadWriteDevicesUdpSessionBase
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModbusRtuOverUdp(UdpSession udpSession) : base(udpSession)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Crc校验
|
||||||
|
/// </summary>
|
||||||
|
[Description("Crc校验")]
|
||||||
|
public bool Crc16CheckEnable { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 站号
|
||||||
|
/// </summary>
|
||||||
|
[Description("站号")]
|
||||||
|
public byte Station { get; set; } = 1;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient = null)
|
||||||
|
{
|
||||||
|
ModbusRtuOverUdpDataHandleAdapter dataHandleAdapter = new()
|
||||||
|
{
|
||||||
|
Crc16CheckEnable = Crc16CheckEnable,
|
||||||
|
};
|
||||||
|
UdpSession.Config.SetUdpDataHandlingAdapter(() =>
|
||||||
|
{
|
||||||
|
return dataHandleAdapter;
|
||||||
|
});
|
||||||
|
UdpSession.Setup(UdpSession.Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
if (FrameTime != 0)
|
||||||
|
Thread.Sleep(FrameTime);
|
||||||
|
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
await Task.Delay(FrameTime, cancellationToken);
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusRtuMessage>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 检测CRC
|
||||||
|
/// </summary>
|
||||||
|
public bool Crc16CheckEnable { get; set; } = true;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override byte[] PackCommand(byte[] command)
|
||||||
|
{
|
||||||
|
return ModbusHelper.AddCrc(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ModbusRtuMessage GetInstance()
|
||||||
|
{
|
||||||
|
return new ModbusRtuMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
|
||||||
|
{
|
||||||
|
var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,421 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Bool;
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
|
||||||
|
/// </summary>
|
||||||
|
public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SerialSession, Task<OperResult>> WriteData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 继电器
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开关输入
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 输入寄存器
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保持寄存器
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModbusSerialServer(SerialSession serialSession) : base(serialSession)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 多站点
|
||||||
|
/// </summary>
|
||||||
|
public bool MulStation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认站点
|
||||||
|
/// </summary>
|
||||||
|
public byte Station { get; set; } = 1;
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var item in ModbusServer01ByteBlocks)
|
||||||
|
{
|
||||||
|
item.Value.SafeDispose();
|
||||||
|
}
|
||||||
|
foreach (var item in ModbusServer02ByteBlocks)
|
||||||
|
{
|
||||||
|
item.Value.SafeDispose();
|
||||||
|
}
|
||||||
|
foreach (var item in ModbusServer03ByteBlocks)
|
||||||
|
{
|
||||||
|
item.Value.SafeDispose();
|
||||||
|
}
|
||||||
|
foreach (var item in ModbusServer04ByteBlocks)
|
||||||
|
{
|
||||||
|
item.Value.SafeDispose();
|
||||||
|
}
|
||||||
|
ModbusServer01ByteBlocks.Clear();
|
||||||
|
ModbusServer02ByteBlocks.Clear();
|
||||||
|
ModbusServer03ByteBlocks.Clear();
|
||||||
|
ModbusServer04ByteBlocks.Clear();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ModbusAddress mAddress;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
if (MulStation)
|
||||||
|
{
|
||||||
|
Init(mAddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Station != mAddress.Station)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>("地址错误");
|
||||||
|
}
|
||||||
|
Init(mAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
|
||||||
|
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
|
||||||
|
switch (mAddress.ReadFunction)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
byte[] bytes0 = new byte[len];
|
||||||
|
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
|
||||||
|
ModbusServer01ByteBlock.Read(bytes0);
|
||||||
|
return OperResult.CreateSuccessResult(bytes0);
|
||||||
|
case 2:
|
||||||
|
byte[] bytes1 = new byte[len];
|
||||||
|
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
|
||||||
|
ModbusServer02ByteBlock.Read(bytes1);
|
||||||
|
return OperResult.CreateSuccessResult(bytes1);
|
||||||
|
case 3:
|
||||||
|
|
||||||
|
byte[] bytes3 = new byte[len];
|
||||||
|
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
|
||||||
|
ModbusServer03ByteBlock.Read(bytes3);
|
||||||
|
return OperResult.CreateSuccessResult(bytes3);
|
||||||
|
case 4:
|
||||||
|
byte[] bytes4 = new byte[len];
|
||||||
|
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
|
||||||
|
ModbusServer04ByteBlock.Read(bytes4);
|
||||||
|
return OperResult.CreateSuccessResult(bytes4);
|
||||||
|
}
|
||||||
|
return new OperResult<byte[]>("功能码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Read(address, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient)
|
||||||
|
{
|
||||||
|
ModbusSerialServerDataHandleAdapter dataHandleAdapter = new();
|
||||||
|
dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
|
||||||
|
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ModbusAddress mAddress;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
if (MulStation)
|
||||||
|
{
|
||||||
|
Init(mAddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Station != mAddress.Station)
|
||||||
|
{
|
||||||
|
return new OperResult("地址错误");
|
||||||
|
}
|
||||||
|
Init(mAddress);
|
||||||
|
}
|
||||||
|
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
|
||||||
|
switch (mAddress.ReadFunction)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
|
||||||
|
ModbusServer03ByteBlock.Write(value);
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
case 4:
|
||||||
|
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
|
||||||
|
ModbusServer04ByteBlock.Write(value);
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
return new OperResult("功能码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ModbusAddress mAddress;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return (new OperResult(ex));
|
||||||
|
}
|
||||||
|
if (MulStation)
|
||||||
|
{
|
||||||
|
Init(mAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Station != mAddress.Station)
|
||||||
|
{
|
||||||
|
return (new OperResult("地址错误"));
|
||||||
|
}
|
||||||
|
Init(mAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
|
||||||
|
switch (mAddress.ReadFunction)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
|
||||||
|
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
|
||||||
|
return (OperResult.CreateSuccessResult());
|
||||||
|
case 2:
|
||||||
|
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
|
||||||
|
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
|
||||||
|
return (OperResult.CreateSuccessResult());
|
||||||
|
}
|
||||||
|
return new OperResult("功能码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Write(address, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Write(address, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override async Task Received(SerialSession client, ReceivedDataEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var requestInfo = e.RequestInfo;
|
||||||
|
//接收外部报文
|
||||||
|
if (requestInfo is ModbusSerialServerMessage modbusServerMessage)
|
||||||
|
{
|
||||||
|
if (modbusServerMessage.CurModbusAddress == null)
|
||||||
|
{
|
||||||
|
return;//无法解析直接返回
|
||||||
|
}
|
||||||
|
if (!modbusServerMessage.IsSuccess)
|
||||||
|
{
|
||||||
|
return;//无法解析直接返回
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
|
||||||
|
{
|
||||||
|
var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
var coreData = data.Content;
|
||||||
|
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
|
||||||
|
{
|
||||||
|
coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
|
||||||
|
}
|
||||||
|
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
|
||||||
|
SerialSession.Send(sendData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(SerialSession, modbusServerMessage);//返回错误码
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else//写入
|
||||||
|
{
|
||||||
|
var coreData = modbusServerMessage.Content;
|
||||||
|
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
|
||||||
|
{
|
||||||
|
//写入继电器
|
||||||
|
if (WriteData != null)
|
||||||
|
{
|
||||||
|
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
|
||||||
|
if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
|
||||||
|
{
|
||||||
|
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
WriteSuccess03(SerialSession, modbusServerMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(SerialSession, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(SerialSession, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//写入内存区
|
||||||
|
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
WriteSuccess03(SerialSession, modbusServerMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(SerialSession, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//写入寄存器
|
||||||
|
if (WriteData != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
|
||||||
|
{
|
||||||
|
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
WriteSuccess03(SerialSession, modbusServerMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(SerialSession, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(SerialSession, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
WriteSuccess03(SerialSession, modbusServerMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(SerialSession, modbusServerMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, ToString());
|
||||||
|
}
|
||||||
|
//返回错误码
|
||||||
|
static void WriteError(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
|
||||||
|
{
|
||||||
|
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2)
|
||||||
|
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
|
||||||
|
sendData[1] = (byte)(sendData[1] + 128);
|
||||||
|
SerialSession.Send(sendData);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteSuccess03(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
|
||||||
|
{
|
||||||
|
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 6);
|
||||||
|
SerialSession.Send(sendData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Init(ModbusAddress mAddress)
|
||||||
|
{
|
||||||
|
if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
|
||||||
|
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
|
||||||
|
if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
|
||||||
|
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
|
||||||
|
if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
|
||||||
|
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
|
||||||
|
if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
|
||||||
|
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class ModbusSerialServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusSerialServerMessage>
|
||||||
|
{
|
||||||
|
private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override byte[] PackCommand(byte[] command)
|
||||||
|
{
|
||||||
|
return ModbusHelper.AddCrc(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取modbus写入数据区内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="response">返回数据</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal OperResult<byte[]> GetModbusData(byte[] response)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var func = ThingsGatewayBitConverter.ToByte(response, 1);
|
||||||
|
if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6)
|
||||||
|
{
|
||||||
|
if (response.Length == 6)
|
||||||
|
return OperResult.CreateSuccessResult(response);
|
||||||
|
}
|
||||||
|
else if (func == 15 || func == 16)
|
||||||
|
{
|
||||||
|
var length = ThingsGatewayBitConverter.ToByte(response, 6);
|
||||||
|
if (response.Length == 7 + length)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" };
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ModbusSerialServerMessage GetInstance()
|
||||||
|
{
|
||||||
|
return new ModbusSerialServerMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override FilterResult UnpackResponse(ModbusSerialServerMessage request, byte[] send, byte[] body, byte[] response)
|
||||||
|
{
|
||||||
|
var result1 = ModbusHelper.GetModbusRtuData(new byte[0], response, true);
|
||||||
|
if (result1.IsSuccess)
|
||||||
|
{
|
||||||
|
var result = GetModbusData(response.RemoveLast(2));
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
//解析01 03 00 00 00 0A
|
||||||
|
var station = ThingsGatewayBitConverter.ToByte(response, 0);
|
||||||
|
var function = ThingsGatewayBitConverter.ToByte(response, 1);
|
||||||
|
int addressStart = ThingsGatewayBitConverter.ToInt16(response, 2);
|
||||||
|
if (addressStart == -1)
|
||||||
|
{
|
||||||
|
addressStart = 65535;
|
||||||
|
}
|
||||||
|
if (function > 4)
|
||||||
|
{
|
||||||
|
if (function > 6)
|
||||||
|
{
|
||||||
|
request.CurModbusAddress = new ModbusAddress()
|
||||||
|
{
|
||||||
|
Station = station,
|
||||||
|
Address = addressStart.ToString(),
|
||||||
|
WriteFunction = function,
|
||||||
|
ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3),
|
||||||
|
};
|
||||||
|
request.Length = ThingsGatewayBitConverter.ToByte(response, 5);
|
||||||
|
request.Content = result.Content.RemoveBegin(7);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.CurModbusAddress = new ModbusAddress()
|
||||||
|
{
|
||||||
|
Station = station,
|
||||||
|
Address = addressStart.ToString(),
|
||||||
|
WriteFunction = function,
|
||||||
|
ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3),
|
||||||
|
};
|
||||||
|
request.Length = 1;
|
||||||
|
request.Content = result.Content.RemoveBegin(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.CurModbusAddress = new ModbusAddress()
|
||||||
|
{
|
||||||
|
Station = station,
|
||||||
|
Address = addressStart.ToString(),
|
||||||
|
ReadFunction = function,
|
||||||
|
};
|
||||||
|
request.Length = ThingsGatewayBitConverter.ToByte(response, 5);
|
||||||
|
}
|
||||||
|
request.ErrorCode = result.ErrorCode;
|
||||||
|
request.Message = result.Message;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.ErrorCode = result.ErrorCode;
|
||||||
|
request.Message = result.Message;
|
||||||
|
return FilterResult.Cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return result1.Content2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusSerialServerMessage : MessageBase, IMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当前关联的地址
|
||||||
|
/// </summary>
|
||||||
|
public ModbusAddress CurModbusAddress { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 当前读写的数据长度
|
||||||
|
/// </summary>
|
||||||
|
public int Length { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override int HeadBytesLength => -1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CheckHeadBytes(byte[] heads)
|
||||||
|
{
|
||||||
|
BodyLength = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class ModbusTcp : ReadWriteDevicesTcpClientBase
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModbusTcp(TcpClient tcpClient) : base(tcpClient)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 检测事务标识符
|
||||||
|
/// </summary>
|
||||||
|
[Description("检测事务标识符")]
|
||||||
|
public bool IsCheckMessageId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 站号
|
||||||
|
/// </summary>
|
||||||
|
[Description("站号")]
|
||||||
|
public byte Station { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
var data = SendThenReturn(commandResult, cancellationToken);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
var data = await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient = null)
|
||||||
|
{
|
||||||
|
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
|
||||||
|
{
|
||||||
|
IsCheckMessageId = IsCheckMessageId,
|
||||||
|
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||||
|
};
|
||||||
|
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
if (FrameTime != 0)
|
||||||
|
Thread.Sleep(FrameTime);
|
||||||
|
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
await Task.Delay(FrameTime, cancellationToken);
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ModbusTcpDataHandleAdapter
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage>
|
||||||
|
{
|
||||||
|
private readonly EasyIncrementCount easyIncrementCount = new(ushort.MaxValue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检测事务标识符
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCheckMessageId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Request?.IsCheckMessageId ?? false;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Request.IsCheckMessageId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override byte[] PackCommand(byte[] command)
|
||||||
|
{
|
||||||
|
return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ModbusTcpMessage GetInstance()
|
||||||
|
{
|
||||||
|
return new ModbusTcpMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override FilterResult UnpackResponse(ModbusTcpMessage request, byte[] send, byte[] body, byte[] response)
|
||||||
|
{
|
||||||
|
//理想状态检测
|
||||||
|
var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
request.ErrorCode = result.ErrorCode;
|
||||||
|
request.Message = result.Message;
|
||||||
|
request.Content = result.Content;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.ErrorCode = result.ErrorCode;
|
||||||
|
request.Message = result.Message;
|
||||||
|
}
|
||||||
|
return result.Content2;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusTcpMessage : MessageBase, IMessage
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override int HeadBytesLength => 6;
|
||||||
|
/// <summary>
|
||||||
|
/// 检测事务标识符
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCheckMessageId { get; set; } = false;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CheckHeadBytes(byte[] heads)
|
||||||
|
{
|
||||||
|
if (heads == null || heads.Length <= 0) return false;
|
||||||
|
HeadBytes = heads;
|
||||||
|
|
||||||
|
int num = (HeadBytes[4] * 256) + HeadBytes[5];
|
||||||
|
BodyLength = num;
|
||||||
|
|
||||||
|
if (!IsCheckMessageId)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return SendBytes[0] == HeadBytes[0] && SendBytes[1] == HeadBytes[1] && HeadBytes[2] == 0 && HeadBytes[3] == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModbusTcpDtu(TcpService tcpService) : base(tcpService)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
ModbusTcpDtuPlugin modbusTcpSalvePlugin = new ModbusTcpDtuPlugin();
|
||||||
|
tcpService.Config.ConfigurePlugins(a =>
|
||||||
|
{
|
||||||
|
a.Add(modbusTcpSalvePlugin);
|
||||||
|
});
|
||||||
|
tcpService.Setup(tcpService.Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检测事务标识符
|
||||||
|
/// </summary>
|
||||||
|
[Description("检测事务标识符")]
|
||||||
|
public bool IsCheckMessageId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 站号
|
||||||
|
/// </summary>
|
||||||
|
[Description("站号")]
|
||||||
|
public byte Station { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient = null)
|
||||||
|
{
|
||||||
|
if (socketClient is SocketClient client)
|
||||||
|
{
|
||||||
|
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
|
||||||
|
{
|
||||||
|
IsCheckMessageId = IsCheckMessageId,
|
||||||
|
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||||
|
};
|
||||||
|
client.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var item in TcpService.GetClients())
|
||||||
|
{
|
||||||
|
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
|
||||||
|
{
|
||||||
|
IsCheckMessageId = IsCheckMessageId,
|
||||||
|
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||||
|
};
|
||||||
|
item.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperResult<byte[]> SendThenReturn(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
if (TcpService.TryGetSocketClient($"ID={id}", out var client))
|
||||||
|
{
|
||||||
|
SetDataAdapter(client);
|
||||||
|
|
||||||
|
var item = commandResult.Content;
|
||||||
|
if (FrameTime != 0)
|
||||||
|
Thread.Sleep(FrameTime);
|
||||||
|
var WaitingClientEx = client.CreateWaitingClient(new() { ThrowBreakException = true });
|
||||||
|
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>("客户端未连接");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return commandResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<OperResult<byte[]>> SendThenReturnAsync(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
if (TcpService.TryGetSocketClient($"ID={id}", out var client))
|
||||||
|
{
|
||||||
|
SetDataAdapter(client);
|
||||||
|
|
||||||
|
var item = commandResult.Content;
|
||||||
|
await Task.Delay(FrameTime, cancellationToken);
|
||||||
|
var WaitingClientEx = client.CreateWaitingClient(new() { ThrowBreakException = true });
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>("客户端未连接");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return commandResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ModbusTcpDtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||||
|
{
|
||||||
|
public Task OnTcpReceiving(ITcpClientBase client, ByteBlockEventArgs e)
|
||||||
|
{
|
||||||
|
if (client is ISocketClient socket)
|
||||||
|
{
|
||||||
|
if (!socket.Id.StartsWith("ID="))
|
||||||
|
{
|
||||||
|
ByteBlock byteBlock = e.ByteBlock;
|
||||||
|
var id = $"ID={byteBlock.ToArray().ToHexString()}";
|
||||||
|
socket.ResetId(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.InvokeNext();//如果本插件无法处理当前数据,请将数据转至下一个插件。
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,441 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Bool;
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusTcpServer : ReadWriteDevicesTcpServerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
|
||||||
|
/// </summary>
|
||||||
|
public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<OperResult>> WriteData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 继电器
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开关输入
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 输入寄存器
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保持寄存器
|
||||||
|
/// </summary>
|
||||||
|
private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModbusTcpServer(TcpService tcpService) : base(tcpService)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 多站点
|
||||||
|
/// </summary>
|
||||||
|
[Description("多站点")]
|
||||||
|
public bool MulStation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认站点
|
||||||
|
/// </summary>
|
||||||
|
[Description("默认站点")]
|
||||||
|
public byte Station { get; set; } = 1;
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var item in ModbusServer01ByteBlocks)
|
||||||
|
{
|
||||||
|
item.Value.SafeDispose();
|
||||||
|
}
|
||||||
|
foreach (var item in ModbusServer02ByteBlocks)
|
||||||
|
{
|
||||||
|
item.Value.SafeDispose();
|
||||||
|
}
|
||||||
|
foreach (var item in ModbusServer03ByteBlocks)
|
||||||
|
{
|
||||||
|
item.Value.SafeDispose();
|
||||||
|
}
|
||||||
|
foreach (var item in ModbusServer04ByteBlocks)
|
||||||
|
{
|
||||||
|
item.Value.SafeDispose();
|
||||||
|
}
|
||||||
|
ModbusServer01ByteBlocks.Clear();
|
||||||
|
ModbusServer02ByteBlocks.Clear();
|
||||||
|
ModbusServer03ByteBlocks.Clear();
|
||||||
|
ModbusServer04ByteBlocks.Clear();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ModbusAddress mAddress;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
if (MulStation)
|
||||||
|
{
|
||||||
|
Init(mAddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Station != mAddress.Station)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>("地址错误");
|
||||||
|
}
|
||||||
|
Init(mAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
|
||||||
|
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
|
||||||
|
switch (mAddress.ReadFunction)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
byte[] bytes0 = new byte[len];
|
||||||
|
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
|
||||||
|
ModbusServer01ByteBlock.Read(bytes0);
|
||||||
|
return OperResult.CreateSuccessResult(bytes0);
|
||||||
|
case 2:
|
||||||
|
byte[] bytes1 = new byte[len];
|
||||||
|
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
|
||||||
|
ModbusServer02ByteBlock.Read(bytes1);
|
||||||
|
return OperResult.CreateSuccessResult(bytes1);
|
||||||
|
case 3:
|
||||||
|
|
||||||
|
byte[] bytes3 = new byte[len];
|
||||||
|
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
|
||||||
|
ModbusServer03ByteBlock.Read(bytes3);
|
||||||
|
return OperResult.CreateSuccessResult(bytes3);
|
||||||
|
case 4:
|
||||||
|
byte[] bytes4 = new byte[len];
|
||||||
|
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
|
||||||
|
ModbusServer04ByteBlock.Read(bytes4);
|
||||||
|
return OperResult.CreateSuccessResult(bytes4);
|
||||||
|
}
|
||||||
|
return new OperResult<byte[]>("功能码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Read(address, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient)
|
||||||
|
{
|
||||||
|
if (socketClient is SocketClient client)
|
||||||
|
{
|
||||||
|
ModbusTcpServerDataHandleAdapter dataHandleAdapter = new();
|
||||||
|
dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
|
||||||
|
client.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var item in TcpService.GetClients())
|
||||||
|
{
|
||||||
|
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
|
||||||
|
{
|
||||||
|
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
|
||||||
|
};
|
||||||
|
item.SetDataHandlingAdapter(dataHandleAdapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ModbusAddress mAddress;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
if (MulStation)
|
||||||
|
{
|
||||||
|
Init(mAddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Station != mAddress.Station)
|
||||||
|
{
|
||||||
|
return new OperResult("地址错误");
|
||||||
|
}
|
||||||
|
Init(mAddress);
|
||||||
|
}
|
||||||
|
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
|
||||||
|
switch (mAddress.ReadFunction)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
|
||||||
|
ModbusServer03ByteBlock.Write(value);
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
case 4:
|
||||||
|
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
|
||||||
|
ModbusServer04ByteBlock.Write(value);
|
||||||
|
return OperResult.CreateSuccessResult();
|
||||||
|
}
|
||||||
|
return new OperResult("功能码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ModbusAddress mAddress;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mAddress = ModbusAddress.ParseFrom(address, Station);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return (new OperResult(ex));
|
||||||
|
}
|
||||||
|
if (MulStation)
|
||||||
|
{
|
||||||
|
Init(mAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Station != mAddress.Station)
|
||||||
|
{
|
||||||
|
return (new OperResult("地址错误"));
|
||||||
|
}
|
||||||
|
Init(mAddress);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
|
||||||
|
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
|
||||||
|
switch (mAddress.ReadFunction)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
|
||||||
|
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
|
||||||
|
return (OperResult.CreateSuccessResult());
|
||||||
|
case 2:
|
||||||
|
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
|
||||||
|
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
|
||||||
|
return (OperResult.CreateSuccessResult());
|
||||||
|
}
|
||||||
|
return new OperResult("功能码错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Write(address, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Write(address, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override async Task Received(SocketClient client, ReceivedDataEventArgs e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var requestInfo = e.RequestInfo;
|
||||||
|
//接收外部报文
|
||||||
|
if (requestInfo is ModbusTcpServerMessage modbusServerMessage)
|
||||||
|
{
|
||||||
|
if (modbusServerMessage.CurModbusAddress == null)
|
||||||
|
{
|
||||||
|
return;//无法解析直接返回
|
||||||
|
}
|
||||||
|
if (!modbusServerMessage.IsSuccess)
|
||||||
|
{
|
||||||
|
return;//无法解析直接返回
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
|
||||||
|
{
|
||||||
|
var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
|
||||||
|
if (data.IsSuccess)
|
||||||
|
{
|
||||||
|
var coreData = data.Content;
|
||||||
|
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
|
||||||
|
{
|
||||||
|
coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
|
||||||
|
}
|
||||||
|
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
|
||||||
|
sendData[5] = (byte)(sendData.Length - 6);
|
||||||
|
client.Send(sendData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(client, modbusServerMessage);//返回错误码
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else//写入
|
||||||
|
{
|
||||||
|
var coreData = modbusServerMessage.Content;
|
||||||
|
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
|
||||||
|
{
|
||||||
|
//写入继电器
|
||||||
|
if (WriteData != null)
|
||||||
|
{
|
||||||
|
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
|
||||||
|
if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
|
||||||
|
{
|
||||||
|
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
WriteSuccess03(client, modbusServerMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(client, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(client, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//写入内存区
|
||||||
|
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
WriteSuccess03(client, modbusServerMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(client, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//写入寄存器
|
||||||
|
if (WriteData != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
|
||||||
|
{
|
||||||
|
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
WriteSuccess03(client, modbusServerMessage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(client, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(client, modbusServerMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
WriteSuccess03(client, modbusServerMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteError(client, modbusServerMessage);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, ToString());
|
||||||
|
}
|
||||||
|
//返回错误码
|
||||||
|
static void WriteError(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
|
||||||
|
{
|
||||||
|
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
|
||||||
|
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
|
||||||
|
sendData[5] = (byte)(sendData.Length - 6);
|
||||||
|
sendData[7] = (byte)(sendData[7] + 128);
|
||||||
|
client.Send(sendData);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteSuccess03(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
|
||||||
|
{
|
||||||
|
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12);
|
||||||
|
sendData[5] = (byte)(sendData.Length - 6);
|
||||||
|
client.Send(sendData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Init(ModbusAddress mAddress)
|
||||||
|
{
|
||||||
|
if (ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
|
||||||
|
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
|
||||||
|
if (ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
|
||||||
|
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
|
||||||
|
if (ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
|
||||||
|
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
|
||||||
|
if (ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128)))
|
||||||
|
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpServerMessage>
|
||||||
|
{
|
||||||
|
private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override byte[] PackCommand(byte[] command)
|
||||||
|
{
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取modbus写入数据区内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="response">返回数据</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal OperResult<byte[]> GetModbusData(byte[] response)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var func = ThingsGatewayBitConverter.ToByte(response, 1);
|
||||||
|
if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6)
|
||||||
|
{
|
||||||
|
if (response.Length == 6)
|
||||||
|
return OperResult.CreateSuccessResult(response);
|
||||||
|
}
|
||||||
|
else if (func == 15 || func == 16)
|
||||||
|
{
|
||||||
|
var length = ThingsGatewayBitConverter.ToByte(response, 6);
|
||||||
|
if (response.Length == 7 + length)
|
||||||
|
{
|
||||||
|
return OperResult.CreateSuccessResult(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" };
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ModbusTcpServerMessage GetInstance()
|
||||||
|
{
|
||||||
|
return new ModbusTcpServerMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override FilterResult UnpackResponse(ModbusTcpServerMessage request, byte[] send, byte[] body, byte[] response)
|
||||||
|
{
|
||||||
|
var result = GetModbusData(response.RemoveBegin(6));
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
//解析01 03 00 00 00 0A
|
||||||
|
var station = ThingsGatewayBitConverter.ToByte(response, 6);
|
||||||
|
var function = ThingsGatewayBitConverter.ToByte(response, 7);
|
||||||
|
int addressStart = ThingsGatewayBitConverter.ToInt16(response, 8);
|
||||||
|
if (addressStart == -1)
|
||||||
|
{
|
||||||
|
addressStart = 65535;
|
||||||
|
}
|
||||||
|
if (function > 4)
|
||||||
|
{
|
||||||
|
if (function > 6)
|
||||||
|
{
|
||||||
|
request.CurModbusAddress = new ModbusAddress()
|
||||||
|
{
|
||||||
|
Station = station,
|
||||||
|
Address = addressStart.ToString(),
|
||||||
|
WriteFunction = function,
|
||||||
|
ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3),
|
||||||
|
};
|
||||||
|
request.Length = ThingsGatewayBitConverter.ToByte(response, 11);
|
||||||
|
request.Content = result.Content.RemoveBegin(7);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.CurModbusAddress = new ModbusAddress()
|
||||||
|
{
|
||||||
|
Station = station,
|
||||||
|
Address = addressStart.ToString(),
|
||||||
|
WriteFunction = function,
|
||||||
|
ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3),
|
||||||
|
};
|
||||||
|
request.Length = 1;
|
||||||
|
request.Content = result.Content.RemoveBegin(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.CurModbusAddress = new ModbusAddress()
|
||||||
|
{
|
||||||
|
Station = station,
|
||||||
|
Address = addressStart.ToString(),
|
||||||
|
ReadFunction = function,
|
||||||
|
};
|
||||||
|
request.Length = ThingsGatewayBitConverter.ToByte(response, 11);
|
||||||
|
}
|
||||||
|
request.ErrorCode = result.ErrorCode;
|
||||||
|
request.Message = result.Message;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
request.ErrorCode = result.ErrorCode;
|
||||||
|
request.Message = result.Message;
|
||||||
|
return FilterResult.Cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusTcpServerMessage : MessageBase, IMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当前关联的地址
|
||||||
|
/// </summary>
|
||||||
|
public ModbusAddress CurModbusAddress { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 当前读写的数据长度
|
||||||
|
/// </summary>
|
||||||
|
public int Length { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override int HeadBytesLength => 6;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CheckHeadBytes(byte[] heads)
|
||||||
|
{
|
||||||
|
if (heads == null || heads.Length != 6) return false;
|
||||||
|
HeadBytes = heads;
|
||||||
|
|
||||||
|
int num = (HeadBytes[4] * 256) + HeadBytes[5];
|
||||||
|
BodyLength = num;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class ModbusUdp : ReadWriteDevicesUdpSessionBase
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ModbusUdp(UdpSession udpSession) : base(udpSession)
|
||||||
|
{
|
||||||
|
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||||
|
RegisterByteLength = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检测事务标识符
|
||||||
|
/// </summary>
|
||||||
|
[Description("检测事务标识符")]
|
||||||
|
public bool IsCheckMessageId { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 站号
|
||||||
|
/// </summary>
|
||||||
|
[Description("站号")]
|
||||||
|
public byte Station { get; set; } = 1;
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||||||
|
{
|
||||||
|
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override string GetAddressDescription()
|
||||||
|
{
|
||||||
|
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SetDataAdapter(object socketClient = null)
|
||||||
|
{
|
||||||
|
ModbusUdpDataHandleAdapter dataHandleAdapter = new()
|
||||||
|
{
|
||||||
|
IsCheckMessageId = IsCheckMessageId
|
||||||
|
};
|
||||||
|
UdpSession.Config.SetUdpDataHandlingAdapter(() =>
|
||||||
|
{
|
||||||
|
return dataHandleAdapter;
|
||||||
|
});
|
||||||
|
UdpSession.Setup(UdpSession.Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Connect(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return SendThenReturn(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||||
|
return await SendThenReturnAsync(commandResult, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperResult(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
if (FrameTime != 0)
|
||||||
|
Thread.Sleep(FrameTime);
|
||||||
|
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (commandResult.IsSuccess)
|
||||||
|
{
|
||||||
|
var item = commandResult.Content;
|
||||||
|
await Task.Delay(FrameTime, cancellationToken);
|
||||||
|
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
|
||||||
|
return (MessageBase)result.RequestInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new OperResult<byte[]>(commandResult.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
/// <summary>
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// </summary>
|
||||||
|
public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusTcpMessage>
|
||||||
|
{
|
||||||
|
private readonly EasyIncrementCount easyIncrementCount = new(ushort.MaxValue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检测事务标识符
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCheckMessageId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Request?.IsCheckMessageId ?? false;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Request.IsCheckMessageId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override byte[] PackCommand(byte[] command)
|
||||||
|
{
|
||||||
|
return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override ModbusTcpMessage GetInstance()
|
||||||
|
{
|
||||||
|
return new ModbusTcpMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
|
||||||
|
{
|
||||||
|
var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
using ThingsGateway.Foundation.Extension.String;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.Modbus;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PackHelper
|
||||||
|
/// </summary>
|
||||||
|
public class PackHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 打包变量,添加到<see href="deviceVariableSourceReads"></see>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device"></param>
|
||||||
|
/// <param name="deviceVariables"></param>
|
||||||
|
/// <param name="maxPack">最大打包长度</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<T> LoadSourceRead<T, T2>(IReadWrite device, List<T2> deviceVariables, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
|
||||||
|
{
|
||||||
|
if (deviceVariables == null)
|
||||||
|
throw new ArgumentNullException(nameof(deviceVariables));
|
||||||
|
|
||||||
|
var deviceVariableSourceReads = new List<T>();
|
||||||
|
var byteConverter = device.ThingsGatewayBitConverter;
|
||||||
|
//需要先剔除额外信息,比如dataformat等
|
||||||
|
foreach (var item in deviceVariables)
|
||||||
|
{
|
||||||
|
var address = item.VariableAddress;
|
||||||
|
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
|
||||||
|
item.ThingsGatewayBitConverter = transformParameter;
|
||||||
|
//item.VariableAddress = address;
|
||||||
|
item.Index = device.GetBitOffset(item.VariableAddress);
|
||||||
|
}
|
||||||
|
var deviceVariableRunTimeGroups = deviceVariables.GroupBy(it => it.IntervalTime);
|
||||||
|
foreach (var group in deviceVariableRunTimeGroups)
|
||||||
|
{
|
||||||
|
Dictionary<ModbusAddress, T2> map = group.ToDictionary(it =>
|
||||||
|
{
|
||||||
|
var lastLen = it.DataTypeEnum.GetByteLength();
|
||||||
|
if (lastLen <= 0)
|
||||||
|
{
|
||||||
|
switch (it.DataTypeEnum)
|
||||||
|
{
|
||||||
|
case DataTypeEnum.String:
|
||||||
|
lastLen = it.ThingsGatewayBitConverter.Length == null ? throw new("数据类型为字符串时,必须指定字符串长度,才能进行打包") : it.ThingsGatewayBitConverter.Length.Value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lastLen = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (it.ThingsGatewayBitConverter.Length != null && it.DataTypeEnum != DataTypeEnum.String)
|
||||||
|
{
|
||||||
|
lastLen *= it.ThingsGatewayBitConverter.Length.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var address = it.VariableAddress;
|
||||||
|
if (address.IndexOf('.') > 0)
|
||||||
|
{
|
||||||
|
var addressSplits = address.SplitDot();
|
||||||
|
address = addressSplits.RemoveLast(1).ArrayToString(".");
|
||||||
|
}
|
||||||
|
var result = ModbusAddress.ParseFrom(address);
|
||||||
|
result.ByteLength = lastLen;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
//获取变量的地址
|
||||||
|
var modbusAddressList = map.Keys;
|
||||||
|
|
||||||
|
//获取功能码
|
||||||
|
var functionCodes = modbusAddressList.Select(t => t.ReadFunction).Distinct();
|
||||||
|
foreach (var functionCode in functionCodes)
|
||||||
|
{
|
||||||
|
var modbusAddressSameFunList = modbusAddressList.Where(t => t.ReadFunction == functionCode);
|
||||||
|
var stationNumbers = modbusAddressSameFunList.Select(t => t.Station).Distinct();
|
||||||
|
foreach (var stationNumber in stationNumbers)
|
||||||
|
{
|
||||||
|
var addressList = modbusAddressSameFunList
|
||||||
|
.Where(t => t.Station == stationNumber)
|
||||||
|
.ToDictionary(t => t, t => map[t]);
|
||||||
|
|
||||||
|
var tempResult = LoadSourceRead<T, T2>(addressList, functionCode, group.Key, maxPack);
|
||||||
|
deviceVariableSourceReads.AddRange(tempResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceVariableSourceReads;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<T> LoadSourceRead<T, T2>(Dictionary<ModbusAddress, T2> addressList, int functionCode, int intervalTime, int maxPack) where T : IDeviceVariableSourceRead<IDeviceVariableRunTime>, new() where T2 : IDeviceVariableRunTime, new()
|
||||||
|
{
|
||||||
|
List<T> sourceReads = new();
|
||||||
|
//按地址和长度排序
|
||||||
|
var orderByAddressEnd = addressList.Keys.OrderBy(it => it.AddressEnd);
|
||||||
|
//按地址和长度排序
|
||||||
|
var orderByAddressStart = addressList.Keys.OrderBy(it => it.AddressStart);
|
||||||
|
//地址最小,在循环中更改
|
||||||
|
var minAddress = orderByAddressStart.First().AddressStart;
|
||||||
|
//地址最大
|
||||||
|
var maxAddress = orderByAddressStart.Last().AddressStart;
|
||||||
|
|
||||||
|
while (maxAddress >= minAddress)
|
||||||
|
{
|
||||||
|
//最大的打包长度
|
||||||
|
int readLength = maxPack;
|
||||||
|
if (functionCode == 1 || functionCode == 2)
|
||||||
|
{
|
||||||
|
readLength = maxPack * 8 * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//获取当前的一组打包地址信息,
|
||||||
|
var tempAddressEnd = orderByAddressEnd.Where(t => t.AddressEnd <= minAddress + readLength).ToList();
|
||||||
|
//起始地址
|
||||||
|
var startAddress = tempAddressEnd.OrderBy(it => it.AddressStart).First();
|
||||||
|
//读取寄存器长度
|
||||||
|
var sourceLen = tempAddressEnd.Last().AddressEnd - startAddress.AddressStart;
|
||||||
|
|
||||||
|
T sourceRead = new()
|
||||||
|
{
|
||||||
|
TimerTick = new TimerTick(intervalTime),
|
||||||
|
//这里只需要根据地址排序的第一个地址,作为实际打包报文中的起始地址
|
||||||
|
VariableAddress = startAddress.ToString(),
|
||||||
|
Length = sourceLen
|
||||||
|
};
|
||||||
|
foreach (var item in tempAddressEnd)
|
||||||
|
{
|
||||||
|
var readNode = addressList[item];
|
||||||
|
if ((functionCode == -1 || functionCode == 3 || functionCode == 4) && readNode.DataTypeEnum == DataTypeEnum.Boolean)
|
||||||
|
{
|
||||||
|
readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 16) + readNode.Index;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (functionCode == 1 || functionCode == 2)
|
||||||
|
readNode.Index = item.AddressStart - startAddress.AddressStart + readNode.Index;
|
||||||
|
else
|
||||||
|
readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 2) + readNode.Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceRead.DeviceVariableRunTimes.Add(readNode);
|
||||||
|
addressList.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceReads.Add(sourceRead);
|
||||||
|
if (orderByAddressEnd.Count() > 0)
|
||||||
|
minAddress = orderByAddressStart.First().AddressStart;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return sourceReads;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,611 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<doc>
|
||||||
|
<assembly>
|
||||||
|
<name>ThingsGateway.Foundation.Adapter.Modbus</name>
|
||||||
|
</assembly>
|
||||||
|
<members>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress">
|
||||||
|
<summary>
|
||||||
|
Modbus协议地址
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressStart">
|
||||||
|
<summary>
|
||||||
|
读取功能码
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ReadFunction">
|
||||||
|
<summary>
|
||||||
|
读取功能码
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Station">
|
||||||
|
<summary>
|
||||||
|
站号信息
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.WriteFunction">
|
||||||
|
<summary>
|
||||||
|
写入功能码
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ByteLength">
|
||||||
|
<summary>
|
||||||
|
打包临时写入,需要读取的字节长度
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressEnd">
|
||||||
|
<summary>
|
||||||
|
读取功能码
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Parse(System.String)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,System.Byte)">
|
||||||
|
<summary>
|
||||||
|
解析地址
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress)">
|
||||||
|
<summary>
|
||||||
|
解析地址
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ToString">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.AddCrc(System.Byte[])">
|
||||||
|
<summary>
|
||||||
|
添加Crc16
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.AddModbusTcpHead(System.Byte[],System.UInt16)">
|
||||||
|
<summary>
|
||||||
|
添加ModbusTcp报文头
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetAddressDescription">
|
||||||
|
<summary>
|
||||||
|
modbus地址格式说明
|
||||||
|
</summary>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetDescriptionByErrorCode(System.Byte)">
|
||||||
|
<summary>
|
||||||
|
通过错误码来获取到对应的文本消息
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetModbusData(System.Byte[],System.Byte[])">
|
||||||
|
<summary>
|
||||||
|
获取modbus数据区内容,返回数据需去除Crc和报文头,例如:01 03 02 00 01,发送数据需报文头
|
||||||
|
</summary>
|
||||||
|
<param name="send">发送数据</param>
|
||||||
|
<param name="response">返回数据</param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetModbusRtuData(System.Byte[],System.Byte[],System.Boolean)">
|
||||||
|
<summary>
|
||||||
|
去除Crc,返回modbus数据区
|
||||||
|
</summary>
|
||||||
|
<param name="send"></param>
|
||||||
|
<param name="response"></param>
|
||||||
|
<param name="crcCheck"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetReadModbusCommand(System.String,System.Int32,System.Byte)">
|
||||||
|
<summary>
|
||||||
|
获取读取报文
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(System.String,System.Boolean[],System.Byte)">
|
||||||
|
<summary>
|
||||||
|
获取写入布尔量报文,根据地址识别功能码
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteModbusCommand(System.String,System.Byte[],System.Byte)">
|
||||||
|
<summary>
|
||||||
|
获取写入字报文,根据地址识别功能码
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetReadModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Int32)">
|
||||||
|
<summary>
|
||||||
|
获取读取报文
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(System.String,System.Boolean,System.Byte)">
|
||||||
|
<summary>
|
||||||
|
获取05写入布尔量报文
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Boolean)">
|
||||||
|
<summary>
|
||||||
|
获取05写入布尔量报文
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteBoolModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Boolean[],System.Int32)">
|
||||||
|
<summary>
|
||||||
|
获取15写入布尔量报文
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Byte[])">
|
||||||
|
<summary>
|
||||||
|
获取16写入字报文
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusHelper.GetWriteOneModbusCommand(ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress,System.Byte[])">
|
||||||
|
<summary>
|
||||||
|
获取6写入字报文
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Crc16CheckEnable">
|
||||||
|
<summary>
|
||||||
|
Crc校验
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Station">
|
||||||
|
<summary>
|
||||||
|
站号
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.GetAddressDescription">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SetDataAdapter(System.Object)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Crc16CheckEnable">
|
||||||
|
<summary>
|
||||||
|
Crc校验
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Station">
|
||||||
|
<summary>
|
||||||
|
站号
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.GetAddressDescription">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SetDataAdapter(System.Object)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.Crc16CheckEnable">
|
||||||
|
<summary>
|
||||||
|
检测CRC
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.PackCommand(System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.GetInstance">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter.UnpackResponse(System.Byte[],System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu">
|
||||||
|
<summary>
|
||||||
|
ModbusRtu
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.#ctor(ThingsGateway.Foundation.Serial.SerialSession)">
|
||||||
|
<summary>
|
||||||
|
ModbusRtu
|
||||||
|
</summary>
|
||||||
|
<param name="serialSession"></param>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Crc16CheckEnable">
|
||||||
|
<summary>
|
||||||
|
Crc校验
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Station">
|
||||||
|
<summary>
|
||||||
|
站号
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.GetAddressDescription">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Read(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.SetDataAdapter(System.Object)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter">
|
||||||
|
<summary>
|
||||||
|
Rtu适配器
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.Crc16CheckEnable">
|
||||||
|
<summary>
|
||||||
|
检测CRC
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.PackCommand(System.Byte[])">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
<param name="command"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.GetInstance">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage,System.Byte[],System.Byte[],System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.HeadBytesLength">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.CheckHeadBytes(System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuMessage.SendBytesThen">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer01ByteBlocks">
|
||||||
|
<summary>
|
||||||
|
继电器
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer02ByteBlocks">
|
||||||
|
<summary>
|
||||||
|
开关输入
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer03ByteBlocks">
|
||||||
|
<summary>
|
||||||
|
输入寄存器
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ModbusServer04ByteBlocks">
|
||||||
|
<summary>
|
||||||
|
保持寄存器
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteData">
|
||||||
|
<summary>
|
||||||
|
接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(ThingsGateway.Foundation.Sockets.TcpService)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.MulStation">
|
||||||
|
<summary>
|
||||||
|
多站点
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Station">
|
||||||
|
<summary>
|
||||||
|
默认站点
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Dispose">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.GetAddressDescription">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Read(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.SetDataAdapter(System.Object)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Received(ThingsGateway.Foundation.Sockets.SocketClient,ThingsGateway.Foundation.Core.IRequestInfo)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.PackCommand(System.Byte[])">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
<param name="command"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.GetModbusData(System.Byte[])">
|
||||||
|
<summary>
|
||||||
|
获取modbus写入数据区内容
|
||||||
|
</summary>
|
||||||
|
<param name="response">返回数据</param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.GetInstance">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage,System.Byte[],System.Byte[],System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.CurModbusAddress">
|
||||||
|
<summary>
|
||||||
|
当前关联的地址
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.Length">
|
||||||
|
<summary>
|
||||||
|
当前读写的数据长度
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.HeadBytesLength">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.CheckHeadBytes(System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.IsCheckMessageId">
|
||||||
|
<summary>
|
||||||
|
检测事务标识符
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Station">
|
||||||
|
<summary>
|
||||||
|
站号
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.GetAddressDescription">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SetDataAdapter(System.Object)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter">
|
||||||
|
<summary>
|
||||||
|
ModbusTcpDataHandleAdapter
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.IsCheckMessageId">
|
||||||
|
<summary>
|
||||||
|
检测事务标识符
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.PackCommand(System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.GetInstance">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.UnpackResponse(ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage,System.Byte[],System.Byte[],System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.HeadBytesLength">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.IsCheckMessageId">
|
||||||
|
<summary>
|
||||||
|
检测事务标识符
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpMessage.CheckHeadBytes(System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.IsCheckMessageId">
|
||||||
|
<summary>
|
||||||
|
检测事务标识符
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Station">
|
||||||
|
<summary>
|
||||||
|
站号
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.GetAddressDescription">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SetDataAdapter(System.Object)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter">
|
||||||
|
<summary>
|
||||||
|
<inheritdoc/>
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.IsCheckMessageId">
|
||||||
|
<summary>
|
||||||
|
检测事务标识符
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.PackCommand(System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.GetInstance">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.UnpackResponse(System.Byte[],System.Byte[])">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.PackHelper">
|
||||||
|
<summary>
|
||||||
|
PackHelper
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.PackHelper.ModbusLoadSourceRead``2(ThingsGateway.Foundation.Core.IReadWrite,System.Collections.Generic.List{``1},System.Int32)">
|
||||||
|
<summary>
|
||||||
|
打包变量,添加到<see href="deviceVariableSourceReads"></see>
|
||||||
|
</summary>
|
||||||
|
<param name="device"></param>
|
||||||
|
<param name="deviceVariables"></param>
|
||||||
|
<param name="MaxPack">最大打包长度</param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
</members>
|
||||||
|
</doc>
|
||||||
@@ -10,12 +10,14 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn;
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn;
|
||||||
|
#pragma warning disable CA1416 // 验证平台兼容性
|
||||||
|
#pragma warning disable IDE0090
|
||||||
|
#pragma warning disable IDE0051
|
||||||
|
|
||||||
internal class ComInterop
|
internal class ComInterop
|
||||||
{
|
{
|
||||||
@@ -152,11 +154,13 @@ internal class ComInterop
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static object CreateInstance(Guid clsid, string hostName)
|
public static object CreateInstance(Guid clsid, string hostName)
|
||||||
{
|
{
|
||||||
COSERVERINFO coserverInfo = new();
|
COSERVERINFO coserverInfo = new()
|
||||||
coserverInfo.pwszName = hostName;
|
{
|
||||||
coserverInfo.pAuthInfo = IntPtr.Zero;
|
pwszName = hostName,
|
||||||
coserverInfo.dwReserved1 = 0;
|
pAuthInfo = IntPtr.Zero,
|
||||||
coserverInfo.dwReserved2 = 0;
|
dwReserved1 = 0,
|
||||||
|
dwReserved2 = 0
|
||||||
|
};
|
||||||
|
|
||||||
GCHandle hIID = GCHandle.Alloc(IID_IUnknown, GCHandleType.Pinned);
|
GCHandle hIID = GCHandle.Alloc(IID_IUnknown, GCHandleType.Pinned);
|
||||||
|
|
||||||
@@ -212,14 +216,14 @@ internal class ComInterop
|
|||||||
|
|
||||||
IntPtr buffer = Marshal.AllocCoTaskMem(MAX_MESSAGE_LENGTH);
|
IntPtr buffer = Marshal.AllocCoTaskMem(MAX_MESSAGE_LENGTH);
|
||||||
|
|
||||||
int result = FormatMessageW(
|
_ = FormatMessageW(
|
||||||
(int)(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM),
|
(int)(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM),
|
||||||
IntPtr.Zero,
|
IntPtr.Zero,
|
||||||
error,
|
error,
|
||||||
0,
|
0,
|
||||||
buffer,
|
buffer,
|
||||||
MAX_MESSAGE_LENGTH - 1,
|
MAX_MESSAGE_LENGTH - 1,
|
||||||
IntPtr.Zero);
|
IntPtr.Zero);
|
||||||
|
|
||||||
string msg = Marshal.PtrToStringUni(buffer);
|
string msg = Marshal.PtrToStringUni(buffer);
|
||||||
Marshal.FreeCoTaskMem(buffer);
|
Marshal.FreeCoTaskMem(buffer);
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 值变化
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcItems"></param>
|
||||||
|
public delegate void OnDataChangedHandler(List<ItemReadResult> opcItems);
|
||||||
|
/// <summary>
|
||||||
|
/// 读取
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcItems"></param>
|
||||||
|
public delegate void OnReadCompletedHandler(List<ItemReadResult> opcItems);
|
||||||
|
/// <summary>
|
||||||
|
/// 写入
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="opcItems"></param>
|
||||||
|
internal delegate void OnWriteCompletedHandler(List<ItemWriteResult> opcItems);
|
||||||
|
/// <summary>
|
||||||
|
/// 返回结果
|
||||||
|
/// </summary>
|
||||||
|
public class ItemReadResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
/// <summary>
|
||||||
|
/// Quality
|
||||||
|
/// </summary>
|
||||||
|
public short Quality { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// TimeStamp
|
||||||
|
/// </summary>
|
||||||
|
public DateTime TimeStamp { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Value
|
||||||
|
/// </summary>
|
||||||
|
public object Value { get; set; } = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ItemWriteResult
|
||||||
|
{
|
||||||
|
internal int Exception { get; set; } = 0;
|
||||||
|
internal string Name { get; set; } = "";
|
||||||
|
}
|
||||||
@@ -10,17 +10,12 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
using ThingsGateway.Foundation.Extension.Json;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
||||||
|
#pragma warning disable CA1416 // 验证平台兼容性
|
||||||
|
|
||||||
internal class OpcGroup : IOPCDataCallback, IDisposable
|
internal class OpcGroup : IOPCDataCallback, IDisposable
|
||||||
{
|
{
|
||||||
@@ -127,7 +122,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps,
|
System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps,
|
||||||
int[] pErrors)
|
int[] pErrors)
|
||||||
{
|
{
|
||||||
List<ItemReadResult> itemChanged = new List<ItemReadResult>();
|
List<ItemReadResult> itemChanged = new();
|
||||||
for (int i = 0; i < dwCount; i++)
|
for (int i = 0; i < dwCount; i++)
|
||||||
{
|
{
|
||||||
int index = OpcItems.FindIndex(x => x.ClientHandle == phClientItems[i]);
|
int index = OpcItems.FindIndex(x => x.ClientHandle == phClientItems[i]);
|
||||||
@@ -145,7 +140,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OnDataChanged?.Invoke(itemChanged.ToArray());
|
OnDataChanged?.Invoke(itemChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnReadComplete(int dwTransid,
|
public void OnReadComplete(int dwTransid,
|
||||||
@@ -159,7 +154,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps,
|
System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps,
|
||||||
int[] pErrors)
|
int[] pErrors)
|
||||||
{
|
{
|
||||||
List<ItemReadResult> itemChanged = new List<ItemReadResult>();
|
List<ItemReadResult> itemChanged = new();
|
||||||
for (int i = 0; i < dwCount; i++)
|
for (int i = 0; i < dwCount; i++)
|
||||||
{
|
{
|
||||||
int index = OpcItems.FindIndex(x => x.ClientHandle == phClientItems[i]);
|
int index = OpcItems.FindIndex(x => x.ClientHandle == phClientItems[i]);
|
||||||
@@ -177,7 +172,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OnReadCompleted?.Invoke(itemChanged.ToArray());
|
OnReadCompleted?.Invoke(itemChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnWriteComplete(int dwTransid,
|
public void OnWriteComplete(int dwTransid,
|
||||||
@@ -187,7 +182,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
int[] pClienthandles,
|
int[] pClienthandles,
|
||||||
int[] pErrors)
|
int[] pErrors)
|
||||||
{
|
{
|
||||||
List<ItemWriteResult> itemwrite = new List<ItemWriteResult>();
|
List<ItemWriteResult> itemwrite = new();
|
||||||
for (int i = 0; i < dwCount; i++)
|
for (int i = 0; i < dwCount; i++)
|
||||||
{
|
{
|
||||||
int index = OpcItems.FindIndex(x => x.ClientHandle == pClienthandles[i]);
|
int index = OpcItems.FindIndex(x => x.ClientHandle == pClienthandles[i]);
|
||||||
@@ -200,10 +195,10 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OnWriteCompleted?.Invoke(itemwrite.ToArray());
|
OnWriteCompleted?.Invoke(itemwrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal OperResult AddOpcItem(OpcItem[] items)
|
internal List<Tuple<OpcItem, int>> AddOpcItem(OpcItem[] items)
|
||||||
{
|
{
|
||||||
IntPtr pResults = IntPtr.Zero;
|
IntPtr pResults = IntPtr.Zero;
|
||||||
IntPtr pErrors = IntPtr.Zero;
|
IntPtr pErrors = IntPtr.Zero;
|
||||||
@@ -230,7 +225,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
m_ItemManagement?.AddItems(items.Length, itemDefyArray, out pResults, out pErrors);
|
m_ItemManagement?.AddItems(items.Length, itemDefyArray, out pResults, out pErrors);
|
||||||
IntPtr Pos = pResults;
|
IntPtr Pos = pResults;
|
||||||
Marshal.Copy(pErrors, errors, 0, items.Length);
|
Marshal.Copy(pErrors, errors, 0, items.Length);
|
||||||
StringBuilder stringBuilder = new();
|
List<Tuple<OpcItem, int>> results = new();
|
||||||
for (int j = 0; j < items.Length; j++)
|
for (int j = 0; j < items.Length; j++)
|
||||||
{
|
{
|
||||||
if (errors[j] == 0)
|
if (errors[j] == 0)
|
||||||
@@ -252,21 +247,10 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stringBuilder.AppendLine(items[j].ItemID + "添加失败,错误代码:" + errors[j]);
|
results.Add(Tuple.Create(items[j], errors[j]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stringBuilder.Length > 0)
|
return results;
|
||||||
{
|
|
||||||
return new OperResult(stringBuilder.ToString());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return OperResult.CreateSuccessResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (COMException ex)
|
|
||||||
{
|
|
||||||
return new OperResult(ex);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -301,7 +285,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
/// 组读取
|
/// 组读取
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="ExternalException"></exception>
|
/// <exception cref="ExternalException"></exception>
|
||||||
internal OperResult ReadAsync()
|
internal void ReadAsync()
|
||||||
{
|
{
|
||||||
IntPtr pErrors = IntPtr.Zero;
|
IntPtr pErrors = IntPtr.Zero;
|
||||||
try
|
try
|
||||||
@@ -314,21 +298,15 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
{
|
{
|
||||||
serverHandle[j] = OpcItems[j].ServerHandle;
|
serverHandle[j] = OpcItems[j].ServerHandle;
|
||||||
}
|
}
|
||||||
int cancelId = 0;
|
m_Async2IO.Read(OpcItems.Count, serverHandle, 2, out int cancelId, out pErrors);
|
||||||
m_Async2IO.Read(OpcItems.Count, serverHandle, 2, out cancelId, out pErrors);
|
|
||||||
Marshal.Copy(pErrors, PErrors, 0, OpcItems.Count);
|
Marshal.Copy(pErrors, PErrors, 0, OpcItems.Count);
|
||||||
if (PErrors.Any(a => a > 0))
|
if (PErrors.Any(a => a > 0))
|
||||||
{
|
{
|
||||||
return new OperResult("读取错误,错误代码为" + pErrors);
|
throw new("读取错误,错误代码:" + pErrors);
|
||||||
}
|
}
|
||||||
return OperResult.CreateSuccessResult();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return new OperResult("连接无效");
|
throw new("连接无效");
|
||||||
}
|
|
||||||
catch (COMException ex)
|
|
||||||
{
|
|
||||||
return new OperResult(ex);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -339,10 +317,9 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal OperResult RemoveItem(OpcItem[] items)
|
internal List<Tuple<OpcItem, int>> RemoveItem(OpcItem[] items)
|
||||||
{
|
{
|
||||||
IntPtr pErrors = IntPtr.Zero;
|
IntPtr pErrors = IntPtr.Zero;
|
||||||
bool[] result = new bool[items.Length];
|
|
||||||
int[] errors = new int[items.Length];
|
int[] errors = new int[items.Length];
|
||||||
int[] handles = new int[items.Length];
|
int[] handles = new int[items.Length];
|
||||||
for (int i = 0; i < items.Length; i++)
|
for (int i = 0; i < items.Length; i++)
|
||||||
@@ -353,7 +330,6 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
{
|
{
|
||||||
m_ItemManagement?.RemoveItems(handles.Length, handles, out pErrors);
|
m_ItemManagement?.RemoveItems(handles.Length, handles, out pErrors);
|
||||||
Marshal.Copy(pErrors, errors, 0, items.Length);
|
Marshal.Copy(pErrors, errors, 0, items.Length);
|
||||||
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -361,51 +337,42 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
{
|
{
|
||||||
Marshal.FreeCoTaskMem(pErrors);
|
Marshal.FreeCoTaskMem(pErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
StringBuilder stringBuilder = new();
|
List<Tuple<OpcItem, int>> results = new();
|
||||||
for (int i = 0; i < errors.Length; i++)
|
for (int i = 0; i < errors.Length; i++)
|
||||||
{
|
{
|
||||||
if (errors[i] != 0)
|
if (errors[i] != 0)
|
||||||
{
|
{
|
||||||
stringBuilder.AppendLine(items[i].ItemID + "移除失败,错误代码:" + errors[i]);
|
results.Add(Tuple.Create(items[i], errors[i]));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OpcItems.Remove(items[i]);
|
OpcItems.Remove(items[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (stringBuilder.Length > 0)
|
return results;
|
||||||
{
|
|
||||||
return new OperResult(stringBuilder.ToString());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return OperResult.CreateSuccessResult();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
internal OperResult Write(object[] values, int[] serverHandle, out int[] errors)
|
|
||||||
|
|
||||||
|
internal List<Tuple<int, int>> Write(object[] values, int[] serverHandle)
|
||||||
{
|
{
|
||||||
IntPtr pErrors = IntPtr.Zero;
|
IntPtr pErrors = IntPtr.Zero;
|
||||||
errors = new int[values.Length];
|
var errors = new int[values.Length];
|
||||||
if (m_Async2IO != null)
|
if (m_Async2IO != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
m_SyncIO.Write(values.Length, serverHandle, values, out pErrors);
|
m_SyncIO.Write(values.Length, serverHandle, values, out pErrors);
|
||||||
Marshal.Copy(pErrors, errors, 0, values.Length);
|
Marshal.Copy(pErrors, errors, 0, values.Length);
|
||||||
if (errors.Any(a => a != 0))
|
List<Tuple<int, int>> results = new();
|
||||||
|
for (int i = 0; i < errors.Length; i++)
|
||||||
{
|
{
|
||||||
return new("写入错误,代码:" + Environment.NewLine + errors.ToJson().FormatJson());
|
if (errors[i] != 0)
|
||||||
|
{
|
||||||
|
results.Add(Tuple.Create(serverHandle[i], errors[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
return results;
|
||||||
{
|
|
||||||
return OperResult.CreateSuccessResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (COMException ex)
|
|
||||||
{
|
|
||||||
return new OperResult(ex);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -416,7 +383,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return new OperResult("连接无效");
|
throw new("连接无效");
|
||||||
}
|
}
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
@@ -462,7 +429,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private OperResult ActiveDataChanged(bool active)
|
private void ActiveDataChanged(bool active)
|
||||||
{
|
{
|
||||||
IntPtr pRequestedUpdateRate = IntPtr.Zero;
|
IntPtr pRequestedUpdateRate = IntPtr.Zero;
|
||||||
IntPtr hClientGroup = IntPtr.Zero;
|
IntPtr hClientGroup = IntPtr.Zero;
|
||||||
@@ -474,19 +441,13 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
|
|||||||
hActive.Target = active ? 1 : 0;
|
hActive.Target = active ? 1 : 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int nRevUpdateRate = 0;
|
|
||||||
m_StateManagement?.SetState(pRequestedUpdateRate,
|
m_StateManagement?.SetState(pRequestedUpdateRate,
|
||||||
out nRevUpdateRate,
|
out int nRevUpdateRate,
|
||||||
hActive.AddrOfPinnedObject(),
|
hActive.AddrOfPinnedObject(),
|
||||||
pTimeBias,
|
pTimeBias,
|
||||||
pDeadband,
|
pDeadband,
|
||||||
pLCID,
|
pLCID,
|
||||||
hClientGroup);
|
hClientGroup);
|
||||||
return OperResult.CreateSuccessResult();
|
|
||||||
}
|
|
||||||
catch (COMException ex)
|
|
||||||
{
|
|
||||||
return new OperResult(ex);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
||||||
|
/// <summary>
|
||||||
|
/// OpcItem
|
||||||
|
/// </summary>
|
||||||
|
public class OpcItem
|
||||||
|
{
|
||||||
|
private static int _hanle = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// OpcItem
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="itemId"></param>
|
||||||
|
public OpcItem(string itemId)
|
||||||
|
{
|
||||||
|
ItemID = itemId;
|
||||||
|
ClientHandle = ++_hanle;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// AccessPath
|
||||||
|
/// </summary>
|
||||||
|
public string AccessPath { get; private set; } = "";
|
||||||
|
/// <summary>
|
||||||
|
/// Blob
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr Blob { get; set; } = IntPtr.Zero;
|
||||||
|
/// <summary>
|
||||||
|
/// BlobSize
|
||||||
|
/// </summary>
|
||||||
|
public int BlobSize { get; set; } = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// ClientHandle
|
||||||
|
/// </summary>
|
||||||
|
public int ClientHandle { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// active(1) or not(0)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据项在opc server的完全名称
|
||||||
|
/// </summary>
|
||||||
|
public string ItemID { get; private set; } = String.Empty;
|
||||||
|
/// <summary>
|
||||||
|
/// Quality
|
||||||
|
/// </summary>
|
||||||
|
public int Quality { get; set; } = Qualities.OPC_QUALITY_BAD;
|
||||||
|
/// <summary>
|
||||||
|
/// RunTimeDataType
|
||||||
|
/// </summary>
|
||||||
|
public short RunTimeDataType { get; set; } = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// ServerHandle
|
||||||
|
/// </summary>
|
||||||
|
public int ServerHandle { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// TimeStamp
|
||||||
|
/// </summary>
|
||||||
|
public DateTime TimeStamp { get; set; } = new DateTime(0);
|
||||||
|
/// <summary>
|
||||||
|
/// Value
|
||||||
|
/// </summary>
|
||||||
|
public object Value { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
||||||
|
#pragma warning disable CA1416 // 验证平台兼容性
|
||||||
|
|
||||||
|
internal class OpcServer : IDisposable
|
||||||
|
{
|
||||||
|
|
||||||
|
private bool disposedValue;
|
||||||
|
|
||||||
|
private IOPCServer m_OpcServer = null;
|
||||||
|
|
||||||
|
internal OpcServer(string name, string host = "localhost")
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
if (string.IsNullOrEmpty(host))
|
||||||
|
{
|
||||||
|
Host = "localhost";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Host = host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string Host { get; private set; }
|
||||||
|
internal bool IsConnected { get; private set; } = false;
|
||||||
|
internal string Name { get; private set; }
|
||||||
|
internal List<OpcGroup> OpcGroups { get; private set; } = new List<OpcGroup>(10);
|
||||||
|
internal ServerStatus ServerStatus { get; private set; } = new ServerStatus();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(disposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal OpcGroup AddGroup(string groupName)
|
||||||
|
{
|
||||||
|
return AddGroup(groupName, true, 1000, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns></returns>
|
||||||
|
internal OpcGroup AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand)
|
||||||
|
{
|
||||||
|
if (null == m_OpcServer || IsConnected == false)
|
||||||
|
throw new("未初始化连接!");
|
||||||
|
OpcGroup group = new(groupName, active, reqUpdateRate, deadBand);
|
||||||
|
Guid riid = typeof(IOPCItemMgt).GUID;
|
||||||
|
m_OpcServer?.AddGroup(group.Name,
|
||||||
|
group.IsActive ? 1 : 0,//IsActive
|
||||||
|
group.RequestUpdateRate,//RequestedUpdateRate 1000ms
|
||||||
|
group.ClientGroupHandle,
|
||||||
|
group.TimeBias.AddrOfPinnedObject(),
|
||||||
|
group.PercendDeadBand.AddrOfPinnedObject(),
|
||||||
|
group.LCID,
|
||||||
|
out group.serverGroupHandle,
|
||||||
|
out group.revisedUpdateRate,
|
||||||
|
ref riid,
|
||||||
|
out group.groupPointer);
|
||||||
|
if (group.groupPointer != null)
|
||||||
|
{
|
||||||
|
group.InitIoInterfaces(group.groupPointer);
|
||||||
|
OpcGroups.Add(group);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new("添加OPC组错误,OPC服务器返回null");
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取节点
|
||||||
|
/// </summary>
|
||||||
|
internal List<BrowseElement> Browse(string itemId = null)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
if (null == m_OpcServer || IsConnected == false)
|
||||||
|
throw new("未初始化连接!");
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
var moreElements = 0;
|
||||||
|
|
||||||
|
var pContinuationPoint = IntPtr.Zero;
|
||||||
|
var pElements = IntPtr.Zero;
|
||||||
|
var filterId = new PropertyID[]
|
||||||
|
{
|
||||||
|
new PropertyID(1),
|
||||||
|
new PropertyID(3),
|
||||||
|
new PropertyID(4),
|
||||||
|
new PropertyID(5),
|
||||||
|
new PropertyID(6),
|
||||||
|
new PropertyID(101),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var server = m_OpcServer as IOPCBrowse;
|
||||||
|
server.Browse(
|
||||||
|
string.IsNullOrEmpty(itemId) ? "" : itemId,
|
||||||
|
ref pContinuationPoint,
|
||||||
|
int.MaxValue,
|
||||||
|
OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
filterId.Length,
|
||||||
|
Interop.GetPropertyIDs(filterId),
|
||||||
|
out moreElements,
|
||||||
|
out count,
|
||||||
|
out pElements);
|
||||||
|
BrowseElement[] browseElements = Interop.GetBrowseElements(ref pElements, count, true);
|
||||||
|
string stringUni = Marshal.PtrToStringUni(pContinuationPoint);
|
||||||
|
Marshal.FreeCoTaskMem(pContinuationPoint);
|
||||||
|
this.ProcessResults(browseElements, filterId);
|
||||||
|
return browseElements?.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Connect()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Host) && !string.IsNullOrEmpty(Name))
|
||||||
|
{
|
||||||
|
var info = Discovery.OpcDiscovery.GetOpcServer(Name, Host);
|
||||||
|
object o = Comn.ComInterop.CreateInstance(info.CLSID, Host);
|
||||||
|
if (o == null)
|
||||||
|
{
|
||||||
|
throw new(string.Format("{0}{1}无法创建com对象", info.CLSID, Host));
|
||||||
|
}
|
||||||
|
m_OpcServer = (IOPCServer)o;
|
||||||
|
IsConnected = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new("应初始化Host与Name");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务器状态
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal ServerStatus GetServerStatus()
|
||||||
|
{
|
||||||
|
ServerStatus serverStatus = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (null == m_OpcServer || IsConnected == false)
|
||||||
|
throw new("未初始化连接!");
|
||||||
|
IntPtr statusPtr = IntPtr.Zero;
|
||||||
|
m_OpcServer?.GetStatus(out statusPtr);
|
||||||
|
OPCSERVERSTATUS status;
|
||||||
|
if (statusPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
|
||||||
|
object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS));
|
||||||
|
|
||||||
|
Marshal.FreeCoTaskMem(statusPtr);
|
||||||
|
|
||||||
|
if (o != null)
|
||||||
|
{
|
||||||
|
status = (OPCSERVERSTATUS)o;
|
||||||
|
serverStatus = new();
|
||||||
|
serverStatus.Version = $"{status.wMajorVersion.ToString()}.{status.wMinorVersion.ToString()}.{status.wBuildNumber.ToString()}";
|
||||||
|
serverStatus.ServerState = status.dwServerState;
|
||||||
|
serverStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime);
|
||||||
|
serverStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime);
|
||||||
|
serverStatus.LastUpdateTime = Comn.Convert.FileTimeToDateTime(status.ftLastUpdateTime);
|
||||||
|
serverStatus.VendorInfo = status.szVendorInfo;
|
||||||
|
IsConnected = true;
|
||||||
|
|
||||||
|
return serverStatus;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsConnected = false;
|
||||||
|
throw new("未知错误");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsConnected = false;
|
||||||
|
throw new("未知错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (serverStatus != null)
|
||||||
|
IsConnected = true;
|
||||||
|
else
|
||||||
|
IsConnected = false;
|
||||||
|
ServerStatus = serverStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RemoveGroup(OpcGroup group)
|
||||||
|
{
|
||||||
|
if (OpcGroups.Contains(group))
|
||||||
|
{
|
||||||
|
m_OpcServer?.RemoveGroup(group.ServerGroupHandle, 1);
|
||||||
|
OpcGroups.Remove(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposedValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < OpcGroups.Count; i++)
|
||||||
|
RemoveGroup(OpcGroups[i]);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
if (m_OpcServer != null)
|
||||||
|
{
|
||||||
|
Marshal.ReleaseComObject(m_OpcServer);
|
||||||
|
m_OpcServer = null;
|
||||||
|
}
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
OpcGroups.Clear();
|
||||||
|
}
|
||||||
|
disposedValue = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessResults(BrowseElement[] elements, PropertyID[] propertyIDs)
|
||||||
|
{
|
||||||
|
if (elements == null)
|
||||||
|
return;
|
||||||
|
foreach (BrowseElement element in elements)
|
||||||
|
{
|
||||||
|
if (element.Properties != null)
|
||||||
|
{
|
||||||
|
foreach (ItemProperty property in element.Properties)
|
||||||
|
{
|
||||||
|
if (propertyIDs != null)
|
||||||
|
{
|
||||||
|
foreach (PropertyID propertyId in propertyIDs)
|
||||||
|
{
|
||||||
|
if (property.ID.Code == propertyId.Code)
|
||||||
|
{
|
||||||
|
property.ID = propertyId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,13 +13,33 @@
|
|||||||
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
|
||||||
|
/// <summary>
|
||||||
|
/// ServerStatus
|
||||||
|
/// </summary>
|
||||||
public class ServerStatus
|
public class ServerStatus
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CurrentTime
|
||||||
|
/// </summary>
|
||||||
public DateTime CurrentTime { get; internal set; } = new DateTime(0);
|
public DateTime CurrentTime { get; internal set; } = new DateTime(0);
|
||||||
|
/// <summary>
|
||||||
|
/// LastUpdateTime
|
||||||
|
/// </summary>
|
||||||
public DateTime LastUpdateTime { get; internal set; } = new DateTime(0);
|
public DateTime LastUpdateTime { get; internal set; } = new DateTime(0);
|
||||||
|
/// <summary>
|
||||||
|
/// ServerState
|
||||||
|
/// </summary>
|
||||||
public OPCSERVERSTATE ServerState { get; internal set; } = OPCSERVERSTATE.OPC_STATUS_NOCONFIG;
|
public OPCSERVERSTATE ServerState { get; internal set; } = OPCSERVERSTATE.OPC_STATUS_NOCONFIG;
|
||||||
|
/// <summary>
|
||||||
|
/// StartTime
|
||||||
|
/// </summary>
|
||||||
public DateTime StartTime { get; internal set; } = new DateTime(0);
|
public DateTime StartTime { get; internal set; } = new DateTime(0);
|
||||||
|
/// <summary>
|
||||||
|
/// VendorInfo
|
||||||
|
/// </summary>
|
||||||
public string VendorInfo { get; internal set; } = "UNKOWN";
|
public string VendorInfo { get; internal set; } = "UNKOWN";
|
||||||
|
/// <summary>
|
||||||
|
/// Version
|
||||||
|
/// </summary>
|
||||||
public string Version { get; internal set; } = "UNKOWN";
|
public string Version { get; internal set; } = "UNKOWN";
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Discovery;
|
||||||
|
/// <summary>
|
||||||
|
/// OpcDiscovery
|
||||||
|
/// </summary>
|
||||||
|
public class OpcDiscovery
|
||||||
|
{
|
||||||
|
private static readonly Guid CATID_OPC_DA10 = new("63D5F430-CFE4-11d1-B2C8-0060083BA1FB");
|
||||||
|
|
||||||
|
private static readonly Guid CATID_OPC_DA20 = new("63D5F432-CFE4-11d1-B2C8-0060083BA1FB");
|
||||||
|
|
||||||
|
private static readonly Guid CATID_OPC_DA30 = new("CC603642-66D7-48f1-B69A-B625E73652D7");
|
||||||
|
|
||||||
|
private static readonly Guid OPCEnumCLSID = new("13486D51-4821-11D2-A494-3CB306C10000");
|
||||||
|
/// <summary>
|
||||||
|
/// GetOpcServer
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverName"></param>
|
||||||
|
/// <param name="host"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static ServerInfo GetOpcServer(string serverName, string host)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(serverName))
|
||||||
|
{
|
||||||
|
throw new("检索失败,需提供OPCName");
|
||||||
|
}
|
||||||
|
ServerInfo result = null;
|
||||||
|
ServerInfo[] serverInfos = null;
|
||||||
|
object o_Server = Comn.ComInterop.CreateInstance(OPCEnumCLSID, host);
|
||||||
|
if (o_Server == null)
|
||||||
|
throw new("检索失败,请检查是否安装OPC Runtime");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Guid catid = CATID_OPC_DA20;
|
||||||
|
|
||||||
|
//两种方式,兼容国产部分OPCServer不支持IOPCServerList2的情况
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IOPCServerList2 m_server2 = (IOPCServerList2)o_Server;
|
||||||
|
GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server2, catid);
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
IOPCServerList m_server = (IOPCServerList)o_Server;
|
||||||
|
GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
IOPCServerList m_server = (IOPCServerList)o_Server;
|
||||||
|
GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid);
|
||||||
|
}
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
foreach (var item in serverInfos)
|
||||||
|
{
|
||||||
|
sb.AppendLine(item.ToString());
|
||||||
|
}
|
||||||
|
throw new($"无法创建OPCServer连接,请检查OPC名称是否一致,以下为{host}中的OPC列表:"
|
||||||
|
+ Environment.NewLine +
|
||||||
|
sb.ToString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Comn.ComInterop.RealseComServer(o_Server);
|
||||||
|
o_Server = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList m_server, Guid catid)
|
||||||
|
{
|
||||||
|
object enumerator = null;
|
||||||
|
//2
|
||||||
|
m_server.EnumClassesOfCategories(
|
||||||
|
1,
|
||||||
|
new Guid[] { catid },
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
out enumerator);
|
||||||
|
Guid[] clsids = Comn.ComInterop.ReadClasses((IEnumGUID)enumerator);
|
||||||
|
//释放
|
||||||
|
Comn.ComInterop.RealseComServer(enumerator);
|
||||||
|
enumerator = null;
|
||||||
|
|
||||||
|
serverInfos = GetServerDetails(clsids?.ToArray(), host, m_server);
|
||||||
|
for (int i = 0; i < serverInfos.Length; i++)
|
||||||
|
{
|
||||||
|
if (serverInfos[i].CLSID.ToString().ToLower() == serverName.ToLower() ||
|
||||||
|
serverInfos[i].ProgID.ToLower() == serverName.ToLower() ||
|
||||||
|
serverInfos[i].VerIndProgID.ToLower() == serverName.ToLower())
|
||||||
|
{
|
||||||
|
result = serverInfos[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList2 m_server, Guid catid)
|
||||||
|
{
|
||||||
|
//1
|
||||||
|
IOPCEnumGUID enumerator = null;
|
||||||
|
m_server.EnumClassesOfCategories(
|
||||||
|
1,
|
||||||
|
new Guid[] { catid },
|
||||||
|
0,
|
||||||
|
null,
|
||||||
|
out enumerator);
|
||||||
|
Guid[] clsids = Comn.ComInterop.ReadClasses(enumerator);
|
||||||
|
//释放
|
||||||
|
Comn.ComInterop.RealseComServer(enumerator);
|
||||||
|
enumerator = null;
|
||||||
|
|
||||||
|
serverInfos = GetServerDetails(clsids?.ToArray(), host, m_server);
|
||||||
|
for (int i = 0; i < serverInfos.Length; i++)
|
||||||
|
{
|
||||||
|
if (serverInfos[i].CLSID.ToString().ToLower() == serverName.ToLower() ||
|
||||||
|
serverInfos[i].ProgID.ToLower() == serverName.ToLower() ||
|
||||||
|
serverInfos[i].VerIndProgID.ToLower() == serverName.ToLower())
|
||||||
|
{
|
||||||
|
result = serverInfos[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static ServerInfo[] GetServerDetails(Guid[] clsids, string host, IOPCServerList m_server)
|
||||||
|
{
|
||||||
|
ArrayList servers = new ArrayList();
|
||||||
|
for (int i = 0; i < clsids?.Length; i++)
|
||||||
|
{
|
||||||
|
Guid clsid = clsids[i];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string progID = null;
|
||||||
|
string description = null;
|
||||||
|
string verIndProgID = null;
|
||||||
|
ServerInfo server1 = new();
|
||||||
|
|
||||||
|
server1.Host = host;
|
||||||
|
server1.CLSID = clsid;
|
||||||
|
|
||||||
|
m_server?.GetClassDetails(
|
||||||
|
ref clsid,
|
||||||
|
out progID,
|
||||||
|
out description,
|
||||||
|
out verIndProgID);
|
||||||
|
if (verIndProgID != null)
|
||||||
|
{
|
||||||
|
server1.VerIndProgID = verIndProgID;
|
||||||
|
}
|
||||||
|
else if (progID != null)
|
||||||
|
{
|
||||||
|
server1.ProgID = progID;
|
||||||
|
}
|
||||||
|
if (description != null)
|
||||||
|
{
|
||||||
|
server1.Description = description;
|
||||||
|
}
|
||||||
|
servers.Add(server1);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (ServerInfo[])servers.ToArray(typeof(ServerInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServerInfo[] GetServerDetails(Guid[] clsids, string host, IOPCServerList2 m_server)
|
||||||
|
{
|
||||||
|
ArrayList servers = new ArrayList();
|
||||||
|
for (int i = 0; i < clsids?.Length; i++)
|
||||||
|
{
|
||||||
|
Guid clsid = clsids[i];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string progID = null;
|
||||||
|
string description = null;
|
||||||
|
string verIndProgID = null;
|
||||||
|
ServerInfo server1 = new();
|
||||||
|
|
||||||
|
server1.Host = host;
|
||||||
|
server1.CLSID = clsid;
|
||||||
|
|
||||||
|
m_server?.GetClassDetails(
|
||||||
|
ref clsid,
|
||||||
|
out progID,
|
||||||
|
out description,
|
||||||
|
out verIndProgID);
|
||||||
|
if (verIndProgID != null)
|
||||||
|
{
|
||||||
|
server1.VerIndProgID = verIndProgID;
|
||||||
|
}
|
||||||
|
else if (progID != null)
|
||||||
|
{
|
||||||
|
server1.ProgID = progID;
|
||||||
|
}
|
||||||
|
if (description != null)
|
||||||
|
{
|
||||||
|
server1.Description = description;
|
||||||
|
}
|
||||||
|
servers.Add(server1);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (ServerInfo[])servers.ToArray(typeof(ServerInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal class ServerInfo
|
||||||
|
{
|
||||||
|
internal Guid CLSID { get; set; }
|
||||||
|
internal string Description { get; set; } = string.Empty;
|
||||||
|
internal string Host { get; set; } = string.Empty;
|
||||||
|
internal string ProgID { get; set; } = string.Empty;
|
||||||
|
internal string VerIndProgID { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.AppendLine($"{nameof(CLSID)}:{CLSID}");
|
||||||
|
stringBuilder.AppendLine($"{nameof(Description)}:{Description}");
|
||||||
|
stringBuilder.AppendLine($"{nameof(Host)}:{Host}");
|
||||||
|
stringBuilder.AppendLine($"{nameof(ProgID)}:{ProgID}");
|
||||||
|
stringBuilder.AppendLine($"{nameof(VerIndProgID)}:{VerIndProgID}");
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class BrowseElement : ICloneable
|
public class BrowseElement : ICloneable
|
||||||
@@ -10,14 +10,13 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using System.
|
using System.
|
||||||
|
|
||||||
Runtime.InteropServices;
|
Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
|
||||||
|
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
|
||||||
/// <exclude />
|
/// <exclude />
|
||||||
[ComImport]
|
[ComImport]
|
||||||
[GuidAttribute("B196B286-BAB4-101A-B69C-00AA00341D07")]
|
[GuidAttribute("B196B286-BAB4-101A-B69C-00AA00341D07")]
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
using System.
|
using System.
|
||||||
|
|
||||||
Runtime.InteropServices;
|
Runtime.InteropServices;
|
||||||
@@ -18,6 +17,7 @@ Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
|
||||||
|
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
|
||||||
|
|
||||||
/// <exclude />
|
/// <exclude />
|
||||||
public enum OPCBROWSEDIRECTION
|
public enum OPCBROWSEDIRECTION
|
||||||
@@ -10,8 +10,6 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -19,6 +17,7 @@ using System.Xml;
|
|||||||
|
|
||||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
|
||||||
|
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
|
||||||
|
|
||||||
public class Interop
|
public class Interop
|
||||||
{
|
{
|
||||||
@@ -800,7 +799,7 @@ public class Interop
|
|||||||
if (input.GetType() == typeof(DateTime))
|
if (input.GetType() == typeof(DateTime))
|
||||||
{
|
{
|
||||||
DateTime dateTime = (DateTime)input;
|
DateTime dateTime = (DateTime)input;
|
||||||
return dateTime != DateTime.MinValue ? (object)dateTime.ToUniversalTime() : (object)dateTime;
|
return dateTime != DateTime.MinValue ? (object)dateTime.ToLocalTime() : (object)dateTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,7 @@ using System.Xml;
|
|||||||
|
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
|
||||||
|
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
|
||||||
|
|
||||||
public enum accessRights
|
public enum accessRights
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.OPCDA;
|
||||||
|
|
||||||
|
internal static class CollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 移除符合条件的元素
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="this"></param>
|
||||||
|
/// <param name="where"></param>
|
||||||
|
internal static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
|
||||||
|
{
|
||||||
|
foreach (var obj in @this.Where(where).ToList())
|
||||||
|
{
|
||||||
|
@this.Remove(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 将项目列表分解为特定大小的块
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <param name="chunksize"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static List<List<T>> ChunkTrivialBetter<T>(this IEnumerable<T> source, int chunksize)
|
||||||
|
{
|
||||||
|
var pos = 0;
|
||||||
|
List<List<T>> n = new();
|
||||||
|
while (source.Skip(pos).Any())
|
||||||
|
{
|
||||||
|
n.Add(source.Skip(pos).Take(chunksize).ToList());
|
||||||
|
pos += chunksize;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||||
|
// Github源代码仓库:https://github.com/RRQM
|
||||||
|
// API首页:http://rrqm_home.gitee.io/touchsocket/
|
||||||
|
// 交流QQ群:234762506
|
||||||
|
// 感谢您的下载和使用
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Adapter.OPCDA;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DictionaryExtension
|
||||||
|
/// </summary>
|
||||||
|
internal static class DictionaryExtension
|
||||||
|
{
|
||||||
|
#region 字典扩展
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除满足条件的项目。
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TKey"></typeparam>
|
||||||
|
/// <typeparam name="TValue"></typeparam>
|
||||||
|
/// <param name="pairs"></param>
|
||||||
|
/// <param name="func"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static int RemoveWhen<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> pairs, Func<KeyValuePair<TKey, TValue>, bool> func)
|
||||||
|
{
|
||||||
|
var list = new List<TKey>();
|
||||||
|
foreach (var item in pairs)
|
||||||
|
{
|
||||||
|
if (func?.Invoke(item) == true)
|
||||||
|
{
|
||||||
|
list.Add(item.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
if (pairs.TryRemove(item, out _))
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NET45_OR_GREATER || NETSTANDARD2_0_OR_GREATER
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试添加
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="Tkey"></typeparam>
|
||||||
|
/// <typeparam name="TValue"></typeparam>
|
||||||
|
/// <param name="dictionary"></param>
|
||||||
|
/// <param name="tkey"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static bool TryAdd<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
|
||||||
|
{
|
||||||
|
if (dictionary.ContainsKey(tkey))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dictionary.Add(tkey, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试添加
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="Tkey"></typeparam>
|
||||||
|
/// <typeparam name="TValue"></typeparam>
|
||||||
|
/// <param name="dictionary"></param>
|
||||||
|
/// <param name="tkey"></param>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static void AddOrUpdate<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
|
||||||
|
{
|
||||||
|
if (dictionary.ContainsKey(tkey))
|
||||||
|
{
|
||||||
|
dictionary[tkey] = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dictionary.Add(tkey, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取值。如果键不存在,则返回默认值。
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="Tkey"></typeparam>
|
||||||
|
/// <typeparam name="TValue"></typeparam>
|
||||||
|
/// <param name="dictionary"></param>
|
||||||
|
/// <param name="tkey"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey)
|
||||||
|
{
|
||||||
|
return dictionary.TryGetValue(tkey, out var value) ? value : default;
|
||||||
|
}
|
||||||
|
#endregion 字典扩展
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#region copyright
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
global using System;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.Linq;
|
||||||
|
global using System.Threading;
|
||||||
|
|
||||||