Compare commits

...

181 Commits

Author SHA1 Message Date
Kimdiego2098
4cf35f7294 发布3.0.1版本 2023-11-03 11:32:13 +08:00
Kimdiego2098
94c77d151b update touchsocket 2023-11-03 11:27:12 +08:00
Kimdiego2098
7f600e2b4b update touchsocket 2023-11-03 11:19:26 +08:00
Kimdiego2098
c809d0ba87 不存在插件时,报错内容优化 2023-11-01 14:51:08 +08:00
Kimdiego2098
50f038ec89 update webapiClient 2023-11-01 14:21:53 +08:00
Kimdiego2098
9199a255a2 调整Timeout属性为int数据类型 2023-10-31 23:49:46 +08:00
Kimdiego2098
d324537b47 更新发布版3.0.0.28 2023-10-31 17:47:07 +08:00
Kimdiego2098
d0c05685f7 更新OPCUAClient,订阅模式适配 分组 2023-10-31 10:55:50 +08:00
Kimdiego2098
1063c930b5 update 2023-10-31 00:24:00 +08:00
Kimdiego2098
79cbd44366 tcpservice停止时执行shutdown方法,修复demo发布不显示页面的问题 2023-10-30 21:31:30 +08:00
Kimdiego2098
7fdac1c5cb 添加部分Pro内容 2023-10-29 19:39:38 +08:00
Kimdiego2098
0c0cf72ebb 添加部分Pro内容 2023-10-29 19:34:17 +08:00
Kimdiego2098
8e2fe175ed 更新文档 2023-10-28 22:06:58 +08:00
Kimdiego2098
d1cff037c9 发布3.0.0.27版本;
优化设备线程启停逻辑
添加winform(blazor) demo
部分页面显示内容优化
2023-10-28 22:03:52 +08:00
Kimdiego2098
fc88a2fafa 发布3.0.0.27版本;
优化设备线程启停逻辑
添加winform(blazor) demo
部分页面显示内容优化
2023-10-28 22:00:35 +08:00
Kimdiego2098
45fcceb056 优化设备线程启停逻辑 2023-10-28 21:58:09 +08:00
Kimdiego2098
7043477038 添加部分Pro内容 2023-10-28 21:21:03 +08:00
Kimdiego2098
7dd685cf54 添加winform(blazor) demo 2023-10-28 15:20:12 +08:00
Kimdiego2098
5f5e4969c0 调整pro类库 2023-10-28 10:54:26 +08:00
Diego2098
8a53fd19e9 设备状态页面显示优化 2023-10-27 20:19:26 +08:00
Diego2098
baf4714c36 状态显示:已退出自动更新 2023-10-27 20:18:57 +08:00
Kimdiego2098
7ba9ac7a5b 报文显示限长500 2023-10-27 14:53:19 +08:00
Kimdiego2098
85b8f26e8e tcpserver 报文输出 添加 ip端口 提示 2023-10-27 14:41:35 +08:00
Kimdiego2098
594a0f1410 update the tcpserver class and add log output for start and stop 2023-10-27 14:24:26 +08:00
Kimdiego2098
d317d757d7 优化线程上下文转换 2023-10-26 19:16:24 +08:00
Kimdiego2098
fdf0ba6318 添加ConfigureAwait 2023-10-26 18:06:33 +08:00
Kimdiego2098
15bf7de5fa 优化设备导出逻辑 2023-10-26 15:15:43 +08:00
Kimdiego2098
d3402b058e 优化设备导出逻辑 2023-10-26 15:10:26 +08:00
Kimdiego2098
e7dfdd4031 3.0.0.25 2023-10-26 13:21:40 +08:00
Kimdiego2098
b2dd7b6364 Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-10-26 13:20:54 +08:00
Kimdiego2098
9bd6d9abbf 3.0.0.25 2023-10-26 13:20:31 +08:00
Kimdiego2098
cd14428fea update 2023-10-26 13:19:18 +08:00
Kimdiego2098
19d9f03c2b s7添加错误返回码提示,组包处理 2023-10-26 13:05:06 +08:00
Kimdiego2098
0d57e72bbf 增加硬件信息 相关json配置 2023-10-26 10:54:48 +08:00
Kimdiego2098
329516a61b 更新nuget依赖 2023-10-26 09:37:01 +08:00
Kimdiego2098
d566869589 硬件信息页面添加刷新条件 2023-10-26 09:20:25 +08:00
Kimdiego2098
9cb8d8e6c7 更新文档 2023-10-25 00:53:27 +08:00
Kimdiego2098
9de3c57e5d update touchsocket 2023-10-25 00:53:17 +08:00
Kimdiego2098
f32ff92b0b <Version>3.0.0.24</Version> 2023-10-24 23:48:20 +08:00
Kimdiego2098
88d71e271e 更改S7协议 设备属性,注意删除了DstTSAP,改为较直观的 机架号/槽位号 2023-10-24 23:47:35 +08:00
Kimdiego2098
fd9c14612a 添加属性识别 16进制写入 2023-10-24 23:46:50 +08:00
Kimdiego2098
e26e5a160f 添加nuget依赖包 2023-10-24 20:50:39 +08:00
Kimdiego2098
b836bfed22 更新nuget包 2023-10-24 00:37:46 +08:00
Kimdiego2098
a4b598c6d0 调整解决方案文件夹 2023-10-24 00:13:03 +08:00
Kimdiego2098
c9ab755839 暂时屏蔽mqttserver 遗留消息错误 2023-10-23 20:51:11 +08:00
Kimdiego2098
9920edba53 调整编码 2023-10-23 20:47:00 +08:00
Kimdiego2098
12bd7280d1 调整mqttlog 2023-10-23 20:46:17 +08:00
Kimdiego2098
d30ea7f63b 调整mqttlog 2023-10-23 20:43:58 +08:00
Kimdiego2098
ebd3390db6 添加部分兼容方法 2023-10-22 02:42:16 +08:00
Kimdiego2098
9a374a9ebc 添加部分兼容方法 2023-10-22 02:26:18 +08:00
Kimdiego2098
b1bc22cb08 update touchsocket 2023-10-22 02:26:04 +08:00
Kimdiego2098
4930d53890 调整代码格式 2023-10-21 19:15:26 +08:00
Kimdiego2098
c31327b5bc update touchsocket 2023-10-21 19:08:21 +08:00
Kimdiego2098
3f2aa1f1e1 调整代码格式 2023-10-21 19:03:15 +08:00
Kimdiego2098
6e78c00a96 调整代码格式 2023-10-21 19:02:58 +08:00
Kimdiego2098
c27dde085e 3.0.0.23 2023-10-20 21:38:22 +08:00
Kimdiego2098
d26cc308c0 优化SQLDB实时表模式,插入/更新 2023-10-20 21:38:07 +08:00
Kimdiego2098
fb1efdf290 sqlsugar提示默认中文 2023-10-20 21:37:42 +08:00
Kimdiego2098
3c99f2a472 update touchsocket 2023-10-20 21:03:29 +08:00
Kimdiego2098
affe9a44e0 优化 ModbusServer 内存占用 2023-10-20 01:53:48 +08:00
Kimdiego2098
43730fa519 3.0.0.21 2023-10-20 01:19:18 +08:00
Kimdiego2098
d39aa22b09 优化OPCUAServer,取消注册,提供多url写入 2023-10-20 01:19:05 +08:00
Kimdiego2098
e232a6b6ea 更新赞助名单 2023-10-19 21:15:35 +08:00
Kimdiego2098
71ebb36fe9 更新文档 2023-10-19 20:36:28 +08:00
Kimdiego2098
78a0b86327 更新版本:3.0.0.20 2023-10-19 20:27:31 +08:00
Kimdiego2098
2636c16a97 优化modbusServer内存管理 2023-10-19 20:24:39 +08:00
Kimdiego2098
fd77c0242d update touchsocket 2023-10-19 20:23:43 +08:00
Kimdiego2098
e74819a900 update toucksocket 2023-10-18 21:51:49 +08:00
Kimdiego2098
9b7f696c9b 更新文档 2023-10-18 20:42:10 +08:00
Kimdiego2098
0230d614e7 发布驱动包 2023-10-18 18:04:37 +08:00
Diego2098
252d99ad78 !12 【轻量级 PR】:修正心跳事件中的参数
Merge pull request !12 from youthalan/N/A
2023-10-18 06:59:17 +00:00
youthalan
1ffc200350 修正心跳事件中的参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 06:58:03 +00:00
Kimdiego2098
807d89b2b2 更新版本号 2023-10-18 13:13:03 +08:00
Kimdiego2098
4013afa1f1 初始化 采集/上传线程时 直接返回线程控制 2023-10-18 13:11:59 +08:00
Kimdiego2098
a580927ceb 更新OPCUAClient类库 2023-10-18 13:09:25 +08:00
Kimdiego2098
bf2cf52034 更新OPCUAClient类库 2023-10-18 12:45:26 +08:00
Kimdiego2098
81bb8b7c31 更新OPCUAClient类库 2023-10-18 12:44:58 +08:00
Kimdiego2098
a825007fb5 更新版本号与nuget发布 2023-10-18 12:39:42 +08:00
Kimdiego2098
988124d96a 更新OPCUAClient类库 2023-10-18 12:38:20 +08:00
Diego2098
f0de815296 !11 补充OPCClient类库事件的缺失文件
Merge pull request !11 from youthalan/N/A
2023-10-18 04:34:34 +00:00
youthalan
0e2d58c887 补充OPCClient类库事件的缺失文件
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 04:32:39 +00:00
Diego2098
b155382626 !10 添加连接或断开事件
Merge pull request !10 from youthalan/N/A
2023-10-18 04:20:22 +00:00
youthalan
f362d740af 修改OPCUAClient类添加连接或断开事件,修改注入时不需要带参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 03:48:05 +00:00
Kimdiego2098
4a85e31a4f update 3.0.0.16 2023-10-18 00:38:36 +08:00
Kimdiego2098
302c270ad5 opcuaClient浏览空间 添加是否显示子变量的选项 2023-10-18 00:36:44 +08:00
Kimdiego2098
3c1517d0f3 更改日志输出内容 2023-10-18 00:15:07 +08:00
Kimdiego2098
f9fb222044 update 3.0.0.15 2023-10-18 00:07:24 +08:00
Kimdiego2098
e8edc02ba3 增加不支持单文件发布的说明 2023-10-17 23:47:24 +08:00
Kimdiego2098
95a44e3053 update tdengineDB plugin 2023-10-17 23:43:48 +08:00
Kimdiego2098
74a9fe9a87 update touchsocket 2023-10-17 23:12:19 +08:00
Kimdiego2098
4d03f9ea1a TD时序库插件,创建时间更改为主键 2023-10-17 23:08:09 +08:00
Kimdiego2098
67c96ca991 update touchsocket 2023-10-17 23:06:21 +08:00
Kimdiego2098
88fb793c68 更新nuget包 2023-10-17 21:00:50 +08:00
Kimdiego2098
d6d02d8cc5 update SQLDB 2023-10-16 20:50:10 +08:00
Kimdiego2098
c5a3f8e2e3 update touchsocket and other 2023-10-16 20:36:51 +08:00
Kimdiego2098
27e8653a1a 3.0.0.13 2023-10-16 17:44:09 +08:00
Kimdiego2098
863beda82c 增加关系库存储插件; 2023-10-16 17:40:17 +08:00
Kimdiego2098
bac84c3ecd 增加时序库存储插件; 2023-10-16 17:40:13 +08:00
Kimdiego2098
2fca2ad9f8 更新pro用户列表 2023-10-16 17:39:14 +08:00
Kimdiego2098
dd75286fe0 3.0.0.12 2023-10-16 08:47:39 +08:00
Kimdiego2098
7f91792cf1 opcuaclient添加是否加载服务端数据类型的选项 2023-10-16 08:46:46 +08:00
Kimdiego2098
0e0ccad311 fix:tcpservice dispose err 2023-10-16 08:46:32 +08:00
Diego2098
0691f72e67 !9 增加可选择安全订阅
Merge pull request !9 from youthalan/N/A
2023-10-16 00:33:19 +00:00
youthalan
7e38a51720 增加可选择安全订阅,以加快订阅速度
Signed-off-by: youthalan <youthalan@126.com>
2023-10-16 00:26:59 +00:00
Kimdiego2098
34ca8243a3 更新3.0.0.11 2023-10-15 20:26:18 +08:00
Diego2098
112fea7632 读取数据类型方法改为批量 2023-10-15 20:21:23 +08:00
Kimdiego2098
378763e4ee 同步Pro版本 2023-10-13 20:14:35 +08:00
Kimdiego2098
517bd0394d update touchsocket 2023-10-13 19:16:12 +08:00
Kimdiego2098
70adb97fb5 update nuget 2023-10-13 16:14:42 +08:00
Kimdiego2098
623d44cabe update 3.0.0.7 2023-10-13 11:54:27 +08:00
Kimdiego2098
0d479ca00b 添加三菱mc 3e帧二进制通讯协议文档 2023-10-13 11:51:51 +08:00
Kimdiego2098
8bc49ef437 update demo 2023-10-12 18:52:44 +08:00
Kimdiego2098
f83fcec786 参数名称修改 2023-10-12 14:57:58 +08:00
Kimdiego2098
93690ce40d fix:mqtt保留消息未更新/更新错误 2023-10-12 14:57:36 +08:00
Kimdiego2098
f82c5f2f27 3.0.0.6 2023-10-11 16:35:10 +08:00
Kimdiego2098
a83c1c3899 update nuget 2023-10-11 16:28:18 +08:00
Kimdiego2098
91d6aed109 调整代码执行顺序 2023-10-11 16:19:34 +08:00
Kimdiego2098
db8f8fe51d update ManageGateway 2023-10-11 10:59:33 +08:00
Kimdiego2098
4596004b17 update ManageGateway 2023-10-11 10:41:09 +08:00
Kimdiego2098
d5540906cb update 3.0.0.5 2023-10-10 21:09:34 +08:00
Kimdiego2098
90796a979d fix:modbusRtu写入返回报文缓存逻辑修复 2023-10-10 21:08:42 +08:00
Kimdiego2098
2190a87772 update touchsocket 2023-10-10 20:03:40 +08:00
Kimdiego2098
c5953b83f8 update touchsocket 2023-10-10 20:02:15 +08:00
Kimdiego2098
24bc60abf0 fix:signalR razor dispose接口 2023-10-09 18:11:23 +08:00
Kimdiego2098
31eee6b009 update uaparser 2023-10-09 10:56:22 +08:00
Kimdiego2098
c5da565a8f 添加MqttRpcDemo 2023-10-07 12:04:17 +08:00
Kimdiego2098
947cd712e1 添加清理日志任务配置参数 2023-10-06 18:28:06 +08:00
Kimdiego2098
edc208f96b update 3.0.0.2 2023-10-05 15:32:50 +08:00
Diego2098
1fb0296ee7 update Directory.Build.props 2023-10-05 15:21:51 +08:00
Kimdiego2098
6488d3df87 修复重启共享通道中的单个设备时,导致通道内其他设备变量异常 2023-10-05 00:34:12 +08:00
Kimdiego2098
56189d78e0 update opcuaclient 2023-10-04 17:12:47 +08:00
Kimdiego2098
bff18127b8 update opcuaclient 2023-10-04 16:59:15 +08:00
Kimdiego2098
363206e0ba update nuget 2023-10-04 02:04:09 +08:00
Kimdiego2098
fd3e378501 update 3.0.0.1 2023-10-04 01:42:27 +08:00
Kimdiego2098
4ba2fe4c9d 实时报警列表线程同步 2023-10-04 01:27:03 +08:00
Kimdiego2098
2c499626ad modbusRtu 适配器 过滤干扰头部数据 2023-10-04 01:26:44 +08:00
Kimdiego2098
2b581a03c3 更新种子数据 2023-10-04 01:24:34 +08:00
Kimdiego2098
450c15210a update windowsService bat 2023-10-03 19:11:42 +08:00
Kimdiego2098
65fed8cc93 update demo 2023-10-03 18:54:01 +08:00
Kimdiego2098
4b64771ea2 rpc调用提示优化 2023-10-03 18:20:39 +08:00
Kimdiego2098
f39977a6ff fix:rpc 特殊方法分类错误 2023-10-03 17:05:49 +08:00
Kimdiego2098
933b535caa update demo 2023-10-02 22:37:50 +08:00
Kimdiego2098
8abc5d2f20 update driverDebugPage 2023-10-02 18:34:55 +08:00
Kimdiego2098
d8783cd994 update opcuaClient 2023-10-02 18:30:58 +08:00
Diego2098
d5d087feb5 add s7 wstring addressSign 2023-10-01 16:54:42 +08:00
Diego2098
6ba3399df7 add s7 wstring addressSign 2023-10-01 16:49:07 +08:00
Diego2098
65124b3aa8 更新demo 2023-10-01 13:33:25 +08:00
Kimdiego2098
98597f4726 update demo csproj 2023-10-01 00:33:33 +08:00
Kimdiego2098
e7981f0d8e add EncodingMapper 2023-10-01 00:25:04 +08:00
Kimdiego2098
cf654427c3 更新文档 2023-09-30 23:28:50 +08:00
Kimdiego2098
ff2f628282 默认不启用远程更新 2023-09-30 23:23:44 +08:00
Kimdiego2098
ae818ca265 更新readme 2023-09-30 23:09:48 +08:00
Kimdiego2098
0f2aed458e 同步3.0.0版本代码 2023-09-30 23:05:53 +08:00
Kimdiego2098
d486c44ff6 更新文档 2023-09-26 20:03:58 +08:00
Kimdiego2098
ca7b9980bf 2.1.0.15 2023-09-20 11:50:06 +08:00
Kimdiego2098
3c71e6a8e3 2.1.0.15 2023-09-20 11:47:10 +08:00
Kimdiego2098
542442864c 添加kafka/mq/iotsharp的间隔上传 2023-09-20 11:46:17 +08:00
Diego2098
5edb64fa85 fix:上传设备选择驱动时没有正确刷新 2023-09-18 00:10:02 +08:00
Kimdiego2098
8dc1c898a3 2.1.0.14 2023-09-15 14:02:15 +08:00
Kimdiego2098
1ed35726b0 ExpressionEvaluator改为多实例 2023-09-15 14:01:54 +08:00
Kimdiego2098
27fae9ebaa 2.1.0.13 2023-09-15 13:23:59 +08:00
Kimdiego2098
b103f25c94 add dispose() 2023-09-15 13:23:39 +08:00
Kimdiego2098
abff450274 2.1.0.12 2023-09-15 13:20:29 +08:00
Kimdiego2098
c260736a11 更新多个依赖包版本 2023-09-15 13:19:04 +08:00
Kimdiego2098
166ac2307a 修复因重复注册cancellationToken.Register导致的内存暴涨! 2023-09-15 13:17:36 +08:00
Kimdiego2098
b21a4e1a4d CancellationToken代替CancellationTokenSource传入 2023-09-15 13:16:56 +08:00
Kimdiego2098
f7dc943fa3 2.1.0.11 2023-09-11 09:35:24 +08:00
Kimdiego2098
bfbd2693ec feat:ManageGatewayWorker part 2023-09-11 09:31:26 +08:00
Kimdiego2098
819e71c993 feat:ManageGatewayWorker part 2023-09-11 09:09:48 +08:00
Kimdiego2098
9fd0b489a2 feat:ManageGatewayWorker part 2023-09-11 09:09:03 +08:00
Kimdiego2098
f5fe9f8dae TGAPPInfp更名APPInfo 2023-09-11 08:57:13 +08:00
Kimdiego2098
f9ffc18145 Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-09-11 08:54:01 +08:00
Kimdiego2098
08db5b983a feat:ManageGatewayWorker part 2023-09-11 08:53:52 +08:00
Diego2098
5b3b4c8c50 !7 修复无法修改变量值问题
Merge pull request !7 from 如阳如木/master
2023-09-07 10:14:45 +00:00
如阳如木
73f914ffc4 删除&& LastSetValue?.ToString() != data?.ToString()
Signed-off-by: 如阳如木 <970143933@qq.com>
2023-09-07 08:42:49 +00:00
Kimdiego2098
d6bdd73ed6 ManageGatewayConfig.json 2023-09-06 17:29:58 +08:00
Kimdiego2098
7370ee7349 fix:_mqttServer null error 2023-09-06 17:27:36 +08:00
Kimdiego2098
4574596bac 2.1.0.10 2023-09-06 17:16:28 +08:00
Kimdiego2098
4d16855e36 从数据库取原属性 2023-09-06 17:14:24 +08:00
Kimdiego2098
13a0d4d282 更改变量 最近一次值 为 上次值 2023-09-06 16:44:58 +08:00
Kimdiego2098
b9cd06b829 更改 变量最近一次值 为 上次值 2023-09-06 16:44:42 +08:00
1647 changed files with 122133 additions and 37809 deletions

View File

@@ -7,7 +7,7 @@
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 [**ThingsGateway.Admin**](https://gitee.com/dotnetchina/ThingsGateway/blob/master/framework/ThingsGateway.Admin.sln)
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
## 文档

View File

@@ -1,13 +1,10 @@
[*.cs]
# CA1822: 将成员标记为 static
dotnet_diagnostic.CA1822.severity = none
# CA1816: Dispose 方法应调用 SuppressFinalize
dotnet_diagnostic.CA1816.severity = none
# CA1848: 使用 LoggerMessage 委托
dotnet_diagnostic.CA1848.severity = none
# CA2254: 模板应为静态表达式
dotnet_diagnostic.CA2254.severity = none
dotnet_diagnostic.CA2254.severity = suggestion
[*.cs]
#### 命名样式 ####
@@ -56,39 +53,6 @@ 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
csharp_using_directive_placement = outside_namespace:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
# CA2208: 正确实例化参数异常
dotnet_diagnostic.CA2208.severity = none
# IDE0057: 使用范围运算符
dotnet_diagnostic.IDE0057.severity = none
# IDE0056: 使用索引运算符
dotnet_diagnostic.IDE0056.severity = none
# CA1830: 最好使用 StringBuilder 的强类型 Append 和 Insert 方法重载
dotnet_diagnostic.CA1830.severity = none
# CA1847: 将字符型文本用于单个字符查找
dotnet_diagnostic.CA1847.severity = none
[*.vb]
#### 命名样式 ####
@@ -137,13 +101,3 @@ dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotnet_naming_style.帕斯卡拼写法.required_suffix =
dotnet_naming_style.帕斯卡拼写法.word_separator =
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
[*.{cs,vb}]
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
end_of_line = crlf
# IDE0060: 删除未使用的参数
dotnet_diagnostic.IDE0060.severity = none

63
framework/.gitattributes vendored Normal file
View 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
View 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

View File

@@ -0,0 +1,14 @@
<Project>
<PropertyGroup>
<Version>3.0.1.0</Version>
<LangVersion>latest</LangVersion>
<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>

View File

@@ -0,0 +1,37 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Photino.Blazor;
namespace ThingsGateway.Foundation.Demo;
internal class Program
{
[STAThread]
private static void Main(string[] args)
{
System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory);
var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args);
appBuilder.RootComponents.Add<App>("#app");
appBuilder.Services.ThingsGatewayComponentsConfigureServices();
var app = appBuilder.Build();
app.MainWindow.SetTitle("ThingsGateway.Foundation.Demo");
app.MainWindow.SetIconFile("wwwroot/favicon.ico");
AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
{
};
app.Run();
}
}

View File

@@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<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>
<ItemGroup>
<Content Update="wwwroot\favicon.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\favicon.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -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>

View 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>

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -10,14 +10,10 @@
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Serial;
using TouchSocket.Core;
namespace ThingsGateway.Blazor;
namespace ThingsGateway.Foundation.Demo;
/// <inheritdoc/>
public partial class SerialSessionPage
public partial class SerialSessionPage : IDisposable
{
/// <summary>
/// 日志输出
@@ -28,34 +24,28 @@ public partial class SerialSessionPage
private readonly SerialProperty serialProperty = new();
private SerialsSession SerialsSession { get; set; } = new();
private SerialSession SerialSession { get; set; } = new();
/// <inheritdoc/>
public void Dispose()
{
SerialsSession.SafeDispose();
}
/// <summary>
/// 获取对象
/// </summary>
/// <returns></returns>
public SerialsSession GetSerialSession()
public SerialSession GetSerialSession()
{
config?.Dispose();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
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);
//载入配置
SerialsSession.Setup(config);
return SerialsSession;
SerialSession.Setup(config);
return SerialSession;
}
private async Task ConnectAsync()
{
try
{
SerialsSession.Close();
SerialSession.Close();
await GetSerialSession().ConnectAsync();
}
catch (Exception ex)
@@ -68,25 +58,49 @@ public partial class SerialSessionPage
{
try
{
SerialsSession.Close();
SerialSession.Close();
}
catch (Exception ex)
{
LogAction?.Invoke(LogLevel.Error, null, null, ex);
}
}
/// <inheritdoc/>
protected override void OnInitialized()
{
config ??= new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
SerialsSession = new SerialsSession();
SerialsSession.Setup(config);
base.OnInitialized();
}
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
/// <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();
}
}

View File

@@ -10,21 +10,19 @@
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Blazor
@namespace ThingsGateway.Foundation.Demo
@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@using System.IO.Ports;
@using System.Collections.Concurrent;
@using ThingsGateway.Foundation;
@using ThingsGateway.Foundation.Serial;
@using ThingsGateway.Foundation.Core;
@using Masa.Blazor
@using TouchSocket.Core;
@using TouchSocket.Sockets;
@implements IDisposable
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start">
<MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP />
<MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port />
<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">
连接
@@ -33,7 +31,4 @@
断开
</MButton>
</MRow>
</MCard>

View File

@@ -10,17 +10,10 @@
//------------------------------------------------------------------------------
#endregion
using Microsoft.AspNetCore.Components;
using ThingsGateway.Foundation;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace ThingsGateway.Blazor;
namespace ThingsGateway.Foundation.Demo;
/// <inheritdoc/>
public partial class TcpClientPage
public partial class TcpClientPage : IDisposable
{
/// <summary>
/// 日志输出
@@ -33,24 +26,17 @@ public partial class TcpClientPage
/// </summary>
private string IP = "127.0.0.1";
/// <summary>
/// 端口
/// Port
/// </summary>
[Parameter]
public int Port { get; set; } = 502;
private TcpClientEx TcpClientEx { get; set; } = new();
/// <inheritdoc/>
public void Dispose()
{
TcpClientEx.SafeDispose();
}
private TcpClient TcpClient { get; set; } = new();
private async Task ConnectAsync()
{
try
{
TcpClientEx.Close();
TcpClient.Close();
await GetTcpClient().ConnectAsync();
}
catch (Exception ex)
@@ -64,7 +50,7 @@ public partial class TcpClientPage
{
try
{
TcpClientEx.Close();
TcpClient.Close();
}
catch (Exception ex)
{
@@ -76,32 +62,55 @@ public partial class TcpClientPage
/// 获取对象
/// </summary>
/// <returns></returns>
public TcpClientEx GetTcpClient()
public TcpClient GetTcpClient()
{
config ??= new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
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));
//载入配置
TcpClientEx.Setup(config);
return TcpClientEx;
TcpClient.Setup(config);
return TcpClient;
}
/// <inheritdoc/>
protected override void OnInitialized()
{
config?.Dispose();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
config.SetRemoteIPHost(new IPHost(IP + ":" + Port));
TcpClientEx = new TcpClientEx();
TcpClientEx.Setup(config);
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();
}
}

View File

@@ -10,30 +10,29 @@
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Blazor
@namespace ThingsGateway.Foundation.Demo
@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@using System.IO.Ports;
@using System.Collections.Concurrent;
@using ThingsGateway.Foundation;
@using ThingsGateway.Foundation.Serial;
@using ThingsGateway.Foundation.Core;
@using Masa.Blazor
@using TouchSocket.Core;
@using TouchSocket.Sockets;
@implements IDisposable
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start">
<MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
<MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
<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>
</MCard>

View File

@@ -10,13 +10,10 @@
//------------------------------------------------------------------------------
#endregion
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace ThingsGateway.Blazor;
namespace ThingsGateway.Foundation.Demo;
/// <inheritdoc/>
public partial class TcpServerPage
public partial class TcpServerPage : IDisposable
{
/// <summary>
/// 日志输出
@@ -31,11 +28,6 @@ public partial class TcpServerPage
private TcpService TcpServer { get; set; } = new();
/// <inheritdoc/>
public void Dispose()
{
TcpServer.SafeDispose();
}
private void Connect()
{
try
@@ -68,30 +60,49 @@ public partial class TcpServerPage
public TcpService GetTcpServer()
{
config ??= new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
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>
protected override void OnInitialized()
/// <param name="firstRender"></param>
protected override void OnAfterRender(bool firstRender)
{
config?.Dispose();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
TcpServer = new TcpService();
TcpServer.Setup(config);
base.OnInitialized();
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();
}
}

View File

@@ -10,21 +10,20 @@
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Blazor
@namespace ThingsGateway.Foundation.Demo
@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@using System.IO.Ports;
@using System.Collections.Concurrent;
@using ThingsGateway.Foundation;
@using ThingsGateway.Foundation.Serial;
@using ThingsGateway.Foundation.Core;
@using Masa.Blazor
@using TouchSocket.Core;
@using TouchSocket.Sockets;
@implements IDisposable
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start">
<MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
<MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
<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">
连接
@@ -32,8 +31,7 @@
<MButton Class="ma-1" OnClick=DisConnect Color="red">
断开
</MButton>
</MRow>
</MCard>

View File

@@ -10,10 +10,7 @@
//------------------------------------------------------------------------------
#endregion
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace ThingsGateway.Blazor;
namespace ThingsGateway.Foundation.Demo;
/// <inheritdoc/>
public partial class UdpSessionPage : IDisposable
@@ -24,18 +21,17 @@ public partial class UdpSessionPage : IDisposable
public Action<LogLevel, object, string, Exception> LogAction;
private TouchSocketConfig config;
private string ip = "127.0.0.1";
private int port = 502;
/// <summary>
/// IP
/// </summary>
public string IP = "127.0.0.1";
/// <summary>
/// Port
/// </summary>
public int Port = 502;
private UdpSession UdpSession { get; set; } = new();
/// <inheritdoc/>
public void Dispose()
{
UdpSession.SafeDispose();
}
private void Connect()
{
try
@@ -68,32 +64,50 @@ public partial class UdpSessionPage : IDisposable
public UdpSession GetUdpSession()
{
config ??= new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
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.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>
protected override void OnInitialized()
public void Dispose()
{
config?.Dispose();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
config.SetRemoteIPHost(new IPHost(ip + ":" + port));
config.SetBindIPHost(new IPHost(0));
UdpSession = new UdpSession();
UdpSession.Setup(config);
base.OnInitialized();
UdpSession.SafeDispose();
}
internal void StateHasChangedAsync()
{
StateHasChanged();
}
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
}

View File

@@ -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();
}
}

View File

@@ -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>();
}

View File

@@ -11,9 +11,11 @@
#endregion
global using System;
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
global using TouchSocket.Core;
global using TouchSocket.Sockets;
global using ThingsGateway.Components;
global using ThingsGateway.Foundation.Core;
global using ThingsGateway.Foundation.Serial;
global using ThingsGateway.Foundation.Sockets;

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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();
}
}
}

View File

@@ -10,34 +10,18 @@
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Admin.Blazor
@using Masa.Blazor.Presets
@using ThingsGateway.Admin.Blazor.Core
@namespace ThingsGateway.Foundation.Demo
@using System.Text;
@inherits LayoutComponentBase
@layout BaseLayout
@if (UserManager.UserId > 0)
{
<SysSignalR></SysSignalR>
}
<PPageTabsProvider>
<CascadingValue Value="@this" IsFixed>
<CascadingValue Value="@Changed" Name="Changed">
<MNavigationDrawer Color="barcolor" @bind-Value="_drawerOpen" App Width="200">
@if (IsMobile)
{
<MSystemBar Color="barcolor" Height="@(BlazorResourceConst.PageTabsHeight)">
<MButton Icon OnClick=@(()=> _drawerOpen = !_drawerOpen)>
<MIcon>
mdi-close-thick
</MIcon>
</MButton>
<MSpacer />
<AppbarButtons />
</MSystemBar>
}
<Logo CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE HeightInt=@(IsMobile?BlazorResourceConst.AppBarHeight:BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight) />
<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" />
@@ -48,18 +32,16 @@
<MButton Class="mr-0" Icon Small=IsMobile OnClick=@(() => _drawerOpen = !_drawerOpen)>
<MIcon>mdi-menu</MIcon>
</MButton>
<AppBarItems CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE>
</AppBarItems>
</MAppBar>
<MMain Style=@($"{(!(IsMobile||_drawerOpen!=true)? "padding-left:200px;":"")}")>
<div class="full-width">
<PageTabs @ref="_pageTabs" />
<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" SelfPatterns="@selfPatterns">
<PPageContainer PageTabs="@_pageTabs?.PPageTabs">
@Body
</PPageContainer>
</MCard>
@@ -71,5 +53,25 @@
</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";
}

View File

@@ -0,0 +1,253 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
namespace ThingsGateway.Foundation.Demo;
public partial class MainLayout
{
private List<NavItem> Navs { get; set; } = new();
private List<PageTabItem> pageTabItems { get; set; } = new();
protected override void OnInitialized()
{
var dataString =
"""
[
{
"Href": "/index",
"Title": "首页"
},
{
"Title": "Modbus",
"Children": [
{
"Href": "/ModbusRtu",
"Title": "ModbusRtu"
},
{
"Href": "/ModbusTcp",
"Title": "ModbusTcp"
},
{
"Href": "/ModbusRtuOverTcp",
"Title": "ModbusRtuOverTcp"
},
{
"Href": "/ModbusRtuOverUdp",
"Title": "ModbusRtuOverUdp"
},
{
"Href": "/ModbusUdp",
"Title": "ModbusUdp"
},
{
"Href": "/ModbusTcpDtu",
"Title": "ModbusTcpDtu"
},
{
"Href": "/ModbusTcpServer",
"Title": "ModbusTcpServer"
},
{
"Href": "/ModbusSerialServer",
"Title": "ModbusSerialServer"
}
]
},
{
"Title": "Siemens",
"Children": [
{
"Href": "/S7_1500",
"Title": "S7_1500"
},
{
"Href": "/S7_1200",
"Title": "S7_1200"
},
{
"Href": "/S7_200",
"Title": "S7_200"
},
{
"Href": "/S7_200SMART",
"Title": "S7_200SMART"
},
{
"Href": "/S7_300",
"Title": "S7_400"
},
{
"Href": "/S7_400",
"Title": "S7_400"
}
]
},
{
"Title": "DLT645",
"Children": [
{
"Href": "/DLT645_2007",
"Title": "DLT645_2007"
},
{
"Href": "/DLT645_2007OverTcp",
"Title": "DLT645_2007OverTcp"
}
]
},
{
"Title": "OPCDA",
"Children": [
{
"Href": "/OPCDAClient",
"Title": "OPCDAClient"
}
]
},
{
"Title": "OPCUA",
"Children": [
{
"Href": "/OPCUAClient",
"Title": "OPCUAClient"
}
]
},
{
"Title": "Mqtt",
"Children": [
{
"Href": "/MqttClient",
"Title": "MqttClient"
}
]
}
]
""";
Navs = dataString.FromJsonString<List<NavItem>>();
#if Pro
var dataStringPro =
"""
[
{
"Title": "Melsec",
"Children": [
{
"Href": "/QnA3E_Binary",
"Title": "QnA3E_Binary"
}
]
},
{
"Title": "ABCIP",
"Children": [
{
"Href": "/ABCIPTCP",
"Title": "ABCIPTCP"
}
]
},
{
"Title": "Omron",
"Children": [
{
"Href": "/OmronFinsTcp",
"Title": "OmronFinsTcp"
},
{
"Href": "/OmronFinsUdp",
"Title": "OmronFinsUdp"
}
]
},
{
"Title": "Secs",
"Children": [
{
"Href": "/SecsTcp",
"Title": "SecsTcp"
}
]
},
{
"Title": "TS550",
"Children": [
{
"Href": "/TS550",
"Title": "TS550"
}
]
},
{
"Title": "Vigor",
"Children": [
{
"Href": "/VigorSerial",
"Title": "VigorSerial"
},
{
"Href": "/VigorSerialOverTcp",
"Title": "VigorSerialOverTcp"
}
]
},
{
"Title": "HZW_QTJC_01",
"Children": [
{
"Href": "/HZW_QTJC_01Serial",
"Title": "HZW_QTJC_01Serial"
},
{
"Href": "/HZW_QTJC_01SerialOverTcp",
"Title": "HZW_QTJC_01SerialOverTcp"
}
]
},
{
"Title": "LQTCP",
"Children": [
{
"Href": "/LQTCP",
"Title": "LQTCP"
}
]
},
{
"Title": "KELID2008",
"Children": [
{
"Href": "/KELID2008",
"Title": "KELID2008"
},
{
"Href": "/KELID2008OverTcp",
"Title": "KELID2008OverTcp"
}
]
},
]
""";
Navs.AddRange(dataStringPro.FromJsonString<List<NavItem>>());
#endif
pageTabItems = Navs.PasePageTabItem();
base.OnInitialized();
}
}

View File

@@ -0,0 +1,159 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
<DefineConstants>Pro</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro' AND '$(Configuration)' == 'Debug'">
<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 Condition="'$(SolutionName)'=='ThingsGateway - Pro' ">
<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.LQTCP\Page\LQTCPDebugPage.razor.cs" Link="Pages\LQTCP\LQTCPDebugPage.razor.cs" />
<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.LQTCP\Page\LQTCPDebugPage.razor" Link="Pages\LQTCP\LQTCPDebugPage.razor" />
<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.LQTCP\ThingsGateway.Foundation.Adapter.LQTCP.csproj" />
<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008DebugPage.razor.cs" Link="Pages\KELID2008\KELID2008DebugPage.razor.cs" />
<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008OverTcpDebugPage.razor.cs" Link="Pages\KELID2008\KELID2008OverTcpDebugPage.razor.cs" />
<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008DebugPage.razor" Link="Pages\KELID2008\KELID2008DebugPage.razor" />
<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.KELID2008\Page\KELID2008OverTcpDebugPage.razor" Link="Pages\KELID2008\KELID2008OverTcpDebugPage.razor" />
<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.KELID2008\ThingsGateway.Foundation.Adapter.KELID2008.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\MqttRpcNameVaueWithId.cs" Link="Pages\Mqtt\MqttRpcNameVaueWithId.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor.cs" Link="Pages\Mqtt\MqttClientDebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor.cs" Link="Pages\Mqtt\MqttClientPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClient.cs" Link="Pages\Mqtt\MqttRpcClient.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClientExtensions.cs" Link="Pages\Mqtt\MqttRpcClientExtensions.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcTopicPair.cs" Link="Pages\Mqtt\MqttRpcTopicPair.cs" />
<Compile Include="..\..\Web\ThingsGateway.Gateway.Application\Workers\ManageGateway\MqttLoggerExtensions.cs" Link="Pages\Mqtt\MqttLoggerExtensions.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.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" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007DebugPage.razor.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>
</Project>

View File

@@ -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;

View File

@@ -12,4 +12,5 @@
global using System;
global using TouchSocket.Core;
global using System.Windows.Forms;

View File

@@ -0,0 +1,77 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Drawing;
namespace ThingsGateway.Foundation.Demo.Winform
{
partial class MainFrom
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainFrom));
blazorWebView1 = new Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView();
SuspendLayout();
//
// blazorWebView1
//
blazorWebView1.Dock = DockStyle.Fill;
blazorWebView1.Location = new Point(0, 0);
blazorWebView1.Margin = new Padding(4);
blazorWebView1.Name = "blazorWebView1";
blazorWebView1.Size = new Size(1029, 529);
blazorWebView1.TabIndex = 0;
blazorWebView1.Text = "blazorWebView1";
//
// MainFrom
//
AutoScaleDimensions = new SizeF(9F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1029, 529);
Controls.Add(blazorWebView1);
Icon = (Icon)resources.GetObject("$this.Icon");
Margin = new Padding(4);
Name = "MainFrom";
Text = "Form1";
FormClosed += MainFrom_FormClosed;
ResumeLayout(false);
}
#endregion
private Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView blazorWebView1;
}
}

View File

@@ -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
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Extensions.DependencyInjection;
using ThingsGateway.Components;
namespace ThingsGateway.Foundation.Demo.Winform
{
public partial class MainFrom : Form
{
public MainFrom()
{
InitializeComponent();
var services = new ServiceCollection();
services.AddWindowsFormsBlazorWebView();
services.ThingsGatewayComponentsConfigureServices();
blazorWebView1.HostPage = "wwwroot/index.html";
blazorWebView1.Services = services.BuildServiceProvider();
this.Text = "ThingsGateway.Foundation.Demo";
blazorWebView1.RootComponents.Add<ThingsGateway.Foundation.Demo.App>("#app");
}
private void MainFrom_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}
}
}

View File

@@ -0,0 +1,197 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgvCjzYkwo82JsKPNifCjzYqwo82IcKPNgLCjzYAAAAAAAAA
AAAAAAAAAAAAAMKPNgDCjzYBwo82HsKPNknCjzZewo82QcKPNhLCjzYAwo82AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82S8KPNtPCjzbiwo8258KPNuDCjzatwo82EsKP
NgAAAAAAAAAAAAAAAADCjzYAwo82BsKPNmvCjzbbwo826MKPNtvCjzbhwo82vcKPNmDCjzYywo82AAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYPwo82O8KPNs7Cjzb/wo82iMKP
Nh7CjzYCwo82AAAAAAAAAAAAwo82AMKPNgDCjzZnwo8298KPNsLCjzY6wo82GsKPNjTCjzbJwo82/8KP
NpTCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYIwo82vsKP
Nv/CjzZowo82AAAAAAAAAAAAAAAAAAAAAADCjzYAwo82JMKPNtnCjzbxwo82QcKPNgDCjzYAwo82AMKP
NpHCjzb/wo82lsKPNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKP
NgjCjza9wo82/8KPNmjCjzYAAAAAAAAAAAAAAAAAwo82AMKPNgDCjzZ2wo82/8KPNrnCjzYKwo82AAAA
AADCjzYAwo82jcKPNv/CjzaWwo82AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADCjzYAwo82CMKPNr3Cjzb/wo82aMKPNgAAAAAAAAAAAAAAAADCjzYAwo82CcKPNrrCjzb/wo82fcKP
NgDCjzYAAAAAAMKPNgDCjzaLwo82/8KPNpbCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAMKPNgDCjzYIwo82vMKPNv/CjzZpwo82AAAAAAAAAAAAAAAAAMKPNgDCjzYfwo8238KP
Nv3CjzZRwo82AMKPNgDCjzYBwo82B8KPNpnCjzb/wo82psKPNgrCjzYAwo82AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgjCjza8wo82/8KPNmnCjzYAAAAAAAAAAAAAAAAAwo82AMKP
NjbCjzbxwo829sKPNj7CjzYAwo82AMKPNiPCjzaywo827cKPNv/Cjzbywo82qMKPNhPCjzYAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82CMKPNrvCjzb/wo82acKPNgAAAAAAAAAAAAAA
AADCjzYAwo82P8KPNvfCjzbzwo82OcKPNgDCjzYAwo82D8KPNlnCjzZiwo82X8KPNmDCjzZRwo82CcKP
NgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKPNgDCjzYHwo82usKPNv/CjzZpwo82AAAA
AAAAAAAAAAAAAMKPNgDCjzY+wo829sKPNvTCjzY8wo82AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgfCjza6wo82/8KP
NmnCjzYAAAAAAAAAAAAAAAAAwo82AMKPNirCjzbpwo82+MKPNkDCjzYAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82B8KP
NrnCjzb/wo82acKPNgAAAAAAAAAAAAAAAADCjzYAwo82FcKPNtLCjzb/wo82VsKPNgAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNgLCjzYTwo82BMKP
NgDCjzYHwo82t8KPNv/CjzZpwo82AMKPNgDCjzYMwo82E8KPNgDCjzYCwo82n8KPNv/CjzaEwo82AMKP
NgAAAAAAwo82AMKPNgDCjzZPwo82XcKPNgLCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82F8KP
NrTCjzY6wo82AMKPNgbCjza0wo82/8KPNmnCjzYAwo82AMKPNnPCjzaawo82A8KPNgDCjzZOwo82+MKP
NsbCjzYRwo82AAAAAADCjzYAwo82DcKPNsLCjzagwo82AMKPNgAAAAAAAAAAAAAAAAAAAAAAAAAAAMKP
NgDCjzYPwo82ysKPNpPCjzYBwo82BcKPNrHCjzb/wo82acKPNgDCjzYUwo82zMKPNpPCjzYAwo82AMKP
NgrCjzanwo82/MKPNmnCjzYAwo82AMKPNgDCjzZRwo8298KPNnbCjzYAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAwo82AMKPNgDCjzaawo827cKPNmHCjzYswo82vMKPNv/CjzaEwo82K8KPNoDCjzb5wo82ZMKP
NgAAAAAAwo82AMKPNi7CjzbWwo825sKPNlnCjzYdwo82SMKPNtTCjzb9wo82UsKPNgAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAwo82AMKPNmHCjzb3wo828cKPNt/Cjzbuwo8298KPNurCjzbjwo829MKP
NurCjzY6wo82AAAAAADCjzYAwo82AMKPNjfCjza8wo826cKPNtvCjzbewo82ycKPNs7CjzYvwo82AAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCjzYAwo82EsKPNj3CjzZAwo82QcKPNkDCjzY/wo82QMKP
NkDCjzY/wo82OMKPNgrCjzYAAAAAAAAAAADCjzYAwo82AMKPNg/CjzY5wo82SsKPNjDCjzYOwo82G8KP
NgXCjzYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////wH4H/8B8Af/AfAH/4f
hx/+H4cf/h8PH/4fDAf+HwwH/h8MB/4fD//+Hw///h8P/+IZD4/iGIcP4BGHH+ABwB/wAeAf8AHwH///
//////////////////////////////////8=
</value>
</data>
</root>

View File

@@ -0,0 +1,29 @@
#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.Winform
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new MainFrom());
}
}
}

View File

@@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<TargetFrameworks>net7.0-windows</TargetFrameworks>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<None Remove="favicon.ico" />
</ItemGroup>
<ItemGroup>
<Content Include="favicon.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="7.0.96" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\favicon.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\favicon.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -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>

View File

@@ -1,7 +1,12 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>2.1.0.9</Version>
<Version>3.0.1.0</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>
@@ -11,38 +16,38 @@
<EmbedAllSources>true</EmbedAllSources>
<RepositoryType>Gitee</RepositoryType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReadmeFile>./README.md</PackageReadmeFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageIcon>icon.png</PackageIcon>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl>
<PackageIcon>icon.png</PackageIcon>
<PackageTags>ThingsGateway;Diego;dotNET China;Blazor;设备采集;边缘网关</PackageTags>
<PackageOutputPath>../../../nupkgs</PackageOutputPath>
<SignAssembly>True</SignAssembly>
<DelaySign>False</DelaySign>
<AssemblyOriginatorKeyFile>..\..\..\..\snks\ThingsGateway.snk</AssemblyOriginatorKeyFile>
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
<PackageOutputPath>../../nupkgs</PackageOutputPath>
<AssemblyOriginatorKeyFile>../../../snks/ThingsGateway.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup>
<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\..\..\README.md" Pack="true" PackagePath="\" />
<None Include="..\..\..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\..\icon.png">
<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>

View File

@@ -1,4 +1,4 @@
#region copyright
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
@@ -10,38 +10,47 @@
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Foundation;
namespace ThingsGateway.Foundation.Adapter.DLT645;
/// <summary>
/// 资源枚举
/// 控制码
/// </summary>
public enum ThingsGatewayStatus : byte
public enum ControlCode : byte
{
/// <summary>
/// 数据转换失败
/// 数据
/// </summary>
[Description("数据转换失败")]
DataTransError,
Read = 0x11,
/// <summary>
/// 接收
/// 读后续数据
/// </summary>
[Description("接收")]
Received,
ReadSub = 0x12,
/// <summary>
/// 发送
/// 读站号
/// </summary>
[Description("发送")]
Send,
ReadStation = 0x13,
/// <summary>
/// 原始字节数据
/// 数据
/// </summary>
[Description("原始字节数据")]
SourceHexData,
Write = 0x14,
/// <summary>
/// 未知错误
/// 写站号
/// </summary>
[Description("未知错误")]
UnknownError,
WriteStation = 0x15,
/// <summary>
/// 广播校时
/// </summary>
BroadcastTime = 0x08,
/// <summary>
/// 冻结
/// </summary>
Freeze = 0x16,
/// <summary>
/// 更新波特率
/// </summary>
WriteBaudRate = 0x17,
/// <summary>
/// 更新密码
/// </summary>
WritePassword = 0x18,
}

View File

@@ -10,11 +10,8 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Text;
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.DLT645;
@@ -38,11 +35,22 @@ public class DataInfo
}
internal static class DLT645Helper
{
internal static byte[] BytesAdd(this byte[] bytes, int value)
internal static string Get2007ErrorMessage(byte buffer)
{
for (int index = 0; index < bytes.Length; ++index)
bytes[index] = (byte)(bytes[index] + value);
return bytes;
string error = buffer switch
{
0x80 => "其他错误",
0x40 => "费率数超",
0x20 => "日时段数超",
0x10 => "年时区数超",
0x08 => "通信速率不能更改",
0x04 => "密码错/未授权",
0x02 => "无请求数据",
_ => "其他错误",
};
return error;
}
/// <summary>
@@ -1134,7 +1142,6 @@ internal static class DLT645Helper
/// </summary>
internal static OperResult<byte[]> GetDLT645_2007Command(
string address,
int length,
byte control,
string defaultStation,
byte[] codes = null,
@@ -1145,12 +1152,8 @@ internal static class DLT645Helper
{
codes ??= new byte[0];
datas ??= new string[0];
var operResult = DLT645_2007Address.ParseFrom(address, length);
if (!operResult.IsSuccess)
{
return operResult.Copy<byte[]>();
}
var buffer = operResult.Content.DataId;
var operResult = DLT645_2007Address.ParseFrom(address);
var buffer = operResult.DataId;
if (buffer.Length > 0)
{
var dataInfos = GetDataInfos(buffer);
@@ -1210,7 +1213,7 @@ internal static class DLT645Helper
byte[] stationBytes;
if (operResult.Content.Station.Length == 0)
if (operResult.Station.Length == 0)
{
if (defaultStation.IsNullOrEmpty()) defaultStation = string.Empty;
if (defaultStation.Length < 12)
@@ -1219,7 +1222,7 @@ internal static class DLT645Helper
}
else
{
stationBytes = operResult.Content.Station;
stationBytes = operResult.Station;
}
return GetDLT645_2007Command(control, buffer, stationBytes);
@@ -1231,25 +1234,6 @@ internal static class DLT645Helper
}
}
internal static string Get2007ErrorMessage(byte buffer)
{
string error = buffer switch
{
0x80 => "其他错误",
0x40 => "费率数超",
0x20 => "日时段数超",
0x10 => "年时区数超",
0x08 => "通信速率不能更改",
0x04 => "密码错/未授权",
0x02 => "无请求数据",
_ => "其他错误",
};
return error;
}
internal static OperResult<byte[]> GetDLT645_2007Command(byte control, byte[] buffer, byte[] stationBytes)
{
buffer ??= new byte[0];

View File

@@ -12,45 +12,30 @@
using System.ComponentModel;
using ThingsGateway.Foundation.Extension.Byte;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.DLT645;
/// <summary>
/// DLT645_2007
/// </summary>
public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
public class DLT645_2007 : ReadWriteDevicesSerialSessionBase
{
/// <summary>
/// DLT645_2007
/// </summary>
/// <param name="tcpClient"></param>
public DLT645_2007OverTcp(TcpClientEx tcpClient) : base(tcpClient)
/// <param name="serialSession"></param>
public DLT645_2007(SerialSession serialSession) : base(serialSession)
{
ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// 组包缓存时间/ms
/// </summary>
[Description("组包缓存时间ms")]
public int CacheTimeout { get; set; } = 1000;
/// <summary>
/// 增加FE FE FE FE的报文头部
/// </summary>
[Description("前导符报文头")]
public bool EnableFEHead { get; set; }
/// <summary>
/// 帧前时间ms
/// </summary>
[Description("帧前时间ms")]
public int FrameTime { get; set; }
/// <summary>
/// 写入需操作员代码
/// </summary>
@@ -69,22 +54,68 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
[Description("通讯地址")]
public string Station { get; set; }
/// <inheritdoc/>
public override string GetAddressDescription() => base.GetAddressDescription() + Environment.NewLine;
public override string GetAddressDescription()
{
var str = """
-----------------------------------------
02010100 A相电压
02020100 A相电流
02030000
00000000 ()
00010000 ()
""";
return base.GetAddressDescription() + Environment.NewLine + str;
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
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
{
await ConnectAsync(token);
var commandResult = DLT645Helper.GetDLT645_2007Command(address, length, (byte)ControlCode.Read, Station);
Connect(cancellationToken);
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
if (commandResult.IsSuccess)
{
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, token);
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
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)
@@ -93,23 +124,22 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
}
/// <inheritdoc/>
public override void SetDataAdapter()
public override void SetDataAdapter(object socketClient = null)
{
var dataHandleAdapter = new DLT645_2007DataHandleAdapter
{
EnableFEHead = EnableFEHead
};
TcpClientEx.SetDataHandlingAdapter(dataHandleAdapter);
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken token = default)
public override OperResult Write(string address, string value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
Connect(cancellationToken);
Password ??= string.Empty;
OperCode ??= string.Empty;
if (Password.Length < 8)
@@ -118,15 +148,15 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
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, 1, (byte)ControlCode.Write, Station, data, strArray);
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
if (commandResult.IsSuccess)
{
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, token);
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -134,34 +164,67 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, byte value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, double value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, float value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, long value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, short value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, int value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
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
@@ -170,23 +233,23 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
/// 广播校时
/// </summary>
/// <param name="dateTime"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public OperResult BroadcastTime(DateTime dateTime, CancellationToken token = default)
public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default)
{
try
{
Connect(token);
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());
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.BroadcastTime, str.ByHexStringToBytes().ToArray(), "999999999999".ByHexStringToBytes());
if (commandResult.IsSuccess)
{
TcpClientEx.Send(commandResult.Content);
SerialSession.Send(commandResult.Content);
return OperResult.CreateSuccessResult();
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -200,20 +263,20 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
/// 冻结
/// </summary>
/// <param name="dateTime"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken token = default)
public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
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());
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, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -222,12 +285,12 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -237,20 +300,22 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
/// <summary>
/// 读取通信地址
/// </summary>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken token = default)
public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
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, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -259,12 +324,12 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult<string>(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult<string>(commandResult);
}
}
catch (Exception ex)
@@ -275,17 +340,18 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
/// <summary>
/// 修改波特率
/// </summary>
/// <param name="baudRate"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken token = default)
public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
await ConnectAsync(cancellationToken);
byte baudRateByte;
switch (baudRate)
{
@@ -302,7 +368,7 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
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, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -310,12 +376,12 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -329,17 +395,17 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
/// 更新通信地址
/// </summary>
/// <param name="station"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken token = default)
public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
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, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -348,12 +414,12 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -368,17 +434,17 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
/// <param name="level"></param>
/// <param name="oldPassword"></param>
/// <param name="newPassword"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken token = default)
public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
await ConnectAsync(cancellationToken);
if (Station.IsNullOrEmpty()) Station = string.Empty;
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
string str = $"04000C{level:D2}";
string str = $"04000C{level + 1:D2}";
var commandResult = DLT645Helper.GetDLT645_2007Command((byte)ControlCode.WritePassword,
str.ByHexStringToBytes().Reverse().ToArray()
@@ -387,7 +453,7 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
, Station.ByHexStringToBytes().Reverse().ToArray());
if (commandResult.IsSuccess)
{
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -395,12 +461,12 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -409,8 +475,6 @@ public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
}
}
#endregion
}

View File

@@ -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();
}
}

View File

@@ -10,10 +10,8 @@
//------------------------------------------------------------------------------
#endregion
using System.Text;
using ThingsGateway.Foundation.Extension.Byte;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.DLT645;
@@ -40,6 +38,19 @@ public class DLT645_2007BitConverter : ThingsGatewayBitConverter
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)
@@ -50,7 +61,7 @@ public class DLT645_2007BitConverter : ThingsGatewayBitConverter
/// <inheritdoc/>
public override string ToString(byte[] buffer, int offset, int length)
{
buffer = buffer.SelectMiddle(offset, length);
buffer = buffer.RemoveBegin(offset);
buffer = buffer.BytesAdd(-0x33);
var dataInfos = DLT645Helper.GetDataInfos(buffer);
StringBuilder stringBuilder = new();
@@ -65,11 +76,11 @@ public class DLT645_2007BitConverter : ThingsGatewayBitConverter
content[0] = (byte)(content[0] - 0x80);
if (dataInfo.Digtal == 0)//无小数点
{
stringBuilder.AppendLine($"-{content.ToHexString()}");
stringBuilder.Append($"-{content.ToHexString()}");
}
else
{
stringBuilder.AppendLine((-(Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
stringBuilder.Append((-(Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
}
}
else
@@ -89,15 +100,15 @@ public class DLT645_2007BitConverter : ThingsGatewayBitConverter
{
if (dataInfo.Digtal < 0)
{
stringBuilder.AppendLine($"{Encoding.ASCII.GetString(content)}");
stringBuilder.Append($"{Encoding.ASCII.GetString(content)}");
}
else if (dataInfo.Digtal == 0)//无小数点
{
stringBuilder.AppendLine($"{content.ToHexString()}");
stringBuilder.Append($"{content.ToHexString()}");
}
else
{
stringBuilder.AppendLine(((Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
stringBuilder.Append(((Convert.ToDouble(content.ToHexString()) / Math.Pow(10.0, dataInfo.Digtal))).ToString());
}
}
}

View File

@@ -12,7 +12,6 @@
using System.ComponentModel;
using ThingsGateway.Foundation.Adapter.Modbus;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.DLT645;
@@ -97,7 +96,7 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
{
//校验错误
request.Message = "和校验错误";
request.ResultCode = ResultCode.Fail;
request.ErrorCode = 999;
return FilterResult.Success;
}
@@ -112,12 +111,12 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
{
if (
(response[headCodeIndex + 1] == 0xAA) &&
(response[headCodeIndex + 2] == 0xAA) &&
(response[headCodeIndex + 3] == 0xAA) &&
(response[headCodeIndex + 4] == 0xAA) &&
(response[headCodeIndex + 5] == 0xAA) &&
(response[headCodeIndex + 6] == 0xAA)
(send[sendHeadCodeIndex + 1] == 0xAA) &&
(send[sendHeadCodeIndex + 2] == 0xAA) &&
(send[sendHeadCodeIndex + 3] == 0xAA) &&
(send[sendHeadCodeIndex + 4] == 0xAA) &&
(send[sendHeadCodeIndex + 5] == 0xAA) &&
(send[sendHeadCodeIndex + 6] == 0xAA)
)//读写通讯地址例外
{
@@ -125,7 +124,7 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
else
{
request.Message = "返回地址不符合规则";
request.ResultCode = ResultCode.Fail;
request.ErrorCode = 999;
return FilterResult.Success;
}
@@ -135,8 +134,8 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
if ((response[headCodeIndex + 8] != send[sendHeadCodeIndex + 8] + 0x80))//控制码不符合时,返回错误
{
request.Message = "返回控制码:" + $"0x{response[headCodeIndex + 8]:X2},请求控制码:" + $"0x{send[sendHeadCodeIndex + 8]:X2},不符合规则";
request.ResultCode = ResultCode.Fail;
request.Message = $"返回控制码0x{response[headCodeIndex + 8]:X2}请求控制码0x{send[sendHeadCodeIndex + 8]:X2},不符合规则";
request.ErrorCode = 999;
return FilterResult.Success;
}
@@ -147,8 +146,8 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
{
byte byte1 = (byte)(response[headCodeIndex + 10] - 0x33);
var error = DLT645Helper.Get2007ErrorMessage(byte1);
request.Message = "异常控制码:" + $"0x{response[headCodeIndex + 8]:X2},错误信息:{error}";
request.ResultCode = ResultCode.Fail;
request.Message = $"异常控制码0x{response[headCodeIndex + 8]:X2},错误信息:{error}";
request.ErrorCode = 999;
return FilterResult.Success;
}
@@ -169,7 +168,7 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
else
{
request.Message = "返回数据标识不符合规则";
request.ResultCode = ResultCode.Fail;
request.ErrorCode = 999;
return FilterResult.Success;
}
@@ -177,13 +176,13 @@ public class DLT645_2007DataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter
request.Content = response.RemoveBegin(headCodeIndex + 10).RemoveLast(response.Length + 2 - len - headCodeIndex);
request.ResultCode = ResultCode.Success;
request.ErrorCode = 0;
return FilterResult.Success;
}
else
{
request.ResultCode = ResultCode.Error;
request.ErrorCode = 999;
return FilterResult.Success;
}

View File

@@ -10,7 +10,7 @@
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.Modbus;
namespace ThingsGateway.Foundation.Adapter.DLT645;
/// <summary>
/// <inheritdoc/>
/// </summary>
@@ -20,15 +20,12 @@ public class DLT645_2007Message : MessageBase, IMessage
public override int HeadBytesLength => -1;
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[] head)
public override bool CheckHeadBytes(byte[] heads)
{
BodyLength = -1;
return true;
}
/// <inheritdoc/>
protected override void SendBytesThen()
{
}
}

View File

@@ -12,46 +12,30 @@
using System.ComponentModel;
using ThingsGateway.Foundation.Extension.Byte;
using ThingsGateway.Foundation.Extension.Generic;
using ThingsGateway.Foundation.Serial;
namespace ThingsGateway.Foundation.Adapter.DLT645;
/// <summary>
/// DLT645_2007
/// </summary>
public class DLT645_2007 : ReadWriteDevicesSerialBase
public class DLT645_2007OverTcp : ReadWriteDevicesTcpClientBase
{
/// <summary>
/// DLT645_2007
/// </summary>
/// <param name="serialSession"></param>
public DLT645_2007(SerialsSession serialSession) : base(serialSession)
/// <param name="tcpClient"></param>
public DLT645_2007OverTcp(TcpClient tcpClient) : base(tcpClient)
{
ThingsGatewayBitConverter = new DLT645_2007BitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// 组包缓存时间/ms
/// </summary>
[Description("组包缓存时间ms")]
public int CacheTimeout { get; set; } = 1000;
/// <summary>
/// 增加FE FE FE FE的报文头部
/// </summary>
[Description("前导符报文头")]
public bool EnableFEHead { get; set; }
/// <summary>
/// 帧前时间ms
/// </summary>
[Description("帧前时间ms")]
public int FrameTime { get; set; }
/// <summary>
/// 写入需操作员代码
/// </summary>
@@ -89,20 +73,49 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
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
{
await ConnectAsync(token);
var commandResult = DLT645Helper.GetDLT645_2007Command(address, length, (byte)ControlCode.Read, Station);
Connect(cancellationToken);
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Read, Station);
if (commandResult.IsSuccess)
{
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, token);
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
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)
@@ -111,23 +124,22 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
}
/// <inheritdoc/>
public override void SetDataAdapter()
public override void SetDataAdapter(object socketClient = null)
{
var dataHandleAdapter = new DLT645_2007DataHandleAdapter
{
EnableFEHead = EnableFEHead
};
SerialsSession.SetDataHandlingAdapter(dataHandleAdapter);
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, string value, CancellationToken token = default)
public override OperResult Write(string address, string value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
Connect(cancellationToken);
Password ??= string.Empty;
OperCode ??= string.Empty;
if (Password.Length < 8)
@@ -136,15 +148,15 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
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, 1, (byte)ControlCode.Write, Station, data, strArray);
var commandResult = DLT645Helper.GetDLT645_2007Command(address, (byte)ControlCode.Write, Station, data, strArray);
if (commandResult.IsSuccess)
{
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, token);
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -152,34 +164,67 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, uint value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, byte value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, double value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, float value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, long value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, ulong value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, ushort value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, short value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, int value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default) => Write(address, value.ToString(), cancellationToken);
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default) => WriteAsync(address, value.ToString(), token);
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
@@ -188,23 +233,23 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
/// 广播校时
/// </summary>
/// <param name="dateTime"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public OperResult BroadcastTime(DateTime dateTime, CancellationToken token = default)
public OperResult BroadcastTime(DateTime dateTime, CancellationToken cancellationToken = default)
{
try
{
Connect(token);
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)
{
SerialsSession.Send(commandResult.Content);
TcpClient.Send(commandResult.Content);
return OperResult.CreateSuccessResult();
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -218,20 +263,20 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
/// 冻结
/// </summary>
/// <param name="dateTime"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken token = default)
public async Task<OperResult> FreezeAsync(DateTime dateTime, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
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, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -240,12 +285,12 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -255,20 +300,22 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
/// <summary>
/// 读取通信地址
/// </summary>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken token = default)
public async Task<OperResult<string>> ReadDeviceStationAsync(CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
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, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -277,12 +324,12 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult<string>(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult<string>(commandResult);
}
}
catch (Exception ex)
@@ -293,17 +340,18 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
/// <summary>
/// 修改波特率
/// </summary>
/// <param name="baudRate"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken token = default)
public async Task<OperResult> WriteBaudRateAsync(int baudRate, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
await ConnectAsync(cancellationToken);
byte baudRateByte;
switch (baudRate)
{
@@ -320,7 +368,7 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
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, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -328,12 +376,12 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -347,17 +395,17 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
/// 更新通信地址
/// </summary>
/// <param name="station"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken token = default)
public async Task<OperResult> WriteDeviceStationAsync(string station, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
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, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -366,12 +414,12 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -386,13 +434,13 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
/// <param name="level"></param>
/// <param name="oldPassword"></param>
/// <param name="newPassword"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken token = default)
public async Task<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(token);
await ConnectAsync(cancellationToken);
if (Station.IsNullOrEmpty()) Station = string.Empty;
if (Station.Length < 12) Station = Station.PadLeft(12, '0');
@@ -405,7 +453,7 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
, Station.ByHexStringToBytes().Reverse().ToArray());
if (commandResult.IsSuccess)
{
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, token);
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
var result1 = ((MessageBase)result.RequestInfo);
if (result1.IsSuccess)
{
@@ -413,12 +461,12 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
else
{
return OperResult.CreateFailedResult<string>(result1);
return new OperResult(result1);
}
}
else
{
return OperResult.CreateFailedResult<string>(commandResult);
return new OperResult(commandResult);
}
}
catch (Exception ex)
@@ -427,50 +475,6 @@ public class DLT645_2007 : ReadWriteDevicesSerialBase
}
}
#endregion
}
/// <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,
}

View File

@@ -10,19 +10,14 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
namespace ThingsGateway.Foundation.Adapter.DLT645;
using ThingsGateway.Application;
using ThingsGateway.Foundation;
namespace ThingsGateway.DLT645;
internal static class DLT645_2007Helper
internal static class PackHelper
{
internal static List<DeviceVariableSourceRead> LoadSourceRead(this List<DeviceVariableRunTime> deviceVariables, IReadWriteDevice device)
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<DeviceVariableSourceRead>();
var result = new List<T>();
//需要先剔除额外信息比如dataformat等
foreach (var item in deviceVariables)
{
@@ -30,13 +25,13 @@ internal static class DLT645_2007Helper
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
item.ThingsGatewayBitConverter = transformParameter;
item.VariableAddress = address;
//item.VariableAddress = address;
item.Index = device.GetBitOffset(item.VariableAddress);
result.Add(new()
{
DeviceVariables = new() { item },
Address = address,
DeviceVariableRunTimes = new() { item },
VariableAddress = address,
Length = 1,
});
}

View File

@@ -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;

View File

@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
</Project>

View File

@@ -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;

View File

@@ -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",
};
}
}

View File

@@ -13,7 +13,6 @@
using System.Text;
using ThingsGateway.Foundation.Extension.Bool;
using ThingsGateway.Foundation.Extension.Byte;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus;
@@ -92,9 +91,17 @@ internal class ModbusHelper
if (response[1] >= 0x80)//错误码
return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
if ((response.Length < response[2] + 3))
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
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)
{
@@ -110,7 +117,7 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[], FilterResult>(ex.Message) { Content2 = FilterResult.Success };
return new OperResult<byte[], FilterResult>(ex) { Content2 = FilterResult.Success };
}
}
/// <summary>
@@ -127,11 +134,21 @@ internal class ModbusHelper
if (response[1] >= 0x80)//错误码
return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
if ((response.Length < response[2] + 5))
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
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] + 5);
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));
@@ -143,12 +160,12 @@ internal class ModbusHelper
{
try
{
ModbusAddress mAddress = new(address, station);
return GetReadModbusCommand(mAddress, length);
var mAddress = ModbusAddress.ParseFrom(address, station);
return OperResult.CreateSuccessResult(GetReadModbusCommand(mAddress, length));
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
/// <summary>
@@ -158,7 +175,8 @@ internal class ModbusHelper
{
try
{
ModbusAddress mAddress = new(address, station);
var mAddress = ModbusAddress.ParseFrom(address, station);
//功能码或实际长度
if (values?.Length > 1 || mAddress.WriteFunction == 15)
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
@@ -167,7 +185,7 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
@@ -178,23 +196,24 @@ internal class ModbusHelper
{
try
{
ModbusAddress mAddress = new(address, station);
var mAddress = ModbusAddress.ParseFrom(address, station);
//功能码或实际长度
if (value?.Length > 2 || mAddress.WriteFunction == 16)
return GetWriteModbusCommand(mAddress, value);
return OperResult.CreateSuccessResult(GetWriteModbusCommand(mAddress, value));
else
return GetWriteOneModbusCommand(mAddress, value);
return OperResult.CreateSuccessResult(GetWriteOneModbusCommand(mAddress, value));
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
/// <summary>
/// 获取读取报文
/// </summary>
private static OperResult<byte[]> GetReadModbusCommand(ModbusAddress mAddress, int length)
internal static byte[] GetReadModbusCommand(ModbusAddress mAddress, int length)
{
byte[] array = new byte[6]
{
@@ -205,34 +224,34 @@ internal class ModbusHelper
BitConverter.GetBytes(length)[1],
BitConverter.GetBytes(length)[0]
};
return OperResult.CreateSuccessResult(array);
return array;
}
/// <summary>
/// 获取05写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
{
try
{
if (address.IndexOf('.') <= 0)
{
ModbusAddress mAddress = new(address, station);
return GetWriteBoolModbusCommand(mAddress, value);
var mAddress = ModbusAddress.ParseFrom(address, station);
return OperResult.CreateSuccessResult(GetWriteBoolModbusCommand(mAddress, value));
}
return new("不支持写入字寄存器的某一位");
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
/// <summary>
/// 获取05写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
private static byte[] GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
{
byte[] array = new byte[6]
{
@@ -253,13 +272,13 @@ internal class ModbusHelper
array[4] = 0;
array[5] = 0;
}
return OperResult.CreateSuccessResult(array);
return array;
}
/// <summary>
/// 获取15写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
internal static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
{
try
{
@@ -277,14 +296,14 @@ internal class ModbusHelper
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
/// <summary>
/// 获取16写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
internal static byte[] GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
{
byte[] numArray = new byte[7 + values.Length];
numArray[0] = (byte)mAddress.Station;
@@ -295,14 +314,13 @@ internal class ModbusHelper
numArray[5] = (byte)(values.Length / 2 % 256);
numArray[6] = (byte)values.Length;
values.CopyTo(numArray, 7);
return OperResult.CreateSuccessResult(numArray);
return numArray;
}
/// <summary>
/// 获取6写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
internal static byte[] GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
{
byte[] numArray = new byte[4 + values.Length];
numArray[0] = (byte)mAddress.Station;
@@ -310,7 +328,7 @@ internal class ModbusHelper
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
values.CopyTo(numArray, 4);
return OperResult.CreateSuccessResult(numArray);
return numArray;
}
}

View File

@@ -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);
}
}
}

View File

@@ -10,6 +10,7 @@
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus;
@@ -42,20 +43,43 @@ public class ModbusRtuDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<M
/// <inheritdoc/>
protected override FilterResult UnpackResponse(ModbusRtuMessage request, byte[] send, byte[] body, byte[] response)
{
//理想状态检测
var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
if (result.IsSuccess)
//链路干扰时需剔除前缀中的多于字节,初步按站号+功能码找寻初始字节
if (send?.Length > 0)
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content1;
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
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
return FilterResult.Success;
}
return result.Content2;
}
}

View File

@@ -20,15 +20,12 @@ public class ModbusRtuMessage : MessageBase, IMessage
public override int HeadBytesLength => -1;
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[] head)
public override bool CheckHeadBytes(byte[] heads)
{
BodyLength = -1;
return true;
}
/// <inheritdoc/>
protected override void SendBytesThen()
{
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -10,8 +10,6 @@
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Extension;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <summary>
@@ -37,10 +35,9 @@ public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAd
}
/// <inheritdoc/>
protected override OperResult<byte[]> UnpackResponse(
byte[] send, byte[] response)
protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
{
var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
return result.Copy();
return result;
}
}

View File

@@ -0,0 +1,452 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Concurrent;
using ThingsGateway.Foundation.Extension.Bool;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
public class ModbusSerialServer : ReadWriteDevicesSerialSessionBase
{
/// <summary>
/// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
/// </summary>
public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SerialSession, Task<OperResult>> WriteData;
/// <summary>
/// 继电器
/// </summary>
private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
/// <summary>
/// 开关输入
/// </summary>
private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
/// <summary>
/// 输入寄存器
/// </summary>
private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
/// <summary>
/// 保持寄存器
/// </summary>
private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
/// <inheritdoc/>
public ModbusSerialServer(SerialSession serialSession) : base(serialSession)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// 多站点
/// </summary>
public bool MulStation { get; set; }
/// <summary>
/// 默认站点
/// </summary>
public byte Station { get; set; } = 1;
/// <inheritdoc/>
public override void Dispose()
{
foreach (var item in ModbusServer01ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer02ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer03ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer04ByteBlocks)
{
item.Value.SafeDispose();
}
ModbusServer01ByteBlocks.Clear();
ModbusServer02ByteBlocks.Clear();
ModbusServer03ByteBlocks.Clear();
ModbusServer04ByteBlocks.Clear();
base.Dispose();
}
/// <inheritdoc/>
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
/// <inheritdoc/>
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
{
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
EasyLock easyLock = new();
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
try
{
easyLock.Wait();
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return new OperResult<byte[]>("地址错误");
}
Init(mAddress);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
switch (mAddress.ReadFunction)
{
case 1:
byte[] bytes0 = new byte[len];
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Read(bytes0);
return OperResult.CreateSuccessResult(bytes0);
case 2:
byte[] bytes1 = new byte[len];
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Read(bytes1);
return OperResult.CreateSuccessResult(bytes1);
case 3:
byte[] bytes3 = new byte[len];
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Read(bytes3);
return OperResult.CreateSuccessResult(bytes3);
case 4:
byte[] bytes4 = new byte[len];
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Read(bytes4);
return OperResult.CreateSuccessResult(bytes4);
}
return new OperResult<byte[]>("功能码错误");
}
finally
{
easyLock.Release();
}
}
/// <inheritdoc/>
public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
return Task.FromResult(Read(address, length));
}
/// <inheritdoc/>
public override void SetDataAdapter(object socketClient)
{
ModbusSerialServerDataHandleAdapter dataHandleAdapter = new();
dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
easyLock.Wait();
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return new OperResult(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return new OperResult("地址错误");
}
Init(mAddress);
}
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 3:
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
case 4:
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
}
return new OperResult("功能码错误");
}
finally
{
easyLock.Release();
}
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
easyLock.Wait();
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return (new OperResult(ex));
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return (new OperResult("地址错误"));
}
Init(mAddress);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 1:
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
case 2:
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
}
return new OperResult("功能码错误");
}
finally
{
easyLock.Release();
}
}
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
return Task.FromResult(Write(address, value));
}
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
return Task.FromResult(Write(address, value));
}
/// <inheritdoc/>
protected override async Task Received(SerialSession client, ReceivedDataEventArgs e)
{
try
{
var requestInfo = e.RequestInfo;
//接收外部报文
if (requestInfo is ModbusSerialServerMessage modbusServerMessage)
{
if (modbusServerMessage.CurModbusAddress == null)
{
return;//无法解析直接返回
}
if (!modbusServerMessage.IsSuccess)
{
return;//无法解析直接返回
}
if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
{
var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
if (data.IsSuccess)
{
var coreData = data.Content;
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
{
coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
}
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
SerialSession.Send(sendData);
}
else
{
WriteError(SerialSession, modbusServerMessage);//返回错误码
}
}
else//写入
{
var coreData = modbusServerMessage.Content;
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
{
//写入继电器
if (WriteData != null)
{
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
{
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
if (result.IsSuccess)
{
WriteSuccess03(SerialSession, modbusServerMessage);
}
else
{
WriteError(SerialSession, modbusServerMessage);
}
}
else
{
WriteError(SerialSession, modbusServerMessage);
}
}
else
{
//写入内存区
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
if (result.IsSuccess)
{
WriteSuccess03(SerialSession, modbusServerMessage);
}
else
{
WriteError(SerialSession, modbusServerMessage);
}
}
}
else
{
//写入寄存器
if (WriteData != null)
{
if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, SerialSession)).IsSuccess)
{
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
if (result.IsSuccess)
{
WriteSuccess03(SerialSession, modbusServerMessage);
}
else
{
WriteError(SerialSession, modbusServerMessage);
}
}
else
{
WriteError(SerialSession, modbusServerMessage);
}
}
else
{
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
if (result.IsSuccess)
{
WriteSuccess03(SerialSession, modbusServerMessage);
}
else
{
WriteError(SerialSession, modbusServerMessage);
}
}
}
}
}
}
catch (Exception ex)
{
Logger.LogError(ex, ToString());
}
//返回错误码
static void WriteError(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 2)
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
sendData[1] = (byte)(sendData[1] + 128);
SerialSession.Send(sendData);
}
}
private static void WriteSuccess03(SerialSession SerialSession, ModbusSerialServerMessage modbusServerMessage)
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 6);
SerialSession.Send(sendData);
}
private void Init(ModbusAddress mAddress)
{
ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -55,13 +55,13 @@ public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<M
var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
if (result.IsSuccess)
{
request.ResultCode = result.ResultCode;
request.ErrorCode = result.ErrorCode;
request.Message = result.Message;
request.Content = result.Content1;
request.Content = result.Content;
}
else
{
request.ResultCode = result.ResultCode;
request.ErrorCode = result.ErrorCode;
request.Message = result.Message;
}
return result.Content2;

View File

@@ -23,10 +23,10 @@ public class ModbusTcpMessage : MessageBase, IMessage
/// </summary>
public bool IsCheckMessageId { get; set; } = false;
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[] head)
public override bool CheckHeadBytes(byte[] heads)
{
if (head == null || head.Length <= 0) return false;
HeadBytes = head;
if (heads == null || heads.Length <= 0) return false;
HeadBytes = heads;
int num = (HeadBytes[4] * 256) + HeadBytes[5];
BodyLength = num;

View File

@@ -0,0 +1,249 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <inheritdoc/>
public class ModbusTcpDtu : ReadWriteDevicesTcpServerBase
{
/// <inheritdoc/>
public ModbusTcpDtu(TcpService tcpService) : base(tcpService)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
ModbusTcpDtuPlugin modbusTcpSalvePlugin = new ModbusTcpDtuPlugin();
tcpService.Config.ConfigurePlugins(a =>
{
a.Add(modbusTcpSalvePlugin);
});
tcpService.Setup(tcpService.Config);
}
/// <summary>
/// 检测事务标识符
/// </summary>
[Description("检测事务标识符")]
public bool IsCheckMessageId { get; set; }
/// <summary>
/// 站号
/// </summary>
[Description("站号")]
public byte Station { get; set; } = 1;
/// <inheritdoc/>
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
/// <inheritdoc/>
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
{
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var mAddress = ModbusAddress.ParseFrom(address, Station);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var mAddress = ModbusAddress.ParseFrom(address, Station);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override void SetDataAdapter(object socketClient = null)
{
if (socketClient is SocketClient client)
{
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
{
IsCheckMessageId = IsCheckMessageId,
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
client.SetDataHandlingAdapter(dataHandleAdapter);
}
else
{
foreach (var item in TcpService.GetClients())
{
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
{
IsCheckMessageId = IsCheckMessageId,
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
item.SetDataHandlingAdapter(dataHandleAdapter);
}
}
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var mAddress = ModbusAddress.ParseFrom(address, Station);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var mAddress = ModbusAddress.ParseFrom(address, Station);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return SendThenReturn(mAddress.SocketId, commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var mAddress = ModbusAddress.ParseFrom(address, Station);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var mAddress = ModbusAddress.ParseFrom(address, Station);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return await SendThenReturnAsync(mAddress.SocketId, commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
private OperResult<byte[]> SendThenReturn(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
if (TcpService.TryGetSocketClient($"ID={id}", out var client))
{
SetDataAdapter(client);
var item = commandResult.Content;
if (FrameTime != 0)
Thread.Sleep(FrameTime);
var WaitingClientEx = client.CreateWaitingClient(new() { });
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>("客户端未连接");
}
}
else
{
return commandResult;
}
}
private async Task<OperResult<byte[]>> SendThenReturnAsync(string id, OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
if (TcpService.TryGetSocketClient($"ID={id}", out var client))
{
SetDataAdapter(client);
var item = commandResult.Content;
await Task.Delay(FrameTime, cancellationToken);
var WaitingClientEx = client.CreateWaitingClient(new() { });
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>("客户端未连接");
}
}
else
{
return commandResult;
}
}
internal class ModbusTcpDtuPlugin : PluginBase, ITcpReceivingPlugin
{
public Task OnTcpReceiving(ITcpClientBase client, ByteBlockEventArgs e)
{
if (client is ISocketClient socket)
{
if (!socket.Id.StartsWith("ID="))
{
ByteBlock byteBlock = e.ByteBlock;
var id = $"ID={byteBlock.ToArray().ToHexString()}";
socket.ResetId(id);
}
}
return e.InvokeNext();//如果本插件无法处理当前数据,请将数据转至下一个插件。
}
}
}

View File

@@ -0,0 +1,470 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Concurrent;
using System.ComponentModel;
using ThingsGateway.Foundation.Extension.Bool;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
public class ModbusTcpServer : ReadWriteDevicesTcpServerBase
{
/// <summary>
/// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
/// </summary>
public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<OperResult>> WriteData;
/// <summary>
/// 继电器
/// </summary>
private ConcurrentDictionary<byte, ByteBlock> ModbusServer01ByteBlocks = new();
/// <summary>
/// 开关输入
/// </summary>
private ConcurrentDictionary<byte, ByteBlock> ModbusServer02ByteBlocks = new();
/// <summary>
/// 输入寄存器
/// </summary>
private ConcurrentDictionary<byte, ByteBlock> ModbusServer03ByteBlocks = new();
/// <summary>
/// 保持寄存器
/// </summary>
private ConcurrentDictionary<byte, ByteBlock> ModbusServer04ByteBlocks = new();
/// <inheritdoc/>
public ModbusTcpServer(TcpService tcpService) : base(tcpService)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// 多站点
/// </summary>
[Description("多站点")]
public bool MulStation { get; set; }
/// <summary>
/// 默认站点
/// </summary>
[Description("默认站点")]
public byte Station { get; set; } = 1;
/// <inheritdoc/>
public override void Dispose()
{
foreach (var item in ModbusServer01ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer02ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer03ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer04ByteBlocks)
{
item.Value.SafeDispose();
}
ModbusServer01ByteBlocks.Clear();
ModbusServer02ByteBlocks.Clear();
ModbusServer03ByteBlocks.Clear();
ModbusServer04ByteBlocks.Clear();
base.Dispose();
}
/// <inheritdoc/>
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
/// <inheritdoc/>
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
{
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
EasyLock easyLock = new();
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
try
{
easyLock.Wait();
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return new OperResult<byte[]>("地址错误");
}
Init(mAddress);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
switch (mAddress.ReadFunction)
{
case 1:
byte[] bytes0 = new byte[len];
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Read(bytes0);
return OperResult.CreateSuccessResult(bytes0);
case 2:
byte[] bytes1 = new byte[len];
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Read(bytes1);
return OperResult.CreateSuccessResult(bytes1);
case 3:
byte[] bytes3 = new byte[len];
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Read(bytes3);
return OperResult.CreateSuccessResult(bytes3);
case 4:
byte[] bytes4 = new byte[len];
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Read(bytes4);
return OperResult.CreateSuccessResult(bytes4);
}
return new OperResult<byte[]>("功能码错误");
}
finally
{
easyLock.Release();
}
}
/// <inheritdoc/>
public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
return Task.FromResult(Read(address, length));
}
/// <inheritdoc/>
public override void SetDataAdapter(object socketClient)
{
if (socketClient is SocketClient client)
{
ModbusTcpServerDataHandleAdapter dataHandleAdapter = new();
dataHandleAdapter.CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout);
client.SetDataHandlingAdapter(dataHandleAdapter);
}
else
{
foreach (var item in TcpService.GetClients())
{
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
{
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
item.SetDataHandlingAdapter(dataHandleAdapter);
}
}
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
easyLock.Wait();
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return new OperResult(ex);
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return new OperResult("地址错误");
}
Init(mAddress);
}
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 3:
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
case 4:
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return OperResult.CreateSuccessResult();
}
return new OperResult("功能码错误");
}
finally
{
easyLock.Release();
}
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
easyLock.Wait();
ModbusAddress mAddress;
try
{
mAddress = ModbusAddress.ParseFrom(address, Station);
}
catch (Exception ex)
{
return (new OperResult(ex));
}
if (MulStation)
{
Init(mAddress);
}
else
{
if (Station != mAddress.Station)
{
return (new OperResult("地址错误"));
}
Init(mAddress);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 1:
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
case 2:
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
return (OperResult.CreateSuccessResult());
}
return new OperResult("功能码错误");
}
finally
{
easyLock.Release();
}
}
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
return Task.FromResult(Write(address, value));
}
/// <inheritdoc/>
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
return Task.FromResult(Write(address, value));
}
/// <inheritdoc/>
protected override async Task Received(SocketClient client, ReceivedDataEventArgs e)
{
try
{
var requestInfo = e.RequestInfo;
//接收外部报文
if (requestInfo is ModbusTcpServerMessage modbusServerMessage)
{
if (modbusServerMessage.CurModbusAddress == null)
{
return;//无法解析直接返回
}
if (!modbusServerMessage.IsSuccess)
{
return;//无法解析直接返回
}
if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)//读取
{
var data = Read(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.Length);
if (data.IsSuccess)
{
var coreData = data.Content;
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
{
coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling(modbusServerMessage.Length / 8.0));
}
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8).SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
sendData[5] = (byte)(sendData.Length - 6);
client.Send(sendData);
}
else
{
WriteError(client, modbusServerMessage);//返回错误码
}
}
else//写入
{
var coreData = modbusServerMessage.Content;
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
{
//写入继电器
if (WriteData != null)
{
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
{
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
else
{
WriteError(client, modbusServerMessage);
}
}
else
{
//写入内存区
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.Length));
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
}
else
{
//写入寄存器
if (WriteData != null)
{
if ((await WriteData(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
{
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
else
{
WriteError(client, modbusServerMessage);
}
}
else
{
var result = Write(modbusServerMessage.CurModbusAddress.ToString(), coreData);
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
}
}
}
}
catch (Exception ex)
{
Logger.LogError(ex, ToString());
}
//返回错误码
static void WriteError(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
sendData[5] = (byte)(sendData.Length - 6);
sendData[7] = (byte)(sendData[7] + 128);
client.Send(sendData);
}
}
private static void WriteSuccess03(SocketClient client, ModbusTcpServerMessage modbusServerMessage)
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12);
sendData[5] = (byte)(sendData.Length - 6);
client.Send(sendData);
}
private void Init(ModbusAddress mAddress)
{
ModbusServer01ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer02ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer03ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
ModbusServer04ByteBlocks.GetOrAdd(mAddress.Station, a => new ByteBlock(1024 * 128));
}
}

View File

@@ -15,7 +15,7 @@ using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <inheritdoc/>
public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusServerMessage>
public class ModbusTcpServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpServerMessage>
{
private readonly ThingsGatewayBitConverter ThingsGatewayBitConverter = new(EndianType.Big);
@@ -53,22 +53,22 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte
}
}
return new OperResult<byte[]>(response) { Message = $"数据长度{response.Length}错误" };
return new OperResult<byte[]>() { Message = $"数据长度{response.Length}错误" };
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
protected override ModbusServerMessage GetInstance()
protected override ModbusTcpServerMessage GetInstance()
{
return new ModbusServerMessage();
return new ModbusTcpServerMessage();
}
/// <inheritdoc/>
protected override FilterResult UnpackResponse(ModbusServerMessage request, byte[] send, byte[] body, byte[] response)
protected override FilterResult UnpackResponse(ModbusTcpServerMessage request, byte[] send, byte[] body, byte[] response)
{
var result = GetModbusData(response.RemoveBegin(6));
if (result.IsSuccess)
@@ -88,11 +88,11 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
Address = addressStart.ToString(),
WriteFunction = function,
ReadFunction = (byte)(function == 16 ? 3 : function == 15 ? 1 : 3),
Length = ThingsGatewayBitConverter.ToByte(response, 11),
};
request.Length = ThingsGatewayBitConverter.ToByte(response, 11);
request.Content = result.Content.RemoveBegin(7);
}
else
@@ -100,11 +100,11 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
Address = addressStart.ToString(),
WriteFunction = function,
ReadFunction = (byte)(function == 6 ? 3 : function == 5 ? 1 : 3),
Length = 1,
};
request.Length = 1;
request.Content = result.Content.RemoveBegin(4);
}
}
@@ -113,18 +113,18 @@ public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
Address = addressStart.ToString(),
ReadFunction = function,
Length = ThingsGatewayBitConverter.ToByte(response, 11),
};
request.Length = ThingsGatewayBitConverter.ToByte(response, 11);
}
request.ResultCode = result.ResultCode;
request.ErrorCode = result.ErrorCode;
request.Message = result.Message;
return FilterResult.Success;
}
else
{
request.ResultCode = result.ResultCode;
request.ErrorCode = result.ErrorCode;
request.Message = result.Message;
return FilterResult.Cache;
}

View File

@@ -15,20 +15,24 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
/// <summary>
/// <inheritdoc/>
/// </summary>
public class ModbusServerMessage : MessageBase, IMessage
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[] head)
public override bool CheckHeadBytes(byte[] heads)
{
if (head == null || head.Length != 6) return false;
HeadBytes = head;
if (heads == null || heads.Length != 6) return false;
HeadBytes = heads;
int num = (HeadBytes[4] * 256) + HeadBytes[5];
BodyLength = num;

View File

@@ -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);
}
}
}

View File

@@ -10,7 +10,6 @@
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus;
@@ -52,6 +51,6 @@ public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<M
protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
{
var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
return result.Copy();
return result;
}
}

View File

@@ -10,131 +10,138 @@
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Application;
using ThingsGateway.Application.Extensions;
using ThingsGateway.Foundation;
using ThingsGateway.Foundation.Adapter.Modbus;
using ThingsGateway.Foundation.Extension.Generic;
using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Modbus;
namespace ThingsGateway.Foundation.Adapter.Modbus;
internal static class ModbusHelper
/// <summary>
/// PackHelper
/// </summary>
public class PackHelper
{
internal static List<DeviceVariableSourceRead> LoadSourceRead(this List<DeviceVariableRunTime> deviceVariables, IReadWriteDevice device, int MaxPack)
/// <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;
var result = new List<DeviceVariableSourceRead>();
//需要先剔除额外信息比如dataformat等
foreach (var item in deviceVariables)
{
var address = item.VariableAddress;
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
item.ThingsGatewayBitConverter = transformParameter;
item.VariableAddress = address;
//item.VariableAddress = address;
item.Index = device.GetBitOffset(item.VariableAddress);
}
//按读取间隔分组
var tags = deviceVariables.GroupBy(it => it.IntervalTime);
foreach (var item in tags)
var deviceVariableRunTimeGroups = deviceVariables.GroupBy(it => it.IntervalTime);
foreach (var group in deviceVariableRunTimeGroups)
{
Dictionary<ModbusAddress, DeviceVariableRunTime> map = item.ToDictionary(it =>
Dictionary<ModbusAddress, T2> map = group.ToDictionary(it =>
{
var lastLen = it.DataTypeEnum.GetByteLength();
if (lastLen <= 0)
{
if (it.DataTypeEnum.GetSystemType() == typeof(bool))
switch (it.DataTypeEnum)
{
lastLen = 2;
}
else if (it.DataTypeEnum.GetSystemType() == typeof(string))
{
lastLen = it.ThingsGatewayBitConverter.StringLength;
}
else if (it.DataTypeEnum.GetSystemType() == typeof(object))
{
lastLen = 2;
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 = new ModbusAddress(address, (ushort)lastLen);
if (result == null)
{
}
var result = ModbusAddress.ParseFrom(address);
result.ByteLength = lastLen;
return result;
});
//获取变量的地址
var modbusAddressList = map.Keys.ToList();
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).ToList();
var stationNumbers = modbusAddressSameFunList
.Select(t => t.Station).Distinct().ToList();
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)
var addressList = modbusAddressSameFunList
.Where(t => t.Station == stationNumber)
.ToDictionary(t => t, t => map[t]);
var tempResult = LoadSourceRead(addressList, functionCode, item.Key, MaxPack);
result.AddRange(tempResult);
var tempResult = LoadSourceRead<T, T2>(addressList, functionCode, group.Key, maxPack);
deviceVariableSourceReads.AddRange(tempResult);
}
}
}
return result;
return deviceVariableSourceReads;
}
private static List<DeviceVariableSourceRead> LoadSourceRead(Dictionary<ModbusAddress, DeviceVariableRunTime> addressList, int functionCode, int timeInterval, int MaxPack)
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<DeviceVariableSourceRead> sourceReads = new();
List<T> sourceReads = new();
//按地址和长度排序
var orderByAddressAndLen = addressList.Keys.OrderBy(it => it.AddressStart + Math.Ceiling(it.Length / 2.0)).ToList();
var orderByAddressEnd = addressList.Keys.OrderBy(it => it.AddressEnd);
//按地址和长度排序
var orderByAddress = addressList.Keys.OrderBy(it => it.AddressStart).ToList();
var orderByAddressStart = addressList.Keys.OrderBy(it => it.AddressStart);
//地址最小,在循环中更改
var minAddress = orderByAddress.First().AddressStart;
var minAddress = orderByAddressStart.First().AddressStart;
//地址最大
var maxAddress = orderByAddress.Last().AddressStart;
var maxAddress = orderByAddressStart.Last().AddressStart;
while (maxAddress >= minAddress)
{
//最大的打包长度
int readLength = MaxPack;
int readLength = maxPack;
if (functionCode == 1 || functionCode == 2)
{
readLength = MaxPack * 8 * 2;
readLength = maxPack * 8 * 2;
}
//获取当前的一组打包地址信息,
var tempAddressAndLen = orderByAddressAndLen.Where(t => (t.AddressStart + (t.Length / 2.0)) <= minAddress + readLength).ToList();
//起始地址
var startAddress = tempAddressAndLen.OrderBy(it => it.AddressStart).First();
//读取寄存器长度
var sourceLen = tempAddressAndLen.Last().AddressStart + (int)Math.Ceiling(tempAddressAndLen.Last().Length / 2.0) - startAddress.AddressStart;
DeviceVariableSourceRead sourceRead = new(timeInterval)
//获取当前的一组打包地址信息,
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),
//这里只需要根据地址排序的第一个地址,作为实际打包报文中的起始地址
Address = startAddress.ToString(),
VariableAddress = startAddress.ToString(),
Length = sourceLen
};
foreach (var item in tempAddressAndLen)
foreach (var item in tempAddressEnd)
{
var readNode = addressList[item];
if ((functionCode == -1 || functionCode == 3 || functionCode == 4) &&
readNode.DataTypeEnum == DataTypeEnum.Boolean)
if ((functionCode == -1 || functionCode == 3 || functionCode == 4) && readNode.DataTypeEnum == DataTypeEnum.Boolean)
{
readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 16) + readNode.Index;
}
@@ -146,15 +153,13 @@ internal static class ModbusHelper
readNode.Index = ((item.AddressStart - startAddress.AddressStart) * 2) + readNode.Index;
}
sourceRead.DeviceVariables.Add(readNode);
orderByAddressAndLen.Remove(item);
orderByAddress.Remove(item);
sourceRead.DeviceVariableRunTimes.Add(readNode);
addressList.Remove(item);
}
sourceReads.Add(sourceRead);
if (orderByAddressAndLen.Count > 0)
minAddress = orderByAddress.First().AddressStart;
if (orderByAddressEnd.Count() > 0)
minAddress = orderByAddressStart.First().AddressStart;
else
break;
}

View File

@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
</Project>

View File

@@ -14,11 +14,10 @@
<inheritdoc/>
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor(System.String,System.UInt16)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor(System.String,System.Byte)">
<inheritdoc/>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressStart">
<summary>
读取功能码
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ReadFunction">
<summary>
@@ -35,16 +34,28 @@
写入功能码
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Parse(System.String,System.Int32)">
<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.Int32)">
<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>
<param name="address"></param>
<param name="length"></param>
<returns></returns>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ToString">
<inheritdoc/>
@@ -135,24 +146,14 @@
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.TcpClientEx)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.CacheTimeout">
<summary>
组包缓存时间/ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Crc16CheckEnable">
<summary>
Crc校验
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Station">
<summary>
站号
@@ -161,10 +162,22 @@
<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.SetDataAdapter">
<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)">
@@ -173,13 +186,10 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(TouchSocket.Sockets.UdpSession)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Crc16CheckEnable">
@@ -187,11 +197,6 @@
Crc校验
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Station">
<summary>
站号
@@ -200,10 +205,22 @@
<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">
<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)">
@@ -212,9 +229,6 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter">
<summary>
<inheritdoc/>
@@ -239,27 +253,17 @@
ModbusRtu
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.#ctor(ThingsGateway.Foundation.Serial.SerialsSession)">
<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.CacheTimeout">
<summary>
组包缓存时间/ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Crc16CheckEnable">
<summary>
Crc校验
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Station">
<summary>
站号
@@ -268,10 +272,22 @@
<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">
<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)">
@@ -347,7 +363,7 @@
接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(TouchSocket.Sockets.TcpService)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(ThingsGateway.Foundation.Sockets.TcpService)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.MulStation">
@@ -360,13 +376,28 @@
默认站点
</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(TouchSocket.Sockets.SocketClient)">
<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)">
@@ -375,10 +406,7 @@
<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.Dispose(System.Boolean)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Received(TouchSocket.Sockets.SocketClient,TouchSocket.Core.IRequestInfo)">
<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">
@@ -414,6 +442,11 @@
当前关联的地址
</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>
@@ -423,19 +456,9 @@
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.TcpClientEx)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.CacheTimeout">
<summary>
组包缓存时间/ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.IsCheckMessageId">
<summary>
检测事务标识符
@@ -449,10 +472,22 @@
<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">
<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)">
@@ -461,9 +496,6 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter">
<summary>
ModbusTcpDataHandleAdapter
@@ -502,14 +534,9 @@
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(TouchSocket.Sockets.UdpSession)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.IsCheckMessageId">
<summary>
检测事务标识符
@@ -520,13 +547,25 @@
站号
</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">
<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)">
@@ -535,9 +574,6 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter">
<summary>
<inheritdoc/>
@@ -557,5 +593,19 @@
<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>

View File

@@ -10,7 +10,6 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Runtime.InteropServices;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;

View File

@@ -10,8 +10,6 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
/// <summary>

View File

@@ -10,10 +10,7 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
@@ -201,7 +198,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
OnWriteCompleted?.Invoke(itemwrite);
}
internal OperResult AddOpcItem(OpcItem[] items)
internal List<Tuple<OpcItem, int>> AddOpcItem(OpcItem[] items)
{
IntPtr pResults = IntPtr.Zero;
IntPtr pErrors = IntPtr.Zero;
@@ -228,7 +225,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
m_ItemManagement?.AddItems(items.Length, itemDefyArray, out pResults, out pErrors);
IntPtr Pos = pResults;
Marshal.Copy(pErrors, errors, 0, items.Length);
StringBuilder stringBuilder = new();
List<Tuple<OpcItem, int>> results = new();
for (int j = 0; j < items.Length; j++)
{
if (errors[j] == 0)
@@ -250,21 +247,10 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
}
else
{
stringBuilder.AppendLine(items[j].ItemID + "添加失败,错误代码:" + errors[j]);
results.Add(Tuple.Create(items[j], errors[j]));
}
}
if (stringBuilder.Length > 0)
{
return new OperResult(stringBuilder.ToString());
}
else
{
return OperResult.CreateSuccessResult();
}
}
catch (COMException ex)
{
return new OperResult(ex);
return results;
}
finally
{
@@ -299,7 +285,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
/// 组读取
/// </summary>
/// <exception cref="ExternalException"></exception>
internal OperResult ReadAsync()
internal void ReadAsync()
{
IntPtr pErrors = IntPtr.Zero;
try
@@ -316,16 +302,11 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
Marshal.Copy(pErrors, PErrors, 0, OpcItems.Count);
if (PErrors.Any(a => a > 0))
{
return new OperResult("读取错误,错误代码:" + pErrors);
throw new("读取错误,错误代码:" + pErrors);
}
return OperResult.CreateSuccessResult();
}
else
return new OperResult("连接无效");
}
catch (COMException ex)
{
return new OperResult(ex);
throw new("连接无效");
}
finally
{
@@ -336,7 +317,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
}
}
internal OperResult RemoveItem(OpcItem[] items)
internal List<Tuple<OpcItem, int>> RemoveItem(OpcItem[] items)
{
IntPtr pErrors = IntPtr.Zero;
int[] errors = new int[items.Length];
@@ -356,59 +337,42 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
{
Marshal.FreeCoTaskMem(pErrors);
}
}
StringBuilder stringBuilder = new();
List<Tuple<OpcItem, int>> results = new();
for (int i = 0; i < errors.Length; i++)
{
if (errors[i] != 0)
{
stringBuilder.AppendLine(items[i].ItemID + "移除失败,错误代码:" + errors[i]);
results.Add(Tuple.Create(items[i], errors[i]));
}
else
{
OpcItems.Remove(items[i]);
}
}
if (stringBuilder.Length > 0)
{
return new OperResult(stringBuilder.ToString());
}
else
{
return OperResult.CreateSuccessResult();
}
return results;
}
internal OperResult Write(object[] values, int[] serverHandle, out int[] errors)
internal List<Tuple<int, int>> Write(object[] values, int[] serverHandle)
{
IntPtr pErrors = IntPtr.Zero;
errors = new int[values.Length];
var errors = new int[values.Length];
if (m_Async2IO != null)
{
try
{
m_SyncIO.Write(values.Length, serverHandle, values, out pErrors);
Marshal.Copy(pErrors, errors, 0, values.Length);
StringBuilder stringBuilder = new();
List<Tuple<int, int>> results = new();
for (int i = 0; i < errors.Length; i++)
{
if (errors[i] != 0)
{
stringBuilder.AppendLine("写入错误,错误代码:" + errors[i]);
results.Add(Tuple.Create(serverHandle[i], errors[i]));
}
}
if (stringBuilder.Length > 0)
{
return new(stringBuilder.ToString());
}
else
{
return OperResult.CreateSuccessResult();
}
}
catch (COMException ex)
{
return new OperResult(ex);
return results;
}
finally
{
@@ -419,7 +383,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
}
}
else
return new OperResult("连接无效");
throw new("连接无效");
}
protected virtual void Dispose(bool disposing)
{
@@ -465,7 +429,7 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
}
}
private OperResult ActiveDataChanged(bool active)
private void ActiveDataChanged(bool active)
{
IntPtr pRequestedUpdateRate = IntPtr.Zero;
IntPtr hClientGroup = IntPtr.Zero;
@@ -484,11 +448,6 @@ internal class OpcGroup : IOPCDataCallback, IDisposable
pDeadband,
pLCID,
hClientGroup);
return OperResult.CreateSuccessResult();
}
catch (COMException ex)
{
return new OperResult(ex);
}
finally
{

View File

@@ -10,8 +10,6 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
@@ -51,58 +49,51 @@ internal class OpcServer : IDisposable
GC.SuppressFinalize(this);
}
internal OperResult<OpcGroup> AddGroup(string groupName)
internal OpcGroup AddGroup(string groupName)
{
return AddGroup(groupName, true, 1000, 0);
}
/// <returns></returns>
internal OperResult<OpcGroup> AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand)
internal OpcGroup AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand)
{
if (null == m_OpcServer || IsConnected == false)
return new OperResult<OpcGroup>("未初始化连接!");
throw new("未初始化连接!");
OpcGroup group = new(groupName, active, reqUpdateRate, deadBand);
Guid riid = typeof(IOPCItemMgt).GUID;
try
m_OpcServer?.AddGroup(group.Name,
group.IsActive ? 1 : 0,//IsActive
group.RequestUpdateRate,//RequestedUpdateRate 1000ms
group.ClientGroupHandle,
group.TimeBias.AddrOfPinnedObject(),
group.PercendDeadBand.AddrOfPinnedObject(),
group.LCID,
out group.serverGroupHandle,
out group.revisedUpdateRate,
ref riid,
out group.groupPointer);
if (group.groupPointer != null)
{
m_OpcServer?.AddGroup(group.Name,
group.IsActive ? 1 : 0,//IsActive
group.RequestUpdateRate,//RequestedUpdateRate 1000ms
group.ClientGroupHandle,
group.TimeBias.AddrOfPinnedObject(),
group.PercendDeadBand.AddrOfPinnedObject(),
group.LCID,
out group.serverGroupHandle,
out group.revisedUpdateRate,
ref riid,
out group.groupPointer);
if (group.groupPointer != null)
{
group.InitIoInterfaces(group.groupPointer);
OpcGroups.Add(group);
}
else
{
return new OperResult<OpcGroup>("添加OPC组错误OPC服务器返回null");
}
return OperResult.CreateSuccessResult(group);
group.InitIoInterfaces(group.groupPointer);
OpcGroups.Add(group);
}
catch (Exception ex)
else
{
return new OperResult<OpcGroup>(ex);
throw new("添加OPC组错误OPC服务器返回null");
}
return group;
}
/// <summary>
/// 获取节点
/// </summary>
internal OperResult<List<BrowseElement>> Browse(string itemId = null)
internal List<BrowseElement> Browse(string itemId = null)
{
lock (this)
{
if (null == m_OpcServer || IsConnected == false)
return new OperResult<List<BrowseElement>>("未初始化连接!");
throw new("未初始化连接!");
var count = 0;
var moreElements = 0;
@@ -118,108 +109,107 @@ internal class OpcServer : IDisposable
new PropertyID(6),
new PropertyID(101),
};
try
{
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);
}
catch (Exception ex)
{
return new OperResult<List<BrowseElement>>(ex);
}
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 OperResult.CreateSuccessResult(browseElements?.ToList());
return browseElements?.ToList();
}
}
internal OperResult Connect()
internal void Connect()
{
if (!string.IsNullOrEmpty(Host) && !string.IsNullOrEmpty(Name))
{
var info = Discovery.OpcDiscovery.GetOpcServer(Name, Host);
if (!info.IsSuccess)
{
return info;
}
object o = Comn.ComInterop.CreateInstance(info.Content.CLSID, Host);
object o = Comn.ComInterop.CreateInstance(info.CLSID, Host);
if (o == null)
{
return new(string.Format("{0}{1}无法创建com对象", info.Content.CLSID, Host));
throw new(string.Format("{0}{1}无法创建com对象", info.CLSID, Host));
}
m_OpcServer = (IOPCServer)o;
IsConnected = true;
return OperResult.CreateSuccessResult();
}
return new("应初始化Host与Name");
else
throw new("应初始化Host与Name");
}
/// <summary>
/// 服务器状态
/// </summary>
/// <returns></returns>
internal OperResult<ServerStatus> GetServerStatus()
internal ServerStatus GetServerStatus()
{
if (null == m_OpcServer || IsConnected == false)
return new OperResult<ServerStatus>("未初始化连接!");
IntPtr statusPtr = IntPtr.Zero;
ServerStatus serverStatus = null;
try
{
if (null == m_OpcServer || IsConnected == false)
throw new("未初始化连接!");
IntPtr statusPtr = IntPtr.Zero;
m_OpcServer?.GetStatus(out statusPtr);
OPCSERVERSTATUS status;
ServerStatus = new ServerStatus();
if (statusPtr != IntPtr.Zero)
{
try
object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS));
Marshal.FreeCoTaskMem(statusPtr);
if (o != null)
{
object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS));
if (null != o)
{
status = (OPCSERVERSTATUS)o;
ServerStatus.Version = status.wMajorVersion.ToString() + "." + status.wMinorVersion.ToString() + "." + status.wBuildNumber.ToString();
ServerStatus.ServerState = status.dwServerState;
ServerStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime);
ServerStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime);
ServerStatus.LastUpdateTime = Comn.Convert.FileTimeToDateTime(status.ftLastUpdateTime);
ServerStatus.VendorInfo = status.szVendorInfo;
}
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 OperResult.CreateSuccessResult(ServerStatus);
return serverStatus;
}
catch (Exception ex)
else
{
IsConnected = false;
return new OperResult<ServerStatus>(ex);
throw new("未知错误");
}
}
else
{
IsConnected = false;
return new OperResult<ServerStatus>("获取状态失败");
throw new("未知错误");
}
}
catch (Exception ex)
finally
{
IsConnected = false;
return new OperResult<ServerStatus>(ex);
if (serverStatus != null)
IsConnected = true;
else
IsConnected = false;
ServerStatus = serverStatus;
}
}
internal void RemoveGroup(OpcGroup group)

View File

@@ -10,15 +10,11 @@
//------------------------------------------------------------------------------
#endregion
using Newtonsoft.Json.Linq;
using System.Collections;
using System.Linq;
using System.Text;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
using TouchSocket.Core;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Discovery;
/// <summary>
/// OpcDiscovery
@@ -38,58 +34,58 @@ public class OpcDiscovery
/// <param name="serverName"></param>
/// <param name="host"></param>
/// <returns></returns>
internal static OperResult<ServerInfo> GetOpcServer(string serverName, string host)
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
{
if (string.IsNullOrEmpty(serverName))
{
return new OperResult<ServerInfo>("检索失败需提供OPCName");
}
ServerInfo result = null;
ServerInfo[] serverInfos = null;
object o_Server = Comn.ComInterop.CreateInstance(OPCEnumCLSID, host);
if (o_Server == null)
return new OperResult<ServerInfo>("检索失败请检查是否安装OPC Runtime");
Guid catid = CATID_OPC_DA20;
//两种方式兼容国产部分OPCServer不支持IOPCServerList2的情况
try
{
Guid catid = CATID_OPC_DA20;
//两种方式兼容国产部分OPCServer不支持IOPCServerList2的情况
try
{
IOPCServerList2 m_server2 = (IOPCServerList2)o_Server;
GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server2, catid);
if (result == null)
{
IOPCServerList m_server = (IOPCServerList)o_Server;
GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid);
}
}
catch (Exception)
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);
}
if (result == null)
{
return new OperResult<ServerInfo>($"无法创建OPCServer连接请检查OPC名称是否一致以下为Host{host}中的OPC列表:"
+ Environment.NewLine +
JToken.Parse(serverInfos.ToJsonString()).ToString()
);
}
return OperResult.CreateSuccessResult(result);
}
finally
catch
{
Comn.ComInterop.RealseComServer(o_Server);
o_Server = null;
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;
}
catch (Exception ex)
finally
{
return new OperResult<ServerInfo>(ex);
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)
@@ -246,4 +242,15 @@ internal class ServerInfo
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();
}
}

View File

@@ -10,9 +10,6 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Linq;
namespace ThingsGateway.Foundation.Adapter.OPCDA;
internal static class CollectionExtensions
@@ -23,11 +20,30 @@ internal static class CollectionExtensions
/// <typeparam name="T"></typeparam>
/// <param name="this"></param>
/// <param name="where"></param>
public static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
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;
}
}

View File

@@ -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
}

View File

@@ -13,5 +13,5 @@
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;
global using System.Threading;

View File

@@ -0,0 +1,457 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Runtime.InteropServices;
using System.Text;
using System.Timers;
using ThingsGateway.Foundation.Adapter.OPCDA.Da;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
using Timer = System.Timers.Timer;
//部分非托管交互代码来自https://gitee.com/Zer0Day/opc-client与OPC基金会opcnet库更改部分逻辑
namespace ThingsGateway.Foundation.Adapter.OPCDA;
/// <summary>
/// 订阅变化项
/// </summary>
/// <param name="values"></param>
public delegate void DataChangedEventHandler(List<ItemReadResult> values);
/// <summary>
/// OPCDAClient
/// </summary>
public class OPCDAClient : IDisposable
{
/// <summary>
/// LogAction
/// </summary>
private readonly Action<byte, object, string, Exception> _logAction;
private readonly object checkLock = new();
private Timer checkTimer;
private int IsExit = 1;
/// <summary>
/// 当前保存的需订阅列表
/// </summary>
private Dictionary<string, List<OpcItem>> ItemDicts = new();
private OpcServer m_server;
private bool publicConnect;
/// <summary>
/// <inheritdoc/>
/// </summary>
public OPCDAClient(Action<byte, object, string, Exception> logAction)
{
#if (NET6_0_OR_GREATER)
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
throw new NotSupportedException("不支持非windows系统");
}
#endif
_logAction = logAction;
}
/// <summary>
/// 数据变化事件
/// </summary>
public event DataChangedEventHandler DataChangedHandler;
/// <summary>
/// 是否连接成功
/// </summary>
public bool IsConnected => m_server?.IsConnected == true;
/// <summary>
/// 当前配置
/// </summary>
public OPCNode OPCNode { get; private set; }
private List<OpcGroup> Groups => m_server.OpcGroups;
/// <summary>
/// 添加节点,需要在连接成功后执行
/// </summary>
/// <param name="items">组名称/变量节点,注意每次添加的组名称不能相同</param>
public void AddItems(Dictionary<string, List<OpcItem>> items)
{
if (IsExit == 1) throw new("对象已释放");
foreach (var item in items)
{
if (IsExit == 1) throw new("对象已释放");
try
{
var subscription = m_server.AddGroup(item.Key, true, OPCNode.UpdateRate, OPCNode.DeadBand);
subscription.ActiveSubscribe = OPCNode.ActiveSubscribe;
subscription.OnDataChanged += Subscription_OnDataChanged;
subscription.OnReadCompleted += Subscription_OnDataChanged;
var result = subscription.AddOpcItem(item.Value.ToArray());
StringBuilder stringBuilder = new StringBuilder();
if (result.Count > 0)
{
foreach (var item1 in result)
{
stringBuilder.Append($"{item1.Item1.ItemID}{item1.Item2}");
}
_logAction?.Invoke(3, this, $"添加变量失败:{stringBuilder}", null);
}
else
{
ItemDicts.AddOrUpdate(item.Key, item.Value.Where(a => !result.Select(b => b.Item1).Contains(a)).ToList());
}
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"添加组失败:{ex.Message}", ex);
}
}
for (int i = 0; i < Groups?.Count; i++)
{
var group = Groups[i];
if (group != null)
{
if (group.OpcItems.Count == 0)
{
ItemDicts.Remove(group.Name);
m_server.RemoveGroup(group);
}
}
}
}
/// <summary>
/// 设置节点并保存,每次重连会自动添加节点
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
public Dictionary<string, List<OpcItem>> AddItemsWithSave(List<string> items)
{
int i = 0;
ItemDicts = items.ToList().ConvertAll(o => new OpcItem(o)).ChunkTrivialBetter(OPCNode.GroupSize).ToDictionary(a => "default" + (i++));
return ItemDicts;
}
/// <summary>
/// 连接服务器
/// </summary>
public void Connect()
{
publicConnect = true;
Interlocked.CompareExchange(ref IsExit, 0, 1);
PrivateConnect();
}
/// <summary>
/// 断开连接
/// </summary>
public void Disconnect()
{
Interlocked.CompareExchange(ref IsExit, 1, 0);
PrivateDisconnect();
}
/// <inheritdoc/>
public void Dispose()
{
try
{
PrivateDisconnect();
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex);
}
Interlocked.CompareExchange(ref IsExit, 1, 0);
}
/// <summary>
/// 浏览节点
/// </summary>
/// <param name="itemId"></param>
/// <returns></returns>
public List<BrowseElement> GetBrowseElements(string itemId = null)
{
return this.m_server?.Browse(itemId);
}
/// <summary>
/// 获取服务状态
/// </summary>
/// <returns></returns>
public ServerStatus GetServerStatus()
{
return this.m_server?.GetServerStatus();
}
/// <summary>
/// 初始化设置
/// </summary>
/// <param name="node"></param>
public void Init(OPCNode node)
{
if (node != null)
OPCNode = node;
checkTimer?.Stop();
checkTimer?.Dispose();
checkTimer = new Timer(OPCNode.CheckRate * 60 * 1000);
checkTimer.Elapsed += CheckTimer_Elapsed;
checkTimer.Start();
try
{
m_server?.Dispose();
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex);
}
m_server = new OpcServer(OPCNode.OPCName, OPCNode.OPCIP);
}
/// <summary>
/// 按OPC组读取组内变量结果会在订阅事件中返回
/// </summary>
/// <param name="groupName">组名称值为null时读取全部组</param>
/// <returns></returns>
public void ReadItemsWithGroup(string groupName = null)
{
PrivateConnect();
{
var groups = groupName != null ? Groups.Where(a => a.Name == groupName) : Groups;
foreach (var group in groups)
{
if (group.OpcItems.Count > 0)
{
group.ReadAsync();
}
}
}
}
/// <summary>
/// 移除节点
/// </summary>
/// <param name="items"></param>
public void RemoveItems(List<string> items)
{
foreach (var item in items)
{
if (IsExit == 1) return;
var opcGroups = Groups.Where(it => it.OpcItems.Any(a => a.ItemID == item));
foreach (var opcGroup in opcGroups)
{
var tag = opcGroup.OpcItems.Where(a => item == a.ItemID);
var result = opcGroup.RemoveItem(tag.ToArray());
if (opcGroup.OpcItems.Count == 0)
{
opcGroup.OnDataChanged -= Subscription_OnDataChanged;
ItemDicts.Remove(opcGroup.Name);
m_server.RemoveGroup(opcGroup);
}
else
{
ItemDicts[opcGroup.Name].RemoveWhere(a => tag.Contains(a) && !result.Select(b => b.Item1).Contains(a));
}
}
}
}
/// <inheritdoc/>
public override string ToString()
{
return OPCNode?.ToString();
}
/// <summary>
/// 批量写入值
/// </summary>
/// <returns></returns>
public Dictionary<string, Tuple<bool, string>> WriteItem(Dictionary<string, object> writeInfos)
{
PrivateConnect();
Dictionary<string, Tuple<bool, string>> results = new();
var valueGroup = writeInfos.GroupBy(itemId =>
{
var group = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == itemId.Key));
return group;
}).ToList();
foreach (var item1 in valueGroup)
{
try
{
if (item1.Key == null)
{
foreach (var item2 in item1)
{
results.AddOrUpdate(item2.Key, Tuple.Create(true, $"不存在该变量{item2.Key}"));
}
}
else
{
List<int> serverHandles = new();
Dictionary<int, OpcItem> handleItems = new();
List<object> values = new();
foreach (var item2 in item1)
{
var opcItem = item1.Key.OpcItems.Where(it => it.ItemID == item2.Key).FirstOrDefault();
serverHandles.Add(opcItem.ServerHandle);
handleItems.AddOrUpdate(opcItem.ServerHandle, opcItem);
var rawWriteValue = item2.Value;
values.Add(rawWriteValue);
}
var result = item1.Key.Write(values.ToArray(), serverHandles.ToArray());
var data = item1.ToList();
foreach (var item2 in result)
{
results.AddOrUpdate(handleItems[item2.Item1].ItemID, Tuple.Create(true, $"错误代码{item2.Item2}"));
}
}
foreach (var item2 in item1)
{
results.AddOrUpdate(item2.Key, Tuple.Create(false, $"成功"));
}
}
catch (Exception ex)
{
var keys = writeInfos.Keys.ToList();
foreach (var item in keys)
{
results.AddOrUpdate(item, Tuple.Create(true, ex.Message));
}
return results;
}
}
return results;
}
private void CheckTimer_Elapsed(object sender, ElapsedEventArgs e)
{
lock (checkLock)
{
if (IsExit == 0)
{
try
{
var status = m_server.GetServerStatus();
}
catch
{
if (IsExit == 0 && publicConnect)
{
try
{
PrivateConnect();
_logAction?.Invoke(1, this, $"重新链接成功", null);
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"重新链接失败:{ex.Message}", ex);
}
}
}
}
else
{
var timeer = sender as Timer;
timeer.Enabled = false;
timeer.Stop();
}
}
}
private void PrivateAddItems()
{
try
{
AddItems(ItemDicts);
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"添加点位失败:{ex.Message}", ex);
}
}
private void PrivateConnect()
{
lock (this)
{
if (m_server?.IsConnected == true)
{
try
{
var status = m_server.GetServerStatus();
}
catch
{
try
{
var status1 = m_server.GetServerStatus();
}
catch
{
Init(OPCNode);
m_server?.Connect();
_logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 连接成功", null);
PrivateAddItems();
}
}
}
else
{
Init(OPCNode);
m_server?.Connect();
_logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 连接成功", null);
PrivateAddItems();
}
}
}
private void PrivateDisconnect()
{
lock (this)
{
if (IsConnected)
_logAction?.Invoke(1, this, $"{m_server.Host} - {m_server.Name} - 断开连接", null);
if (checkTimer != null)
{
checkTimer.Enabled = false;
checkTimer.Stop();
}
try
{
m_server?.Dispose();
m_server = null;
}
catch (Exception ex)
{
_logAction?.Invoke(3, this, $"连接释放失败:{ex.Message}", ex);
}
}
}
private void Subscription_OnDataChanged(List<ItemReadResult> values)
{
DataChangedHandler?.Invoke(values);
}
}

View File

@@ -21,7 +21,7 @@ public class OPCNode
/// <summary>
/// 是否订阅
/// </summary>
[Description("ActiveSubscribe")]
[Description("订阅")]
public bool ActiveSubscribe { get; set; } = true;
/// <summary>
/// 内部检测重连间隔/min

View File

@@ -0,0 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
</Project>

Some files were not shown because too many files have changed in this diff Show More