Compare commits

..

313 Commits

Author SHA1 Message Date
2248356998 qq.com
07ca1a4de8 更新nuget包 2023-07-12 21:21:50 +08:00
2248356998 qq.com
24f289e692 V1.7.0发布
1、增加采集通道冗余
2、优化多个界面
3、导出导入功能优化
4、增加中间变量
5、OPCUAClient支持动态类型
6、离线缓存多处覆盖,包含上传插件/历史报警/时序库
7、增加通用调试界面,增加s7调试
8、其他优化
2023-07-12 21:16:38 +08:00
2248356998 qq.com
01bcdaae2d 修复AppDataTable清空MForm模型导致筛选列清空的问题 2023-07-10 13:18:14 +08:00
2248356998 qq.com
55890008d1 修复写入s7协议 bit值 偏移错误(以byte数据块为准) 2023-07-08 17:01:44 +08:00
2248356998 qq.com
5ab9b01879 修复读取s7协议 bit值 偏移错误(以byte数据块为准) 2023-07-08 11:44:57 +08:00
2248356998 qq.com
e4abb333b3 默认添加kafka插件 种子数据 2023-07-07 17:13:00 +08:00
2248356998 qq.com
09f476c745 修复布尔量解析时反转字节导致结果值不符的错误 2023-07-06 14:05:09 +08:00
2248356998 qq.com
8806e68dce null传播 2023-07-03 14:13:00 +08:00
2248356998 qq.com
2ef1e25cd8 添加x86架构 2023-07-03 10:48:52 +08:00
2248356998 qq.com
10e7f202aa 补充插件实例内容 2023-07-02 19:50:09 +08:00
2248356998 qq.com
ccd7000c09 blazor组件初始化并行执行,导致sugar单例DB出现线程偶发错误,暂增加copyNew()解决 2023-06-29 16:42:19 +08:00
2248356998 qq.com
8ee7b798cf blazor组件初始化并行执行,导致sugar单例DB出现线程偶发错误,暂增加copyNew()解决 2023-06-29 16:36:03 +08:00
2248356998 qq.com
7733cf5bf0 sqlsugar偶发线程故障,添加copyNew方法 2023-06-28 18:54:45 +08:00
2248356998 qq.com
a05ce86dd7 登录跳转uri优化 2023-06-28 15:47:35 +08:00
2248356998 qq.com
91f51c32e8 更新开源说明 2023-06-28 10:48:33 +08:00
2248356998 qq.com
f910202bba update openapiUser datatableUI 2023-06-26 19:51:02 +08:00
2248356998 qq.com
6d77194a8f css脚本结果不采用 System.Text.Json.JsonSerializer 2023-06-26 19:44:17 +08:00
2248356998 qq.com
9deb89c15f OPCUAClient修复当变量为string类型时的数值过滤失败导致添加订阅失效 2023-06-26 19:43:00 +08:00
2248356998 qq.com
4b62a092b4 停用采集设备时,变量获取运行态出错 2023-06-26 16:43:48 +08:00
2248356998 qq.com
81c8f626f9 登录后跳转原url 2023-06-26 14:46:24 +08:00
2248356998 qq.com
3e846c42fb cookie授权验证失败时返回登录界面 2023-06-26 14:36:41 +08:00
Diego2098
63ad7fd766 更新nuget类库 2023-06-25 18:59:09 +08:00
2248356998 qq.com
9ff1e9aa34 添加报警事件None枚举 2023-06-20 13:10:58 +08:00
2248356998 qq.com
8d162b6f3d 修复cpu核心等于1时报错,修正登录错误提示 2023-06-20 09:09:08 +08:00
Diego2098
9844d10bef 修复modbusServer 初始化错误 2023-06-17 18:14:13 +08:00
2248356998 qq.com
b908fa8489 sugar添加取消令箭 2023-06-15 17:38:36 +08:00
2248356998 qq.com
15a10643a7 增加变量运行态 CollectVariableRuntime 所在设备属性 2023-06-15 16:50:08 +08:00
2248356998 qq.com
299617aca1 parallel.foreach无序体验不好,退回为foreach 2023-06-14 11:04:09 +08:00
2248356998 qq.com
45647d697a update 1.6.1 version 2023-06-13 22:39:47 +08:00
2248356998 qq.com
48f5105d38 update nuget package 2023-06-13 22:39:25 +08:00
2248356998 qq.com
fe1c741d68 update driver messagesui 2023-06-13 22:26:19 +08:00
2248356998 qq.com
fa42cc1f00 并行关闭线程 2023-06-12 14:41:08 +08:00
2248356998 qq.com
42cf5e7a81 添加mqtt/kafka上传内容显示 2023-06-12 14:40:53 +08:00
2248356998 qq.com
47905e1aa1 优化大量变量excel上传的验证过程 2023-06-12 11:35:27 +08:00
2248356998 qq.com
9a8e907df3 更新文档站点地址 2023-06-11 18:37:50 +08:00
2248356998 qq.com
106fe85582 删除docs 2023-06-11 18:22:15 +08:00
2248356998 qq.com
4b3571bd57 更新1.6.0版本 2023-06-11 17:58:57 +08:00
2248356998 qq.com
96b537401a 缓存最大默认2000 2023-06-11 17:56:34 +08:00
2248356998 qq.com
721c9eb057 添加离线缓存大小限制配置 2023-06-11 17:55:03 +08:00
2248356998 qq.com
51701bf6d6 上传插件线程等待时间改为10ms 2023-06-11 17:47:16 +08:00
2248356998 qq.com
dbde68bd56 导入变量优化 2023-06-11 17:46:23 +08:00
2248356998 qq.com
ad2c9f585a 添加mqttClient离线缓存 2023-06-11 17:45:46 +08:00
2248356998 qq.com
562093c468 添加kafka离线缓存 2023-06-11 17:45:27 +08:00
Diego2098
b0295584a3 !6 部分配置会导致SqlServer自动建库失败
Merge pull request !6 from samisgod/master
2023-06-10 06:55:34 +00:00
samisgod
208c54de98 fix db init for SqlServer 2023-06-10 14:51:29 +08:00
2248356998 qq.com
63e2d941a1 增加pwa 2023-06-09 17:53:41 +08:00
2248356998 qq.com
3956838e9c 修复停用验证码时登录失败显示null报错 2023-06-09 17:09:17 +08:00
2248356998 qq.com
abeee58bb0 统一文件编码 2023-06-09 15:04:54 +08:00
2248356998 qq.com
d5b1b49722 update solution folder 2023-06-09 14:30:53 +08:00
2248356998 qq.com
564ed03ff8 upload deviceStatusPage 2023-06-09 10:32:11 +08:00
2248356998 qq.com
70db4c76b4 update deviceStatusPage 2023-06-09 09:49:03 +08:00
2248356998 qq.com
d059f7975b remove dotNET China declaration 2023-06-09 09:10:30 +08:00
2248356998 qq.com
4e74e6dc2d add dotNET China declaration 2023-06-09 09:02:17 +08:00
Diego2098
b6deb96658 重启线程时增加运行状态界面空传播防止报错 2023-06-08 21:35:07 +08:00
2248356998 qq.com
3839e966be add upload plugin code description 2023-06-08 17:47:53 +08:00
2248356998 qq.com
3dd035849c 迁移导入变量功能到驱动调试内 2023-06-08 15:11:58 +08:00
2248356998 qq.com
3d6532b5d6 plugin unload test,but failed 2023-06-08 10:52:39 +08:00
2248356998 qq.com
bf7c175ee7 更改默认api文档为Knife4j 2023-06-07 20:34:31 +08:00
2248356998 qq.com
f84af35ed6 缓存键增加Type-TypeHandle句柄 2023-06-07 18:42:58 +08:00
2248356998 qq.com
99063b3eb1 修改OPCUA证书路径,增加默认接收不收信任证书与其选项;
OPCDA整理;
2023-06-07 18:03:57 +08:00
2248356998 qq.com
3bec18f28d 重启线程WebApi修改 2023-06-07 11:32:18 +08:00
2248356998 qq.com
15de7a7894 添加注释提示 2023-06-07 11:28:11 +08:00
2248356998 qq.com
e20e04e677 GC策略更改,大量变量实例手动清空以快速内存释放 2023-06-07 11:19:24 +08:00
2248356998 qq.com
5fc6ae2835 上传1.5.1 2023-06-06 19:37:04 +08:00
2248356998 qq.com
7d281b8c96 Merge branch 'master' of https://gitee.com/diego2098/ThingsGateway 2023-06-05 08:33:50 +08:00
Diego2098
4880b801a7 S7-TCP连接修复死锁 2023-06-02 22:20:21 +08:00
2248356998 qq.com
74e354456a 增加KINGVIEW 配置 2023-05-29 01:32:37 +08:00
2248356998 qq.com
af2e03aa36 超管用户名称不可更改 2023-05-26 00:14:05 +08:00
2248356998 qq.com
d8fa660ab6 初步添加OPCUAClient调试界面;更新依赖 2023-05-25 23:41:11 +08:00
2248356998 qq.com
1a62d48297 OPCUA安全策略添加全部选项 2023-05-25 00:59:21 +08:00
2248356998 qq.com
7ba01be13d OPCUA安全策略添加全部选项 2023-05-25 00:58:47 +08:00
2248356998 qq.com
1a83d64db7 添加OPCDAClient调试页面 2023-05-25 00:11:00 +08:00
2248356998 qq.com
5b53014c40 修改程序根目录为文件所在目录 2023-05-24 14:37:45 +08:00
2248356998 qq.com
83685340af 默认添加服务守护支持 2023-05-24 14:32:21 +08:00
2248356998 qq.com
31e0cc4dec ModbusServer添加自定义数据类型;修复发布时静态文件路径错误 2023-05-24 12:31:20 +08:00
2248356998 qq.com
56b87fc1f5 Add copyright notices 2023-05-23 23:54:28 +08:00
2248356998 qq.com
6b956a2dd7 update TGTcpClient 2023-05-23 22:42:48 +08:00
2248356998 qq.com
1937623d7d 添加Modbus系列插件调试页面;添加Modbus组包解析缓存超时时间; 2023-05-23 20:50:44 +08:00
2248356998 qq.com
3b60b10945 update HardwareInfoService 2023-05-23 12:04:04 +08:00
2248356998 qq.com
7173acd350 nuget更新 2023-05-23 11:59:28 +08:00
2248356998 qq.com
6310d87338 硬件界面添加限值防报错 2023-05-23 11:46:19 +08:00
2248356998 qq.com
49a1ed7c18 初步添加插件驱动调试页面 2023-05-22 18:50:30 +08:00
2248356998 qq.com
d426e280d9 初步添加插件驱动调试页面 2023-05-22 18:41:09 +08:00
2248356998 qq.com
6154fb29f1 Razor文件格式化清理 2023-05-22 18:40:50 +08:00
2248356998 qq.com
97d48ef9d6 删除调试代码... 2023-05-22 17:09:21 +08:00
2248356998 qq.com
88992625c4 更改变量读取间隔限制最低为10ms 2023-05-22 15:17:22 +08:00
2248356998 qq.com
bc6eb44218 明确System.Management版本 2023-05-22 14:51:02 +08:00
2248356998 qq.com
cf9ccd799d 硬件信息获取添加异常拦截 2023-05-22 14:33:30 +08:00
2248356998 qq.com
ffa0e4e771 update adapter 2023-05-22 12:42:31 +08:00
2248356998 qq.com
60fa9c196c 代码清理 2023-05-21 22:39:33 +08:00
2248356998 qq.com
df860d22fb 优化opcua质量戳提示;添加数据转换基础方法;TCP等待返回时默认断开连接立即返回 2023-05-21 10:51:56 +08:00
2248356998 qq.com
cb46ff326c modbus 组包优化 2023-05-20 21:41:16 +08:00
2248356998 qq.com
f277a853ef 类型更换 2023-05-20 17:13:14 +08:00
2248356998 qq.com
9ae34f67c3 可指定OPCUA节点数据类型 2023-05-20 15:40:17 +08:00
2248356998 qq.com
c9223218cc 更新文档 2023-05-20 13:52:23 +08:00
2248356998 qq.com
c0dd645aba 添加自定义OPCUAServer数据类型 2023-05-20 12:34:06 +08:00
2248356998 qq.com
2e948eb5b6 导入规则优化,主动抛出名称重复错误 2023-05-19 22:55:53 +08:00
2248356998 qq.com
c3276889cf 更改插件时开启刷新设备属性 2023-05-19 22:26:37 +08:00
2248356998 qq.com
a76ca8282d 判断OPCUAServer状态时增加tryCatch 2023-05-19 21:27:20 +08:00
2248356998 qq.com
8ce6b8362f 判断OPCUAServer状态时增加tryCatch 2023-05-19 21:24:56 +08:00
2248356998 qq.com
842fb12f05 优化tcp拆包组包 2023-05-19 20:28:51 +08:00
2248356998 qq.com
d63e1511af 快捷方式null错误 2023-05-19 16:33:57 +08:00
2248356998 qq.com
278783b8e0 更新依赖包 2023-05-19 16:23:21 +08:00
2248356998 qq.com
d24e3c922d UDP通讯优化 2023-05-19 16:21:42 +08:00
2248356998 qq.com
1d02cd2283 添加链路锁 2023-05-19 14:27:47 +08:00
2248356998 qq.com
8edeb82a87 分包数量显示错误 2023-05-19 10:13:48 +08:00
2248356998 qq.com
146e9279de Modbus读写锁更新 2023-05-19 10:05:54 +08:00
2248356998 qq.com
47105f50a9 Modbus读写锁更新 2023-05-19 09:53:56 +08:00
2248356998 qq.com
16c9c80f37 删除sqlsugar旧版本代码,会导致sqlite不兼容 2023-05-19 09:15:32 +08:00
2248356998 qq.com
8e7e4bc95a 适配mysql;修复写入表达式转换;优化web页面写入体验 2023-05-18 23:38:40 +08:00
2248356998 qq.com
0aa3d2f930 数据库初始化修复 2023-05-18 22:24:27 +08:00
2248356998 qq.com
ce77755a1e 数据库连接自动释放 2023-05-18 21:06:06 +08:00
2248356998 qq.com
0f31f20c87 编辑页面添加 变量 允许远程写入选项 2023-05-18 21:03:44 +08:00
2248356998 qq.com
ee6da2aaa5 修复枚举类型在mysql中类型出错的问题 2023-05-18 20:58:14 +08:00
Diego2098
a35f087cd9 修改文件大小限制 2023-05-17 22:29:59 +08:00
Diego2098
6e029b44dd 更新设备读写锁 2023-05-17 22:05:31 +08:00
2248356998 qq.com
973c0cff34 touchSocket修复UDP在windows下重置连接的问题;更新nuget 2023-05-17 16:54:33 +08:00
2248356998 qq.com
2027eea6ac 代码格式清理 2023-05-17 16:49:25 +08:00
2248356998 qq.com
2f43692f33 修复CancellationTokenSource未释放导致Linux下偶发内存问题 2023-05-17 16:44:52 +08:00
2248356998 qq.com
6d24992f88 OPCUAServer节点数据类型增加在读取表达式转换后的判断 2023-05-16 09:05:58 +08:00
Diego2098
b4388a58d6 ModbusClient添加帧前时间 2023-05-15 22:45:55 +08:00
2248356998 qq.com
158aa05fac 修复using作用域导致获取的服务可能被释放的问题 2023-05-15 19:21:35 +08:00
2248356998 qq.com
f2731bf55e nuget更新 2023-05-15 18:55:27 +08:00
2248356998 qq.com
7304e99fce mqttClient同步间隔上传的实体类 2023-05-12 18:22:06 +08:00
2248356998 qq.com
02700b83eb update mqttClient 2023-05-12 16:40:55 +08:00
2248356998 qq.com
676b25acf9 MqttClient增加间隔上传选项;更改线程循环间隔说明定义 2023-05-12 16:31:57 +08:00
Diego2098
556359ea2d update readme 2023-05-10 21:18:43 +08:00
Diego2098
b72923e0f5 增加api控制采集启停等方法 2023-05-10 21:17:34 +08:00
Diego2098
115ac9f75e 提交kafka插件 2023-05-10 21:17:09 +08:00
2248356998 qq.com
32e36f6708 update readme 2023-05-10 17:49:52 +08:00
2248356998 qq.com
d949b7a4f9 共享链路写入变量修复;Nuget更新 2023-05-10 16:33:59 +08:00
2248356998 qq.com
eae1171ff5 更新文档 2023-05-09 17:51:55 +08:00
2248356998 qq.com
76a1b75a51 更新文档 2023-05-09 17:47:22 +08:00
2248356998 qq.com
8882c0daea OPCUAClient/Server 修改证书有效期为100年 2023-05-09 14:37:18 +08:00
2248356998 qq.com
07ebc16d59 添加控制台logo 2023-05-08 18:10:07 +08:00
2248356998 qq.com
0ceb109964 Code Cleanup 2023-05-08 18:03:32 +08:00
2248356998 qq.com
118b0d0038 add GetSciptValue 2023-05-08 17:45:13 +08:00
2248356998 qq.com
5e87067792 modbus rtu 粘包优化 2023-05-08 15:59:35 +08:00
2248356998 qq.com
c946a252e8 modbus rtu报文粘包优化;共享链路切换延时;上传插件添加报文界面 2023-05-08 15:57:33 +08:00
2248356998 qq.com
f9ad2ba1dd plc read with CancellationToken 2023-05-08 13:33:01 +08:00
2248356998 qq.com
0d0ecd33bd 粘包优化 2023-05-08 10:55:16 +08:00
2248356998 qq.com
e4b98fd05b Rpc条件bug修复 2023-05-07 18:06:27 +08:00
2248356998 qq.com
95a5933303 OPCUAServer修复匿名登录 2023-05-07 18:06:09 +08:00
2248356998 qq.com
da3b55fa64 更新文档 2023-05-07 16:56:41 +08:00
2248356998 qq.com
fbbabfb90e 属性顺序调整 2023-05-05 09:46:11 +08:00
2248356998 qq.com
f13da6830d 更新文档 2023-05-04 22:58:36 +08:00
2248356998 qq.com
f560a8e2f8 update CollectDeviceThread 2023-05-04 10:58:45 +08:00
2248356998 qq.com
56f1139c2f 更新文档 2023-05-02 22:11:55 +08:00
2248356998 qq.com
773bdfc1e2 更新1.5.0 2023-05-02 22:06:09 +08:00
2248356998 qq.com
f449666628 1、共享链路支持;
2、设备报文查看;
3、采集线程重构;
2023-05-02 21:58:11 +08:00
2248356998 qq.com
3f282de0ab mqtt重连锁优化 2023-04-27 11:19:32 +08:00
2248356998 qq.com
440dd8d22f 添加常用转换 2023-04-24 17:54:00 +08:00
2248356998 qq.com
dcff9de2f7 masa更新 2023-04-24 11:16:28 +08:00
2248356998 qq.com
a192866543 更新包 2023-04-24 09:26:47 +08:00
Diego2098
10081416de 报警后台服务去除多余接口
Signed-off-by: Diego2098 <2248356998@qq.com>
2023-04-19 00:48:13 +00:00
2248356998 qq.com
e2bed618f9 添加docker文件 2023-04-17 17:39:12 +08:00
2248356998 qq.com
03ab1f3823 单文件发布 2023-04-17 15:13:40 +08:00
2248356998 qq.com
ac8aeb63d9 update SqlSugarConfig 2023-04-17 11:58:41 +08:00
2248356998 qq.com
2e16d822fa 删除其他信息 2023-04-17 11:05:38 +08:00
2248356998 qq.com
e407d873fa 验证码更新修复 2023-04-17 09:05:44 +08:00
2248356998 qq.com
fd712a1dbe 1、字段null约束修改
2、ModbusServer绑定端口优化
2023-04-16 15:02:09 +08:00
2248356998 qq.com
e9028b40ce 更新readme 2023-04-16 13:34:29 +08:00
2248356998 qq.com
c9da3dee7c 更新1.4.0
注意Excel导入已不适用以前版本
1、去除动态更新插件
2、改用MiniExcel,支持动态导入,excel配置更简单
3、优化多处界面与后台逻辑,部分方法规范更名
4、修复外网地址获取错误等
2023-04-15 20:48:56 +08:00
2248356998 qq.com
c8c224e202 修复报警文本逻辑 2023-04-11 13:40:13 +08:00
2248356998 qq.com
f34559daaf 1,修复控制台报错(echarts.js问题,已删除)2,多处Dispose修正 2023-04-07 08:48:34 +08:00
2248356998 qq.com
9fefbf4c27 过滤 2023-04-06 14:46:24 +08:00
2248356998 qq.com
1af9fd73ea 历史数据库选择为sqlite时查询转换日期错误 2023-04-05 18:04:33 +08:00
2248356998 qq.com
75ef394eff 启用开发环境web详细日志,调整App.Razor位置,添加网页ico 2023-04-05 17:14:26 +08:00
2248356998 qq.com
ec6cc2c63e update console/file datetime format 2023-04-05 15:49:00 +08:00
2248356998 qq.com
06bc2e192b 更新readme 2023-04-04 18:41:56 +08:00
2248356998 qq.com
78701ec7c1 😀版本1.3.1 2023-04-04 17:55:52 +08:00
2248356998 qq.com
c925fab7e4 网关软件时间统一UTC 2023-04-04 17:55:15 +08:00
2248356998 qq.com
42fd72c164 update iotSharpClient 2023-04-04 11:07:05 +08:00
2248356998 qq.com
7fd160e1a2 😀 更新1.3.0 2023-04-04 10:15:34 +08:00
2248356998 qq.com
97a0d940eb update iotSharpClient 2023-04-04 09:25:16 +08:00
2248356998 qq.com
efaa099d81 update iotSharpClient 2023-04-04 09:23:22 +08:00
2248356998 qq.com
47864a804b IosSharpClient Rpc优化 2023-04-04 09:22:22 +08:00
2248356998 qq.com
91136c0e43 IotSharp Rpc方法完善 2023-04-04 09:19:48 +08:00
2248356998 qq.com
28c3b1bd61 添加写入多个变量的api方法 2023-04-04 09:18:58 +08:00
2248356998 qq.com
551352bc40 update IotSharpClient 2023-04-03 20:16:38 +08:00
2248356998 qq.com
e73c24c925 更新种子 2023-04-03 19:34:12 +08:00
2248356998 qq.com
7ec4c286cc 添加IotSharp插件 2023-04-03 19:30:52 +08:00
2248356998 qq.com
6705e2ec4b 分页显示令牌 2023-04-03 15:29:35 +08:00
2248356998 qq.com
6f0373063b 后台启动时Furion RootServices NULL值 2023-04-03 15:19:59 +08:00
2248356998 qq.com
f64eef60b5 修复不存在采集设备时,初始化报警/历史服务bug 2023-04-03 14:12:56 +08:00
2248356998 qq.com
89546bf86b 弹窗消息在SignalR订阅方法中需InvokeAsync 2023-04-03 13:35:14 +08:00
2248356998 qq.com
793678feca 修复规范化结果包装2次导致登录返回结果不正确的问题 2023-04-03 12:45:19 +08:00
2248356998 qq.com
923cc3019a 更新演示地址 2023-04-03 10:47:49 +08:00
2248356998 qq.com
10eb98a5f6 readme 2023-04-02 18:12:18 +08:00
2248356998 qq.com
bd9e89d8dd readme 2023-04-02 18:08:56 +08:00
2248356998 qq.com
1926b4ce73 更新readme 2023-04-02 18:05:54 +08:00
2248356998 qq.com
4ef3062d74 更新readme 2023-04-02 18:05:28 +08:00
2248356998 qq.com
abb6e0f60f 更新包 2023-04-02 17:10:32 +08:00
2248356998 qq.com
f204d8d84e 添加注释 2023-04-02 16:59:46 +08:00
2248356998 qq.com
fa301656f1 调整依赖,添加关系图 2023-04-01 17:28:35 +08:00
2248356998 qq.com
7e1221028f 调整依赖,更新版本1.2.1 2023-04-01 15:45:02 +08:00
2248356998 qq.com
41308cb2dd 整理 2023-04-01 13:57:57 +08:00
2248356998 qq.com
130600521c 😀 OPCUAServer支持历史查询数据 2023-03-31 18:32:55 +08:00
2248356998 qq.com
cd57548a48 添加ThingsGateway.Foundation注释 2023-03-31 16:25:33 +08:00
2248356998 qq.com
efacc99f76 硬件信息获取添加延时 2023-03-30 20:51:08 +08:00
2248356998 qq.com
f0d236e172 脚本显示优化 2023-03-30 19:51:38 +08:00
2248356998 qq.com
a8118bd8c6 更新文档 2023-03-30 19:39:35 +08:00
2248356998 qq.com
0e58f2ef53 mqtt/mq上传 添加上传实体自定义脚本 2023-03-30 19:07:18 +08:00
2248356998 qq.com
f4b22b3a0c 表达式整理 2023-03-30 16:23:03 +08:00
2248356998 qq.com
df5bd281c7 更新版本 2023-03-30 14:12:39 +08:00
2248356998 qq.com
a3f23837ce 更新文档 2023-03-30 14:11:16 +08:00
2248356998 qq.com
612d989b97 readme 2023-03-30 14:06:03 +08:00
2248356998 qq.com
42c01ee9a2 更新readme,nuget 2023-03-30 14:05:00 +08:00
2248356998 qq.com
14074db591 删除多余代码 2023-03-30 13:53:57 +08:00
2248356998 qq.com
43dfdd7942 opcua 写入添加用户名日志 2023-03-30 13:32:32 +08:00
2248356998 qq.com
f397b97ccf 去除ua数据类型验证 2023-03-30 13:20:26 +08:00
2248356998 qq.com
95f8716144 更新readme 2023-03-30 13:13:44 +08:00
2248356998 qq.com
17ba472b2e 类命名错误更改 2023-03-30 13:11:46 +08:00
2248356998 qq.com
42d82571ab 发布文件添加 2023-03-30 13:11:43 +08:00
2248356998 qq.com
9119a28141 添加OPCUAServer插件,修复个别bug 2023-03-30 13:10:31 +08:00
Diego2098
a32263d838 1、OPCUAClient修复客户端证书未自动生成
2、修复异步方法错误使用
2023-03-30 00:15:13 +08:00
2248356998 qq.com
208ae2bb88 添加部分代码注释 2023-03-29 16:22:01 +08:00
2248356998 qq.com
4d85462a85 驱动支持主机名称 2023-03-29 11:07:21 +08:00
2248356998 qq.com
f601aa9ca0 修复初始化失败导致的一系列问题 2023-03-29 10:53:42 +08:00
2248356998 qq.com
8aee3ad455 上传插件间隔时间修正 2023-03-28 16:40:00 +08:00
2248356998 qq.com
6a2a1e9561 历史变量enable失效 2023-03-28 16:38:29 +08:00
2248356998 qq.com
5f8786c9dc 历史报表时间格式改为yyyy-MM-dd HH:mm:ss ffffff 2023-03-28 16:31:53 +08:00
2248356998 qq.com
73f1d3eead 采集设备状态判断错误 2023-03-28 16:21:30 +08:00
2248356998 qq.com
2bf21bb3c3 采集设备状态判断错误 2023-03-28 15:55:01 +08:00
2248356998 qq.com
f80f0dbb11 修复历史保存值 频繁时出现值相同的问题 2023-03-28 15:14:30 +08:00
2248356998 qq.com
37518c70c4 opc 活动时间修正 2023-03-28 14:15:14 +08:00
2248356998 qq.com
e5951b5bef 添加写入采集时间的接口,而不是固定DateTime.Now 2023-03-28 14:03:11 +08:00
2248356998 qq.com
ab320bd90b 更新tcpclient 2023-03-28 13:52:33 +08:00
2248356998 qq.com
7bd36b5371 修复linux 分隔符错误问题 2023-03-28 11:48:16 +08:00
2248356998 qq.com
b882b0f2bc 更改插件文件不存在时的日志信息 2023-03-28 10:21:39 +08:00
2248356998 qq.com
38d7ae73cc 更改项目引用结构 2023-03-28 10:21:23 +08:00
2248356998 qq.com
4527c6ee5d swagger文档 2023-03-27 19:01:04 +08:00
Iot边缘设备
85829e70c1 !5 添加部署文档
* 添加部署文档
2023-03-27 10:22:30 +00:00
2248356998 qq.com
256c08d82a 属性/字段缓存获取只包含public 2023-03-27 14:49:09 +08:00
2248356998 qq.com
c2ce03c047 更新masa-1.0.0-preview.10 2023-03-27 14:27:15 +08:00
2248356998 qq.com
f2af19e198 优化变量上传属性页 2023-03-27 12:00:10 +08:00
2248356998 qq.com
930b7c092d 采集设备活动时间应正常刷新 2023-03-27 10:51:54 +08:00
2248356998 qq.com
00757c69c6 添加设备复制功能 2023-03-27 10:42:02 +08:00
2248356998 qq.com
55f267d0fc update code collation 2023-03-27 08:49:10 +08:00
2248356998 qq.com
6b96aff6e8 update code collation 2023-03-26 19:48:08 +08:00
2248356998 qq.com
32b773a8fa 更新nuget包 2023-03-26 18:52:16 +08:00
2248356998 qq.com
03089adad6 发行1.10版本 2023-03-26 18:10:12 +08:00
2248356998 qq.com
4a1fe746ab 实时数据界面优化 2023-03-26 17:39:05 +08:00
2248356998 qq.com
aa52c05d2c 1、采用异步AutoResetEvent,修复多个设备下采集太慢的问题
2、弃用IntelligentConcurrentQueue(touchsocket集成类)
2023-03-26 17:32:29 +08:00
2248356998 qq.com
26407a43e7 1、优化设备状态页,减少signalr刷新所需包大小
2、日志前置等级添加
2023-03-24 18:32:10 +08:00
2248356998 qq.com
a02934bf19 修复导出表格时,null值报错 2023-03-23 22:09:53 +08:00
2248356998 qq.com
09c65fba09 导入错误时,忽略大于10行的错误信息显示 2023-03-23 20:01:19 +08:00
2248356998 qq.com
4305c727d0 更新readme 2023-03-22 15:29:35 +08:00
2248356998 qq.com
188339897f 优化log等级、json配置注解 2023-03-22 15:26:42 +08:00
2248356998 qq.com
4ecff9a707 OPCUA写入测试 2023-03-22 14:06:32 +08:00
2248356998 qq.com
355aed49c6 设备运行状态页面改为扩展 2023-03-22 13:46:23 +08:00
2248356998 qq.com
4717b6b0f0 更新文档 2023-03-22 10:44:44 +08:00
Diego2098
45ebe9048d rbmq插件优化 2023-03-21 23:38:00 +08:00
Diego2098
b2170c49a3 分包失败提示 2023-03-21 23:28:30 +08:00
Diego2098
dc2f4d6115 设备组列宽调整,补充上传设备组 2023-03-21 23:10:59 +08:00
Diego2098
1eb132440f 1、更新DataTypeEnum
2、OPCUA写入时需实际数据类型
2023-03-21 22:47:10 +08:00
Diego2098
a464bbc37a 更新Tests 2023-03-21 22:46:12 +08:00
2248356998 qq.com
ed995697c2 表格无数据时,修改默认宽度 2023-03-21 18:17:18 +08:00
2248356998 qq.com
163cd84c7b S7nuget版本 2023-03-21 15:44:50 +08:00
2248356998 qq.com
293d7cc292 masa nuget版本更改 2023-03-21 14:31:36 +08:00
2248356998 qq.com
5de1b4e74c GC策略修改 2023-03-21 14:07:17 +08:00
2248356998 qq.com
7b474975da 更新文档 2023-03-21 14:03:49 +08:00
2248356998 qq.com
beab51516b 更新文档 2023-03-21 13:56:14 +08:00
2248356998 qq.com
fe8685a50c 修改s7属性注释 2023-03-21 13:51:05 +08:00
2248356998 qq.com
f9af5d0885 更新readme 2023-03-21 13:49:10 +08:00
2248356998 qq.com
e8136a9720 更新S7协议插件 2023-03-21 13:45:58 +08:00
2248356998 qq.com
531e5d4556 修改S7特殊方法 2023-03-21 13:41:32 +08:00
2248356998 qq.com
e66255963a 修改s7 2023-03-21 13:38:43 +08:00
2248356998 qq.com
246aac8ee4 添加西门子S7协议插件 2023-03-21 13:37:17 +08:00
2248356998 qq.com
23cfeff685 版本恢复 2023-03-21 11:45:18 +08:00
2248356998 qq.com
a5e7e0d126 更新nuget,暂缓链路复用功能 2023-03-20 13:34:59 +08:00
2248356998 qq.com
5bebc30ba0 默认不开启转储 2023-03-20 09:22:11 +08:00
2248356998 qq.com
0e7057f5b9 更新opcda文档 2023-03-19 23:18:24 +08:00
2248356998 qq.com
7c6c365ba4 格式清理 2023-03-19 23:12:58 +08:00
2248356998 qq.com
424c9bb0c5 修复null值报错 2023-03-19 23:02:08 +08:00
2248356998 qq.com
9d0f26594c 添加设备组,变量按设备组别搜索 2023-03-19 22:56:02 +08:00
2248356998 qq.com
99c17de079 更新readme,opcda核心库提示 2023-03-19 20:40:54 +08:00
2248356998 qq.com
b1e3dd0af6 尝试修复中文在mac上乱码 2023-03-19 17:29:01 +08:00
2248356998 qq.com
261cb89530 修正中文在mac上乱码 2023-03-19 16:45:14 +08:00
2248356998 qq.com
ff6773ba37 删除未知文件 2023-03-19 16:35:24 +08:00
2248356998 qq.com
bdfbbfcbbd Merge branch 'master' of https://gitee.com/diego2098/ThingsGateway 2023-03-19 16:34:26 +08:00
2248356998 qq.com
0c4cd56758 添加设备组功能 2023-03-19 16:34:15 +08:00
士心
4a36658321 !4 修复MacOS环境下,数据库报错的问题
* 修复macos环境下,数据库报错
2023-03-19 08:28:54 +00:00
2248356998 qq.com
7aae938685 准备更新设备组/变量组功能 2023-03-19 11:49:06 +08:00
2248356998 qq.com
3723401e7a 更新ReadMe 2023-03-18 17:56:18 +08:00
2248356998 qq.com
70631366a9 1、最后校验失败时应该提示日志
2、暂停时设备状态修正
2023-03-18 17:49:08 +08:00
2248356998 qq.com
0e40bbda3e 更新文档 2023-03-18 16:47:29 +08:00
2248356998 qq.com
e9aa475398 ByteBlock实际长度 2023-03-18 16:45:39 +08:00
2248356998 qq.com
8d2a811184 修正串口描述类ToString 2023-03-18 16:32:15 +08:00
2248356998 qq.com
dd7f5b6700 更新文档 2023-03-18 16:23:23 +08:00
2248356998 qq.com
a4f6277737 更新nuget 2023-03-18 16:16:27 +08:00
2248356998 qq.com
c2bfaacbb7 更新readme 2023-03-18 16:15:14 +08:00
2248356998 qq.com
a17cbfa2d4 添加ModbusRtu种子数据 2023-03-18 16:14:37 +08:00
2248356998 qq.com
fb9a101555 添加ModbusRtu插件 2023-03-18 16:14:25 +08:00
2248356998 qq.com
e319cf0200 添加串口基础类 2023-03-18 16:14:08 +08:00
2248356998 qq.com
0a8395ef6a 更新文档 2023-03-17 18:19:14 +08:00
2248356998 qq.com
38df5e01be 更新文档 2023-03-17 17:52:04 +08:00
2248356998 qq.com
ebd891a868 种子数据更改 2023-03-17 17:51:58 +08:00
2248356998 qq.com
4ab2395cbe 采集检测间隔修改 2023-03-17 17:40:34 +08:00
2248356998 qq.com
5f1f989fc9 上传插件添加循环间隔属性 2023-03-17 17:30:06 +08:00
2248356998 qq.com
44b709eee3 添加rbmq插件种子数据 2023-03-17 16:30:02 +08:00
2248356998 qq.com
d0d7726597 rbmq插件文件夹更名 2023-03-17 16:16:23 +08:00
2248356998 qq.com
054c342aeb 优化内存queue,修复mqtt插件可能超出字节限制的情况 2023-03-17 16:04:24 +08:00
2248356998 qq.com
c79c33baf7 添加rbmq插件 2023-03-17 16:03:44 +08:00
2248356998 qq.com
23b00e35b2 设备禁用时,变量绑定的设备对应选项会显示禁用(灰色) 2023-03-17 11:29:14 +08:00
2248356998 qq.com
fe51079266 更新readme 2023-03-16 17:33:25 +08:00
2248356998 qq.com
0791b0bbee 选择插件时自动更新属性 2023-03-16 17:33:17 +08:00
2248356998 qq.com
dbf04c8eeb 更新赞助名单 2023-03-16 11:35:58 +08:00
2248356998 qq.com
6204256df8 限流服务默认不开启 2023-03-16 09:47:20 +08:00
2248356998 qq.com
93cc8c2327 增加简易定时看板 2023-03-15 16:01:54 +08:00
2248356998 qq.com
68a2e5bbbc 更新历史配置说明 2023-03-15 09:11:33 +08:00
Diego2098
72792153f2 !3 update handbook/docs/05、网关配置/5.5、其他配置.mdx.
Merge pull request !3 from zhubanghao/N/A
2023-03-14 13:39:16 +00:00
zhubanghao
88b6ef1897 update handbook/docs/05、网关配置/5.5、其他配置.mdx.
Signed-off-by: zhubanghao <58813184@qq.com>
2023-03-14 13:05:33 +00:00
990 changed files with 133831 additions and 29862 deletions

63
.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

13
.gitignore vendored
View File

@@ -360,4 +360,15 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
/src/Plugins/Other
/src/ThingsGateway.Web.Server/*.db
/src/PluginPro*/
/src/*Pro*
/src/TestResults*/
/src/ThingsGateway.Web.Server/ThingsGateway.db
/handbook/

BIN
Image/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
Image/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
Image/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

BIN
Image/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
Image/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
Image/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
Image/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

BIN
Image/8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
Image/9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
Image/gitLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
Image/pay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

View File

@@ -1,4 +1,3 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -87,7 +86,7 @@
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Cachetribution. You may reproduce and distribute copies of the
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
@@ -187,16 +186,16 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023-present Diego
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

180
README.md
View File

@@ -1,95 +1,129 @@
# ThingsGateway

<div align='center'>
<img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/gitLogo.png" height=100 />
</div>
## Introduction
#### 介绍

A cross-platform, high-performance edge data collection gateway based on net8, capable of handling millions of data points per.

基于Net6/7+Blazor Server的跨平台边缘采集网关支持南北端插件式开发
并拥有较完善的北端Rpc权限管理。
## Documentation
[Github地址](https://github.com/kimdiego2098/ThingsGateway)

[Documentation](https://thingsgateway.cn/).

[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)

<div >
如果对您有帮助请点击右上角⭐Star关注感谢支持开源
</div>
### Plugin List
#### 开源说明

Apache 2.0+[附加协议](https://diego2098.gitee.io/thingsgateway-docs/docs/)
#### Data Collection Plugins
Apache 2.0 开源协议的核心内容是以保护和尊重原作者的著作权为主要目的。对使用复制修改商用不做过多限制但必须包含原著的License信息。
#### 功能亮点
- Blazor Server架构开发部署更简单
- 采集/上传配置完全支持Excel导入导出
- 插件式驱动,方便驱动二次开发
- 时序数据库存储
- 实时/历史报警(Sql转储),支持布尔/高低限值
#### 演示
http://120.24.62.140:5000/
默认账户密码superAdmin 111111
| Plugin Name | Remarks |
| ----------- | ------------------------------------------------------------- |
| Modbus | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links |
| SiemensS7 | Siemens PLC S7 series |
| Dlt6452007 | Supports Serial/Tcp/Udp links |
| OpcDaMaster | Compiled for 64-bit |
| OpcUaMaster | Supports certificate login, object extension, Json read/write |
#### 社区版采集插件
> 支持分包解析/订阅
- Modbus(Rtu/Tcp/Udp)
- OPCDAClient支持导入节点
- OPCUAClient支持导入节点
- 西门子S7协议
#### Business Plugins
#### 社区版上传插件
> 支持Rpc写入
- Modbus Server
- OPCUA Server (支持历史查询)
- Mqtt Server (支持自定义json)
- Mqtt Client (支持自定义json)
- IotSharp Client (IotSharp网关插件Rpc待测试)
> 不支持Rpc
- RabbitMQ (支持自定义json)
- Kafka
| Plugin Name | Remarks |
| ---------------- | ------------------------------------------------------------------------------------------------- |
| ModbusSlave | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links, supports Rpc reverse writing |
| OpcUaServer | OpcUa server, supports Rpc reverse writing |
| MqttClient | Mqtt client, supports Rpc reverse writing, script-customizable upload content |
| MqttServer | Mqtt server, supports WebSocket, supports Rpc reverse writing, script-customizable upload content |
| KafkaProducer | Script-customizable upload content |
| RabbitMQProducer | Script-customizable upload content |
| SqlDB | Relational database storage, supports historical storage and real-time data updates |
| SqlHistoryAlarm | Alarm historical data relational database storage |
| TDengineDB | Time-series database storage |
| QuestDB | Time-series database storage |
#### nuget

## License

[Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE)

## Demo

[Demo](http://47.119.161.158:5000/)

Account: **SuperAdmin**

Password: **111111**

**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
## Docker
```shell
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
- Modbus库支持ModbusTcp、ModbusRtu、ModbusRtuOverTcp、ModbusUdp、ModbusServer等
``` powershell
dotnet add package ThingsGateway.Foundation.Adapter.Modbus
```
- OPCDA客户端库支持X64支持NetCore支持检测重连
``` powershell
dotnet add package ThingsGateway.Foundation.Adapter.OPCDA
```
- OPCUA客户端库
``` powershell
dotnet add package ThingsGateway.Foundation.Adapter.OPCUA
```

- S7库
``` powershell
dotnet add package ThingsGateway.Foundation.Adapter.Siemens
```
## Sponsorship
#### 效果图
<table>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/1.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/2.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/3.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/4.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/5.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/6.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/7.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/8.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/9.png"/></td>
</tr>
</table>

[Sponsorship Approach](https://thingsgateway.cn/docs/1000)

## Community
#### 文档

QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)

使用前请查看Gitee Pages [文档站点](https://diego2098.gitee.io/thingsgateway-docs/)
## Pro Plugins
#### 特别鸣谢
- Furion[https://dotnetchina.gitee.io/furion](https://dotnetchina.gitee.io/furion)
- SqlSugar[https://gitee.com/dotnetchina/SqlSugar](https://gitee.com/dotnetchina/SqlSugar)
- Simple.Admin[https://gitee.com/zxzyjs/SimpleAdmin](https://gitee.com/zxzyjs/SimpleAdmin)
- Masa.Blazor[https://www.masastack.com/blazor](https://www.masastack.com/blazor)
- MiniExcel[https://gitee.com/dotnetchina/MiniExcel](https://gitee.com/dotnetchina/MiniExcel)
- TouchSocket[https://gitee.com/rrqm_home/touchsocket](https://gitee.com/rrqm_home/touchsocket)
- IdGenerator[https://github.com/yitter/idgenerator](https://github.com/yitter/idgenerator)
- CodingSeb.ExpressionEvaluator[https://github.com/codingseb/ExpressionEvaluator](https://github.com/codingseb/ExpressionEvaluator)
- Hardware.Info[https://github.com/Jinjinov/Hardware.Info](https://github.com/Jinjinov/Hardware.Info)
- UAParser[https://github.com/ua-parser/uap-csharp](https://github.com/ua-parser/uap-csharp)
#### 补充说明
* 使用OPC相关插件时请遵循OPC基金会的授权规则
* 使用OPCDA插件时需安装OPC核心库[文件地址](https://gitee.com/diego2098/ThingsGateway/attach_files)
#### 支持作者
如果对您有帮助请点击右上角⭐Star关注感谢支持开源
若希望捐赠项目请查看以下捐赠码或使用Gitee捐赠功能
<img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/pay.png" height=180 />
#### 联系作者
* QQ群605534569
* 邮箱2248356998@qq.com

[Plugin List](https://thingsgateway.cn/docs/1001)

View File

@@ -1,75 +0,0 @@
# ThingsGateway
## 介绍
基于net8的跨平台高性能边缘采集网关单机采集数据点位可达百万
## 文档
[文档](https://thingsgateway.cn/)
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
### 插件列表
#### 采集插件
| 插件名称 | 备注 |
| ----------- | ------------------------------------- |
| Modbus | Rtu/Tcp报文格式支持串口/Tcp/Udp链路 |
| SiemensS7 | 西门子PLC S7系列 |
| Dlt6452007 | 支持串口/Tcp/Udp链路 |
| OpcDaMaster | 64位编译 |
| OpcUaMaster | 支持证书登录扩展对象Json读写 |
#### 业务插件
| 插件名称 | 备注 |
| ---------------- | ---------------------------------------------------------- |
| ModbusSlave | Rtu/Tcp报文格式支持串口/Tcp/Udp链路支持Rpc反写 |
| OpcUaServer | OpcUa服务端支持Rpc反写 |
| MqttClient | Mqtt客户端支持Rpc反写脚本自定义上传内容 |
| MqttServer | Mqtt服务端支持WebSocket支持Rpc反写脚本自定义上传内容 |
| KafkaProducer | 脚本自定义上传内容 |
| RabbitMQProducer | 脚本自定义上传内容 |
| SqlDB | 关系数据库存储,支持历史存储和实时数据更新 |
| SqlHistoryAlarm | 报警历史数据关系数据库存储 |
| TDengineDB | 时序数据库存储 |
| QuestDB | 时序数据库存储 |
## 协议
[Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE)
## 演示
[ThingsGateway演示地址](http://47.119.161.158:5000/)
账户 : **SuperAdmin**
密码 : **111111**
**右上角个人弹出框中,切换到物联网关模块**
## Docker
```shell
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
```
## 赞助
[赞助途径](https://thingsgateway.cn/docs/1000)
## 社区
QQ群605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
## Pro插件
[插件列表](https://thingsgateway.cn/docs/1001)

BIN
icon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,248 +0,0 @@
root = true
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
###############################
# Core EditorConfig Options #
###############################
# All files
[*]
indent_style = space
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
spelling_exclusion_path = .\exclusion.dic
# Microsoft .NET properties
csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True
dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field
dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef
dotnet_naming_rule.unity_serialized_field_rule.severity = warning
dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style
dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = *
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
# ReSharper properties
resharper_autodetect_indent_settings = true
resharper_formatter_off_tag = @formatter:off
resharper_formatter_on_tag = @formatter:on
resharper_formatter_tags_enabled = true
resharper_new_line_before_while = true
resharper_place_attribute_on_same_line = false
resharper_show_autodetect_configure_formatting_tip = false
resharper_use_indent_from_vs = false
# ReSharper inspection severities
resharper_arrange_redundant_parentheses_highlighting = hint
resharper_arrange_this_qualifier_highlighting = hint
resharper_arrange_type_member_modifiers_highlighting = hint
resharper_arrange_type_modifiers_highlighting = hint
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
resharper_built_in_type_reference_style_highlighting = hint
resharper_redundant_base_qualifier_highlighting = warning
resharper_suggest_var_or_type_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint
resharper_suggest_var_or_type_simple_types_highlighting = hint
resharper_web_config_module_not_resolved_highlighting = warning
resharper_web_config_type_not_resolved_highlighting = warning
resharper_web_config_wrong_module_highlighting = warning
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
[*.{cs,css,js,json,*html,razor,txt,log}]
charset = utf-8-bom
[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]
indent_size = 2
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
[*.json]
indent_size = 2
[*.{ps1,psm1}]
indent_size = 4
[*.sh]
indent_size = 4
end_of_line = lf
###############################
# .NET Coding Conventions #
###############################
[*.{cs,vb}]
# Organize usings
dotnet_sort_system_directives_first = false
# this. preferences
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_readonly_field = true:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
###############################
# Naming Conventions #
###############################
# Style Definitions
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# Use PascalCase for constant fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
end_of_line = crlf
dotnet_style_prefer_collection_expression = when_types_exactly_match:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
dotnet_code_quality_unused_parameters = all:suggestion
###############################
# C# Coding Conventions #
###############################
[*.cs]
# var preferences
csharp_style_var_for_built_in_types = true:silent
csharp_style_var_when_type_is_apparent = true:silent
csharp_style_var_elsewhere = true:silent
csharp_prefer_static_local_function = true:silent
# Expression-bodied members
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
# Null-checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
# Expression-level preferences
csharp_prefer_braces = true:silent
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
###############################
# C# Formatting Rules #
###############################
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
# Space preferences
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
###############################
# VB Coding Conventions #
###############################
[*.vb]
# Modifier preferences
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
[*.cs]
# Add file header
file_header_template = ------------------------------------------------------------------------------\n此代码版权声明为全文件覆盖如有原作者特别声明会在下方手动补充\n此代码版权除特别声明外的代码归作者本人Diego所有\n源代码使用协议遵循本仓库的开源协议及附加协议\nGitee源代码仓库https://gitee.com/diego2098/ThingsGateway\nGithub源代码仓库https://github.com/kimdiego2098/ThingsGateway\n使用文档https://thingsgateway.cn/\nQQ群605534569\n------------------------------------------------------------------------------
csharp_style_namespace_declarations = file_scoped:suggestion
csharp_style_expression_bodied_local_functions = true:silent
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_prefer_readonly_struct_member = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
csharp_style_prefer_switch_expression = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion

View File

@@ -1,7 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
</Project>

View File

@@ -1,18 +0,0 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
namespace AutoUpdate;
internal class Program
{
private static void Main(string[] args)
{
}
}

View File

@@ -1,40 +0,0 @@
{
"$schema": "null",
"AllowedHosts": "*",
"AppSettings": {
"InjectSpecificationDocument": true, // 生产环境是否开启Swagger
"ExternalAssemblies": [ "Plugins" ], // 插件目录
// nuget动态加载的程序集
"SupportPackageNamePrefixs": [
"ThingsGateway.Foundation.Razor",
"ThingsGateway.Debug.Razor",
"ThingsGateway.Core",
"ThingsGateway.Razor"
]
},
"DynamicApiControllerSettings": {
//"DefaultRoutePrefix": "api", // 默认路由前缀
"CamelCaseSeparator": "", // 驼峰命名分隔符
"SplitCamelCase": false, // 切割骆驼(驼峰)/帕斯卡命名
"LowercaseRoute": false, // 小写路由格式
"AsLowerCamelCase": true, // 小驼峰命名(首字母小写)
"KeepVerb": false, // 保留动作方法请求谓词
"KeepName": false // 保持原有名称不处理
},
"FriendlyExceptionSettings": {
"DefaultErrorMessage": "系统异常,请联系管理员",
"ThrowBah": true, // 是否将 Oops.Oh 默认抛出为业务异常
"LogError": false // 是否输出异常日志
},
"CorsAccessorSettings": {
"PolicyName": "ThingsGateway",
"WithExposedHeaders": [ "Content-Disposition", "X-Pagination", "access-token", "x-access-token" ], // 如果前端不代理且是axios请求
"SignalRSupport": true // 启用 SignalR 跨域支持
}
}

View File

@@ -1,33 +0,0 @@
{
//BootstrapBlazor配置
"BootstrapBlazorOptions": {
"ToastDelay": 4000,
"MessageDelay": 4000,
"SwalDelay": 4000,
"EnableErrorLogger": true,
"FallbackCulture": "zh-CN",
"SupportedCultures": [
"zh-CN",
"en-US",
"zh-TW"
],
"DefaultCultureInfo": "zh-CN", //修改默认语言
"TableSettings": {
"CheckboxColumnWidth": 36
},
"IgnoreLocalizerMissing": true,
"StepSettings": {
"Short": 1,
"Int": 1,
"Long": 1,
"Float": 0.1,
"Double": 0.01,
"Decimal": 0.01
}
}
}

View File

@@ -1,48 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"EventLog": {
"LogLevel": {
"Default": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
} //windows事件输出日志等级
},
"Console": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
} //控制台输出日志等级
},
"BackendLog": {
"LogLevel": {
"Default": "Warning"
}
}
},
"Monitor": {
"GlobalEnabled": false, // 启用全局拦截日志
"IncludeOfMethods": [], // 拦截特定方法当GlobalEnabled=false有效
"ExcludeOfMethods": [], // 排除特定方法当GlobalEnabled=true有效
"BahLogLevel": "Information", // Oops.Oh 和 Oops.Bah 业务日志输出级别
"WithReturnValue": true, // 是否包含返回值默认true
"ReturnValueThreshold": 500, // 返回值字符串阈值默认0全量输出
"JsonBehavior": "None", // 是否输出Json默认None(OnlyJson、All)
"JsonIndented": false, // 是否格式化Json
"UseUtcTimestamp": false // 时间格式UTC、LOCAL
},
//日志配置
"LogJob": {
"DaysAgo": 10 //清理10天前日志
}
}

View File

@@ -1,40 +0,0 @@
{
"Menu": {
"MenuItems": [
{
"Url": "/",
"Text": "首页"
},
{
"Text": "Modbus",
"Items": [
{
"Url": "/ModbusMaster",
"Text": "ModbusMaster"
},
{
"Url": "/ModbusSlave",
"Text": "ModbusSlave"
}
]
},
{
"Url": "/SiemensS7Master",
"Text": "Siemens"
},
{
"Url": "/Dlt645_2007Master",
"Text": "Dlt645_2007Master"
},
{
"Url": "/OpcUaMaster",
"Text": "OpcUaMaster"
},
{
"Url": "/OpcDaMaster",
"Text": "OpcDaMaster"
}
]
}
}

View File

@@ -1,13 +0,0 @@
{
//网站配置
"Website": {
"Copyright": "版权所有 © 2023-present Diego",
"IsShowAbout": true, //是否显示关于页面
"SourceUrl": "https://gitee.com/diego2098/ThingsGateway",
"WikiUrl": "https://thingsgateway.cn/",
"QQGroup1Link": "http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569",
"QQGroup1Number": "605534569",
"Title": "ThingsGateway",
"Demo": false
}
}

View File

@@ -1,7 +0,0 @@
{
"ThingsGateway.Debug.MainLayout": {
"FullScreenButton": "Full Screen",
"About": "About"
}
}

View File

@@ -1,6 +0,0 @@
{
"ThingsGateway.Debug.MainLayout": {
"FullScreenButton": "全屏",
"About": "关于"
}
}

View File

@@ -1,6 +0,0 @@
{
"ThingsGateway.Debug.MainLayout": {
"FullScreenButton": "全屏",
"About": "關於"
}
}

View File

@@ -1,74 +0,0 @@
@inherits LayoutComponentBase
@layout BaseLayout
@namespace ThingsGateway.Debug
@using BootstrapBlazor.Components
@using ThingsGateway.Extension
@using ThingsGateway.NewLife.Extension
@using ThingsGateway.Razor
@inject NavigationManager NavigationManager
<div class="mainlayout">
<Layout SideWidth="0" IsPage="true" IsFullSide="true" IsFixedHeader="true"
ShowGotoTop="true" ShowCollapseBar="true" Menus="@MenuService.MenuItems"
AllowDragTab=true AdditionalAssemblies="@_assemblyList"
UseTabSet="false" TabDefaultUrl="/">
<Header>
<div class="flex-fill"></div>
@* 搜索框 *@
<GlobalSearch Menus=@(MenuService.SameLevelMenuItems) />
@* 语言选择 *@
<div class="d-none d-xl-flex ">
<CultureChooser />
</div>
@* 全屏按钮 *@
<FullScreenButton class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-arrows-alt"
TooltipPlacement=Placement.Bottom TooltipText="@Localizer[nameof(FullScreenButton)]" />
@if (WebsiteOption.Value.IsShowAbout)
{
<Button OnClick="ShowAbout" class="layout-header-bar d-none d-lg-flex px-2" Icon="fa fa-info" Color="Color.None" TooltipText="@Localizer[nameof(About)]" />
}
@* 版本号 *@
<div class="px-1 navbar-header-text d-none d-lg-block">@_versionString</div>
@* 主题切换 *@
@* <ThemeToggle /> *@
<ThemeProvider class="layout-header-bar d-none d-lg-flex px-0"></ThemeProvider>
</Header>
<Side>
<div class="layout-banner">
<span class="avatar">
@WebsiteOption.Value.Title?.GetNameLen2()
</span>
<div class="layout-title d-flex align-items-center justify-content-center">
<span>@WebsiteOption.Value.Title</span>
</div>
</div>
</Side>
<Main>
<Tab ClickTabToNavigation="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
Menus="@MenuService.MenuItems" AdditionalAssemblies="@_assemblyList"
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
{
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
})>
</Tab>
</Main>
<NotAuthorized>
<Redirect />
</NotAuthorized>
</Layout>
</div>

View File

@@ -1,80 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using ThingsGateway.Razor;
namespace ThingsGateway.Debug;
public partial class MainLayout
{
private List<Assembly> _assemblyList = new();
private string _versionString = string.Empty;
[Inject]
[NotNull]
private DialogService? DialogService { get; set; }
[Inject]
[NotNull]
private IStringLocalizer<MainLayout>? Localizer { get; set; }
[Inject]
[NotNull]
private IMenuService? MenuService { get; set; }
[Inject]
[NotNull]
private IAppVersionService? VersionService { get; set; }
[Inject]
[NotNull]
private IOptions<WebsiteOptions>? WebsiteOption { get; set; }
protected override void OnInitialized()
{
_assemblyList = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a =>
a.GetTypes()).Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
&& u.IsDefined(typeof(Microsoft.AspNetCore.Components.RouteAttribute), true)).Select(a => a.Assembly)
//.Where(a => a != typeof(BlazorApp).Assembly)
.Distinct().ToList();
base.OnInitialized();
}
protected override Task OnInitializedAsync()
{
_versionString = $"v{VersionService.Version}";
return base.OnInitializedAsync();
}
private async Task ShowAbout()
{
DialogOption? op = null;
op = new DialogOption()
{
IsScrolling = false,
Size = Size.Medium,
ShowFooter = false,
Title = Localizer["About"],
BodyTemplate = BootstrapDynamicComponent.CreateComponent<About>().Render(),
};
await DialogService.Show(op);
}
}

View File

@@ -1,138 +0,0 @@
::deep .avatar {
border-radius: 1.5rem;
width: 36px;
height: 36px;
background-color: var(--bs-green);
color: #fff;
flex: 0 0 auto;
font-size: 1rem;
}
.mainlayout ::deep .menu-icon {
width: 16px;
}
.mainlayout ::deep .layout-main > .tabs > .tabs-body {
background-color: var(--tabs-body-bg);
}
.mainlayout ::deep .layout-main > .tabs > .tabs-body > .tabs-body-content {
height: var(--bb-layout-body-height);
background-color: var(--bs-body-bg);
padding: 4px;
}
.mainlayout ::deep .tabs {
--bb-tabs-item-height: 32px;
--bb-tabs-body-padding: 0.5rem;
}
.mainlayout ::deep .tabs.tabs-border-card {
box-shadow: 0 0px 0px 0 rgba(0,0,0,0),0 0 6px 0 rgba(0,0,0,0);
}
.mainlayout ::deep .tabs .extend .nav-link-bar.left {
border-width: 0px 0px 0px 0px;
}
.mainlayout ::deep .tabs-nav-wrap > .nav-link-bar.dropdown {
border-width: 0px 0px 0px 0px;
}
.mainlayout ::deep .tabs .extend .nav-link-bar.right {
border-width: 0px 0px 0px 0px;
}
.mainlayout ::deep .tabs .tabs-item-fix {
border-width: 0px 0px 0px 0px;
}
.mainlayout ::deep .tabs.tabs-card > .tabs-header .tabs-item {
border-width: 0px 0px 0px 0px;
border: none;
}
.mainlayout ::deep .tabs.tabs-border-card > .tabs-header .tabs-item {
border-width: 0px 0px 0px 0px;
border: none;
}
.mainlayout ::deep .tabs.tabs-card .tabs-header .tabs-item.active {
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: var(--bb-tabs-item-active-color);
}
.mainlayout ::deep .tabs.tabs-border-card .tabs-header .tabs-item.active {
border-width: 0px 0px 1px 0px;
border-style: solid;
border-color: var(--bb-tabs-item-active-color);
}
.mainlayout ::deep .tabs.tabs-card .tabs-header .tabs-item.active {
background-color: var(--bs-primary-bg1);
}
.mainlayout ::deep.tabs.tabs-card .tabs-header .tabs-item:hover {
background-color: var(--bs-primary-bg1);
}
.mainlayout ::deep.tabs.tabs-border-card .tabs-header .tabs-item:hover {
background-color: var(--bs-primary-bg1);
}
.mainlayout ::deep .tabs-nav-wrap .nav-link-bar {
font-size: 0.7rem;
}
.mainlayout ::deep .tabs-item .tabs-item-close {
top: 5px;
}
.mainlayout ::deep .table-wrapper {
border-radius: unset;
}
.mainlayout ::deep .layout-side {
box-shadow: inset -1px 0 0px 0px var(--bs-border-color); /* 下内阴影 */
}
.mainlayout ::deep .layout-banner {
box-shadow: inset -1px 0 0px 0px var(--bs-border-color); /* 下内阴影 */
}
.mainlayout ::deep .layout {
--bb-layout-header-height: 44px;
--bb-layout-headerbar-background: transparent;
--bs-navbar-color: var(--bb-layout-header-color);
--bs-navbar-hover-color: var(--bs-primary);
--bb-layout-header-background: var(--tg-nav-bg);
--bb-layout-sidebar-background: var(--tg-nav-bg);
--bb-layout-footer-background: var(--tg-nav-bg);
--bb-layout-sidebar-banner-background: var(--tg-nav-bg);
--bb-layout-banner-font-size: 1.2rem;
--bb-layout-banner-logo-width: 36px;
--bb-layout-banner-logo-height: 36px;
--line-chart-height: 350px;
--bb-layout-body-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - 20px);
--line-chart-table-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - var(--line-chart-height) - 30px);
--table-height: calc(100vh - var(--bs-header-height) - var(--bb-layout-header-height) - 30px);
--bs-header-height: 30px;
}
.mainlayout ::deep .dropdown-logout {
--bb-logout-avatar-width: 32px;
--bb-logout-avatar-height: 32px;
--bb-logout-user-bg: rgba(52,58,64,0.7);
--bb-logout-menu-border-color: var(--bs-border-color);
}
.mainlayout ::deep .layout-header-bar {
border-color: transparent;
border: 0px;
color: var(--bb-layout-header-color);
}
.mainlayout ::deep .layout-header-bar:hover {
color: var(--bs-navbar-hover-color);
}

View File

@@ -1,96 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Photino.Blazor;
using System.Text;
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.Debug;
internal class Program
{
[STAThread]
private static void Main(string[] args)
{
//当前工作目录设为程序集的基目录
System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory);
// 增加中文编码支持
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
#region Logo
Console.Write(Environment.NewLine);
Console.ForegroundColor = ConsoleColor.Yellow;
XTrace.WriteLine(string.Empty);
Console.WriteLine(
"""
_______ _ _ _____ _
|__ __|| | (_) / ____| | |
| | | |__ _ _ __ __ _ ___ | | __ __ _ | |_ ___ __ __ __ _ _ _
| | | '_ \ | || '_ \ / _` |/ __|| | |_ | / _` || __|/ _ \\ \ /\ / // _` || | | |
| | | | | || || | | || (_| |\__ \| |__| || (_| || |_| __/ \ V V /| (_| || |_| |
|_| |_| |_||_||_| |_| \__, ||___/ \_____| \__,_| \__|\___| \_/\_/ \__,_| \__, |
__/ | __/ |
|___/ |___/
"""
);
Console.ResetColor();
#endregion Logo
var builder = PhotinoBlazorAppBuilder.CreateDefault(args);
builder.RootComponents.Add<Routes>("#app");
var options = GenericRunOptions.DefaultSilence
.ConfigureServices(services =>
{
foreach (var item in builder.Services)
{
services.Add(item);
}
});
;
Serve.BuildApplication(options, out var app);
app.Start();
var hybridApp = builder.Build(app.Services);
hybridApp.MainWindow.ContextMenuEnabled = false;
hybridApp.MainWindow.DevToolsEnabled = true;
hybridApp.MainWindow.GrantBrowserPermissions = true;
hybridApp.MainWindow.SetUseOsDefaultLocation(false);
hybridApp.MainWindow.SetUseOsDefaultSize(false);
hybridApp.MainWindow.SetSize(new System.Drawing.Size(1920, 1080));
hybridApp.MainWindow.SetTitle("ThingsGateway");
hybridApp.MainWindow.SetIconFile("wwwroot/favicon.ico");
AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
{
};
hybridApp.MainWindow.WindowClosing += (sender, e) =>
{
app.StopAsync();
return false;
};
hybridApp.Run();
Thread.Sleep(5000);
}
}

View File

@@ -1,11 +0,0 @@
{
"profiles": {
"ThingsGateway.Debug.Photino": {
"commandName": "Project"
},
"WSL": {
"commandName": "WSL2",
"distributionName": ""
}
}
}

View File

@@ -1,48 +0,0 @@
@using Microsoft.AspNetCore.Components.Routing
@using ThingsGateway.Debug
@namespace ThingsGateway
@{
#if NET6_0
}
<Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState>
<Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="App.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(Razor.BaseLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState>
@{
#else
}
<Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="App.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(Razor.BaseLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
@{
#endif
}

View File

@@ -1,65 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="$(SolutionDir)Version.props" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<TargetFrameworks>net8.0;net6.0</TargetFrameworks>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>true</GarbageCollectionAdaptationMode>
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
</PropertyGroup>
<ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Remove="Locales\*.json" />
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="ThingsGateway.Razor" Version="$(AdminVersion)" />
<PackageReference Include="ThingsGateway.Debug.Razor" Version="$(PluginVersion)" />
<PackageReference Include="Photino.NET" Version="3.1.18" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="6.0.33" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="8.0.10" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\ThingsGateway.Photino\Photino\**" LinkBase="Photino">
</Compile>
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -1,6 +0,0 @@
{
"ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹自动合并该文件夹里面所有json文件
"IgnoreConfigurationFiles": [ "" ],
"ExternalAssemblies": [ "" ]
}

View File

@@ -1,6 +0,0 @@
{
"ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹自动合并该文件夹里面所有json文件
"IgnoreConfigurationFiles": [ "" ],
"ExternalAssemblies": [ "" ]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>ThingsGateway.Debug</title>
<base href="/" />
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link href="_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css" rel="stylesheet">
<link href="_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css" rel="stylesheet">
<link href="_content/BootstrapBlazor/css/motronic.min.css" rel="stylesheet">
<link href="ThingsGateway.Debug.Photino.styles.css" rel="stylesheet" />
<link href="/_content/ThingsGateway.Razor/css/site.css" rel="stylesheet" />
<script src="/_content/ThingsGateway.Razor/js/theme.js" type="module"></script><!-- 初始主题 -->
<script src="/_content/ThingsGateway.Razor/js/culture.js"></script>
</head>
<body>
<div id="app"></div>
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
<script src="_framework/blazor.webview.js" autostart="true"></script>
</body>
</html>

View File

@@ -1,51 +0,0 @@
using System.Drawing;
using System.Windows.Forms;
namespace ThingsGateway.Debug
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
var resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
SuspendLayout();
//
// MainForm
//
AutoScaleDimensions = new SizeF(9F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1902, 1033);
Icon = (Icon)resources.GetObject("$this.Icon");
Margin = new Padding(2);
Name = "MainForm";
Text = "ThingsGateway";
ResumeLayout(false);
}
#endregion
}
}

View File

@@ -1,116 +0,0 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using Microsoft.AspNetCore.Components.WebView;
using Microsoft.AspNetCore.Components.WebView.WindowsForms;
using Microsoft.Web.WebView2.Core;
using System.Windows.Forms;
namespace ThingsGateway.Debug
{
public partial class MainForm : Form
{
protected string UploadPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "uploads");
private BlazorWebView blazorWebView;
public MainForm(IServiceProvider serviceProvider)
{
InitializeComponent();
//默认全屏
//this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
//this.FormBorderStyle =FormBorderStyle.None;
//this.TopMost = true;
//this.KeyPreview = true;
KeyUp += new System.Windows.Forms.KeyEventHandler(MainForm_KeyUp);
blazorWebView = new BlazorWebView()
{
Dock = DockStyle.Fill,
HostPage = "wwwroot/index.html",
Services = serviceProvider
};
FormClosing += Program.Closing;
blazorWebView.RootComponents.Add<Routes>("#app");
Controls.Add(blazorWebView);
blazorWebView.BringToFront();
blazorWebView.KeyUp += new System.Windows.Forms.KeyEventHandler(MainForm_KeyUp);
blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized;
blazorWebView.UrlLoading +=
(sender, urlLoadingEventArgs) =>
{
if (urlLoadingEventArgs.Url.Host != "0.0.0.0")
{
//外部链接WebView内打开,例如pdf浏览器
Console.WriteLine(urlLoadingEventArgs.Url);
urlLoadingEventArgs.UrlLoadingStrategy =
UrlLoadingStrategy.OpenInWebView;
}
};
}
private void BlazorWebViewInitialized(object? sender, EventArgs e)
{
//下载开始时引发 DownloadStarting阻止默认下载
blazorWebView.WebView.CoreWebView2.DownloadStarting += CoreWebView2_DownloadStarting;
//指定下载保存位置
blazorWebView.WebView.CoreWebView2.Profile.DefaultDownloadFolderPath = UploadPath;
////[无依赖发布webview2程序] 固定版本运行时环境的方式来实现加载网页
////设置web用户文件夹
//var browserExecutableFolder = "c:\\wb2";
//var userData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "BlazorWinFormsApp");
//Directory.CreateDirectory(userData);
//var creationProperties = new CoreWebView2CreationProperties()
//{
// UserDataFolder = userData,
// BrowserExecutableFolder = browserExecutableFolder
//};
//mainBlazorWebView.WebView.CreationProperties = creationProperties;
}
private void CoreWebView2_DownloadStarting(object? sender, CoreWebView2DownloadStartingEventArgs e)
{
var downloadOperation = e.DownloadOperation;
string fileName = Path.GetFileName(e.ResultFilePath);
var filePath = Path.Combine(UploadPath, fileName);
//指定下载保存位置
e.ResultFilePath = filePath;
MessageBox.Show($"下载文件完成 {fileName}", "提示");
}
private void MainForm_KeyUp(object? sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
if (WindowState == System.Windows.Forms.FormWindowState.Normal)
{
WindowState = System.Windows.Forms.FormWindowState.Maximized;
FormBorderStyle = FormBorderStyle.None;
}
else
{
WindowState = System.Windows.Forms.FormWindowState.Normal;
FormBorderStyle = FormBorderStyle.Sizable;
}
}
}
}
}

View File

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

View File

@@ -1,87 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Text;
using System.Windows.Forms;
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.Debug;
internal class Program
{
internal static void Closing(object? sender, FormClosingEventArgs e)
{
host.StopAsync();
}
[STAThread]
private static void Main(string[] args)
{
//当前工作目录设为程序集的基目录
System.IO.Directory.SetCurrentDirectory(AppContext.BaseDirectory);
// 增加中文编码支持
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
#region Logo
Console.Write(Environment.NewLine);
Console.ForegroundColor = ConsoleColor.Yellow;
XTrace.WriteLine(string.Empty);
Console.WriteLine(
"""
_______ _ _ _____ _
|__ __|| | (_) / ____| | |
| | | |__ _ _ __ __ _ ___ | | __ __ _ | |_ ___ __ __ __ _ _ _
| | | '_ \ | || '_ \ / _` |/ __|| | |_ | / _` || __|/ _ \\ \ /\ / // _` || | | |
| | | | | || || | | || (_| |\__ \| |__| || (_| || |_| __/ \ V V /| (_| || |_| |
|_| |_| |_||_||_| |_| \__, ||___/ \_____| \__,_| \__|\___| \_/\_/ \__,_| \__, |
__/ | __/ |
|___/ |___/
"""
);
Console.ResetColor();
#endregion Logo
var options = GenericRunOptions.DefaultSilence
.ConfigureServices(services =>
{
services.AddWindowsFormsBlazorWebView();
});
;
Serve.BuildApplication(options, out var app);
host = app;
app.Start();
AppDomain.CurrentDomain.UnhandledException += (sender, error) =>
{
MessageBox.Show(text: error.ExceptionObject.ToString(), caption: "Error");
};
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(app.Services));
Thread.Sleep(5000);
}
private static IHost host;
}

View File

@@ -1,48 +0,0 @@
@using Microsoft.AspNetCore.Components.Routing
@using ThingsGateway.Debug
@namespace ThingsGateway
@{
#if NET6_0
}
<Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState>
<Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="App.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(Razor.BaseLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</Microsoft.AspNetCore.Components.Authorization.CascadingAuthenticationState>
@{
#else
}
<Router AppAssembly="@typeof(Routes).Assembly" AdditionalAssemblies="App.RazorAssemblies.Where(a=>a!=typeof(Routes).Assembly)">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(Razor.BaseLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
@{
#endif
}

View File

@@ -1,61 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="$(SolutionDir)Version.props" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<TargetFrameworks>net8.0-windows;</TargetFrameworks>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>true</GarbageCollectionAdaptationMode>
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="ThingsGateway.Razor" Version="$(AdminVersion)" />
<PackageReference Include="ThingsGateway.Debug.Razor" Version="$(PluginVersion)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="8.0.91" />
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<EmbeddedResource Include="..\ThingsGateway.Debug.Photino\Locales\en-US.json" Link="Locales\en-US.json" />
<EmbeddedResource Include="..\ThingsGateway.Debug.Photino\Locales\zh-CN.json" Link="Locales\zh-CN.json" />
<EmbeddedResource Include="..\ThingsGateway.Debug.Photino\Locales\zh-TW.json">
<Link>Locales\zh-TW.json</Link>
</EmbeddedResource>
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Include="..\ThingsGateway.Debug.Photino\MainLayout.razor" Link="MainLayout.razor" />
<Compile Include="..\ThingsGateway.Debug.Photino\MainLayout.razor.cs" Link="MainLayout.razor.cs" />
<Content Include="..\ThingsGateway.Debug.Photino\MainLayout.razor.css" Link="MainLayout.razor.css" />
<Content Include="..\ThingsGateway.Debug.Photino\Configuration\*" LinkBase="Configuration">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -1,6 +0,0 @@
{
"ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹自动合并该文件夹里面所有json文件
"IgnoreConfigurationFiles": [ "" ],
"ExternalAssemblies": [ "" ]
}

View File

@@ -1,6 +0,0 @@
{
"ConfigurationScanDirectories": [ "Configuration", "" ], // 扫描配置文件json文件夹自动合并该文件夹里面所有json文件
"IgnoreConfigurationFiles": [ "" ],
"ExternalAssemblies": [ "" ]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,25 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>ThingsGateway.Debug</title>
<base href="/" />
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link href="_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css" rel="stylesheet">
<link href="_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css" rel="stylesheet">
<link href="_content/BootstrapBlazor/css/motronic.min.css" rel="stylesheet">
<link href="ThingsGateway.Debug.Winform.styles.css" rel="stylesheet" />
<link href="/_content/ThingsGateway.Razor/css/site.css" rel="stylesheet" />
<script src="/_content/ThingsGateway.Razor/js/theme.js" type="module"></script><!-- 初始主题 -->
<script src="/_content/ThingsGateway.Razor/js/culture.js"></script>
</head>
<body>
<div id="app"></div>
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
<script src="_framework/blazor.webview.js" autostart="true"></script>
</body>
</html>

View File

@@ -1,34 +0,0 @@
<Project>
<PropertyGroup>
<AdminVersion>7.0.0.63</AdminVersion>
<PluginVersion>9.0.0.13</PluginVersion>
<ProPluginVersion>9.0.0.16</ProPluginVersion>
</PropertyGroup>
<PropertyGroup>
<NoWarn>CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;CS8714;CS8619;CS8629;CS8765;CS8634;CS8621;CS8767;CS8633;CS8620;CS8610;CS8631;CS8605;CS8622;CS8613;NU5100;</NoWarn>
<TargetFrameworks>net8.0;net6.0;</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Authors>Diego</Authors>
<Company>Diego</Company>
<Product>Diego</Product>
<Copyright>版权所有 © 2023-present Diego</Copyright>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<RepositoryType>Gitee</RepositoryType>
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
</PropertyGroup>
<ItemGroup>
<None Include="$(SolutionDir)Directory.Build.props" Pack="true" PackagePath="\" />
</ItemGroup>
<PropertyGroup>
<DebugSymbols>True</DebugSymbols>
<DebugType>Embedded</DebugType>
<EmbedAllSources>True</EmbedAllSources>
</PropertyGroup>
</Project>

View File

@@ -1,14 +0,0 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net462;netstandard2.0;net8.0;net6.0;</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Content Remove="Locales\*.json" />
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="$(SolutionDir)Foundation.props" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>

View File

@@ -1,26 +0,0 @@
<Project>
<PropertyGroup>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>$(SolutionDir)..\..\nupkgs</PackageOutputPath>
<PackageVersion>$(Version)</PackageVersion>
<PackageTags>ThingsGateway;Diego;Blazor;IOT;设备采集;边缘网关;物联网</PackageTags>
<PackageProjectUrl>https://gitee.com/diego2098/ThingsGateway</PackageProjectUrl>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.zh-CN.md</PackageReadmeFile>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<None Include="$(SolutionDir)PackNuget.props" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<None Include="$(SolutionDir)..\README.md" Pack="true" PackagePath="\" />
<None Include="$(SolutionDir)..\README.zh-CN.md" Pack="true" PackagePath="\" />
<None Include="$(SolutionDir)..\LICENSE" Pack="true" PackagePath="\" />
<None Include="$(SolutionDir)..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>

View File

@@ -1,32 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# 17
VisualStudioVersion = 17.9.34622.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{1A92A426-94A8-444D-83EB-5760150384D0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Razor", "ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj", "{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1A92A426-94A8-444D-83EB-5760150384D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A92A426-94A8-444D-83EB-5760150384D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A92A426-94A8-444D-83EB-5760150384D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A92A426-94A8-444D-83EB-5760150384D0}.Release|Any CPU.Build.0 = Release|Any CPU
{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E3E1902-6ECB-40B1-92C0-909C5DF3332F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282}
RESX_Rules = {"EnabledRules":[]}
RESX_NeutralResourcesLanguage = zh-Hans
EndGlobalSection
EndGlobal

View File

@@ -1,14 +1,16 @@
//------------------------------------------------------------------------------
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
global using Microsoft.Extensions.DependencyInjection;
global using System;
global using ThingsGateway.Admin.Application;
global using ThingsGateway.Foundation;
global using TouchSocket.Core;
global using TouchSocket.Sockets;

View File

@@ -0,0 +1,147 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Text;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
/// <summary>
/// Modbus协议地址
/// </summary>
public class ModbusAddress : DeviceAddressBase
{
public ModbusAddress()
{
}
public ModbusAddress(string address, ushort len)
{
Station = -1;
AddressStart = 0;
Parse(address, len);
}
public ModbusAddress(string address, byte station)
{
Station = station;
AddressStart = 0;
Parse(address, 0);
}
/// <summary>
/// 读取功能码
/// </summary>
public int ReadFunction { get; set; }
/// <summary>
/// 站号信息
/// </summary>
public int Station { get; set; }
/// <summary>
/// 写入功能码
/// </summary>
public int WriteFunction { get; set; }
public override void Parse(string address, int length)
{
Length = length;
if (address.IndexOf(';') < 0)
{
Address(address);
}
else
{
string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
for (int index = 0; index < strArray.Length; ++index)
{
if (strArray[index].ToUpper().StartsWith("S="))
{
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
Station = byte.Parse(strArray[index].Substring(2));
}
else if (strArray[index].ToUpper().StartsWith("W="))
{
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
this.WriteFunction = (int)byte.Parse(strArray[index].Substring(2));
}
else if (!strArray[index].Contains("="))
{
Address(strArray[index]);
}
}
}
void Address(string address)
{
var readF = ushort.Parse(address.Substring(0, 1));
if (readF > 4)
throw new("功能码错误");
GetFunction(readF);
AddressStart = int.Parse(address.Substring(1)) - 1;
}
}
public override string ToString()
{
StringBuilder stringGeter = new StringBuilder();
if (Station > 0)
{
stringGeter.Append("s=" + Station.ToString() + ";");
}
if (WriteFunction > 0)
{
stringGeter.Append("w=" + WriteFunction.ToString() + ";");
}
stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString());
return stringGeter.ToString();
}
private void GetFunction(ushort readF)
{
switch (readF)
{
case 0:
ReadFunction = 1;
break;
case 1:
ReadFunction = 2;
break;
case 3:
ReadFunction = 4;
break;
case 4:
ReadFunction = 3;
break;
}
}
private string GetFunctionString(int readF)
{
switch (readF)
{
case 1:
return "0";
case 2:
return "1";
case 3:
return "4";
case 4:
return "3";
}
return "4";
}
}
}

View File

@@ -0,0 +1,311 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Text;
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
internal class ModbusHelper
{
/// <summary>
/// modbus地址格式说明
/// </summary>
/// <returns></returns>
internal static string GetAddressDescription()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Modbus寄存器");
stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。");
stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。");
stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。");
stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。");
stringBuilder.AppendLine("举例:");
stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
stringBuilder.AppendLine("额外格式:");
stringBuilder.AppendLine("设备站号 比如40001;s=2; 代表设备地址为2的保持寄存器第一个寄存器");
stringBuilder.AppendLine("写入功能码 比如40001;w=16; 代表保持寄存器第一个寄存器写入值时采用0x10功能码而不是默认的0x06功能码");
return stringBuilder.ToString();
}
/// <summary>
/// 添加Crc16
/// </summary>
internal static byte[] AddCrc(byte[] command)
{
return EasyCRC16.CRC16(command);
}
/// <summary>
/// 添加ModbusTcp报文头
/// </summary>
internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
{
byte[] tcp = new byte[modbus.Length + 6];
tcp[0] = BitConverter.GetBytes(id)[1];
tcp[1] = BitConverter.GetBytes(id)[0];
tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
modbus.CopyTo(tcp, 6);
return tcp;
}
/// <summary>
/// 通过错误码来获取到对应的文本消息
/// </summary>
internal static string GetDescriptionByErrorCode(byte code)
{
switch (code)
{
case 1:
return "不支持的功能码";
case 2:
return "读取寄存器越界";
case 3:
return "读取长度超限";
case 4:
return "读写异常";
default:
return "未知错误";
}
}
/// <summary>
/// 获取modbus数据区内容返回数据需去除Crc和报文头例如01 03 02 00 01发送数据需报文头
/// </summary>
/// <param name="send">发送数据</param>
/// <param name="response">返回数据</param>
/// <returns></returns>
internal static OperResult<byte[]> GetModbusData(byte[] send, byte[] response)
{
try
{
if (response[1] >= 0x80)//错误码
return new OperResult<byte[]>(GetDescriptionByErrorCode(response[2]));
if (send.Length == 0)
{
var result = OperResult.CreateSuccessResult(GenericHelpers.ArrayRemoveBegin(response, 3));
result.Message = "接收数据正确,但主机并没有主动请求数据";
result.ResultCode = ResultCode.Canceled;
return result;
}
if (send[0] != response[0])
return new OperResult<byte[]>(string.Format("站号不一致", send[0], response[0]));
if (send[1] != response[1])
return new OperResult<byte[]>(response) { Message = "功能码不一致" };
if (response.Length > 3)
return OperResult.CreateSuccessResult(GenericHelpers.ArrayRemoveBegin(response, 3));
else
return new OperResult<byte[]>(response) { Message = "数据长度为0" };
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 去除Crc返回modbus数据区
/// </summary>
/// <param name="send"></param>
/// <param name="response"></param>
/// <param name="crcCheck"></param>
/// <returns></returns>
internal static OperResult<byte[]> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
{
if (response.Length < 5)
return new OperResult<byte[]>("数据长度不足" + response.ToHexString());
if (crcCheck && !EasyCRC16.CheckCRC16(response))
return new OperResult<byte[]>("Crc校验失败" + DataHelper.ByteToHexString(response, ' '));
return GetModbusData(send, response.RemoveLast(2));
}
/// <summary>
/// 获取读取报文
/// </summary>
internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
{
try
{
ModbusAddress mAddress = new ModbusAddress(address, station);
return GetReadModbusCommand(mAddress, length);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取写入布尔量报文,根据地址识别功能码
/// </summary>
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
{
try
{
ModbusAddress mAddress = new ModbusAddress(address, station);
//功能码或实际长度
if (values?.Length > 1 || mAddress.WriteFunction == 15)
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
else
return GetWriteBoolModbusCommand(address, values[0], station);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取写入字报文,根据地址识别功能码
/// </summary>
internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
{
try
{
ModbusAddress mAddress = new ModbusAddress(address, station);
//功能码或实际长度
if (value?.Length > 2 || mAddress.WriteFunction == 16)
return GetWriteModbusCommand(mAddress, value);
else
return GetWriteOneModbusCommand(mAddress, value);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取读取报文
/// </summary>
private static OperResult<byte[]> GetReadModbusCommand(ModbusAddress mAddress, int length)
{
byte[] array = new byte[6]
{
(byte) mAddress.Station,
(byte) mAddress.ReadFunction,
BitConverter.GetBytes(mAddress.AddressStart)[1],
BitConverter.GetBytes(mAddress.AddressStart)[0],
BitConverter.GetBytes(length)[1],
BitConverter.GetBytes(length)[0]
};
return OperResult.CreateSuccessResult(array);
}
/// <summary>
/// 获取05写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
{
try
{
if (address.IndexOf('.') <= 0)
{
ModbusAddress mAddress = new ModbusAddress(address, station);
return GetWriteBoolModbusCommand(mAddress, value);
}
return new("不支持写入字寄存器的某一位");
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取05写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
{
byte[] array = new byte[6]
{
(byte) mAddress.Station,
(byte)5,
BitConverter.GetBytes(mAddress.AddressStart)[1],
BitConverter.GetBytes(mAddress.AddressStart)[0],
0,
0
};
if (value)
{
array[4] = 0xFF;
array[5] = 0;
}
else
{
array[4] = 0;
array[5] = 0;
}
return OperResult.CreateSuccessResult(array);
}
/// <summary>
/// 获取15写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
{
try
{
byte[] numArray1 = values.BoolArrayToByte();
byte[] numArray2 = new byte[7 + numArray1.Length];
numArray2[0] = (byte)mAddress.Station;
numArray2[1] = (byte)15;
numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
numArray2[4] = (byte)(length / 256);
numArray2[5] = (byte)(length % 256);
numArray2[6] = (byte)numArray1.Length;
numArray1.CopyTo(numArray2, 7);
return OperResult.CreateSuccessResult(numArray2);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取16写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
{
byte[] numArray = new byte[7 + values.Length];
numArray[0] = (byte)mAddress.Station;
numArray[1] = (byte)16;
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
numArray[4] = (byte)(values.Length / 2 / 256);
numArray[5] = (byte)(values.Length / 2 % 256);
numArray[6] = (byte)values.Length;
values.CopyTo(numArray, 7);
return OperResult.CreateSuccessResult(numArray);
}
/// <summary>
/// 获取6写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
{
byte[] numArray = new byte[4 + values.Length];
numArray[0] = (byte)mAddress.Station;
numArray[1] = (byte)6;
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
values.CopyTo(numArray, 4);
return OperResult.CreateSuccessResult(numArray);
}
}
}

View File

@@ -0,0 +1,155 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using ThingsGateway.Foundation.Serial;
using TouchSocket.Resources;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtu : ReadWriteDevicesSerialBase
{
public ModbusRtuDataHandleAdapter DataHandleAdapter = new();
private IWaitingClient<SerialClient> waitingClient;
public ModbusRtu(SerialClient serialClient) : base(serialClient)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
waitingClient = SerialClient.GetTGWaitingClient(new());
}
[Description("组包缓存时间")]
public double CacheTimeout { get; set; } = 1;
[Description("Crc校验")]
public bool Crc16CheckEnable { get; set; }
[Description("帧前时间")]
public int FrameTime { get; set; }
[Description("站号")]
public byte Station { get; set; } = 1;
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable;
DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout);
SerialClient.SetDataHandlingAdapter(DataHandleAdapter);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token)
{
try
{
var item = commandResult.Content;
await SerialClient.EasyLock.LockAsync();
await Task.Delay(FrameTime, token);
var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token);
return result;
}
finally
{
SerialClient.EasyLock.UnLock();
}
}
}
}

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
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuDataHandleAdapter : ReadWriteDevicesSerialDataHandleAdapter<ModbusRtuMessage>
{
public bool Crc16CheckEnable { get; set; } = true;
public override byte[] PackCommand(byte[] command)
{
return ModbusHelper.AddCrc(command);
}
protected override ModbusRtuMessage GetInstance()
{
return new ModbusRtuMessage();
}
/// <summary>
/// 解包获取实际数据包
/// </summary>
protected override FilterResult GetResponse(ByteBlock byteBlock, ModbusRtuMessage request, byte[] allBytes, byte[] bytes)
{
var unpackbytes = UnpackResponse(request.SendBytes, allBytes);
request.Message = unpackbytes.Message;
request.ResultCode = unpackbytes.ResultCode;
if (unpackbytes.IsSuccess)
{
request.Content = unpackbytes.Content;
request.ReceivedBytes = allBytes;
return FilterResult.Success;
}
else
{
byteBlock.Pos = byteBlock.Len;
request.ReceivedBytes = allBytes;
request.Message = unpackbytes.Message;
if (allBytes.Length <= 1)
{
return FilterResult.Cache;
}
if (!(allBytes[1] <= 0x10))
{
return FilterResult.Success;
}
else
{
if ((allBytes.Length > allBytes[2] + 4))
{
return FilterResult.Success;
}
else
{
return FilterResult.Cache;
}
}
}
}
protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
{
return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
}
}
}

View File

@@ -0,0 +1,30 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuMessage : MessageBase, IMessage
{
public override int HeadBytesLength => -1;
public override bool CheckHeadBytes(byte[] head)
{
return true;
}
protected override void SendBytesThen()
{
BodyLength = -1;
}
}
}

View File

@@ -0,0 +1,146 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Resources;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase
{
public ModbusRtuOverTcpDataHandleAdapter DataHandleAdapter = new();
private IWaitingClient<TGTcpClient> waitingClient;
public ModbusRtuOverTcp(TGTcpClient tcpClient) : base(tcpClient)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
waitingClient = TGTcpClient.GetTGWaitingClient(new());
}
public double CacheTimeout { get; set; } = 1;
public bool Crc16CheckEnable { get; set; }
public int FrameTime { get; set; }
public byte Station { get; set; } = 1;
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable;
DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout);
TGTcpClient.SetDataHandlingAdapter(DataHandleAdapter);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token)
{
try
{
var item = commandResult.Content;
await TGTcpClient.EasyLock.LockAsync();
await Task.Delay(FrameTime, token);
var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token);
return result;
}
finally
{
TGTcpClient.EasyLock.UnLock();
}
}
}
}

View File

@@ -0,0 +1,76 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuOverTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusRtuMessage>
{
public bool Crc16CheckEnable { get; set; } = true;
public override byte[] PackCommand(byte[] command)
{
return ModbusHelper.AddCrc(command);
}
protected override ModbusRtuMessage GetInstance()
{
return new ModbusRtuMessage();
}
/// <summary>
/// 解包获取实际数据包
/// </summary>
protected override FilterResult GetResponse(ByteBlock byteBlock, ModbusRtuMessage request, byte[] allBytes, byte[] bytes)
{
var unpackbytes = UnpackResponse(request.SendBytes, allBytes);
request.Message = unpackbytes.Message;
request.ResultCode = unpackbytes.ResultCode;
if (unpackbytes.IsSuccess)
{
request.Content = unpackbytes.Content;
request.ReceivedBytes = allBytes;
return FilterResult.Success;
}
else
{
byteBlock.Pos = byteBlock.Len;
request.ReceivedBytes = allBytes;
request.Message = unpackbytes.Message;
if (allBytes.Length <= 4)
{
return FilterResult.Cache;
}
if (!(allBytes[1] <= 0x10))
{
return FilterResult.Success;
}
else
{
if ((allBytes.Length > allBytes[2] + 4))
{
return FilterResult.Success;
}
else
{
return FilterResult.Cache;
}
}
}
}
protected override OperResult<byte[]> UnpackResponse(
byte[] send, byte[] response)
{
return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
}
}
}

View File

@@ -0,0 +1,145 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Resources;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuOverUdp : ReadWriteDevicesUdpBase
{
public ModbusRtuOverUdpDataHandleAdapter DataHandleAdapter = new();
private IWaitingClient<TGUdpSession> waitingClient;
public ModbusRtuOverUdp(TGUdpSession udpSession) : base(udpSession)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
waitingClient = TGUdpSession.GetTGWaitingClient(new());
}
public bool Crc16CheckEnable { get => DataHandleAdapter.Crc16CheckEnable; set => DataHandleAdapter.Crc16CheckEnable = value; }
public int FrameTime { get; set; }
public byte Station { get; set; } = 1;
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable;
TGUdpSession.SetDataHandlingAdapter(DataHandleAdapter);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token)
{
try
{
var item = commandResult.Content;
await TGUdpSession.EasyLock.LockAsync();
await Task.Delay(FrameTime, token);
var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token);
return result;
}
finally
{
TGUdpSession.EasyLock.UnLock();
}
}
}
}

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
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusRtuMessage>
{
public bool Crc16CheckEnable { get; set; } = true;
public override byte[] PackCommand(byte[] command)
{
return ModbusHelper.AddCrc(command);
}
protected override OperResult<byte[]> UnpackResponse(
byte[] send, byte[] response)
{
return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
}
protected override ModbusRtuMessage GetInstance()
{
return new ModbusRtuMessage();
}
}
}

View File

@@ -0,0 +1,402 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusServer : ReadWriteDevicesTcpServerBase
{
public ModbusServerDataHandleAdapter DataHandleAdapter = new();
/// <summary>
/// 继电器
/// </summary>
public Dictionary<int, ByteBlock> ModbusServer01ByteBlocks = new();
/// <summary>
/// 开关输入
/// </summary>
public Dictionary<int, ByteBlock> ModbusServer02ByteBlocks = new();
/// <summary>
/// 输入寄存器
/// </summary>
public Dictionary<int, ByteBlock> ModbusServer03ByteBlocks = new();
/// <summary>
/// 保持寄存器
/// </summary>
public Dictionary<int, ByteBlock> ModbusServer04ByteBlocks = new();
public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<OperResult>> Write;
public ModbusServer(TcpService tcpService) : base(tcpService)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// 多站点
/// </summary>
public bool MulStation { get; set; }
/// <summary>
/// 默认站点
/// </summary>
public byte Station { get; set; } = 1;
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
ModbusAddress mAddress = null;
try
{
mAddress = new ModbusAddress(address, Station);
}
catch (Exception ex)
{
return Task.FromResult(new OperResult<byte[]>(ex));
}
if (MulStation)
{
ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
}
else
{
if (Station != mAddress.Station)
{
return Task.FromResult(new OperResult<byte[]>("地址错误"));
}
ModbusServer01ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer02ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer03ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer04ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer01ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer02ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer03ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer04ByteBlocks[Station].SetLength(1024 * 128);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
switch (mAddress.ReadFunction)
{
case 1:
byte[] bytes0 = new byte[len];
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Read(bytes0);
return Task.FromResult(OperResult.CreateSuccessResult(bytes0));
case 2:
byte[] bytes1 = new byte[len];
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Read(bytes1);
return Task.FromResult(OperResult.CreateSuccessResult(bytes1));
case 3:
byte[] bytes3 = new byte[len];
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Read(bytes3);
return Task.FromResult(OperResult.CreateSuccessResult(bytes3));
case 4:
byte[] bytes4 = new byte[len];
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Read(bytes4);
return Task.FromResult(OperResult.CreateSuccessResult(bytes4));
}
return Task.FromResult(new OperResult<byte[]>("功能码错误"));
}
public override void SetDataAdapter(SocketClient client)
{
DataHandleAdapter = new();
client.SetDataHandlingAdapter(DataHandleAdapter);
}
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
ModbusAddress mAddress = null;
try
{
mAddress = new ModbusAddress(address, Station);
}
catch (Exception ex)
{
return Task.FromResult(new OperResult(ex));
}
if (MulStation)
{
ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
}
else
{
if (Station != mAddress.Station)
{
return Task.FromResult(new OperResult("地址错误"));
}
ModbusServer03ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer04ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer03ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer04ByteBlocks[Station].SetLength(1024 * 128);
}
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 3:
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return Task.FromResult(OperResult.CreateSuccessResult());
case 4:
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return Task.FromResult(OperResult.CreateSuccessResult());
}
return Task.FromResult(new OperResult("功能码错误"));
}
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
ModbusAddress mAddress = null;
try
{
mAddress = new ModbusAddress(address, Station);
}
catch (Exception ex)
{
return Task.FromResult(new OperResult(ex));
}
if (MulStation)
{
ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
}
else
{
if (Station != mAddress.Station)
{
return Task.FromResult(new OperResult("地址错误"));
}
ModbusServer01ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer02ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer01ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer02ByteBlocks[Station].SetLength(1024 * 128);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 1:
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
return Task.FromResult(OperResult.CreateSuccessResult());
case 2:
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
return Task.FromResult(OperResult.CreateSuccessResult());
}
return Task.FromResult(new OperResult("功能码错误"));
}
protected override void Dispose(bool disposing)
{
foreach (var item in ModbusServer01ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer02ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer03ByteBlocks)
{
item.Value.SafeDispose();
}
foreach (var item in ModbusServer04ByteBlocks)
{
item.Value.SafeDispose();
}
ModbusServer01ByteBlocks.Clear();
ModbusServer02ByteBlocks.Clear();
ModbusServer03ByteBlocks.Clear();
ModbusServer04ByteBlocks.Clear();
Disconnect();
base.Dispose(disposing);
}
protected override async Task ReceivedAsync(SocketClient client, IRequestInfo requestInfo)
{
if (requestInfo is ModbusServerMessage modbusServerMessage)
{
if (!modbusServerMessage.IsSuccess)
{
return;
}
if (modbusServerMessage.CurModbusAddress == null)
{
WriteError(client, modbusServerMessage);
}
if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)
{
var data = await ReadAsync(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.CurModbusAddress.Length);
if (data.IsSuccess)
{
var coreData = data.Content;
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
{
coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling((double)modbusServerMessage.CurModbusAddress.Length / 8.0));
}
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
.SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
sendData[5] = (byte)(sendData.Length - 6);
client.Send(sendData);
}
else
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
sendData[5] = (byte)(sendData.Length - 6);
sendData[7] = (byte)(sendData[7] + 128);
client.Send(sendData);
}
}
else
{
var coreData = modbusServerMessage.Content;
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
{
//写入继电器
if (Write != null)
{
if ((await Write(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
{
var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.CurModbusAddress.Length));
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
}
else
{
var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.CurModbusAddress.Length));
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
}
else
{
//写入寄存器
if (Write != null)
{
if ((await Write(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
{
var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData);
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
else
{
WriteError(client, modbusServerMessage);
}
}
else
{
var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData);
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
}
}
}
static void WriteError(SocketClient client, ModbusServerMessage modbusServerMessage)
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
sendData[5] = (byte)(sendData.Length - 6);
sendData[7] = (byte)(sendData[7] + 128);
client.Send(sendData);
}
}
private static void WriteSuccess03(SocketClient client, ModbusServerMessage modbusServerMessage)
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12);
sendData[5] = (byte)(sendData.Length - 6);
client.Send(sendData);
}
}
}

View File

@@ -0,0 +1,144 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusServerMessage>
{
public override byte[] PackCommand(byte[] command)
{
return command;
}
protected override ModbusServerMessage GetInstance()
{
return new ModbusServerMessage();
}
protected override OperResult<byte[]> UnpackResponse(
byte[] send,
byte[] response)
{
return GetModbusData(response.RemoveBegin(6));
}
public ThingsGatewayBitConverter ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
protected override FilterResult GetResponse(ByteBlock byteBlock, ModbusServerMessage request, byte[] allBytes, byte[] bytes)
{
var unpackbytes = UnpackResponse(request.SendBytes, allBytes);
request.Message = unpackbytes.Message;
request.ResultCode = unpackbytes.ResultCode;
if (unpackbytes.IsSuccess)
{
request.ReceivedBytes = allBytes;
//解析01 03 00 00 00 0A
var station = ThingsGatewayBitConverter.ToByte(bytes, 6);
var function = ThingsGatewayBitConverter.ToByte(bytes, 7);
int addressStart = ThingsGatewayBitConverter.ToInt16(bytes, 8);
if (addressStart == -1)
{
addressStart = 65535;
}
if (function > 4)
{
if (function > 6)
{
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
WriteFunction = function,
ReadFunction = function == 16 ? 3 : function == 15 ? 1 : 3,
Length = ThingsGatewayBitConverter.ToByte(bytes, 11),
};
request.Content = unpackbytes.Content.RemoveBegin(7);
}
else
{
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
WriteFunction = function,
ReadFunction = function == 6 ? 3 : function == 5 ? 1 : 3,
Length = 1,
};
request.Content = unpackbytes.Content.RemoveBegin(4);
}
}
else
{
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
ReadFunction = function,
Length = ThingsGatewayBitConverter.ToByte(bytes, 11),
};
}
return FilterResult.Success;
}
else
{
byteBlock.Pos = byteBlock.Len;
request.ReceivedBytes = allBytes;
return FilterResult.Success;
}
}
/// <summary>
/// 获取modbus写入数据区内容
/// </summary>
/// <param name="send">发送数据</param>
/// <param name="response">返回数据</param>
/// <returns></returns>
internal OperResult<byte[]> GetModbusData(byte[] response)
{
try
{
var func = ThingsGatewayBitConverter.ToByte(response, 1);
if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6)
{
if (response.Length == 6)
return OperResult.CreateSuccessResult(response);
}
else if (func == 15 || func == 16)
{
var length = ThingsGatewayBitConverter.ToByte(response, 6);
if (response.Length == 7 + length)
{
return OperResult.CreateSuccessResult(response);
}
}
return new OperResult<byte[]>(response) { Message = $"数据长度{response.Length}错误" };
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
}
}

View File

@@ -0,0 +1,32 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusServerMessage : MessageBase, IMessage
{
public override int HeadBytesLength => 6;
public override bool CheckHeadBytes(byte[] head)
{
if (head == null || head.Length != 6) return false;
HeadBytes = head;
int num = (HeadBytes[4] * 256) + HeadBytes[5];
BodyLength = num;
return true;
}
public ModbusAddress CurModbusAddress { get; set; }
}
}

View File

@@ -0,0 +1,156 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Resources;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusTcp : ReadWriteDevicesTcpClientBase
{
public ModbusTcpDataHandleAdapter DataHandleAdapter = new();
private IWaitingClient<TGTcpClient> waitingClient;
public ModbusTcp(TGTcpClient tcpClient) : base(tcpClient)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
waitingClient = TGTcpClient.GetTGWaitingClient(new());
}
[Description("组包缓存时间")]
public double CacheTimeout { get; set; } = 1;
[Description("帧前时间")]
public int FrameTime { get; set; }
[Description("检测事务标识符")]
public bool IsCheckMessageId { get; set; }
[Description("站号")]
public byte Station { get; set; } = 1;
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
DataHandleAdapter.IsCheckMessageId = IsCheckMessageId;
DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout);
TGTcpClient.SetDataHandlingAdapter(DataHandleAdapter);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token)
{
try
{
var item = commandResult.Content;
await TGTcpClient.EasyLock.LockAsync();
await Task.Delay(FrameTime, token);
var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token);
return result;
}
finally
{
TGTcpClient.EasyLock.UnLock();
}
}
}
}

View File

@@ -0,0 +1,54 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage>
{
private readonly EasyIncrementCount easyIncrementCount = new EasyIncrementCount(ushort.MaxValue);
public bool IsCheckMessageId
{
get
{
return Request?.IsCheckMessageId ?? false;
}
set
{
Request.IsCheckMessageId = value;
}
}
public EasyIncrementCount MessageId => easyIncrementCount;
public override byte[] PackCommand(byte[] command)
{
return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue());
}
protected override ModbusTcpMessage GetInstance()
{
return new ModbusTcpMessage();
}
protected override OperResult<byte[]> UnpackResponse(
byte[] send,
byte[] response)
{
return ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
}
}
}

View File

@@ -0,0 +1,36 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusTcpMessage : MessageBase, IMessage
{
public override int HeadBytesLength => 6;
public bool IsCheckMessageId { get; set; } = false;
public override bool CheckHeadBytes(byte[] head)
{
if (head == null || head.Length <= 0) return false;
HeadBytes = head;
int num = (HeadBytes[4] * 256) + HeadBytes[5];
BodyLength = num;
if (!IsCheckMessageId)
return true;
else
return SendBytes[0] == HeadBytes[0] && SendBytes[1] == HeadBytes[1] && HeadBytes[2] == 0 && HeadBytes[3] == 0;
}
}
}

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 System.Threading;
using System.Threading.Tasks;
using TouchSocket.Resources;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusUdp : ReadWriteDevicesUdpBase
{
public ModbusUdpDataHandleAdapter DataHandleAdapter = new();
private IWaitingClient<TGUdpSession> waitingClient;
public ModbusUdp(TGUdpSession udpSession) : base(udpSession)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
waitingClient = TGUdpSession.GetTGWaitingClient(new());
}
public int FrameTime { get; set; }
public bool IsCheckMessageId { get => DataHandleAdapter.IsCheckMessageId; set => DataHandleAdapter.IsCheckMessageId = value; }
public byte Station { get; set; } = 1;
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
DataHandleAdapter.IsCheckMessageId = IsCheckMessageId;
TGUdpSession.SetDataHandlingAdapter(DataHandleAdapter);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
try
{
await ConnectAsync(token);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
ResponsedData result = await SendThenReturnAsync(commandResult, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token)
{
try
{
var item = commandResult.Content;
await TGUdpSession.EasyLock.LockAsync();
await Task.Delay(FrameTime, token);
var result = await waitingClient.SendThenResponseAsync(item, TimeOut, token);
return result;
}
finally
{
TGUdpSession.EasyLock.UnLock();
}
}
}
}

View File

@@ -0,0 +1,51 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<ModbusTcpMessage>
{
private readonly EasyIncrementCount easyIncrementCount = new EasyIncrementCount(ushort.MaxValue);
public bool IsCheckMessageId
{
get
{
return Request?.IsCheckMessageId ?? false;
}
set
{
Request.IsCheckMessageId = value;
}
}
public EasyIncrementCount MessageId => easyIncrementCount;
public override byte[] PackCommand(byte[] command)
{
return ModbusHelper.AddModbusTcpHead(command, (ushort)easyIncrementCount.GetCurrentValue());
}
protected override ModbusTcpMessage GetInstance()
{
return new ModbusTcpMessage();
}
protected override OperResult<byte[]> UnpackResponse(
byte[] send,
byte[] response)
{
return ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
}
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
<Version>1.7.0</Version>
<Title>ThingsGateway.Foundation.Adapter.Modbus</Title>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Authors>Diego</Authors>
<Description>Modbus通讯类库支持MdobusTcp;ModbusRtu;ModbusRtuOverTcp;ModbusTcpServer</Description>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild>
<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<PackageOutputPath>$(SolutionDir)</PackageOutputPath>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Platforms>AnyCPU;x86</Platforms>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,367 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Runtime.InteropServices;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn;
internal class ComInterop
{
private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
#region const
private const uint EOAC_ACCESS_CONTROL = 0x04;
private const uint EOAC_APPID = 0x08;
private const uint EOAC_CLOAKING = 0x10;
private const uint EOAC_DYNAMIC_CLOAKING = 0x40;
private const uint EOAC_MUTUAL_AUTH = 0x01;
private const uint EOAC_NONE = 0x00;
private const uint EOAC_SECURE_REFS = 0x02;
private const uint EOAC_STATIC_CLOAKING = 0x20;
/// <summary>
/// The WIN32 system default locale.
/// </summary>
private const int LOCALE_SYSTEM_DEFAULT = 0x800;
/// <summary>
/// The WIN32 user default locale.
/// </summary>
private const int LOCALE_USER_DEFAULT = 0x400;
private const uint RPC_C_AUTHN_DCE_PRIVATE = 1;
private const uint RPC_C_AUTHN_DCE_PUBLIC = 2;
private const uint RPC_C_AUTHN_DEC_PUBLIC = 4;
private const uint RPC_C_AUTHN_DEFAULT = 0xFFFFFFFF;
private const uint RPC_C_AUTHN_DIGEST = 21;
private const uint RPC_C_AUTHN_DPA = 17;
private const uint RPC_C_AUTHN_GSS_KERBEROS = 16;
private const uint RPC_C_AUTHN_GSS_NEGOTIATE = 9;
private const uint RPC_C_AUTHN_GSS_SCHANNEL = 14;
private const uint RPC_C_AUTHN_LEVEL_CALL = 3;
private const uint RPC_C_AUTHN_LEVEL_CONNECT = 2;
private const uint RPC_C_AUTHN_LEVEL_DEFAULT = 0;
private const uint RPC_C_AUTHN_LEVEL_NONE = 1;
private const uint RPC_C_AUTHN_LEVEL_PKT = 4;
private const uint RPC_C_AUTHN_LEVEL_PKT_INTEGRITY = 5;
private const uint RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6;
private const uint RPC_C_AUTHN_MQ = 100;
private const uint RPC_C_AUTHN_MSN = 18;
private const uint RPC_C_AUTHN_NONE = 0;
private const uint RPC_C_AUTHN_WINNT = 10;
private const uint RPC_C_AUTHZ_DCE = 2;
private const uint RPC_C_AUTHZ_DEFAULT = 0xffffffff;
private const uint RPC_C_AUTHZ_NAME = 1;
private const uint RPC_C_AUTHZ_NONE = 0;
private const uint RPC_C_IMP_LEVEL_ANONYMOUS = 1;
private const uint RPC_C_IMP_LEVEL_DELEGATE = 4;
private const uint RPC_C_IMP_LEVEL_IDENTIFY = 2;
private const uint RPC_C_IMP_LEVEL_IMPERSONATE = 3;
#endregion const
#region struct
public struct COSERVERINFO
{
public uint dwReserved1;
[MarshalAs(UnmanagedType.LPWStr)]
public string pwszName;
public IntPtr pAuthInfo;
public uint dwReserved2;
};
public struct MULTI_QI
{
public IntPtr iid;
[MarshalAs(UnmanagedType.IUnknown)]
public object pItf;
public uint hr;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SOLE_AUTHENTICATION_SERVICE
{
public uint dwAuthnSvc;
public uint dwAuthzSvc;
[MarshalAs(UnmanagedType.LPWStr)]
public string pPrincipalName;
public int hr;
}
#endregion struct
#region win32 api
[DllImport("ole32.dll")]
private static extern void CoCreateInstanceEx(ref Guid clsid,
[MarshalAs(UnmanagedType.IUnknown)] object punkOuter,
uint dwClsCtx,
[In] ref COSERVERINFO pServerInfo,
uint dwCount,
[In, Out] MULTI_QI[] pResults);
[DllImport("ole32.dll")]
private static extern int CoInitializeSecurity(
IntPtr pSecDesc,
int cAuthSvc,
SOLE_AUTHENTICATION_SERVICE[] asAuthSvc,
IntPtr pReserved1,
uint dwAuthnLevel,
uint dwImpLevel,
IntPtr pAuthList,
uint dwCapabilities,
IntPtr pReserved3);
[DllImport("Kernel32.dll")]
private static extern int FormatMessageW(
int dwFlags,
IntPtr lpSource,
int dwMessageId,
int dwLanguageId,
IntPtr lpBuffer,
int nSize,
IntPtr Arguments);
[DllImport("Kernel32.dll")]
private static extern int GetSystemDefaultLangID();
[DllImport("Kernel32.dll")]
private static extern int GetUserDefaultLangID();
#endregion win32 api
/// <summary>
/// 创建一个COM服务器的实例。
/// </summary>
public static object CreateInstance(Guid clsid, string hostName)
{
COSERVERINFO coserverInfo = new();
coserverInfo.pwszName = hostName;
coserverInfo.pAuthInfo = IntPtr.Zero;
coserverInfo.dwReserved1 = 0;
coserverInfo.dwReserved2 = 0;
GCHandle hIID = GCHandle.Alloc(IID_IUnknown, GCHandleType.Pinned);
MULTI_QI[] results = new MULTI_QI[1];
results[0].iid = hIID.AddrOfPinnedObject();
results[0].pItf = null;
results[0].hr = 0;
try
{
// 检查是否在本地或远程连接。
uint clsctx = 0x01 | 0x04;
if (hostName != null && hostName.Length > 0 && hostName.ToLower() != "localhost" && hostName != "127.0.0.1")
{
clsctx = 0x04 | 0x10;
}
// create an instance.
CoCreateInstanceEx(
ref clsid,
null,
clsctx,
ref coserverInfo,
1,
results);
}
catch (Exception ex)
{
throw new ExternalException("CoCreateInstanceEx: " + ex.Message);
}
finally
{
if (hIID.IsAllocated) hIID.Free();
}
if (results[0].hr != 0)
{
throw new ExternalException("CoCreateInstanceEx: " + GetSystemMessage((int)results[0].hr));
}
return results[0].pItf;
}
/// <summary>
/// 指定错误消息文本检索系统。
/// </summary>
internal static string GetSystemMessage(int error)
{
const int MAX_MESSAGE_LENGTH = 1024;
const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
IntPtr buffer = Marshal.AllocCoTaskMem(MAX_MESSAGE_LENGTH);
int result = FormatMessageW(
(int)(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM),
IntPtr.Zero,
error,
0,
buffer,
MAX_MESSAGE_LENGTH - 1,
IntPtr.Zero);
string msg = Marshal.PtrToStringUni(buffer);
Marshal.FreeCoTaskMem(buffer);
if (!string.IsNullOrEmpty(msg))
{
return msg;
}
return string.Format("0x{0,0:X}", error);
}
/// <summary>
/// 初始化COM安全。
/// </summary>
internal static void InitializeSecurity()
{
int error = CoInitializeSecurity(
IntPtr.Zero,
-1,
null,
IntPtr.Zero,
RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE,
IntPtr.Zero,
EOAC_NONE,
IntPtr.Zero);
if (error != 0)
{
throw new ExternalException("COM初始化安全: " + GetSystemMessage(error), error);
}
}
/// <summary>
/// 从枚举器读取guid。
/// </summary>
internal static Guid[] ReadClasses(IOPCEnumGUID enumerator)
{
List<Guid> guids = new List<Guid>();
int fetched = 0;
Guid[] buffer = new Guid[10];
do
{
try
{
IntPtr pGuids = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)) * buffer.Length);
try
{
enumerator.Next(buffer.Length, pGuids, out fetched);
if (fetched > 0)
{
IntPtr pos = pGuids;
for (int ii = 0; ii < fetched; ii++)
{
object o = Marshal.PtrToStructure(pos, typeof(Guid));
if (o != null)
{
buffer[ii] = (Guid)o;
}
pos = IntPtr.Add(pos, Marshal.SizeOf(typeof(Guid)));
guids.Add(buffer[ii]);
}
}
}
finally
{
Marshal.FreeCoTaskMem(pGuids);
}
}
catch (Exception)
{
break;
}
}
while (fetched > 0);
return guids.ToArray();
}
/// <summary>
/// 从枚举器读取guid。
/// </summary>
internal static Guid[] ReadClasses(IEnumGUID enumerator)
{
List<Guid> guids = new List<Guid>();
int fetched = 0;
Guid[] buffer = new Guid[10];
do
{
try
{
IntPtr pGuids = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(Guid)) * buffer.Length);
try
{
enumerator.Next(buffer.Length, pGuids, out fetched);
if (fetched > 0)
{
IntPtr pos = pGuids;
for (int ii = 0; ii < fetched; ii++)
{
object o = Marshal.PtrToStructure(pos, typeof(Guid));
if (o != null)
{
buffer[ii] = (Guid)o;
}
pos = IntPtr.Add(pos, Marshal.SizeOf(typeof(Guid)));
guids.Add(buffer[ii]);
}
}
}
finally
{
Marshal.FreeCoTaskMem(pGuids);
}
}
catch (Exception)
{
break;
}
}
while (fetched > 0);
return guids.ToArray();
}
/// <summary>
/// 释放 COM 对象
/// </summary>
/// <param name="m_server"></param>
internal static void RealseComServer(object m_server)
{
if (m_server != null && m_server.GetType().IsCOMObject)
{
Marshal.ReleaseComObject(m_server);
}
}
}

View File

@@ -0,0 +1,83 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.OPCDA.Comn;
internal static class Convert
{
/// <summary>
/// windows的filetime是从1601-1-1 00:00:00开始的datetime是从1-1-1 00:00:00开始的
/// datetime和filetime的滴答单位都是100ns100纳秒千万分之一秒所以转换时只需要考虑开始时间即可
/// </summary>
private static readonly DateTime FILETIME_BaseTime = new DateTime(1601, 1, 1);
private static bool m_preserveUTC = false;
internal static object Clone(object source)
{
if (source == null)
{
return null;
}
if (source.GetType().IsValueType)
{
return source;
}
if (source.GetType().IsArray || source.GetType() == typeof(Array))
{
Array array = (Array)((Array)source).Clone();
for (int i = 0; i < array.Length; i++)
{
array.SetValue(Clone(array.GetValue(i)), i);
}
return array;
}
try
{
return ((ICloneable)source).Clone();
}
catch
{
throw new NotSupportedException("Object cannot be cloned.");
}
}
internal static DateTime FileTimeToDateTime(System.Runtime.InteropServices.ComTypes.FILETIME filetime)
{
long num = filetime.dwHighDateTime;
if (num < 0)
{
num += 4294967296L;
}
long num2 = num << 32;
num = filetime.dwLowDateTime;
if (num < 0)
{
num += 4294967296L;
}
num2 += num;
if (num2 == 0)
{
return DateTime.MinValue;
}
if (m_preserveUTC)
{
DateTime fILETIME_BaseTime = FILETIME_BaseTime;
return fILETIME_BaseTime.Add(new TimeSpan(num2));
}
DateTime fILETIME_BaseTime2 = FILETIME_BaseTime;
return fILETIME_BaseTime2.Add(new TimeSpan(num2)).ToLocalTime();
}
}

View File

@@ -0,0 +1,30 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
public delegate void OnDataChangedHandler(ItemReadResult[] opcItems);
public delegate void OnReadCompletedHandler(ItemReadResult[] opcItems);
public delegate void OnWriteCompletedHandler(ItemWriteResult[] opcItems);
public class ItemReadResult
{
public string Name { get; set; } = "";
public short Quality { get; set; }
public DateTime TimeStamp { get; set; }
public object Value { get; set; } = 0;
}
public class ItemWriteResult
{
public int Exception { get; set; } = 0;
public string Name { get; set; } = "";
}

View File

@@ -0,0 +1,489 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
internal class OpcGroup : IOPCDataCallback, IDisposable
{
internal object groupPointer = null;
internal int revisedUpdateRate = 0;
internal int serverGroupHandle = 0;
private static int _handle = 0;
private bool _bSubscribe = false;
private bool disposedValue;
private int lcid = 0x0;
private IOPCAsyncIO2 m_Async2IO = null;
private IConnectionPoint m_ConnectionPoint = null;
private int m_connectionpoint_cookie = 0;
private IConnectionPointContainer m_ConnectionPointContainer = null;
private IOPCItemMgt m_ItemManagement = null;
private IOPCGroupStateMgt m_StateManagement = null;
private IOPCSyncIO m_SyncIO = null;
private GCHandle percendDeadBand = GCHandle.Alloc(0, GCHandleType.Pinned);
private GCHandle timeBias = GCHandle.Alloc(0, GCHandleType.Pinned);
internal OpcGroup(string name)
{
Name = name;
ClientGroupHandle = ++_handle;
}
internal OpcGroup(string groupName, bool active, int reqUpdateRate, float deadBand)
{
Name = groupName;
IsActive = active;
RequestUpdateRate = reqUpdateRate;
DeadBand = deadBand;
ClientGroupHandle = ++_handle;
}
internal delegate void CancelCompletedHandler(int dwTransid, int hGroup);
internal event CancelCompletedHandler OnCancelCompleted;
internal event OnDataChangedHandler OnDataChanged;
internal event OnReadCompletedHandler OnReadCompleted;
internal event OnWriteCompletedHandler OnWriteCompleted;
internal bool ActiveSubscribe
{
get
{
return _bSubscribe;
}
set
{
_bSubscribe = value;
ActiveDataChanged(_bSubscribe);
}
}
internal int ClientGroupHandle { get; private set; }
internal float DeadBand { get; set; } = 0.0f;
internal object GroupPointer => groupPointer;
internal bool IsActive { get; set; } = true;
internal int LCID
{
get => lcid;
set => lcid = value;
}
internal string Name { get; private set; } = string.Empty;
internal List<OpcItem> OpcItems { get; private set; } = new List<OpcItem> { };
internal GCHandle PercendDeadBand
{
get => percendDeadBand;
set => percendDeadBand = value;
}
internal int RequestUpdateRate { get; set; } = 1000;
internal int RevisedUpdateRate => revisedUpdateRate;
internal int ServerGroupHandle => serverGroupHandle;
internal GCHandle TimeBias
{
get => timeBias;
set => timeBias = value;
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
public void OnCancelComplete(int dwTransid, int hGroup)
{
OnCancelCompleted?.Invoke(dwTransid, hGroup);
}
public void OnDataChange(int dwTransid,
int hGroup,
int hrMasterquality,
int hrMastererror,
int dwCount,
int[] phClientItems,
object[] pvValues,
short[] pwQualities,
System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps,
int[] pErrors)
{
List<ItemReadResult> itemChanged = new List<ItemReadResult>();
for (int i = 0; i < dwCount; i++)
{
int index = OpcItems.FindIndex(x => x.ClientHandle == phClientItems[i]);
if (index >= 0)
{
OpcItems[index].Value = pvValues[i];
OpcItems[index].Quality = pwQualities[i];
OpcItems[index].TimeStamp = Comn.Convert.FileTimeToDateTime(pftTimeStamps[i]);
itemChanged.Add(new ItemReadResult
{
Name = OpcItems[index].ItemID,
Value = pvValues[i],
Quality = pwQualities[i],
TimeStamp = OpcItems[index].TimeStamp
});
}
}
OnDataChanged?.Invoke(itemChanged.ToArray());
}
public void OnReadComplete(int dwTransid,
int hGroup,
int hrMasterquality,
int hrMastererror,
int dwCount,
int[] phClientItems,
object[] pvValues,
short[] pwQualities,
System.Runtime.InteropServices.ComTypes.FILETIME[] pftTimeStamps,
int[] pErrors)
{
List<ItemReadResult> itemChanged = new List<ItemReadResult>();
for (int i = 0; i < dwCount; i++)
{
int index = OpcItems.FindIndex(x => x.ClientHandle == phClientItems[i]);
if (index >= 0)
{
OpcItems[index].Value = pvValues[i];
OpcItems[index].Quality = pwQualities[i];
OpcItems[index].TimeStamp = Comn.Convert.FileTimeToDateTime(pftTimeStamps[i]);
itemChanged.Add(new ItemReadResult
{
Name = OpcItems[index].ItemID,
Value = pvValues[i],
Quality = pwQualities[i],
TimeStamp = OpcItems[index].TimeStamp
});
}
}
OnReadCompleted?.Invoke(itemChanged.ToArray());
}
public void OnWriteComplete(int dwTransid,
int hGroup,
int hrMastererr,
int dwCount,
int[] pClienthandles,
int[] pErrors)
{
List<ItemWriteResult> itemwrite = new List<ItemWriteResult>();
for (int i = 0; i < dwCount; i++)
{
int index = OpcItems.FindIndex(x => x.ClientHandle == pClienthandles[i]);
if (index >= 0)
{
itemwrite.Add(new ItemWriteResult
{
Name = OpcItems[index].ItemID,
Exception = pErrors[i]
});
}
}
OnWriteCompleted?.Invoke(itemwrite.ToArray());
}
internal OperResult AddOpcItem(OpcItem[] items)
{
IntPtr pResults = IntPtr.Zero;
IntPtr pErrors = IntPtr.Zero;
OPCITEMDEF[] itemDefyArray = new OPCITEMDEF[items.Length];
int i = 0;
int[] errors = new int[items.Length];
int[] itemServerHandle = new int[items.Length];
try
{
foreach (OpcItem item in items)
{
if (item != null)
{
itemDefyArray[i].szAccessPath = item.AccessPath;
itemDefyArray[i].szItemID = item.ItemID;
itemDefyArray[i].bActive = item.IsActive ? 1 : 0;
itemDefyArray[i].hClient = item.ClientHandle;
itemDefyArray[i].dwBlobSize = item.BlobSize;
itemDefyArray[i].pBlob = item.Blob;
i++;
}
}
//添加OPC项组
m_ItemManagement?.AddItems(items.Length, itemDefyArray, out pResults, out pErrors);
IntPtr Pos = pResults;
Marshal.Copy(pErrors, errors, 0, items.Length);
StringBuilder stringBuilder = new();
for (int j = 0; j < items.Length; j++)
{
if (errors[j] == 0)
{
if (j != 0)
{
Pos = IntPtr.Add(Pos, Marshal.SizeOf(typeof(OPCITEMRESULT)));
}
object o = Marshal.PtrToStructure(Pos, typeof(OPCITEMRESULT));
if (o != null)
{
var result = (OPCITEMRESULT)o;
items[j].RunTimeDataType = result.vtCanonicalDataType;
itemServerHandle[j] = items[j].ServerHandle = result.hServer;
Marshal.DestroyStructure(Pos, typeof(OPCITEMRESULT));
OpcItems.Add(items[j]);
}
}
else
{
stringBuilder.AppendLine(items[j].ItemID + "添加失败,错误代码:" + errors[j]);
}
}
if (stringBuilder.Length > 0)
{
return new OperResult(stringBuilder.ToString());
}
else
{
return OperResult.CreateSuccessResult();
}
}
catch (COMException ex)
{
return new OperResult(ex);
}
finally
{
if (pResults != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pResults);
}
if (pErrors != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pErrors);
}
}
}
/// <summary>
/// 建立连接
/// </summary>
/// <param name="handle"></param>
internal void InitIoInterfaces(object handle)
{
groupPointer = handle;
m_ItemManagement = (IOPCItemMgt)groupPointer;
m_Async2IO = (IOPCAsyncIO2)groupPointer;
m_SyncIO = (IOPCSyncIO)groupPointer;
m_StateManagement = (IOPCGroupStateMgt)groupPointer;
m_ConnectionPointContainer = (IConnectionPointContainer)groupPointer;
Guid iid = typeof(IOPCDataCallback).GUID;
m_ConnectionPointContainer.FindConnectionPoint(ref iid, out m_ConnectionPoint);
//创建客户端与服务端之间的连接
m_ConnectionPoint.Advise(this, out m_connectionpoint_cookie);
}
/// <summary>
/// 组读取
/// </summary>
/// <exception cref="ExternalException"></exception>
internal OperResult ReadAsync()
{
IntPtr pErrors = IntPtr.Zero;
try
{
if (m_Async2IO != null)
{
int[] serverHandle = new int[OpcItems.Count];
int[] PErrors = new int[OpcItems.Count];
for (int j = 0; j < OpcItems.Count; j++)
{
serverHandle[j] = OpcItems[j].ServerHandle;
}
int cancelId = 0;
m_Async2IO.Read(OpcItems.Count, serverHandle, 2, out cancelId, out pErrors);
Marshal.Copy(pErrors, PErrors, 0, OpcItems.Count);
if (PErrors.Any(a => a > 0))
{
return new OperResult("读取错误,错误代码为" + pErrors);
}
return OperResult.CreateSuccessResult();
}
else
return new OperResult("连接无效");
}
catch (COMException ex)
{
return new OperResult(ex);
}
finally
{
if (pErrors != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pErrors);
}
}
}
internal OperResult RemoveItem(OpcItem[] items)
{
IntPtr pErrors = IntPtr.Zero;
bool[] result = new bool[items.Length];
int[] errors = new int[items.Length];
int[] handles = new int[items.Length];
for (int i = 0; i < items.Length; i++)
{
handles[i] = items[i].ServerHandle;
}
try
{
m_ItemManagement?.RemoveItems(handles.Length, handles, out pErrors);
Marshal.Copy(pErrors, errors, 0, items.Length);
}
finally
{
if (pErrors != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pErrors);
}
}
StringBuilder stringBuilder = new();
for (int i = 0; i < errors.Length; i++)
{
if (errors[i] != 0)
{
stringBuilder.AppendLine(items[i].ItemID + "移除失败,错误代码:" + errors[i]);
}
else
{
OpcItems.Remove(items[i]);
}
}
if (stringBuilder.Length > 0)
{
return new OperResult(stringBuilder.ToString());
}
else
{
return OperResult.CreateSuccessResult();
}
}
internal OperResult WriteAsync(object[] values, int[] serverHandle, out int[] errors)
{
IntPtr pErrors = IntPtr.Zero;
errors = new int[values.Length];
if (m_Async2IO != null)
{
try
{
int cancelId, transactionID = 0;
m_Async2IO.Write(values.Length, serverHandle, values, transactionID, out cancelId, out pErrors);
Marshal.Copy(pErrors, errors, 0, values.Length);
return OperResult.CreateSuccessResult();
}
catch (COMException ex)
{
return new OperResult(ex);
}
finally
{
if (pErrors != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(pErrors);
}
}
}
else
return new OperResult("连接无效");
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (TimeBias.IsAllocated)
{
TimeBias.Free();
}
if (PercendDeadBand.IsAllocated)
{
PercendDeadBand.Free();
}
ActiveSubscribe = false;
m_ConnectionPoint?.Unadvise(m_connectionpoint_cookie);
m_connectionpoint_cookie = 0;
if (null != m_ConnectionPoint) Marshal.ReleaseComObject(m_ConnectionPoint);
m_ConnectionPoint = null;
if (null != m_ConnectionPointContainer) Marshal.ReleaseComObject(m_ConnectionPointContainer);
m_ConnectionPointContainer = null;
if (m_Async2IO != null)
{
Marshal.ReleaseComObject(m_Async2IO);
m_Async2IO = null;
}
if (m_SyncIO != null)
{
Marshal.ReleaseComObject(m_SyncIO);
m_SyncIO = null;
}
if (m_StateManagement != null)
{
Marshal.ReleaseComObject(m_StateManagement);
m_StateManagement = null;
}
if (groupPointer != null)
{
Marshal.ReleaseComObject(groupPointer);
groupPointer = null;
}
m_ItemManagement = null;
disposedValue = true;
}
}
private OperResult ActiveDataChanged(bool active)
{
IntPtr pRequestedUpdateRate = IntPtr.Zero;
IntPtr hClientGroup = IntPtr.Zero;
IntPtr pTimeBias = IntPtr.Zero;
IntPtr pDeadband = IntPtr.Zero;
IntPtr pLCID = IntPtr.Zero;
int nActive = 0;
GCHandle hActive = GCHandle.Alloc(nActive, GCHandleType.Pinned);
hActive.Target = active ? 1 : 0;
try
{
int nRevUpdateRate = 0;
m_StateManagement?.SetState(pRequestedUpdateRate,
out nRevUpdateRate,
hActive.AddrOfPinnedObject(),
pTimeBias,
pDeadband,
pLCID,
hClientGroup);
return OperResult.CreateSuccessResult();
}
catch (COMException ex)
{
return new OperResult(ex);
}
finally
{
hActive.Free();
}
}
}

View File

@@ -0,0 +1,49 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
public class OpcItem
{
private static int _hanle = 0;
public OpcItem(string itemId)
{
ItemID = itemId;
ClientHandle = ++_hanle;
}
public string AccessPath { get; private set; } = "";
public IntPtr Blob { get; set; } = IntPtr.Zero;
public int BlobSize { get; set; } = 0;
public int ClientHandle { get; private set; }
/// <summary>
/// active(1) or not(0)
/// </summary>
public bool IsActive { get; set; } = true;
/// <summary>
/// 数据项在opc server的完全名称
/// </summary>
public string ItemID { get; private set; } = String.Empty;
public int Quality { get; set; } = Qualities.OPC_QUALITY_BAD;
public short RunTimeDataType { get; set; } = 0;
public int ServerHandle { get; set; }
public DateTime TimeStamp { get; set; } = new DateTime(0);
public object Value { get; set; }
}

View File

@@ -0,0 +1,284 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
internal class OpcServer : IDisposable
{
private bool disposedValue;
private IOPCServer m_OpcServer = null;
internal OpcServer(string name, string host = "localhost")
{
Name = name;
if (host.IsNullOrEmpty())
{
Host = "localhost";
}
else
{
Host = host;
}
}
internal string Host { get; private set; }
internal bool IsConnected { get; private set; } = false;
internal string Name { get; private set; }
internal List<OpcGroup> OpcGroups { get; private set; } = new List<OpcGroup>(10);
internal ServerStatus ServerStatus { get; private set; } = new ServerStatus();
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
internal OperResult<OpcGroup> AddGroup(string groupName)
{
return AddGroup(groupName, true, 1000, 0);
}
/// <returns></returns>
internal OperResult<OpcGroup> AddGroup(string groupName, bool active, int reqUpdateRate, float deadBand)
{
if (null == m_OpcServer || IsConnected == false)
return new OperResult<OpcGroup>("未初始化连接!");
OpcGroup group = new OpcGroup(groupName, active, reqUpdateRate, deadBand);
Guid riid = typeof(IOPCItemMgt).GUID;
try
{
m_OpcServer?.AddGroup(group.Name,
group.IsActive ? 1 : 0,//IsActive
group.RequestUpdateRate,//RequestedUpdateRate 1000ms
group.ClientGroupHandle,
group.TimeBias.AddrOfPinnedObject(),
group.PercendDeadBand.AddrOfPinnedObject(),
group.LCID,
out group.serverGroupHandle,
out group.revisedUpdateRate,
ref riid,
out group.groupPointer);
if (group.groupPointer != null)
{
group.InitIoInterfaces(group.groupPointer);
OpcGroups.Add(group);
}
else
{
return new OperResult<OpcGroup>("添加OPC组错误OPC服务器返回null");
}
return OperResult.CreateSuccessResult(group);
}
catch (Exception ex)
{
return new OperResult<OpcGroup>(ex);
}
}
/// <summary>
/// 获取节点
/// </summary>
internal OperResult<List<BrowseElement>> Browse(string itemId = null)
{
lock (this)
{
if (null == m_OpcServer || IsConnected == false)
return new OperResult<List<BrowseElement>>("未初始化连接!");
var count = 0;
var moreElements = 0;
var pContinuationPoint = IntPtr.Zero;
var pElements = IntPtr.Zero;
var filterId = new PropertyID[]
{
new PropertyID(1),
new PropertyID(3),
new PropertyID(4),
new PropertyID(5),
new PropertyID(6),
new PropertyID(101),
};
try
{
var server = m_OpcServer as IOPCBrowse;
server.Browse(
itemId.IsNullOrEmpty() ? "" : itemId,
ref pContinuationPoint,
int.MaxValue,
OPCBROWSEFILTER.OPC_BROWSE_FILTER_ALL,
"",
"",
0,
1,
filterId.Length,
Interop.GetPropertyIDs(filterId),
out moreElements,
out count,
out pElements);
}
catch (Exception ex)
{
return new OperResult<List<BrowseElement>>(ex);
}
BrowseElement[] browseElements = Interop.GetBrowseElements(ref pElements, count, true);
string stringUni = Marshal.PtrToStringUni(pContinuationPoint);
Marshal.FreeCoTaskMem(pContinuationPoint);
this.ProcessResults(browseElements, filterId);
return OperResult.CreateSuccessResult(browseElements?.ToList());
}
}
internal OperResult Connect()
{
if (!string.IsNullOrEmpty(Host) && !string.IsNullOrEmpty(Name))
{
var info = Discovery.OpcDiscovery.GetOpcServer(Name, Host);
if (!info.IsSuccess)
{
return info;
}
object o = Comn.ComInterop.CreateInstance(info.Content.CLSID, Host);
if (o == null)
{
return new(string.Format("{0}{1}无法创建com对象", info.Content.CLSID, Host));
}
m_OpcServer = (IOPCServer)o;
IsConnected = true;
return OperResult.CreateSuccessResult();
}
return new("应初始化Host与Name");
}
/// <summary>
/// 服务器状态
/// </summary>
/// <returns></returns>
internal OperResult<ServerStatus> GetServerStatus()
{
if (null == m_OpcServer || IsConnected == false)
return new OperResult<ServerStatus>("未初始化连接!");
IntPtr statusPtr = IntPtr.Zero;
try
{
m_OpcServer?.GetStatus(out statusPtr);
OPCSERVERSTATUS status;
ServerStatus = new ServerStatus();
if (statusPtr != IntPtr.Zero)
{
try
{
object o = Marshal.PtrToStructure(statusPtr, typeof(OPCSERVERSTATUS));
if (null != o)
{
status = (OPCSERVERSTATUS)o;
ServerStatus.Version = status.wMajorVersion.ToString() + "." + status.wMinorVersion.ToString() + "." + status.wBuildNumber.ToString();
ServerStatus.ServerState = status.dwServerState;
ServerStatus.StartTime = Comn.Convert.FileTimeToDateTime(status.ftStartTime);
ServerStatus.CurrentTime = Comn.Convert.FileTimeToDateTime(status.ftCurrentTime);
ServerStatus.LastUpdateTime = Comn.Convert.FileTimeToDateTime(status.ftLastUpdateTime);
ServerStatus.VendorInfo = status.szVendorInfo;
}
IsConnected = true;
return OperResult.CreateSuccessResult(ServerStatus);
}
catch (Exception ex)
{
IsConnected = false;
return new OperResult<ServerStatus>(ex);
}
}
else
{
IsConnected = false;
return new OperResult<ServerStatus>("获取状态失败");
}
}
catch (Exception ex)
{
IsConnected = false;
return new OperResult<ServerStatus>(ex);
}
}
internal void RemoveGroup(OpcGroup group)
{
if (OpcGroups.Contains(group))
{
m_OpcServer?.RemoveGroup(group.ServerGroupHandle, 1);
OpcGroups.Remove(group);
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
try
{
for (int i = 0; i < OpcGroups.Count; i++)
RemoveGroup(OpcGroups[i]);
}
catch
{
}
if (m_OpcServer != null)
{
Marshal.ReleaseComObject(m_OpcServer);
m_OpcServer = null;
}
if (disposing)
{
OpcGroups.Clear();
}
disposedValue = true;
}
}
private void ProcessResults(BrowseElement[] elements, PropertyID[] propertyIDs)
{
if (elements == null)
return;
foreach (BrowseElement element in elements)
{
if (element.Properties != null)
{
foreach (ItemProperty property in element.Properties)
{
if (propertyIDs != null)
{
foreach (PropertyID propertyId in propertyIDs)
{
if (property.ID.Code == propertyId.Code)
{
property.ID = propertyId;
break;
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,25 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Da;
public class ServerStatus
{
public DateTime CurrentTime { get; internal set; } = new DateTime(0);
public DateTime LastUpdateTime { get; internal set; } = new DateTime(0);
public OPCSERVERSTATE ServerState { get; internal set; } = OPCSERVERSTATE.OPC_STATUS_NOCONFIG;
public DateTime StartTime { get; internal set; } = new DateTime(0);
public string VendorInfo { get; internal set; } = "UNKOWN";
public string Version { get; internal set; } = "UNKOWN";
}

View File

@@ -0,0 +1,237 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections;
using System.Linq;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
using ThingsGateway.Foundation.Extension.Json;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Discovery;
public class OpcDiscovery
{
private static readonly Guid CATID_OPC_DA10 = new("63D5F430-CFE4-11d1-B2C8-0060083BA1FB");
private static readonly Guid CATID_OPC_DA20 = new("63D5F432-CFE4-11d1-B2C8-0060083BA1FB");
private static readonly Guid CATID_OPC_DA30 = new("CC603642-66D7-48f1-B69A-B625E73652D7");
private static readonly Guid OPCEnumCLSID = new("13486D51-4821-11D2-A494-3CB306C10000");
public static OperResult<ServerInfo> GetOpcServer(string serverName, string host)
{
try
{
if (serverName.IsNullOrEmpty())
{
return new OperResult<ServerInfo>("检索失败需提供OPCName");
}
ServerInfo result = null;
ServerInfo[] serverInfos = null;
object o_Server = Comn.ComInterop.CreateInstance(OPCEnumCLSID, host);
if (o_Server == null)
return new OperResult<ServerInfo>("检索失败请检查是否安装OPC Runtime");
try
{
Guid catid = CATID_OPC_DA20;
//两种方式兼容国产部分OPCServer不支持IOPCServerList2的情况
try
{
IOPCServerList2 m_server2 = (IOPCServerList2)o_Server;
GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server2, catid);
if (result == null)
{
IOPCServerList m_server = (IOPCServerList)o_Server;
GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid);
}
}
catch (Exception)
{
IOPCServerList m_server = (IOPCServerList)o_Server;
GetIOPCServerList(ref result, ref serverInfos, serverName, host, m_server, catid);
}
if (result == null)
{
return new OperResult<ServerInfo>($"无法创建OPCServer连接请检查OPC名称是否一致以下为Host{host}中的OPC列表:"
+ Environment.NewLine +
serverInfos.ToJson().FormatJson()
);
}
return OperResult.CreateSuccessResult(result);
}
finally
{
Comn.ComInterop.RealseComServer(o_Server);
o_Server = null;
}
}
catch (Exception ex)
{
return new OperResult<ServerInfo>(ex);
}
}
private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList m_server, Guid catid)
{
object enumerator = null;
//2
m_server.EnumClassesOfCategories(
1,
new Guid[] { catid },
0,
null,
out enumerator);
Guid[] clsids = Comn.ComInterop.ReadClasses((IEnumGUID)enumerator);
//释放
Comn.ComInterop.RealseComServer(enumerator);
enumerator = null;
serverInfos = GetServerDetails(clsids?.ToArray(), host, m_server);
for (int i = 0; i < serverInfos.Length; i++)
{
if (serverInfos[i].CLSID.ToString().ToLower() == serverName.ToLower() ||
serverInfos[i].ProgID.ToLower() == serverName.ToLower() ||
serverInfos[i].VerIndProgID.ToLower() == serverName.ToLower())
{
result = serverInfos[i];
break;
}
}
}
private static void GetIOPCServerList(ref ServerInfo result, ref ServerInfo[] serverInfos, string serverName, string host, IOPCServerList2 m_server, Guid catid)
{
//1
IOPCEnumGUID enumerator = null;
m_server.EnumClassesOfCategories(
1,
new Guid[] { catid },
0,
null,
out enumerator);
Guid[] clsids = Comn.ComInterop.ReadClasses(enumerator);
//释放
Comn.ComInterop.RealseComServer(enumerator);
enumerator = null;
serverInfos = GetServerDetails(clsids?.ToArray(), host, m_server);
for (int i = 0; i < serverInfos.Length; i++)
{
if (serverInfos[i].CLSID.ToString().ToLower() == serverName.ToLower() ||
serverInfos[i].ProgID.ToLower() == serverName.ToLower() ||
serverInfos[i].VerIndProgID.ToLower() == serverName.ToLower())
{
result = serverInfos[i];
break;
}
}
}
private static ServerInfo[] GetServerDetails(Guid[] clsids, string host, IOPCServerList m_server)
{
ArrayList servers = new ArrayList();
for (int i = 0; i < clsids?.Length; i++)
{
Guid clsid = clsids[i];
try
{
string progID = null;
string description = null;
string verIndProgID = null;
ServerInfo server1 = new();
server1.Host = host;
server1.CLSID = clsid;
m_server?.GetClassDetails(
ref clsid,
out progID,
out description,
out verIndProgID);
if (verIndProgID != null)
{
server1.VerIndProgID = verIndProgID;
}
else if (progID != null)
{
server1.ProgID = progID;
}
if (description != null)
{
server1.Description = description;
}
servers.Add(server1);
}
catch
{
}
}
return (ServerInfo[])servers.ToArray(typeof(ServerInfo));
}
private static ServerInfo[] GetServerDetails(Guid[] clsids, string host, IOPCServerList2 m_server)
{
ArrayList servers = new ArrayList();
for (int i = 0; i < clsids?.Length; i++)
{
Guid clsid = clsids[i];
try
{
string progID = null;
string description = null;
string verIndProgID = null;
ServerInfo server1 = new();
server1.Host = host;
server1.CLSID = clsid;
m_server?.GetClassDetails(
ref clsid,
out progID,
out description,
out verIndProgID);
if (verIndProgID != null)
{
server1.VerIndProgID = verIndProgID;
}
else if (progID != null)
{
server1.ProgID = progID;
}
if (description != null)
{
server1.Description = description;
}
servers.Add(server1);
}
catch
{
}
}
return (ServerInfo[])servers.ToArray(typeof(ServerInfo));
}
}
public class ServerInfo
{
public Guid CLSID { get; set; }
public string Description { get; set; } = string.Empty;
public string Host { get; set; } = string.Empty;
public string ProgID { get; set; } = string.Empty;
public string VerIndProgID { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,102 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
[Serializable]
public class BrowseElement : ICloneable
{
private bool m_hasChildren;
private bool m_isItem;
private string m_itemName;
private string m_itemPath;
private string m_name;
private ItemProperty[] m_properties = new ItemProperty[0];
public bool HasChildren
{
get
{
return m_hasChildren;
}
set
{
m_hasChildren = value;
}
}
public bool IsItem
{
get
{
return m_isItem;
}
set
{
m_isItem = value;
}
}
public string ItemName
{
get
{
return m_itemName;
}
set
{
m_itemName = value;
}
}
public string ItemPath
{
get
{
return m_itemPath;
}
set
{
m_itemPath = value;
}
}
public string Name
{
get
{
return m_name;
}
set
{
m_name = value;
}
}
public ItemProperty[] Properties
{
get
{
return m_properties;
}
set
{
m_properties = value;
}
}
public virtual object Clone()
{
BrowseElement obj = (BrowseElement)MemberwiseClone();
obj.m_properties = (ItemProperty[])Comn.Convert.Clone(m_properties);
return obj;
}
}

View File

@@ -0,0 +1,334 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.
Runtime.InteropServices;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
/// <exclude />
[ComImport]
[GuidAttribute("B196B286-BAB4-101A-B69C-00AA00341D07")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IConnectionPoint
{
void GetConnectionInterface(
[Out]
out Guid pIID);
void GetConnectionPointContainer(
[Out]
out IConnectionPointContainer ppCPC);
void Advise(
[MarshalAs(UnmanagedType.IUnknown)]
object pUnkSink,
[Out][MarshalAs(UnmanagedType.I4)]
out int pdwCookie);
void Unadvise(
[MarshalAs(UnmanagedType.I4)]
int dwCookie);
void EnumConnections(
[Out]
out IEnumConnections ppEnum);
}
/// <exclude />
[ComImport]
[GuidAttribute("B196B284-BAB4-101A-B69C-00AA00341D07")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IConnectionPointContainer
{
void EnumConnectionPoints(
[Out]
out IEnumConnectionPoints ppEnum);
void FindConnectionPoint(
ref Guid riid,
[Out]
out IConnectionPoint ppCP);
}
/// <exclude />
[ComImport]
[GuidAttribute("B196B285-BAB4-101A-B69C-00AA00341D07")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumConnectionPoints
{
void RemoteNext(
[MarshalAs(UnmanagedType.I4)]
int cConnections,
[Out]
IntPtr ppCP,
[Out][MarshalAs(UnmanagedType.I4)]
out int pcFetched);
void Skip(
[MarshalAs(UnmanagedType.I4)]
int cConnections);
void Reset();
void Clone(
[Out]
out IEnumConnectionPoints ppEnum);
}
/// <exclude />
[ComImport]
[GuidAttribute("B196B287-BAB4-101A-B69C-00AA00341D07")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumConnections
{
void RemoteNext(
[MarshalAs(UnmanagedType.I4)]
int cConnections,
[Out]
IntPtr rgcd,
[Out][MarshalAs(UnmanagedType.I4)]
out int pcFetched);
void Skip(
[MarshalAs(UnmanagedType.I4)]
int cConnections);
void Reset();
void Clone(
[Out]
out IEnumConnections ppEnum);
}
/// <exclude />
[ComImport]
[GuidAttribute("0002E000-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumGUID
{
void Next(
[MarshalAs(UnmanagedType.I4)]
int celt,
[Out]
IntPtr rgelt,
[Out][MarshalAs(UnmanagedType.I4)]
out int pceltFetched);
void Skip(
[MarshalAs(UnmanagedType.I4)]
int celt);
void Reset();
void Clone(
[Out]
out IEnumGUID ppenum);
}
/// <exclude />
[ComImport]
[GuidAttribute("00000101-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumString
{
[PreserveSig]
int RemoteNext(
[MarshalAs(UnmanagedType.I4)]
int celt,
IntPtr rgelt,
[Out][MarshalAs(UnmanagedType.I4)]
out int pceltFetched);
void Skip(
[MarshalAs(UnmanagedType.I4)]
int celt);
void Reset();
void Clone(
[Out]
out IEnumString ppenum);
}
/// <exclude />
[ComImport]
[GuidAttribute("00000100-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IEnumUnknown
{
void RemoteNext(
[MarshalAs(UnmanagedType.I4)]
int celt,
[Out]
IntPtr rgelt,
[Out][MarshalAs(UnmanagedType.I4)]
out int pceltFetched);
void Skip(
[MarshalAs(UnmanagedType.I4)]
int celt);
void Reset();
void Clone(
[Out]
out IEnumUnknown ppenum);
}
/// <exclude />
[ComImport]
[GuidAttribute("F31DFDE2-07B6-11d2-B2D8-0060083BA1FB")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOPCCommon
{
void SetLocaleID(
[MarshalAs(UnmanagedType.I4)]
int dwLcid);
void GetLocaleID(
[Out][MarshalAs(UnmanagedType.I4)]
out int pdwLcid);
void QueryAvailableLocaleIDs(
[Out][MarshalAs(UnmanagedType.I4)]
out int pdwCount,
[Out]
out IntPtr pdwLcid);
void GetErrorString(
[MarshalAs(UnmanagedType.I4)]
int dwError,
[Out][MarshalAs(UnmanagedType.LPWStr)]
out String ppString);
void SetClientName(
[MarshalAs(UnmanagedType.LPWStr)]
String szName);
}
/// <exclude />
[ComImport]
[GuidAttribute("55C382C8-21C7-4e88-96C1-BECFB1E3F483")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOPCEnumGUID
{
void Next(
[MarshalAs(UnmanagedType.I4)]
int celt,
[Out]
IntPtr rgelt,
[Out][MarshalAs(UnmanagedType.I4)]
out int pceltFetched);
void Skip(
[MarshalAs(UnmanagedType.I4)]
int celt);
void Reset();
void Clone(
[Out]
out IOPCEnumGUID ppenum);
}
/// <exclude />
[ComImport]
[GuidAttribute("13486D50-4821-11D2-A494-3CB306C10000")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOPCServerList
{
void EnumClassesOfCategories(
[MarshalAs(UnmanagedType.I4)]
int cImplemented,
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStruct, SizeParamIndex=0)]
Guid[] rgcatidImpl,
[MarshalAs(UnmanagedType.I4)]
int cRequired,
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStruct, SizeParamIndex=2)]
Guid[] rgcatidReq,
[Out][MarshalAs(UnmanagedType.IUnknown)]
out object ppenumClsid);
void GetClassDetails(
ref Guid clsid,
[Out][MarshalAs(UnmanagedType.LPWStr)]
out string ppszProgID,
[Out][MarshalAs(UnmanagedType.LPWStr)]
out string ppszUserType,
[Out][MarshalAs(UnmanagedType.LPWStr)]
out string ppszVerIndProgID);
void CLSIDFromProgID(
[MarshalAs(UnmanagedType.LPWStr)]
string szProgId,
[Out]
out Guid clsid);
}
/// <exclude />
[ComImport]
[GuidAttribute("9DD0B56C-AD9E-43ee-8305-487F3188BF7A")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOPCServerList2
{
void EnumClassesOfCategories(
[MarshalAs(UnmanagedType.I4)]
int cImplemented,
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStruct, SizeParamIndex=0)]
Guid[] rgcatidImpl,
[MarshalAs(UnmanagedType.I4)]
int cRequired,
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStruct, SizeParamIndex=0)]
Guid[] rgcatidReq,
[Out]
out IOPCEnumGUID ppenumClsid);
void GetClassDetails(
ref Guid clsid,
[Out][MarshalAs(UnmanagedType.LPWStr)]
out string ppszProgID,
[Out][MarshalAs(UnmanagedType.LPWStr)]
out string ppszUserType,
[Out][MarshalAs(UnmanagedType.LPWStr)]
out string ppszVerIndProgID);
void CLSIDFromProgID(
[MarshalAs(UnmanagedType.LPWStr)]
string szProgId,
[Out]
out Guid clsid);
}
/// <exclude />
[ComImport]
[GuidAttribute("F31DFDE1-07B6-11d2-B2D8-0060083BA1FB")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOPCShutdown
{
void ShutdownRequest(
[MarshalAs(UnmanagedType.LPWStr)]
string szReason);
}
/// <exclude />
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CONNECTDATA
{
[MarshalAs(UnmanagedType.IUnknown)]
object pUnk;
[MarshalAs(UnmanagedType.I4)]
int dwCookie;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,869 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
public class Interop
{
public static PropertyID GetPropertyID(int input)
{
foreach (FieldInfo field in typeof(Property).GetFields(BindingFlags.Static | BindingFlags.Public))
{
PropertyID propertyId = (PropertyID)field.GetValue((object)typeof(PropertyID));
if (input == propertyId.Code)
return propertyId;
}
return new PropertyID(input);
}
internal static BrowseElement GetBrowseElement(IntPtr pInput, bool deallocate)
{
BrowseElement browseElement = (BrowseElement)null;
if (pInput != IntPtr.Zero)
{
OPCBROWSEELEMENT structure = (OPCBROWSEELEMENT)Marshal.PtrToStructure(pInput, typeof(OPCBROWSEELEMENT));
browseElement = new BrowseElement();
browseElement.Name = structure.szName;
browseElement.ItemPath = (string)null;
browseElement.ItemName = structure.szItemID;
browseElement.IsItem = (structure.dwFlagValue & 2) != 0;
browseElement.HasChildren = (structure.dwFlagValue & 1) != 0;
browseElement.Properties = Interop.GetItemProperties(ref structure.ItemProperties, deallocate);
if (deallocate)
Marshal.DestroyStructure(pInput, typeof(OPCBROWSEELEMENT));
}
return browseElement;
}
internal static OPCBROWSEELEMENT GetBrowseElement(
BrowseElement input,
bool propertiesRequested)
{
OPCBROWSEELEMENT browseElement = new OPCBROWSEELEMENT();
if (input != null)
{
browseElement.szName = input.Name;
browseElement.szItemID = input.ItemName;
browseElement.dwFlagValue = 0;
browseElement.ItemProperties = Interop.GetItemProperties(input.Properties);
if (input.IsItem)
browseElement.dwFlagValue |= 2;
if (input.HasChildren)
browseElement.dwFlagValue |= 1;
}
return browseElement;
}
internal static BrowseElement[] GetBrowseElements(
ref IntPtr pInput,
int count,
bool deallocate)
{
BrowseElement[] browseElements = (BrowseElement[])null;
if (pInput != IntPtr.Zero && count > 0)
{
browseElements = new BrowseElement[count];
IntPtr pInput1 = pInput;
for (int index = 0; index < count; ++index)
{
browseElements[index] = Interop.GetBrowseElement(pInput1, deallocate);
pInput1 = (IntPtr)(pInput1.ToInt64() + (long)Marshal.SizeOf(typeof(OPCBROWSEELEMENT)));
}
if (deallocate)
{
Marshal.FreeCoTaskMem(pInput);
pInput = IntPtr.Zero;
}
}
return browseElements;
}
internal static IntPtr GetBrowseElements(BrowseElement[] input, bool propertiesRequested)
{
IntPtr browseElements = IntPtr.Zero;
if (input != null && input.Length != 0)
{
browseElements = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OPCBROWSEELEMENT)) * input.Length);
IntPtr ptr = browseElements;
for (int index = 0; index < input.Length; ++index)
{
Marshal.StructureToPtr((object)Interop.GetBrowseElement(input[index], propertiesRequested), ptr, false);
ptr = (IntPtr)(ptr.ToInt64() + (long)Marshal.SizeOf(typeof(OPCBROWSEELEMENT)));
}
}
return browseElements;
}
internal static ItemProperty[] GetItemProperties(
ref OPCITEMPROPERTIES input,
bool deallocate)
{
ItemProperty[] itemProperties = (ItemProperty[])null;
if (input.dwNumProperties > 0)
{
itemProperties = new ItemProperty[input.dwNumProperties];
IntPtr pInput = input.pItemProperties;
for (int index = 0; index < itemProperties.Length; ++index)
{
try
{
itemProperties[index] = Interop.GetItemProperty(pInput, deallocate);
}
catch (Exception ex)
{
itemProperties[index] = new ItemProperty();
itemProperties[index].Description = ex.Message;
itemProperties[index].ResultID = ResultID.E_FAIL;
}
pInput = (IntPtr)(pInput.ToInt64() + (long)Marshal.SizeOf(typeof(OPCITEMPROPERTY)));
}
if (deallocate)
{
Marshal.FreeCoTaskMem(input.pItemProperties);
input.pItemProperties = IntPtr.Zero;
}
}
return itemProperties;
}
internal static OPCITEMPROPERTIES GetItemProperties(ItemProperty[] input)
{
OPCITEMPROPERTIES itemProperties = new OPCITEMPROPERTIES();
if (input != null && input.Length != 0)
{
itemProperties.hrErrorID = 0;
itemProperties.dwReserved = 0;
itemProperties.dwNumProperties = input.Length;
itemProperties.pItemProperties = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(OPCITEMPROPERTY)) * input.Length);
bool flag = false;
IntPtr ptr = itemProperties.pItemProperties;
for (int index = 0; index < input.Length; ++index)
{
Marshal.StructureToPtr((object)Interop.GetItemProperty(input[index]), ptr, false);
ptr = (IntPtr)(ptr.ToInt64() + (long)Marshal.SizeOf(typeof(OPCITEMPROPERTY)));
if (input[index].ResultID.Failed())
flag = true;
}
if (flag)
itemProperties.hrErrorID = 1;
}
return itemProperties;
}
internal static ItemProperty GetItemProperty(IntPtr pInput, bool deallocate)
{
ItemProperty itemProperty = (ItemProperty)null;
if (pInput != IntPtr.Zero)
{
OPCITEMPROPERTY structure = (OPCITEMPROPERTY)Marshal.PtrToStructure(pInput, typeof(OPCITEMPROPERTY));
itemProperty = new ItemProperty()
{
ID = Interop.GetPropertyID(structure.dwPropertyID),
Description = structure.szDescription,
DataType = Interop.GetType((VarEnum)structure.vtDataType),
ItemPath = (string)null,
ItemName = structure.szItemID
};
itemProperty.Value = Interop.UnmarshalPropertyValue(itemProperty.ID, structure.vValue);
itemProperty.ResultID = Interop.GetResultID(structure.hrErrorID);
if (structure.hrErrorID == -1073479674)
itemProperty.ResultID = new ResultID(ResultID.Da.E_WRITEONLY, -1073479674L);
if (deallocate)
Marshal.DestroyStructure(pInput, typeof(OPCITEMPROPERTY));
}
return itemProperty;
}
internal static OPCITEMPROPERTY GetItemProperty(ItemProperty input)
{
OPCITEMPROPERTY itemProperty = new OPCITEMPROPERTY();
if (input != null)
{
itemProperty.dwPropertyID = input.ID.Code;
itemProperty.szDescription = input.Description;
itemProperty.vtDataType = (short)Interop.GetType(input.DataType);
itemProperty.vValue = Interop.MarshalPropertyValue(input.ID, input.Value);
itemProperty.wReserved = (short)0;
itemProperty.hrErrorID = Interop.GetResultID(input.ResultID);
PropertyDescription propertyDescription = PropertyDescription.Find(input.ID);
if (propertyDescription != null)
itemProperty.vtDataType = (short)Interop.GetType(propertyDescription.Type);
if (input.ResultID == ResultID.Da.E_WRITEONLY)
itemProperty.hrErrorID = -1073479674;
}
return itemProperty;
}
internal static int[] GetPropertyIDs(PropertyID[] propertyIDs)
{
ArrayList arrayList = new ArrayList();
if (propertyIDs != null)
{
foreach (PropertyID propertyId in propertyIDs)
arrayList.Add((object)propertyId.Code);
}
return (int[])arrayList.ToArray(typeof(int));
}
internal static ResultID GetResultID(int input)
{
switch (input)
{
case -2147467262:
return new ResultID(ResultID.E_NOTSUPPORTED, (long)input);
case -2147467259:
return new ResultID(ResultID.E_FAIL, (long)input);
case -2147352571:
return new ResultID(ResultID.Da.E_BADTYPE, (long)input);
case -2147352566:
return new ResultID(ResultID.Da.E_RANGE, (long)input);
case -2147217401:
return new ResultID(ResultID.Hda.W_NOFILTER, (long)input);
case -2147024882:
return new ResultID(ResultID.E_OUTOFMEMORY, (long)input);
case -2147024809:
return new ResultID(ResultID.E_INVALIDARG, (long)input);
case -1073479679:
return new ResultID(ResultID.Da.E_INVALIDHANDLE, (long)input);
case -1073479676:
return new ResultID(ResultID.Da.E_BADTYPE, (long)input);
case -1073479673:
return new ResultID(ResultID.Da.E_UNKNOWN_ITEM_NAME, (long)input);
case -1073479672:
return new ResultID(ResultID.Da.E_INVALID_ITEM_NAME, (long)input);
case -1073479671:
return new ResultID(ResultID.Da.E_INVALID_FILTER, (long)input);
case -1073479670:
return new ResultID(ResultID.Da.E_UNKNOWN_ITEM_PATH, (long)input);
case -1073479669:
return new ResultID(ResultID.Da.E_RANGE, (long)input);
case -1073479165:
return new ResultID(ResultID.Da.E_INVALID_PID, (long)input);
case -1073479164:
return new ResultID(ResultID.Ae.E_INVALIDTIME, (long)input);
case -1073479163:
return new ResultID(ResultID.Ae.E_BUSY, (long)input);
case -1073479162:
return new ResultID(ResultID.Ae.E_NOINFO, (long)input);
case -1073478655:
return new ResultID(ResultID.Da.E_NO_ITEM_DEADBAND, (long)input);
case -1073478654:
return new ResultID(ResultID.Da.E_NO_ITEM_BUFFERING, (long)input);
case -1073478653:
return new ResultID(ResultID.Da.E_INVALIDCONTINUATIONPOINT, (long)input);
case -1073478650:
return new ResultID(ResultID.Da.E_NO_WRITEQT, (long)input);
case -1073478649:
return new ResultID(ResultID.Cpx.E_TYPE_CHANGED, (long)input);
case -1073478648:
return new ResultID(ResultID.Cpx.E_FILTER_DUPLICATE, (long)input);
case -1073478647:
return new ResultID(ResultID.Cpx.E_FILTER_INVALID, (long)input);
case -1073478646:
return new ResultID(ResultID.Cpx.E_FILTER_ERROR, (long)input);
case -1073477888:
return new ResultID(ResultID.Dx.E_PERSISTING, (long)input);
case -1073477887:
return new ResultID(ResultID.Dx.E_NOITEMLIST, (long)input);
case -1073477886:
return new ResultID(ResultID.Dx.E_VERSION_MISMATCH, (long)input);
case -1073477885:
return new ResultID(ResultID.Dx.E_VERSION_MISMATCH, (long)input);
case -1073477884:
return new ResultID(ResultID.Dx.E_UNKNOWN_ITEM_PATH, (long)input);
case -1073477883:
return new ResultID(ResultID.Dx.E_UNKNOWN_ITEM_NAME, (long)input);
case -1073477882:
return new ResultID(ResultID.Dx.E_INVALID_ITEM_PATH, (long)input);
case -1073477881:
return new ResultID(ResultID.Dx.E_INVALID_ITEM_NAME, (long)input);
case -1073477880:
return new ResultID(ResultID.Dx.E_INVALID_NAME, (long)input);
case -1073477879:
return new ResultID(ResultID.Dx.E_DUPLICATE_NAME, (long)input);
case -1073477878:
return new ResultID(ResultID.Dx.E_INVALID_BROWSE_PATH, (long)input);
case -1073477877:
return new ResultID(ResultID.Dx.E_INVALID_SERVER_URL, (long)input);
case -1073477876:
return new ResultID(ResultID.Dx.E_INVALID_SERVER_TYPE, (long)input);
case -1073477875:
return new ResultID(ResultID.Dx.E_UNSUPPORTED_SERVER_TYPE, (long)input);
case -1073477874:
return new ResultID(ResultID.Dx.E_CONNECTIONS_EXIST, (long)input);
case -1073477873:
return new ResultID(ResultID.Dx.E_TOO_MANY_CONNECTIONS, (long)input);
case -1073477872:
return new ResultID(ResultID.Dx.E_OVERRIDE_BADTYPE, (long)input);
case -1073477871:
return new ResultID(ResultID.Dx.E_OVERRIDE_RANGE, (long)input);
case -1073477870:
return new ResultID(ResultID.Dx.E_SUBSTITUTE_BADTYPE, (long)input);
case -1073477869:
return new ResultID(ResultID.Dx.E_SUBSTITUTE_RANGE, (long)input);
case -1073477868:
return new ResultID(ResultID.Dx.E_INVALID_TARGET_ITEM, (long)input);
case -1073477867:
return new ResultID(ResultID.Dx.E_UNKNOWN_TARGET_ITEM, (long)input);
case -1073477866:
return new ResultID(ResultID.Dx.E_TARGET_ALREADY_CONNECTED, (long)input);
case -1073477865:
return new ResultID(ResultID.Dx.E_UNKNOWN_SERVER_NAME, (long)input);
case -1073477864:
return new ResultID(ResultID.Dx.E_UNKNOWN_SOURCE_ITEM, (long)input);
case -1073477863:
return new ResultID(ResultID.Dx.E_INVALID_SOURCE_ITEM, (long)input);
case -1073477862:
return new ResultID(ResultID.Dx.E_INVALID_QUEUE_SIZE, (long)input);
case -1073477861:
return new ResultID(ResultID.Dx.E_INVALID_DEADBAND, (long)input);
case -1073477860:
return new ResultID(ResultID.Dx.E_INVALID_CONFIG_FILE, (long)input);
case -1073477859:
return new ResultID(ResultID.Dx.E_PERSIST_FAILED, (long)input);
case -1073477858:
return new ResultID(ResultID.Dx.E_TARGET_FAULT, (long)input);
case -1073477857:
return new ResultID(ResultID.Dx.E_TARGET_NO_ACCESSS, (long)input);
case -1073477856:
return new ResultID(ResultID.Dx.E_SOURCE_SERVER_FAULT, (long)input);
case -1073477855:
return new ResultID(ResultID.Dx.E_SOURCE_SERVER_NO_ACCESSS, (long)input);
case -1073477854:
return new ResultID(ResultID.Dx.E_SUBSCRIPTION_FAULT, (long)input);
case -1073477853:
return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BADRIGHTS, (long)input);
case -1073477852:
return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BAD_QUALITY, (long)input);
case -1073477851:
return new ResultID(ResultID.Dx.E_SOURCE_ITEM_BADTYPE, (long)input);
case -1073477850:
return new ResultID(ResultID.Dx.E_SOURCE_ITEM_RANGE, (long)input);
case -1073477849:
return new ResultID(ResultID.Dx.E_SOURCE_SERVER_NOT_CONNECTED, (long)input);
case -1073477848:
return new ResultID(ResultID.Dx.E_SOURCE_SERVER_TIMEOUT, (long)input);
case -1073477847:
return new ResultID(ResultID.Dx.E_TARGET_ITEM_DISCONNECTED, (long)input);
case -1073477846:
return new ResultID(ResultID.Dx.E_TARGET_NO_WRITES_ATTEMPTED, (long)input);
case -1073477845:
return new ResultID(ResultID.Dx.E_TARGET_ITEM_BADTYPE, (long)input);
case -1073477844:
return new ResultID(ResultID.Dx.E_TARGET_ITEM_RANGE, (long)input);
case -1073475583:
return new ResultID(ResultID.Hda.E_MAXEXCEEDED, (long)input);
case -1073475580:
return new ResultID(ResultID.Hda.E_INVALIDAGGREGATE, (long)input);
case -1073475576:
return new ResultID(ResultID.Hda.E_UNKNOWNATTRID, (long)input);
case -1073475575:
return new ResultID(ResultID.Hda.E_NOT_AVAIL, (long)input);
case -1073475574:
return new ResultID(ResultID.Hda.E_INVALIDDATATYPE, (long)input);
case -1073475573:
return new ResultID(ResultID.Hda.E_DATAEXISTS, (long)input);
case -1073475572:
return new ResultID(ResultID.Hda.E_INVALIDATTRID, (long)input);
case -1073475571:
return new ResultID(ResultID.Hda.E_NODATAEXISTS, (long)input);
case 0:
return new ResultID(ResultID.S_OK, (long)input);
case 262157:
return new ResultID(ResultID.Da.S_UNSUPPORTEDRATE, (long)input);
case 262158:
return new ResultID(ResultID.Da.S_CLAMP, (long)input);
case 262656:
return new ResultID(ResultID.Ae.S_ALREADYACKED, (long)input);
case 262657:
return new ResultID(ResultID.Ae.S_INVALIDBUFFERTIME, (long)input);
case 262658:
return new ResultID(ResultID.Ae.S_INVALIDMAXSIZE, (long)input);
case 262659:
return new ResultID(ResultID.Ae.S_INVALIDKEEPALIVETIME, (long)input);
case 263172:
return new ResultID(ResultID.Da.S_DATAQUEUEOVERFLOW, (long)input);
case 263179:
return new ResultID(ResultID.Cpx.S_FILTER_NO_DATA, (long)input);
case 264064:
return new ResultID(ResultID.Dx.S_TARGET_SUBSTITUTED, (long)input);
case 264065:
return new ResultID(ResultID.Dx.S_TARGET_OVERRIDEN, (long)input);
case 264066:
return new ResultID(ResultID.Dx.S_CLAMP, (long)input);
case 1074008066:
return new ResultID(ResultID.Hda.S_NODATA, (long)input);
case 1074008067:
return new ResultID(ResultID.Hda.S_MOREDATA, (long)input);
case 1074008069:
return new ResultID(ResultID.Hda.S_CURRENTVALUE, (long)input);
case 1074008070:
return new ResultID(ResultID.Hda.S_EXTRADATA, (long)input);
case 1074008078:
return new ResultID(ResultID.Hda.S_INSERTED, (long)input);
case 1074008079:
return new ResultID(ResultID.Hda.S_REPLACED, (long)input);
default:
if ((input & 2147418112) == 65536)
return new ResultID(ResultID.E_NETWORK_ERROR, (long)input);
return input >= 0 ? new ResultID(ResultID.S_FALSE, (long)input) : new ResultID(ResultID.E_FAIL, (long)input);
}
}
internal static int GetResultID(ResultID input)
{
if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/DataAccess/")
{
if (input == ResultID.S_OK)
return 0;
if (input == ResultID.E_FAIL)
return -2147467259;
if (input == ResultID.E_INVALIDARG)
return -2147024809;
if (input == ResultID.Da.E_BADTYPE)
return -1073479676;
if (input == ResultID.Da.E_READONLY || input == ResultID.Da.E_WRITEONLY)
return -1073479674;
if (input == ResultID.Da.E_RANGE)
return -1073479669;
if (input == ResultID.E_OUTOFMEMORY)
return -2147024882;
if (input == ResultID.E_NOTSUPPORTED)
return -2147467262;
if (input == ResultID.Da.E_INVALIDHANDLE)
return -1073479679;
if (input == ResultID.Da.E_UNKNOWN_ITEM_NAME)
return -1073479673;
if (input == ResultID.Da.E_INVALID_ITEM_NAME || input == ResultID.Da.E_INVALID_ITEM_PATH)
return -1073479672;
if (input == ResultID.Da.E_UNKNOWN_ITEM_PATH)
return -1073479670;
if (input == ResultID.Da.E_INVALID_FILTER)
return -1073479671;
if (input == ResultID.Da.S_UNSUPPORTEDRATE)
return 262157;
if (input == ResultID.Da.S_CLAMP)
return 262158;
if (input == ResultID.Da.E_INVALID_PID)
return -1073479165;
if (input == ResultID.Da.E_NO_ITEM_DEADBAND)
return -1073478655;
if (input == ResultID.Da.E_NO_ITEM_BUFFERING)
return -1073478654;
if (input == ResultID.Da.E_NO_WRITEQT)
return -1073478650;
if (input == ResultID.Da.E_INVALIDCONTINUATIONPOINT)
return -1073478653;
if (input == ResultID.Da.S_DATAQUEUEOVERFLOW)
return 263172;
}
else if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/ComplexData/")
{
if (input == ResultID.Cpx.E_TYPE_CHANGED)
return -1073478649;
if (input == ResultID.Cpx.E_FILTER_DUPLICATE)
return -1073478648;
if (input == ResultID.Cpx.E_FILTER_INVALID)
return -1073478647;
if (input == ResultID.Cpx.E_FILTER_ERROR)
return -1073478646;
if (input == ResultID.Cpx.S_FILTER_NO_DATA)
return 263179;
}
else if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/HistoricalDataAccess/")
{
if (input == ResultID.Hda.E_MAXEXCEEDED)
return -1073475583;
if (input == ResultID.Hda.S_NODATA)
return 1074008066;
if (input == ResultID.Hda.S_MOREDATA)
return 1074008067;
if (input == ResultID.Hda.E_INVALIDAGGREGATE)
return -1073475580;
if (input == ResultID.Hda.S_CURRENTVALUE)
return 1074008069;
if (input == ResultID.Hda.S_EXTRADATA)
return 1074008070;
if (input == ResultID.Hda.E_UNKNOWNATTRID)
return -1073475576;
if (input == ResultID.Hda.E_NOT_AVAIL)
return -1073475575;
if (input == ResultID.Hda.E_INVALIDDATATYPE)
return -1073475574;
if (input == ResultID.Hda.E_DATAEXISTS)
return -1073475573;
if (input == ResultID.Hda.E_INVALIDATTRID)
return -1073475572;
if (input == ResultID.Hda.E_NODATAEXISTS)
return -1073475571;
if (input == ResultID.Hda.S_INSERTED)
return 1074008078;
if (input == ResultID.Hda.S_REPLACED)
return 1074008079;
}
if (input.Name != (XmlQualifiedName)null && input.Name.Namespace == "http://opcfoundation.org/DataExchange/")
{
if (input == ResultID.Dx.E_PERSISTING)
return -1073477888;
if (input == ResultID.Dx.E_NOITEMLIST)
return -1073477887;
if (input == ResultID.Dx.E_SERVER_STATE || input == ResultID.Dx.E_VERSION_MISMATCH)
return -1073477885;
if (input == ResultID.Dx.E_UNKNOWN_ITEM_PATH)
return -1073477884;
if (input == ResultID.Dx.E_UNKNOWN_ITEM_NAME)
return -1073477883;
if (input == ResultID.Dx.E_INVALID_ITEM_PATH)
return -1073477882;
if (input == ResultID.Dx.E_INVALID_ITEM_NAME)
return -1073477881;
if (input == ResultID.Dx.E_INVALID_NAME)
return -1073477880;
if (input == ResultID.Dx.E_DUPLICATE_NAME)
return -1073477879;
if (input == ResultID.Dx.E_INVALID_BROWSE_PATH)
return -1073477878;
if (input == ResultID.Dx.E_INVALID_SERVER_URL)
return -1073477877;
if (input == ResultID.Dx.E_INVALID_SERVER_TYPE)
return -1073477876;
if (input == ResultID.Dx.E_UNSUPPORTED_SERVER_TYPE)
return -1073477875;
if (input == ResultID.Dx.E_CONNECTIONS_EXIST)
return -1073477874;
if (input == ResultID.Dx.E_TOO_MANY_CONNECTIONS)
return -1073477873;
if (input == ResultID.Dx.E_OVERRIDE_BADTYPE)
return -1073477872;
if (input == ResultID.Dx.E_OVERRIDE_RANGE)
return -1073477871;
if (input == ResultID.Dx.E_SUBSTITUTE_BADTYPE)
return -1073477870;
if (input == ResultID.Dx.E_SUBSTITUTE_RANGE)
return -1073477869;
if (input == ResultID.Dx.E_INVALID_TARGET_ITEM)
return -1073477868;
if (input == ResultID.Dx.E_UNKNOWN_TARGET_ITEM)
return -1073477867;
if (input == ResultID.Dx.E_TARGET_ALREADY_CONNECTED)
return -1073477866;
if (input == ResultID.Dx.E_UNKNOWN_SERVER_NAME)
return -1073477865;
if (input == ResultID.Dx.E_UNKNOWN_SOURCE_ITEM)
return -1073477864;
if (input == ResultID.Dx.E_INVALID_SOURCE_ITEM)
return -1073477863;
if (input == ResultID.Dx.E_INVALID_QUEUE_SIZE)
return -1073477862;
if (input == ResultID.Dx.E_INVALID_DEADBAND)
return -1073477861;
if (input == ResultID.Dx.E_INVALID_CONFIG_FILE)
return -1073477860;
if (input == ResultID.Dx.E_PERSIST_FAILED)
return -1073477859;
if (input == ResultID.Dx.E_TARGET_FAULT)
return -1073477858;
if (input == ResultID.Dx.E_TARGET_NO_ACCESSS)
return -1073477857;
if (input == ResultID.Dx.E_SOURCE_SERVER_FAULT)
return -1073477856;
if (input == ResultID.Dx.E_SOURCE_SERVER_NO_ACCESSS)
return -1073477855;
if (input == ResultID.Dx.E_SUBSCRIPTION_FAULT)
return -1073477854;
if (input == ResultID.Dx.E_SOURCE_ITEM_BADRIGHTS)
return -1073477853;
if (input == ResultID.Dx.E_SOURCE_ITEM_BAD_QUALITY)
return -1073477852;
if (input == ResultID.Dx.E_SOURCE_ITEM_BADTYPE)
return -1073477851;
if (input == ResultID.Dx.E_SOURCE_ITEM_RANGE)
return -1073477850;
if (input == ResultID.Dx.E_SOURCE_SERVER_NOT_CONNECTED)
return -1073477849;
if (input == ResultID.Dx.E_SOURCE_SERVER_TIMEOUT)
return -1073477848;
if (input == ResultID.Dx.E_TARGET_ITEM_DISCONNECTED)
return -1073477847;
if (input == ResultID.Dx.E_TARGET_NO_WRITES_ATTEMPTED)
return -1073477846;
if (input == ResultID.Dx.E_TARGET_ITEM_BADTYPE)
return -1073477845;
if (input == ResultID.Dx.E_TARGET_ITEM_RANGE)
return -1073477844;
if (input == ResultID.Dx.S_TARGET_SUBSTITUTED)
return 264064;
if (input == ResultID.Dx.S_TARGET_OVERRIDEN)
return 264065;
if (input == ResultID.Dx.S_CLAMP)
return 264066;
}
else if (input.Code == -1)
return input.Succeeded() ? 1 : -2147467259;
return input.Code;
}
internal static VarEnum GetType(System.Type input)
{
if (input == (System.Type)null)
return VarEnum.VT_EMPTY;
if (input == typeof(sbyte))
return VarEnum.VT_I1;
if (input == typeof(byte))
return VarEnum.VT_UI1;
if (input == typeof(short))
return VarEnum.VT_I2;
if (input == typeof(ushort))
return VarEnum.VT_UI2;
if (input == typeof(int))
return VarEnum.VT_I4;
if (input == typeof(uint))
return VarEnum.VT_UI4;
if (input == typeof(long))
return VarEnum.VT_I8;
if (input == typeof(ulong))
return VarEnum.VT_UI8;
if (input == typeof(float))
return VarEnum.VT_R4;
if (input == typeof(double))
return VarEnum.VT_R8;
if (input == typeof(Decimal))
return VarEnum.VT_CY;
if (input == typeof(bool))
return VarEnum.VT_BOOL;
if (input == typeof(DateTime))
return VarEnum.VT_DATE;
if (input == typeof(string))
return VarEnum.VT_BSTR;
if (input == typeof(object))
return VarEnum.VT_EMPTY;
if (input == typeof(sbyte[]))
return VarEnum.VT_I1 | VarEnum.VT_ARRAY;
if (input == typeof(byte[]))
return VarEnum.VT_UI1 | VarEnum.VT_ARRAY;
if (input == typeof(short[]))
return VarEnum.VT_I2 | VarEnum.VT_ARRAY;
if (input == typeof(ushort[]))
return VarEnum.VT_UI2 | VarEnum.VT_ARRAY;
if (input == typeof(int[]))
return VarEnum.VT_I4 | VarEnum.VT_ARRAY;
if (input == typeof(uint[]))
return VarEnum.VT_UI4 | VarEnum.VT_ARRAY;
if (input == typeof(long[]))
return VarEnum.VT_I8 | VarEnum.VT_ARRAY;
if (input == typeof(ulong[]))
return VarEnum.VT_UI8 | VarEnum.VT_ARRAY;
if (input == typeof(float[]))
return VarEnum.VT_R4 | VarEnum.VT_ARRAY;
if (input == typeof(double[]))
return VarEnum.VT_R8 | VarEnum.VT_ARRAY;
if (input == typeof(Decimal[]))
return VarEnum.VT_CY | VarEnum.VT_ARRAY;
if (input == typeof(bool[]))
return VarEnum.VT_BOOL | VarEnum.VT_ARRAY;
if (input == typeof(DateTime[]))
return VarEnum.VT_DATE | VarEnum.VT_ARRAY;
if (input == typeof(string[]))
return VarEnum.VT_BSTR | VarEnum.VT_ARRAY;
if (input == typeof(object[]))
return VarEnum.VT_VARIANT | VarEnum.VT_ARRAY;
if (input == Type.ILLEGAL_TYPE)
return (VarEnum)System.Enum.ToObject(typeof(VarEnum), (int)short.MaxValue);
if (input == typeof(System.Type) || input == typeof(Quality))
return VarEnum.VT_I2;
return input == typeof(accessRights) || input == typeof(euType) ? VarEnum.VT_I4 : VarEnum.VT_EMPTY;
}
internal static System.Type GetType(VarEnum input)
{
switch (input)
{
case VarEnum.VT_EMPTY:
return (System.Type)null;
case VarEnum.VT_I2:
return typeof(short);
case VarEnum.VT_I4:
return typeof(int);
case VarEnum.VT_R4:
return typeof(float);
case VarEnum.VT_R8:
return typeof(double);
case VarEnum.VT_CY:
return typeof(Decimal);
case VarEnum.VT_DATE:
return typeof(DateTime);
case VarEnum.VT_BSTR:
return typeof(string);
case VarEnum.VT_BOOL:
return typeof(bool);
case VarEnum.VT_I1:
return typeof(sbyte);
case VarEnum.VT_UI1:
return typeof(byte);
case VarEnum.VT_UI2:
return typeof(ushort);
case VarEnum.VT_UI4:
return typeof(uint);
case VarEnum.VT_I8:
return typeof(long);
case VarEnum.VT_UI8:
return typeof(ulong);
case VarEnum.VT_I2 | VarEnum.VT_ARRAY:
return typeof(short[]);
case VarEnum.VT_I4 | VarEnum.VT_ARRAY:
return typeof(int[]);
case VarEnum.VT_R4 | VarEnum.VT_ARRAY:
return typeof(float[]);
case VarEnum.VT_R8 | VarEnum.VT_ARRAY:
return typeof(double[]);
case VarEnum.VT_CY | VarEnum.VT_ARRAY:
return typeof(Decimal[]);
case VarEnum.VT_DATE | VarEnum.VT_ARRAY:
return typeof(DateTime[]);
case VarEnum.VT_BSTR | VarEnum.VT_ARRAY:
return typeof(string[]);
case VarEnum.VT_BOOL | VarEnum.VT_ARRAY:
return typeof(bool[]);
case VarEnum.VT_VARIANT | VarEnum.VT_ARRAY:
return typeof(object[]);
case VarEnum.VT_I1 | VarEnum.VT_ARRAY:
return typeof(sbyte[]);
case VarEnum.VT_UI1 | VarEnum.VT_ARRAY:
return typeof(byte[]);
case VarEnum.VT_UI2 | VarEnum.VT_ARRAY:
return typeof(ushort[]);
case VarEnum.VT_UI4 | VarEnum.VT_ARRAY:
return typeof(uint[]);
case VarEnum.VT_I8 | VarEnum.VT_ARRAY:
return typeof(long[]);
case VarEnum.VT_UI8 | VarEnum.VT_ARRAY:
return typeof(ulong[]);
default:
return Type.ILLEGAL_TYPE;
}
}
internal static object MarshalPropertyValue(PropertyID propertyID, object input)
{
if (input == null)
return (object)null;
try
{
if (propertyID == Property.DATATYPE)
return (object)(short)Interop.GetType((System.Type)input);
if (propertyID == Property.ACCESSRIGHTS)
{
switch ((accessRights)input)
{
case accessRights.readable:
return (object)1;
case accessRights.writable:
return (object)2;
case accessRights.readWritable:
return (object)3;
default:
return (object)null;
}
}
else if (propertyID == Property.EUTYPE)
{
switch ((euType)input)
{
case euType.noEnum:
return (object)OPCEUTYPE.OPC_NOENUM;
case euType.analog:
return (object)OPCEUTYPE.OPC_ANALOG;
case euType.enumerated:
return (object)OPCEUTYPE.OPC_ENUMERATED;
default:
return (object)null;
}
}
else
{
if (propertyID == Property.QUALITY)
return (object)((Quality)input).GetCode();
if (propertyID == Property.TIMESTAMP)
{
if (input.GetType() == typeof(DateTime))
{
DateTime dateTime = (DateTime)input;
return dateTime != DateTime.MinValue ? (object)dateTime.ToUniversalTime() : (object)dateTime;
}
}
}
}
catch
{
}
return input;
}
internal static object UnmarshalPropertyValue(PropertyID propertyID, object input)
{
if (input == null)
return (object)null;
try
{
if (propertyID == Property.DATATYPE)
return (object)Interop.GetType((VarEnum)System.Convert.ToUInt16(input));
if (propertyID == Property.ACCESSRIGHTS)
{
switch (System.Convert.ToInt32(input))
{
case 1:
return (object)accessRights.readable;
case 2:
return (object)accessRights.writable;
case 3:
return (object)accessRights.readWritable;
default:
return (object)null;
}
}
else if (propertyID == Property.EUTYPE)
{
switch ((OPCEUTYPE)input)
{
case OPCEUTYPE.OPC_NOENUM:
return (object)euType.noEnum;
case OPCEUTYPE.OPC_ANALOG:
return (object)euType.analog;
case OPCEUTYPE.OPC_ENUMERATED:
return (object)euType.enumerated;
default:
return (object)null;
}
}
else
{
if (propertyID == Property.QUALITY)
return (object)new Quality(System.Convert.ToInt16(input));
if (propertyID == Property.TIMESTAMP)
{
if (input.GetType() == typeof(DateTime))
{
DateTime dateTime = (DateTime)input;
return dateTime != DateTime.MinValue ? (object)dateTime.ToLocalTime() : (object)dateTime;
}
}
}
}
catch
{
}
return input;
}
}

View File

@@ -0,0 +1,960 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml;
namespace ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
public enum accessRights
{
readable = 1,
writable,
readWritable
}
public enum euType
{
noEnum = 1,
analog,
enumerated
}
public enum limitBits
{
none,
low,
high,
constant
}
public enum qualityBits
{
good = 192,
goodLocalOverride = 216,
bad = 0,
badConfigurationError = 4,
badNotConnected = 8,
badDeviceFailure = 12,
badSensorFailure = 0x10,
badLastKnownValue = 20,
badCommFailure = 24,
badOutOfService = 28,
badWaitingForInitialData = 0x20,
uncertain = 0x40,
uncertainLastUsableValue = 68,
uncertainSensorNotAccurate = 80,
uncertainEUExceeded = 84,
uncertainSubNormal = 88
}
public interface IResult
{
string DiagnosticInfo { get; set; }
ResultID ResultID { get; set; }
}
[Serializable]
public struct PropertyID : ISerializable
{
private int m_code;
private XmlQualifiedName m_name;
public PropertyID(XmlQualifiedName name)
{
m_name = name;
m_code = 0;
}
public PropertyID(int code)
{
m_name = null;
m_code = code;
}
public PropertyID(string name, int code, string ns)
{
m_name = new XmlQualifiedName(name, ns);
m_code = code;
}
private PropertyID(SerializationInfo info, StreamingContext context)
{
SerializationInfoEnumerator enumerator = info.GetEnumerator();
string name = "";
string ns = "";
enumerator.Reset();
while (enumerator.MoveNext())
{
if (enumerator.Current.Name.Equals("NA"))
{
name = (string)enumerator.Current.Value;
}
else if (enumerator.Current.Name.Equals("NS"))
{
ns = (string)enumerator.Current.Value;
}
}
m_name = new XmlQualifiedName(name, ns);
m_code = (int)info.GetValue("CO", typeof(int));
}
public int Code => m_code;
public XmlQualifiedName Name => m_name;
public static bool operator !=(PropertyID a, PropertyID b)
{
return !a.Equals(b);
}
public static bool operator ==(PropertyID a, PropertyID b)
{
return a.Equals(b);
}
public override bool Equals(object target)
{
if (target != null && target.GetType() == typeof(PropertyID))
{
PropertyID propertyID = (PropertyID)target;
if (propertyID.Code != 0 && Code != 0)
{
return propertyID.Code == Code;
}
if (propertyID.Name != null && Name != null)
{
return propertyID.Name == Name;
}
}
return false;
}
public override int GetHashCode()
{
if (Code != 0)
{
return Code.GetHashCode();
}
if (Name != null)
{
return Name.GetHashCode();
}
return base.GetHashCode();
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (m_name != null)
{
info.AddValue("NA", m_name.Name);
info.AddValue("NS", m_name.Namespace);
}
info.AddValue("CO", m_code);
}
public override string ToString()
{
if (Name != null && Code != 0)
{
return $"{Name.Name} ({Code})";
}
if (Name != null)
{
return Name.Name;
}
if (Code != 0)
{
return $"{Code}";
}
return "";
}
private class Names
{
internal const string CODE = "CO";
internal const string NAME = "NA";
internal const string NAMESPACE = "NS";
}
}
[Serializable]
public struct Quality
{
public static readonly Quality Bad = new Quality(qualityBits.bad);
public static readonly Quality Good = new Quality(qualityBits.good);
private limitBits m_limitBits;
private qualityBits m_qualityBits;
private byte m_vendorBits;
public Quality(qualityBits quality)
{
m_qualityBits = quality;
m_limitBits = limitBits.none;
m_vendorBits = 0;
}
public Quality(short code)
{
m_qualityBits = (qualityBits)(code & 0xFC);
m_limitBits = (limitBits)(code & 3);
m_vendorBits = (byte)((code & -253) >> 8);
}
public limitBits LimitBits
{
get
{
return m_limitBits;
}
set
{
m_limitBits = value;
}
}
public qualityBits QualityBits
{
get
{
return m_qualityBits;
}
set
{
m_qualityBits = value;
}
}
public byte VendorBits
{
get
{
return m_vendorBits;
}
set
{
m_vendorBits = value;
}
}
public static bool operator !=(Quality a, Quality b)
{
return !a.Equals(b);
}
public static bool operator ==(Quality a, Quality b)
{
return a.Equals(b);
}
public override bool Equals(object target)
{
if (target == null || target.GetType() != typeof(Quality))
{
return false;
}
Quality quality = (Quality)target;
if (QualityBits != quality.QualityBits)
{
return false;
}
if (LimitBits != quality.LimitBits)
{
return false;
}
if (VendorBits != quality.VendorBits)
{
return false;
}
return true;
}
public short GetCode()
{
ushort num = 0;
num = (ushort)(num | (ushort)QualityBits);
num = (ushort)(num | (ushort)LimitBits);
num = (ushort)(num | (ushort)(VendorBits << 8));
if (num > 32767)
{
return (short)(-(65536 - num));
}
return (short)num;
}
public override int GetHashCode()
{
return GetCode();
}
public void SetCode(short code)
{
m_qualityBits = (qualityBits)(code & 0xFC);
m_limitBits = (limitBits)(code & 3);
m_vendorBits = (byte)((code & -253) >> 8);
}
public override string ToString()
{
string text = QualityBits.ToString();
if (LimitBits != 0)
{
text += $"[{LimitBits.ToString()}]";
}
if (VendorBits != 0)
{
text += string.Format(":{0,0:X}", VendorBits);
}
return text;
}
}
[Serializable]
public struct ResultID : ISerializable
{
public static readonly ResultID E_ACCESS_DENIED = new ResultID("E_ACCESS_DENIED", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_FAIL = new ResultID("E_FAIL", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_INVALIDARG = new ResultID("E_INVALIDARG", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_NETWORK_ERROR = new ResultID("E_NETWORK_ERROR", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_NOTSUPPORTED = new ResultID("E_NOTSUPPORTED", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_OUTOFMEMORY = new ResultID("E_OUTOFMEMORY", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_TIMEDOUT = new ResultID("E_TIMEDOUT", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID S_FALSE = new ResultID("S_FALSE", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID S_OK = new ResultID("S_OK", "http://opcfoundation.org/DataAccess/");
private int m_code;
private XmlQualifiedName m_name;
public ResultID(XmlQualifiedName name)
{
m_name = name;
m_code = -1;
}
public ResultID(long code)
{
m_name = null;
if (code > int.MaxValue)
{
code = -(4294967296L - code);
}
m_code = (int)code;
}
public ResultID(string name, string ns)
{
m_name = new XmlQualifiedName(name, ns);
m_code = -1;
}
public ResultID(ResultID resultID, long code)
{
m_name = resultID.Name;
if (code > int.MaxValue)
{
code = -(4294967296L - code);
}
m_code = (int)code;
}
private ResultID(SerializationInfo info, StreamingContext context)
{
string name = (string)info.GetValue("NA", typeof(string));
string ns = (string)info.GetValue("NS", typeof(string));
m_name = new XmlQualifiedName(name, ns);
m_code = (int)info.GetValue("CO", typeof(int));
}
public int Code => m_code;
public XmlQualifiedName Name => m_name;
public static bool operator !=(ResultID a, ResultID b)
{
return !a.Equals(b);
}
public static bool operator ==(ResultID a, ResultID b)
{
return a.Equals(b);
}
public override bool Equals(object target)
{
if (target != null && target.GetType() == typeof(ResultID))
{
ResultID resultID = (ResultID)target;
if (resultID.Code != -1 && Code != -1)
{
if (resultID.Code == Code)
{
return resultID.Name == Name;
}
return false;
}
if (resultID.Name != null && Name != null)
{
return resultID.Name == Name;
}
}
return false;
}
public bool Failed()
{
if (Code != -1)
{
return Code < 0;
}
if (Name != null)
{
return Name.Name.StartsWith("E_");
}
return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (m_name != null)
{
info.AddValue("NA", m_name.Name);
info.AddValue("NS", m_name.Namespace);
}
info.AddValue("CO", m_code);
}
public bool Succeeded()
{
if (Code != -1)
{
return Code >= 0;
}
if (Name != null)
{
return Name.Name.StartsWith("S_");
}
return false;
}
public override string ToString()
{
if (Name != null)
{
return Name.Name;
}
return string.Format("0x{0,0:X}", Code);
}
public class Ae
{
public static readonly ResultID E_BUSY = new ResultID("E_BUSY", "http://opcfoundation.org/AlarmAndEvents/");
public static readonly ResultID E_INVALIDBRANCHNAME = new ResultID("E_INVALIDBRANCHNAME", "http://opcfoundation.org/AlarmAndEvents/");
public static readonly ResultID E_INVALIDTIME = new ResultID("E_INVALIDTIME", "http://opcfoundation.org/AlarmAndEvents/");
public static readonly ResultID E_NOINFO = new ResultID("E_NOINFO", "http://opcfoundation.org/AlarmAndEvents/");
public static readonly ResultID S_ALREADYACKED = new ResultID("S_ALREADYACKED", "http://opcfoundation.org/AlarmAndEvents/");
public static readonly ResultID S_INVALIDBUFFERTIME = new ResultID("S_INVALIDBUFFERTIME", "http://opcfoundation.org/AlarmAndEvents/");
public static readonly ResultID S_INVALIDKEEPALIVETIME = new ResultID("S_INVALIDKEEPALIVETIME", "http://opcfoundation.org/AlarmAndEvents/");
public static readonly ResultID S_INVALIDMAXSIZE = new ResultID("S_INVALIDMAXSIZE", "http://opcfoundation.org/AlarmAndEvents/");
}
public class Cpx
{
public static readonly ResultID E_FILTER_DUPLICATE = new ResultID("E_FILTER_DUPLICATE", "http://opcfoundation.org/ComplexData/");
public static readonly ResultID E_FILTER_ERROR = new ResultID("E_FILTER_ERROR", "http://opcfoundation.org/ComplexData/");
public static readonly ResultID E_FILTER_INVALID = new ResultID("E_FILTER_INVALID", "http://opcfoundation.org/ComplexData/");
public static readonly ResultID E_TYPE_CHANGED = new ResultID("E_TYPE_CHANGED", "http://opcfoundation.org/ComplexData/");
public static readonly ResultID S_FILTER_NO_DATA = new ResultID("S_FILTER_NO_DATA", "http://opcfoundation.org/ComplexData/");
}
public class Da
{
public static readonly ResultID E_BADTYPE = new ResultID("E_BADTYPE", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_INVALID_FILTER = new ResultID("E_INVALID_FILTER", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_INVALID_ITEM_NAME = new ResultID("E_INVALID_ITEM_NAME", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_INVALID_ITEM_PATH = new ResultID("E_INVALID_ITEM_PATH", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_INVALID_PID = new ResultID("E_INVALID_PID", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_INVALIDCONTINUATIONPOINT = new ResultID("E_INVALIDCONTINUATIONPOINT", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_INVALIDHANDLE = new ResultID("E_INVALIDHANDLE", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_NO_ITEM_BUFFERING = new ResultID("E_NO_ITEM_BUFFERING", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_NO_ITEM_DEADBAND = new ResultID("E_NO_ITEM_DEADBAND", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_NO_ITEM_SAMPLING = new ResultID("E_NO_ITEM_SAMPLING", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_NO_WRITEQT = new ResultID("E_NO_WRITEQT", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_RANGE = new ResultID("E_RANGE", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_READONLY = new ResultID("E_READONLY", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_UNKNOWN_ITEM_NAME = new ResultID("E_UNKNOWN_ITEM_NAME", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_UNKNOWN_ITEM_PATH = new ResultID("E_UNKNOWN_ITEM_PATH", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID E_WRITEONLY = new ResultID("E_WRITEONLY", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID S_CLAMP = new ResultID("S_CLAMP", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID S_DATAQUEUEOVERFLOW = new ResultID("S_DATAQUEUEOVERFLOW", "http://opcfoundation.org/DataAccess/");
public static readonly ResultID S_UNSUPPORTEDRATE = new ResultID("S_UNSUPPORTEDRATE", "http://opcfoundation.org/DataAccess/");
}
public class Dx
{
public static readonly ResultID E_CONNECTIONS_EXIST = new ResultID("E_CONNECTIONS_EXIST", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_DUPLICATE_NAME = new ResultID("E_DUPLICATE_NAME", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_BROWSE_PATH = new ResultID("E_INVALID_BROWSE_PATH", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_CONFIG_FILE = new ResultID("E_INVALID_CONFIG_FILE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_DEADBAND = new ResultID("E_INVALID_DEADBAND", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_ITEM_NAME = new ResultID("E_INVALID_ITEM_NAME", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_ITEM_PATH = new ResultID("E_INVALID_ITEM_PATH", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_NAME = new ResultID("E_INVALID_NAME", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_QUEUE_SIZE = new ResultID("E_INVALID_QUEUE_SIZE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_SERVER_TYPE = new ResultID("E_INVALID_SERVER_TYPE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_SERVER_URL = new ResultID("E_INVALID_SERVER_URL", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_SOURCE_ITEM = new ResultID("E_INVALID_SOURCE_ITEM", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_INVALID_TARGET_ITEM = new ResultID("E_INVALID_TARGET_ITEM", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_NOITEMLIST = new ResultID("E_NOITEMLIST", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_OVERRIDE_BADTYPE = new ResultID("E_OVERRIDE_BADTYPE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_OVERRIDE_RANGE = new ResultID("E_OVERRIDE_RANGE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_PERSIST_FAILED = new ResultID("E_PERSIST_FAILED", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_PERSISTING = new ResultID("E_PERSISTING", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SERVER_STATE = new ResultID("E_SERVER_STATE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SOURCE_ITEM_BAD_QUALITY = new ResultID("E_SOURCE_ITEM_BAD_QUALITY", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SOURCE_ITEM_BADRIGHTS = new ResultID("E_SOURCE_ITEM_BADRIGHTS", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SOURCE_ITEM_BADTYPE = new ResultID("E_SOURCE_ITEM_BADTYPE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SOURCE_ITEM_RANGE = new ResultID("E_SOURCE_ITEM_RANGE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SOURCE_SERVER_FAULT = new ResultID("E_SOURCE_SERVER_FAULT", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SOURCE_SERVER_NO_ACCESSS = new ResultID("E_SOURCE_SERVER_NO_ACCESSS", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SOURCE_SERVER_NOT_CONNECTED = new ResultID("E_SOURCE_SERVER_NOT_CONNECTED", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SOURCE_SERVER_TIMEOUT = new ResultID("E_SOURCE_SERVER_TIMEOUT", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SUBSCRIPTION_FAULT = new ResultID("E_SUBSCRIPTION_FAULT", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SUBSTITUTE_BADTYPE = new ResultID("E_SUBSTITUTE_BADTYPE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_SUBSTITUTE_RANGE = new ResultID("E_SUBSTITUTE_RANGE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_TARGET_ALREADY_CONNECTED = new ResultID("E_TARGET_ALREADY_CONNECTED", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_TARGET_FAULT = new ResultID("E_TARGET_FAULT", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_TARGET_ITEM_BADTYPE = new ResultID("E_TARGET_ITEM_BADTYPE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_TARGET_ITEM_DISCONNECTED = new ResultID("E_TARGET_ITEM_DISCONNECTED", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_TARGET_ITEM_RANGE = new ResultID("E_TARGET_ITEM_RANGE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_TARGET_NO_ACCESSS = new ResultID("E_TARGET_NO_ACCESSS", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_TARGET_NO_WRITES_ATTEMPTED = new ResultID("E_TARGET_NO_WRITES_ATTEMPTED", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_TOO_MANY_CONNECTIONS = new ResultID("E_TOO_MANY_CONNECTIONS", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_UNKNOWN_ITEM_NAME = new ResultID("E_UNKNOWN_ITEM_NAME", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_UNKNOWN_ITEM_PATH = new ResultID("E_UNKNOWN_ITEM_PATH", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_UNKNOWN_SERVER_NAME = new ResultID("E_UNKNOWN_SERVER_NAME", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_UNKNOWN_SOURCE_ITEM = new ResultID("E_UNKNOWN_SOURCE_ITEM", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_UNKNOWN_TARGET_ITEM = new ResultID("E_UNKNOWN_TARGET_ITEM", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_UNSUPPORTED_SERVER_TYPE = new ResultID("E_UNSUPPORTED_SERVER_TYPE", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID E_VERSION_MISMATCH = new ResultID("E_VERSION_MISMATCH", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID S_CLAMP = new ResultID("S_CLAMP", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID S_TARGET_OVERRIDEN = new ResultID("S_TARGET_OVERRIDEN", "http://opcfoundation.org/DataExchange/");
public static readonly ResultID S_TARGET_SUBSTITUTED = new ResultID("S_TARGET_SUBSTITUTED", "http://opcfoundation.org/DataExchange/");
}
public class Hda
{
public static readonly ResultID E_DATAEXISTS = new ResultID("E_DATAEXISTS", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID E_INVALIDAGGREGATE = new ResultID("E_INVALIDAGGREGATE", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID E_INVALIDATTRID = new ResultID("E_INVALIDATTRID", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID E_INVALIDDATATYPE = new ResultID("E_INVALIDDATATYPE", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID E_MAXEXCEEDED = new ResultID("E_MAXEXCEEDED", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID E_NODATAEXISTS = new ResultID("E_NODATAEXISTS", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID E_NOT_AVAIL = new ResultID("E_NOT_AVAIL", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID E_UNKNOWNATTRID = new ResultID("E_UNKNOWNATTRID", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID S_CURRENTVALUE = new ResultID("S_CURRENTVALUE", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID S_EXTRADATA = new ResultID("S_EXTRADATA", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID S_INSERTED = new ResultID("S_INSERTED", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID S_MOREDATA = new ResultID("S_MOREDATA", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID S_NODATA = new ResultID("S_NODATA", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID S_REPLACED = new ResultID("S_REPLACED", "http://opcfoundation.org/HistoricalDataAccess/");
public static readonly ResultID W_NOFILTER = new ResultID("W_NOFILTER", "http://opcfoundation.org/HistoricalDataAccess/");
}
private class Names
{
internal const string CODE = "CO";
internal const string NAME = "NA";
internal const string NAMESPACE = "NS";
}
}
[Serializable]
public class ItemProperty : ICloneable, IResult
{
private System.Type m_datatype;
private string m_description;
private string m_diagnosticInfo;
private PropertyID m_id;
private string m_itemName;
private string m_itemPath;
private ResultID m_resultID = ResultID.S_OK;
private object m_value;
public System.Type DataType
{
get
{
return m_datatype;
}
set
{
m_datatype = value;
}
}
public string Description
{
get
{
return m_description;
}
set
{
m_description = value;
}
}
public string DiagnosticInfo
{
get
{
return m_diagnosticInfo;
}
set
{
m_diagnosticInfo = value;
}
}
public PropertyID ID
{
get
{
return m_id;
}
set
{
m_id = value;
}
}
public string ItemName
{
get
{
return m_itemName;
}
set
{
m_itemName = value;
}
}
public string ItemPath
{
get
{
return m_itemPath;
}
set
{
m_itemPath = value;
}
}
public ResultID ResultID
{
get
{
return m_resultID;
}
set
{
m_resultID = value;
}
}
public object Value
{
get
{
return m_value;
}
set
{
m_value = value;
}
}
public virtual object Clone()
{
ItemProperty obj = (ItemProperty)MemberwiseClone();
obj.Value = Comn.Convert.Clone(Value);
return obj;
}
}
public class Property
{
public static readonly PropertyID ACCESSRIGHTS = new PropertyID("accessRights", 5, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID ALARM_AREA_LIST = new PropertyID("alarmAreaList", 302, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID ALARM_QUICK_HELP = new PropertyID("alarmQuickHelp", 301, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID CLOSELABEL = new PropertyID("closeLabel", 106, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID CONDITION_LOGIC = new PropertyID("conditionLogic", 304, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID CONDITION_STATUS = new PropertyID("conditionStatus", 300, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID CONSISTENCY_WINDOW = new PropertyID("consistencyWindow", 605, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID DATA_FILTER_VALUE = new PropertyID("dataFilterValue", 609, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID DATATYPE = new PropertyID("dataType", 1, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID DEADBAND = new PropertyID("deadband", 306, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID DESCRIPTION = new PropertyID("description", 101, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID DEVIATION_LIMIT = new PropertyID("deviationLimit", 312, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID DICTIONARY = new PropertyID("dictionary", 603, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID DICTIONARY_ID = new PropertyID("dictionaryID", 601, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID ENGINEERINGUINTS = new PropertyID("engineeringUnits", 100, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID EUINFO = new PropertyID("euInfo", 8, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID EUTYPE = new PropertyID("euType", 7, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID HI_LIMIT = new PropertyID("hiLimit", 308, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID HIGHEU = new PropertyID("highEU", 102, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID HIGHIR = new PropertyID("highIR", 104, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID HIHI_LIMIT = new PropertyID("hihiLimit", 307, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID LIMIT_EXCEEDED = new PropertyID("limitExceeded", 305, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID LO_LIMIT = new PropertyID("loLimit", 309, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID LOLO_LIMIT = new PropertyID("loloLimit", 310, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID LOWEU = new PropertyID("lowEU", 103, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID LOWIR = new PropertyID("lowIR", 105, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID MAXIMUM_VALUE = new PropertyID("maximumValue", 110, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID MINIMUM_VALUE = new PropertyID("minimumValue", 109, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID OPENLABEL = new PropertyID("openLabel", 107, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID PRIMARY_ALARM_AREA = new PropertyID("primaryAlarmArea", 303, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID QUALITY = new PropertyID("quality", 3, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID RATE_CHANGE_LIMIT = new PropertyID("rangeOfChangeLimit", 311, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID SCANRATE = new PropertyID("scanRate", 6, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID SOUNDFILE = new PropertyID("soundFile", 313, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID TIMESTAMP = new PropertyID("timestamp", 4, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID TIMEZONE = new PropertyID("timeZone", 108, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID TYPE_DESCRIPTION = new PropertyID("typeDescription", 604, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID TYPE_ID = new PropertyID("typeID", 602, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID TYPE_SYSTEM_ID = new PropertyID("typeSystemID", 600, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID UNCONVERTED_ITEM_ID = new PropertyID("unconvertedItemID", 607, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID UNFILTERED_ITEM_ID = new PropertyID("unfilteredItemID", 608, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID VALUE = new PropertyID("value", 2, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID VALUE_PRECISION = new PropertyID("valuePrecision", 111, "http://opcfoundation.org/DataAccess/");
public static readonly PropertyID WRITE_BEHAVIOR = new PropertyID("writeBehavior", 606, "http://opcfoundation.org/DataAccess/");
}
public class Type
{
public static System.Type ANY_TYPE = typeof(object);
public static System.Type ARRAY_ANY_TYPE = typeof(object[]);
public static System.Type ARRAY_BOOLEAN = typeof(bool[]);
public static System.Type ARRAY_DATETIME = typeof(DateTime[]);
public static System.Type ARRAY_DECIMAL = typeof(decimal[]);
public static System.Type ARRAY_DOUBLE = typeof(double[]);
public static System.Type ARRAY_FLOAT = typeof(float[]);
public static System.Type ARRAY_INT = typeof(int[]);
public static System.Type ARRAY_LONG = typeof(long[]);
public static System.Type ARRAY_SHORT = typeof(short[]);
public static System.Type ARRAY_STRING = typeof(string[]);
public static System.Type ARRAY_UINT = typeof(uint[]);
public static System.Type ARRAY_ULONG = typeof(ulong[]);
public static System.Type ARRAY_USHORT = typeof(ushort[]);
public static System.Type BINARY = typeof(byte[]);
public static System.Type BOOLEAN = typeof(bool);
public static System.Type BYTE = typeof(byte);
public static System.Type DATETIME = typeof(DateTime);
public static System.Type DECIMAL = typeof(decimal);
public static System.Type DOUBLE = typeof(double);
public static System.Type DURATION = typeof(TimeSpan);
public static System.Type FLOAT = typeof(float);
public static System.Type ILLEGAL_TYPE = typeof(Type);
public static System.Type INT = typeof(int);
public static System.Type LONG = typeof(long);
public static System.Type SBYTE = typeof(sbyte);
public static System.Type SHORT = typeof(short);
public static System.Type STRING = typeof(string);
public static System.Type UINT = typeof(uint);
public static System.Type ULONG = typeof(ulong);
public static System.Type USHORT = typeof(ushort);
public static System.Type[] Enumerate()
{
ArrayList arrayList = new ArrayList();
FieldInfo[] fields = typeof(Type).GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (FieldInfo fieldInfo in fields)
{
arrayList.Add(fieldInfo.GetValue(typeof(System.Type)));
}
return (System.Type[])arrayList.ToArray(typeof(System.Type));
}
}
[Serializable]
public class PropertyDescription
{
public static readonly PropertyDescription ACCESSRIGHTS = new PropertyDescription(Property.ACCESSRIGHTS, typeof(accessRights), "Item Access Rights");
public static readonly PropertyDescription ALARM_AREA_LIST = new PropertyDescription(Property.ALARM_AREA_LIST, typeof(string), "Alarm Area List");
public static readonly PropertyDescription ALARM_QUICK_HELP = new PropertyDescription(Property.ALARM_QUICK_HELP, typeof(string), "Alarm Quick Help");
public static readonly PropertyDescription CLOSELABEL = new PropertyDescription(Property.CLOSELABEL, typeof(string), "Contact Close Label");
public static readonly PropertyDescription CONDITION_LOGIC = new PropertyDescription(Property.CONDITION_LOGIC, typeof(string), "Condition Logic");
public static readonly PropertyDescription CONDITION_STATUS = new PropertyDescription(Property.CONDITION_STATUS, typeof(string), "Condition Status");
public static readonly PropertyDescription CONSISTENCY_WINDOW = new PropertyDescription(Property.CONSISTENCY_WINDOW, typeof(string), "Consistency Window");
public static readonly PropertyDescription DATA_FILTER_VALUE = new PropertyDescription(Property.DATA_FILTER_VALUE, typeof(string), "Data Filter Value");
public static readonly PropertyDescription DATATYPE = new PropertyDescription(Property.DATATYPE, typeof(System.Type), "Item Canonical DataType");
public static readonly PropertyDescription DEADBAND = new PropertyDescription(Property.DEADBAND, typeof(double), "Deadband");
public static readonly PropertyDescription DESCRIPTION = new PropertyDescription(Property.DESCRIPTION, typeof(string), "Item Description");
public static readonly PropertyDescription DEVIATION_LIMIT = new PropertyDescription(Property.DEVIATION_LIMIT, typeof(double), "Deviation Limit");
public static readonly PropertyDescription DICTIONARY = new PropertyDescription(Property.DICTIONARY, typeof(object), "Dictionary");
public static readonly PropertyDescription DICTIONARY_ID = new PropertyDescription(Property.DICTIONARY_ID, typeof(string), "Dictionary ID");
public static readonly PropertyDescription ENGINEERINGUINTS = new PropertyDescription(Property.ENGINEERINGUINTS, typeof(string), "EU Units");
public static readonly PropertyDescription EUINFO = new PropertyDescription(Property.EUINFO, typeof(string[]), "Item EU Info");
public static readonly PropertyDescription EUTYPE = new PropertyDescription(Property.EUTYPE, typeof(euType), "Item EU Type");
public static readonly PropertyDescription HI_LIMIT = new PropertyDescription(Property.HI_LIMIT, typeof(double), "Hi Limit");
public static readonly PropertyDescription HIGHEU = new PropertyDescription(Property.HIGHEU, typeof(double), "High EU");
public static readonly PropertyDescription HIGHIR = new PropertyDescription(Property.HIGHIR, typeof(double), "High Instrument Range");
public static readonly PropertyDescription HIHI_LIMIT = new PropertyDescription(Property.HIHI_LIMIT, typeof(double), "HiHi Limit");
public static readonly PropertyDescription LIMIT_EXCEEDED = new PropertyDescription(Property.LIMIT_EXCEEDED, typeof(string), "Limit Exceeded");
public static readonly PropertyDescription LO_LIMIT = new PropertyDescription(Property.LO_LIMIT, typeof(double), "Lo Limit");
public static readonly PropertyDescription LOLO_LIMIT = new PropertyDescription(Property.LOLO_LIMIT, typeof(double), "LoLo Limit");
public static readonly PropertyDescription LOWEU = new PropertyDescription(Property.LOWEU, typeof(double), "Low EU");
public static readonly PropertyDescription LOWIR = new PropertyDescription(Property.LOWIR, typeof(double), "Low Instrument Range");
public static readonly PropertyDescription MAXIMUM_VALUE = new PropertyDescription(Property.MAXIMUM_VALUE, typeof(object), "Maximum Value");
public static readonly PropertyDescription MINIMUM_VALUE = new PropertyDescription(Property.MINIMUM_VALUE, typeof(object), "Minimum Value");
public static readonly PropertyDescription OPENLABEL = new PropertyDescription(Property.OPENLABEL, typeof(string), "Contact Open Label");
public static readonly PropertyDescription PRIMARY_ALARM_AREA = new PropertyDescription(Property.PRIMARY_ALARM_AREA, typeof(string), "Primary Alarm Area");
public static readonly PropertyDescription QUALITY = new PropertyDescription(Property.QUALITY, typeof(Quality), "Item Quality");
public static readonly PropertyDescription RATE_CHANGE_LIMIT = new PropertyDescription(Property.RATE_CHANGE_LIMIT, typeof(double), "Rate of Change Limit");
public static readonly PropertyDescription SCANRATE = new PropertyDescription(Property.SCANRATE, typeof(float), "Server Scan Rate");
public static readonly PropertyDescription SOUNDFILE = new PropertyDescription(Property.SOUNDFILE, typeof(string), "Sound File");
public static readonly PropertyDescription TIMESTAMP = new PropertyDescription(Property.TIMESTAMP, typeof(DateTime), "Item Timestamp");
public static readonly PropertyDescription TIMEZONE = new PropertyDescription(Property.TIMEZONE, typeof(int), "Timezone");
public static readonly PropertyDescription TYPE_DESCRIPTION = new PropertyDescription(Property.TYPE_DESCRIPTION, typeof(string), "Type Description");
public static readonly PropertyDescription TYPE_ID = new PropertyDescription(Property.TYPE_ID, typeof(string), "Type ID");
public static readonly PropertyDescription TYPE_SYSTEM_ID = new PropertyDescription(Property.TYPE_SYSTEM_ID, typeof(string), "Type System ID");
public static readonly PropertyDescription UNCONVERTED_ITEM_ID = new PropertyDescription(Property.UNCONVERTED_ITEM_ID, typeof(string), "Unconverted Item ID");
public static readonly PropertyDescription UNFILTERED_ITEM_ID = new PropertyDescription(Property.UNFILTERED_ITEM_ID, typeof(string), "Unfiltered Item ID");
public static readonly PropertyDescription VALUE = new PropertyDescription(Property.VALUE, typeof(object), "Item Value");
public static readonly PropertyDescription VALUE_PRECISION = new PropertyDescription(Property.VALUE_PRECISION, typeof(object), "Value Precision");
public static readonly PropertyDescription WRITE_BEHAVIOR = new PropertyDescription(Property.WRITE_BEHAVIOR, typeof(string), "Write Behavior");
private PropertyID m_id;
private string m_name;
private System.Type m_type;
public PropertyDescription(PropertyID id, System.Type type, string name)
{
ID = id;
Type = type;
Name = name;
}
public PropertyID ID
{
get
{
return m_id;
}
set
{
m_id = value;
}
}
public string Name
{
get
{
return m_name;
}
set
{
m_name = value;
}
}
public System.Type Type
{
get
{
return m_type;
}
set
{
m_type = value;
}
}
public static PropertyDescription[] Enumerate()
{
ArrayList arrayList = new ArrayList();
FieldInfo[] fields = typeof(PropertyDescription).GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (FieldInfo fieldInfo in fields)
{
arrayList.Add(fieldInfo.GetValue(typeof(PropertyDescription)));
}
return (PropertyDescription[])arrayList.ToArray(typeof(PropertyDescription));
}
public static PropertyDescription Find(PropertyID id)
{
FieldInfo[] fields = typeof(PropertyDescription).GetFields(BindingFlags.Static | BindingFlags.Public);
for (int i = 0; i < fields.Length; i++)
{
PropertyDescription propertyDescription = (PropertyDescription)fields[i].GetValue(typeof(PropertyDescription));
if (propertyDescription.ID == id)
{
return propertyDescription;
}
}
return null;
}
public override string ToString()
{
return Name;
}
}

View File

@@ -1,11 +1,15 @@
//------------------------------------------------------------------------------
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
global using System;
global using TouchSocket.Core;

View File

@@ -0,0 +1,477 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using ThingsGateway.Foundation.Adapter.OPCDA.Da;
using ThingsGateway.Foundation.Adapter.OPCDA.Rcw;
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.Enumerator;
using ThingsGateway.Foundation.Extension.Generic;
using Timer = System.Timers.Timer;
//部分非托管交互代码来自https://gitee.com/Zer0Day/opc-client与OPC基金会opcnet库更改部分逻辑
namespace ThingsGateway.Foundation.Adapter.OPCDA;
public delegate void DataChangedEventHandler(List<ItemReadResult> values);
public class OPCDAClient : DisposableObject
{
/// <summary>
/// 当前配置
/// </summary>
public OPCNode OPCNode;
private ILog _logger;
private EasyLock checkLock = new();
private Timer checkTimer;
private bool FirstConnect;
private int IsExit = 1;
/// <summary>
/// 当前保存的需订阅列表
/// </summary>
private Dictionary<string, List<OpcItem>> ItemDicts = new();
/// <summary>
/// 订阅返回值队列
/// </summary>
private ConcurrentQueue<ItemReadResult> ItemReadResults = new ConcurrentQueue<ItemReadResult>();
private OpcServer m_server;
//定义组对象(订阅者)
public OPCDAClient(ILog logger)
{
_logger = logger;
Task.Run(dataChangedHandlerInvoke);
}
public event DataChangedEventHandler DataChangedHandler;
public bool IsConnected => m_server?.IsConnected == true;
private List<OpcGroup> Groups => m_server.OpcGroups;
/// <summary>
/// 添加节点,需要在连接成功后执行
/// </summary>
/// <param name="tags">组名称/变量节点,注意每次添加的组名称不能相同</param>
public OperResult AddTags(Dictionary<string, List<OpcItem>> tags)
{
try
{
StringBuilder stringBuilder = new StringBuilder();
if (IsExit == 1) return new("对象已释放");
foreach (var item in tags)
{
if (IsExit == 1) return new("对象已释放");
var subscription = m_server.AddGroup(item.Key, true, OPCNode.UpdateRate, OPCNode.DeadBand);
if (subscription.IsSuccess)
{
subscription.Content.ActiveSubscribe = OPCNode.ActiveSubscribe;
subscription.Content.OnDataChanged += Subscription_OnDataChanged;
subscription.Content.OnReadCompleted += Subscription_OnDataChanged;
var result = subscription.Content.AddOpcItem(item.Value.ToArray());
if (!result.IsSuccess)
{
stringBuilder.AppendLine("添加变量失败" + result.Message);
}
else
{
ItemDicts.AddOrUpdate(item.Key, item.Value);
_logger?.Debug($"添加变量{item.Value.Select(a => a.ItemID).ToList().ToJson()}成功");
}
}
else
{
stringBuilder.AppendLine("添加组失败" + subscription.Message);
}
}
for (int i = 0; i < Groups?.Count; i++)
{
var group = Groups[i];
if (group != null)
{
if (group.OpcItems.Count == 0)
{
ItemDicts.Remove(group.Name);
m_server.RemoveGroup(group);
}
}
}
if (stringBuilder.Length > 0)
{
return new(stringBuilder.ToString());
}
else
{
return OperResult.CreateSuccessResult();
}
}
catch (Exception ex)
{
return new(ex);
}
}
/// <summary>
/// 连接服务器
/// </summary>
public void Connect()
{
Interlocked.CompareExchange(ref IsExit, 0, 1);
connect();
FirstConnect = true;
}
/// <summary>
/// 断开连接
/// </summary>
public void Disconnect()
{
Interlocked.CompareExchange(ref IsExit, 1, 0);
disconnect();
}
/// <summary>
/// 浏览节点
/// </summary>
/// <param name="itemId"></param>
/// <returns></returns>
public OperResult<List<BrowseElement>> GetBrowse(string itemId = null)
{
return this.m_server?.Browse(itemId);
}
/// <summary>
/// 获取服务状态
/// </summary>
/// <returns></returns>
public OperResult<ServerStatus> GetStatus()
{
return this.m_server?.GetServerStatus();
}
/// <summary>
/// 初始化设置
/// </summary>
/// <param name="node"></param>
public void Init(OPCNode node = null)
{
if (node != null)
OPCNode = node;
checkTimer?.Stop();
checkTimer?.SafeDispose();
checkTimer = new Timer(OPCNode.CheckRate);
checkTimer.Elapsed += checkTimer_Elapsed;
checkTimer.Start();
m_server?.SafeDispose();
m_server = new OpcServer(OPCNode.OPCName, OPCNode.OPCIP);
}
/// <summary>
/// 手动读取变量,结果会在订阅事件中返回
/// </summary>
/// <param name="groupName"></param>
/// <returns></returns>
public OperResult ReadGroup(string groupName = null)
{
try
{
if (connect())
{
var groups = groupName != null ? Groups.Where(a => a.Name == groupName) : Groups;
foreach (var group in groups)
{
if (group.OpcItems.Count > 0)
{
return group.ReadAsync();
}
else
{
return new OperResult("不存在任何变量");
}
}
return new OperResult("不存在任何变量");
}
return new OperResult("未初始化连接");
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <summary>
/// 移除节点
/// </summary>
/// <param name="tags"></param>
public void RemoveTags(List<string> tags)
{
foreach (var item in tags)
{
if (IsExit == 1) return;
var opcGroup = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == item));
if (opcGroup == null)
{
_logger.Warning("找不到变量" + item);
continue;
}
var tag = opcGroup.OpcItems.Where(a => item == a.ItemID);
var result = opcGroup.RemoveItem(tag.ToArray());
if (!result.IsSuccess)
{
_logger.Warning($"移除变量{item}-" + result.Message);
}
else
{
_logger?.Debug($"移除变量{item}成功");
}
if (opcGroup.OpcItems.Count == 0)
{
ItemDicts.Remove(opcGroup.Name);
m_server.RemoveGroup(opcGroup);
}
else
{
ItemDicts[opcGroup.Name].RemoveWhere(a => tag.Contains(a));
}
}
}
/// <summary>
/// 设置节点并保存
/// </summary>
/// <param name="tags"></param>
/// <returns></returns>
public Dictionary<string, List<OpcItem>> SetTags(List<string> tags)
{
int i = 0;
ItemDicts = tags.ToList().ConvertAll(o => new OpcItem(o)
).ChunkTrivialBetter(OPCNode.GroupSize).ToDictionary(a => "default" + (i++));
return ItemDicts;
}
public override string ToString()
{
return OPCNode.ToString();
}
/// <summary>
/// 写入值
/// </summary>
/// <param name="valueName">写入</param>
/// <param name="value"></param>
/// <returns></returns>
public OperResult Write(string valueName, object value)
{
if (connect())
{
try
{
var group = Groups.FirstOrDefault(it => it.OpcItems.Any(a => a.ItemID == valueName));
if (group == null)
return new OperResult("不存在该变量" + valueName);
var item = group.OpcItems.Where(it => it.ItemID == valueName).FirstOrDefault();
int[] serverHandle = new int[1] { item.ServerHandle };
object[] Value = new object[1] { value };
int[] PErrors = new int[1];
group.WriteAsync(Value, serverHandle, out PErrors);
//itemvalue.Value = Convert.ChangeType(value, item.RunTimeDataType);
if (PErrors != null && PErrors.First() == 0)
{
return OperResult.CreateSuccessResult();
}
else
{
return new OperResult();
}
}
catch (Exception ex)
{
return new OperResult(ex.Message);
}
}
return new OperResult();
}
protected override void Dispose(bool disposing)
{
disconnect();
base.Dispose(disposing);
}
private void AddTags()
{
var result = AddTags(ItemDicts);
if (!result.IsSuccess)
{
_logger.Warning(result.Message);
}
}
private void checkTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if (checkLock.IsWaitting) return;
checkLock.Lock();
try
{
if (IsExit == 0)
{
var status = m_server.GetServerStatus();
if (status.IsSuccess)
{
_logger?.Trace(OPCNode.ToString() + "OPC状态检查正常!");
}
else
{
if (IsExit == 0 && FirstConnect)
{
if (connect())
{
_logger?.Warning(OPCNode.ToString() + "OPC重新链接成功!");
}
else
{
}
}
}
}
else
{
var timeer = sender as Timer;
timeer.Enabled = false;
timeer.Stop();
}
}
finally { checkLock.UnLock(); }
}
private bool connect()
{
lock (this)
{
try
{
if (m_server?.IsConnected == true)
{
var status = m_server.GetServerStatus();
if (!status.IsSuccess)
{
var status1 = m_server.GetServerStatus();
if (!status1.IsSuccess)
{
_logger?.Error(status1.Message);
//失败重新连接
try
{
//disconnect();
Init(OPCNode);
_logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 正在连接");
var result = m_server?.Connect();
if (result.IsSuccess)
{
_logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 连接成功");
AddTags();
}
else
{
_logger?.Error(result.Message);
return IsConnected;
}
}
catch (Exception ex2)
{
_logger?.Exception(ex2);
return IsConnected;
}
}
}
}
else
{
//disconnect();
Init(OPCNode);
_logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 正在连接");
var result = m_server?.Connect();
if (result.IsSuccess)
{
_logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 连接成功");
AddTags();
}
else
{
_logger?.Error(result.Message);
return IsConnected;
}
}
}
catch (Exception ex)
{
_logger?.Exception(OPCNode.ToString(), ex);
return false;
}
return IsConnected;
}
}
/// <summary>
/// 订阅通知线程
/// </summary>
/// <returns></returns>
private async Task dataChangedHandlerInvoke()
{
while (!DisposedValue)
{
if (ItemReadResults.Count > 0)
DataChangedHandler?.Invoke(ItemReadResults.ToListWithDequeue());
if (OPCNode == null)
await Task.Delay(1000);
else
await Task.Delay(OPCNode.UpdateRate == 0 ? 1000 : OPCNode.UpdateRate);
}
}
private void disconnect()
{
if (IsConnected)
_logger?.Trace($"{m_server.Host + " - " + m_server.Name} - 断开连接");
checkTimer.Enabled = false;
checkTimer.Stop();
try
{
m_server?.SafeDispose();
m_server = null;
}
catch (Exception ex)
{
_logger?.Exception(ToString(), ex);
}
}
private void Subscription_OnDataChanged(ItemReadResult[] values)
{
for (int i = 0; i < values.Length; i++)
{
ItemReadResults.Enqueue(values[i]);
}
}
}

View File

@@ -0,0 +1,27 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.OPCDA;
public class OPCNode
{
public bool ActiveSubscribe { get; set; } = true;
public int CheckRate { get; set; } = 600000;
public float DeadBand { get; set; } = 0;
public int GroupSize { get; set; } = 500;
public string OPCIP { get; set; } = "localhost";
public string OPCName { get; set; } = "Kepware.KEPServerEX.V6";
public int UpdateRate { get; set; } = 1000;
public override string ToString()
{
return $"{(OPCIP.IsNullOrEmpty() ? "localhost" : OPCIP)}:{OPCName}";
}
}

View File

@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
<NoWarn>CA1416;CS8625;1591;</NoWarn>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<Version>1.7.0</Version>
<Platforms>AnyCPU</Platforms>
<OutputType>Library</OutputType>
<Title>ThingsGateway.Foundation.Adapter.Modbus</Title>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Authors>Diego</Authors>
<Description>OPCDA通讯类库支持OPCDAClient</Description>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild>
<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<PackageOutputPath>$(SolutionDir)</PackageOutputPath>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Platforms>AnyCPU;x86</Platforms>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,16 +1,15 @@
//------------------------------------------------------------------------------
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Gateway.Application;
global using System;
public interface IDriverUIBase
{
public object Driver { get; set; }
}
global using TouchSocket.Core;

View File

@@ -0,0 +1,171 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Opc.Ua;
using Opc.Ua.Client;
namespace WebPlatform.OPCUALayer
{
public class DataTypeAnalyzer
{
private Session m_session;
public DataTypeAnalyzer(Session session)
{
this.m_session = session;
}
public static BuiltInType GetBuiltinTypeFromTypeName(string nameSpace, string type)
{
switch (nameSpace)
{
case "opc":
return GetBuiltinTypeFromBinaryTypeName(type);
case "ua":
return GetBuiltinTypeFromUaTypeName(type);
default:
return GetBuiltinTypeFromUaTypeName(type);
}
}
private static BuiltInType GetBuiltinTypeFromUaTypeName(string type)
{
Type mType = Type.GetType("Opc.Ua." + type + ", Opc.Ua.Core");
BuiltInType builtInType = TypeInfo.GetBuiltInType(TypeInfo.GetDataTypeId(mType));
return builtInType;
}
private static BuiltInType GetBuiltinTypeFromBinaryTypeName(string type)
{
switch (type)
{
case "Bit":
case "Boolean":
return BuiltInType.Boolean;
case "SByte":
return BuiltInType.SByte;
case "Byte":
return BuiltInType.Byte;
case "Int16":
return BuiltInType.Int16;
case "UInt16":
return BuiltInType.UInt16;
case "Int32":
return BuiltInType.Int32;
case "UInt32":
return BuiltInType.UInt32;
case "Int64":
return BuiltInType.Int64;
case "UInt64":
return BuiltInType.UInt64;
case "Float":
return BuiltInType.Float;
case "Double":
return BuiltInType.Double;
case "Char":
case "WideChar":
case "String":
case "CharArray":
case "WideString":
case "WideCharArray":
return BuiltInType.String;
case "DateTime":
return BuiltInType.DateTime;
case "ByteString":
return BuiltInType.ByteString;
case "Guid":
return BuiltInType.Guid;
default:
return BuiltInType.Null;
}
}
internal NodeId GetDataTypeEncodingNodeId(NodeId dataTypeNodeId)
{
m_session.Browse(
null,
null,
dataTypeNodeId,
0u,
BrowseDirection.Forward,
ReferenceTypeIds.HasEncoding,
true,
(uint)NodeClass.Object,
out var continuationPoint,
out var refDescriptionCollection);
//Choose always first encoding
return (NodeId)refDescriptionCollection[0].NodeId;
}
internal NodeId GetDataTypeDescriptionNodeId(NodeId dataTypeEncodingNodeId)
{
m_session.Browse(
null,
null,
dataTypeEncodingNodeId, //starting node is always an EncodingNode
0u,
BrowseDirection.Forward,
ReferenceTypeIds.HasDescription, //HasDescription reference
true,
(uint)NodeClass.Variable,
out var continuationPoint,
out var refDescriptionCollection);
return (NodeId)refDescriptionCollection[0].NodeId;
}
internal string GetDictionary(NodeId dataTypeDescriptionNodeId)
{
m_session.Browse(
null,
null,
dataTypeDescriptionNodeId, //the starting node is a DataTypeDescription
0u,
BrowseDirection.Inverse, //It is an inverse Reference
ReferenceTypeIds.HasComponent, //So it is ComponentOf
true,
(uint)NodeClass.Variable,
out var continuationPoint,
out var refDescriptionCollection);
var dataTypeDictionaryNodeId = (NodeId)refDescriptionCollection[0].NodeId;
var dataValueCollection = Read(dataTypeDictionaryNodeId, Attributes.Value);
return System.Text.Encoding.UTF8.GetString((byte[])dataValueCollection[0].Value);
}
private DataValueCollection Read(NodeId nodeId, uint attributeId)
{
ReadValueIdCollection nodeToRead = new ReadValueIdCollection(1);
ReadValueId vId = new ReadValueId()
{
NodeId = nodeId,
AttributeId = attributeId
};
nodeToRead.Add(vId);
var responseRead = m_session.Read(null,
0,
TimestampsToReturn.Both,
nodeToRead,
out var dataValueCollection,
out var diagnCollection
);
return dataValueCollection;
}
}
}

View File

@@ -0,0 +1,147 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Opc.Ua;
namespace WebPlatform.OPCUALayer
{
public static class DataTypeSchemaGenerator
{
public static JSchema GenerateSchemaForArray(int[] dimensions, JSchema innermostSchema)
{
var dimLen = dimensions.Length;
if (dimLen == 1)
{
var schema = new JSchema
{
Type = JSchemaType.Array,
Items = { innermostSchema },
MinimumItems = dimensions[0],
MaximumItems = dimensions[0]
};
return schema;
}
JSchema innerSchema = new JSchema();
JSchema outerSchema = new JSchema();
for (int dim = dimLen - 1; dim >= 0; dim--)
{
if (dim == dimLen - 1)
{
innerSchema = new JSchema
{
Type = JSchemaType.Array,
Items = { innermostSchema },
MinimumItems = dimensions[dim],
MaximumItems = dimensions[dim]
};
}
else
{
outerSchema = new JSchema
{
Type = JSchemaType.Array,
Items = { innerSchema },
MinimumItems = dimensions[dim],
MaximumItems = dimensions[dim]
};
innerSchema = outerSchema;
}
}
return outerSchema;
}
/// <summary>
/// Used for generating schema based on standard types used in DataTypeDictionary.
/// They are defined in Part 3 - Table C.9
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static JSchema GenerateSchemaForStandardTypeDescription(BuiltInType type)
{
switch (type)
{
case BuiltInType.Boolean:
return new JSchema { Type = JSchemaType.Boolean };
case BuiltInType.SByte:
case BuiltInType.Byte:
case BuiltInType.Int16:
case BuiltInType.UInt16:
case BuiltInType.Int32:
case BuiltInType.UInt32:
case BuiltInType.Int64:
case BuiltInType.UInt64:
case BuiltInType.Float:
case BuiltInType.Double:
return new JSchema { Type = JSchemaType.Number };
case BuiltInType.String:
case BuiltInType.DateTime:
case BuiltInType.Guid:
case BuiltInType.DiagnosticInfo:
case BuiltInType.NodeId:
case BuiltInType.ExpandedNodeId:
case BuiltInType.XmlElement:
case BuiltInType.ByteString:
return new JSchema { Type = JSchemaType.String };
case BuiltInType.LocalizedText:
return new JSchema()
{
Type = JSchemaType.Object,
Properties = {
{ "Locale", new JSchema { Type = JSchemaType.String } },
{ "Text", new JSchema { Type = JSchemaType.String } }
}
}; ;
case BuiltInType.StatusCode:
return new JSchema
{
Type = JSchemaType.Object,
Properties =
{
{ "code", new JSchema
{
Type = JSchemaType.String,
Enum = { "Good", "Uncertain", "Bad" }
}
},
{ "structureChanged", new JSchema{ Type = JSchemaType.Boolean } }
}
}; ;
case BuiltInType.QualifiedName:
return new JSchema
{
Type = JSchemaType.Object,
Properties =
{
{ "NamespaceIndex", new JSchema{ Type = JSchemaType.Integer} },
{ "Name", new JSchema{ Type = JSchemaType.String } }
}
};
case BuiltInType.Enumeration:
return new JSchema
{
Type = JSchemaType.Object,
Properties =
{
{ "EnumValue", new JSchema { Type = JSchemaType.Integer } },
{ "EnumLabel", new JSchema { Type = JSchemaType.String } }
}
}; ;
default:
return new JSchema();
}
}
}
}

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/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace WebPlatform.OPCUALayer
{
public class EmptyClass
{
public EmptyClass()
{
}
}
}

View File

@@ -0,0 +1,381 @@
#region copyright
//------------------------------------------------------------------------------
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4><EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway/
// QQȺ<51><C8BA>605534569
//------------------------------------------------------------------------------
#endregion
using Newtonsoft.Json.Linq;
using Opc.Ua;
using Opc.Ua.Client;
using System.Linq;
using System.Text.RegularExpressions;
namespace WebPlatform.Extensions
{
public static class ExpandedNodeIdExtensionMethods
{
public static string ToStringId(this ExpandedNodeId expandedNodeId, NamespaceTable namespaceTable)
{
var nodeId = ExpandedNodeId.ToNodeId(expandedNodeId, namespaceTable);
return $"{nodeId.NamespaceIndex}-{nodeId.Identifier}";
}
}
public static class CollectionInitializerExtensionMethods
{
public static void Add(this IList<JToken> list, IList<JToken> toAdd)
{
foreach (var a in toAdd)
{
list.Add(a);
}
}
}
public static class JTokenExtensionMethods
{
public static int[] GetJsonArrayDimensions(this JToken jToken)
{
if (jToken.Type != JTokenType.Array)
throw new ValueToWriteTypeException("Expected a JSON Array but received a " + jToken.Type);
while (jToken.HasValues)
{
var children = jToken.Children();
var count = children.First().Count();
//if(children.All(x => x.Count() == count)) throw new ValueToWriteTypeException("The array sent must have the same number of element in each dimension");
foreach (var child in children)
{
if (child.Count() != count)
throw new ValueToWriteTypeException("The array sent must have the same number of element in each dimension");
}
jToken = jToken.Last;
}
const string pattern = @"\[(\d+)\]";
var regex = new Regex(pattern);
var matchColl = regex.Matches(jToken.Path);
var dimensions = new int[matchColl.Count];
for (var i = 0; i < matchColl.Count; i++)
{
dimensions[i] = int.Parse(matchColl[i].Groups[1].Value) + 1;
}
return dimensions;
}
public static JArray ToOneDimensionJArray(this JToken jToken)
{
var dimensions = jToken.GetJsonArrayDimensions();
return jToken.ToOneDimensionJArray(dimensions);
}
public static JArray ToOneDimensionJArray(this JToken jToken, int[] dimensions)
{
var flatValuesToWrite = jToken.Children().ToArray();
for (var i = 0; i < dimensions.Length - 1; i++)
flatValuesToWrite = flatValuesToWrite.SelectMany(a => a).ToArray();
return new JArray(flatValuesToWrite);
}
public static int CalculateActualValueRank(this JToken jToken)
{
if (jToken.Type != JTokenType.Array)
return -1;
var jArray = jToken.ToArray();
int numDimensions = 1;
while (jArray.GetElementsType() == JTokenType.Array)
{
jArray = jArray.Children().ToArray();
numDimensions++;
}
return numDimensions;
}
public static JTokenType GetElementsType(this JToken[] jTokens)
{
if (!jTokens.ElementsHasSameType())
throw new ValueToWriteTypeException("The array sent must have the same type of element in each dimension");
return jTokens.First().Type;
}
private static bool ElementsHasSameType(this JToken[] jTokens)
{
var checkType = jTokens[0].Type == JTokenType.Integer ? JTokenType.Float : jTokens[0].Type;
return jTokens
.Select(x => (x.Type == JTokenType.Integer) ? JTokenType.Float : x.Type)
.All(t => t == checkType);
}
}
public static class BuiltInExtensionMethods
{
public static Func<Variant> GetDecodeDelegate(this BuiltInType builtIn, PlatformJsonDecoder decoder)
{
switch (builtIn)
{
case BuiltInType.Boolean:
return () => new Variant(decoder.ReadBoolean("Value"));
case BuiltInType.SByte:
return () => new Variant(decoder.ReadBoolean("Value"));
case BuiltInType.Byte:
return () => new Variant(decoder.ReadSByte("Value"));
case BuiltInType.Int16:
return () => new Variant(decoder.ReadInt16("Value"));
case BuiltInType.UInt16:
return () => new Variant(decoder.ReadUInt16("Value"));
case BuiltInType.Int32:
return () => new Variant(decoder.ReadInt32("Value"));
case BuiltInType.UInt32:
return () => new Variant(decoder.ReadUInt32("Value"));
case BuiltInType.Int64:
return () => new Variant(decoder.ReadInt64("Value"));
case BuiltInType.UInt64:
return () => new Variant(decoder.ReadUInt64("Value"));
case BuiltInType.Float:
return () => new Variant(decoder.ReadFloat("Value"));
case BuiltInType.Double:
return () => new Variant(decoder.ReadDouble("Value"));
case BuiltInType.String:
return () => new Variant(decoder.ReadString("Value"));
case BuiltInType.DateTime:
return () => new Variant(decoder.ReadDateTime("Value"));
case BuiltInType.Guid:
return () => new Variant(decoder.ReadGuid("Value"));
case BuiltInType.ByteString:
return () => new Variant(decoder.ReadByteString("Value"));
case BuiltInType.XmlElement:
return () => new Variant(decoder.ReadXmlElement("Value"));
case BuiltInType.NodeId:
return () => new Variant(decoder.ReadNodeId("Value"));
case BuiltInType.ExpandedNodeId:
return () => new Variant(decoder.ReadExpandedNodeId("Value"));
case BuiltInType.StatusCode:
return () => new Variant(decoder.ReadStatusCode("Value"));
case BuiltInType.QualifiedName:
return () => new Variant(decoder.ReadQualifiedName("Value"));
case BuiltInType.LocalizedText:
return () => new Variant(decoder.ReadLocalizedText("Value"));
case BuiltInType.ExtensionObject:
return () => new Variant(decoder.ReadExtensionObject("Value"));
case BuiltInType.DiagnosticInfo:
return () => new Variant(decoder.ReadDiagnosticInfo("Value"));
case BuiltInType.Enumeration:
return () => new Variant(decoder.ReadEnumeration("Value"));
default:
throw new NotImplementedException();
}
}
public static Func<Variant> GetDecodeArrayDelegate(this BuiltInType builtIn, PlatformJsonDecoder decoder)
{
switch (builtIn)
{
case BuiltInType.Boolean:
return () => new Variant(decoder.ReadBooleanArray("Value").ToArray());
case BuiltInType.SByte:
return () => new Variant(decoder.ReadSByteArray("Value").ToArray());
case BuiltInType.Byte:
return () => new Variant(decoder.ReadByteArray("Value").ToArray());
case BuiltInType.Int16:
return () => new Variant(decoder.ReadInt16Array("Value").ToArray());
case BuiltInType.UInt16:
return () => new Variant(decoder.ReadUInt16Array("Value").ToArray());
case BuiltInType.Int32:
return () => new Variant(decoder.ReadInt32Array("Value").ToArray());
case BuiltInType.UInt32:
return () => new Variant(decoder.ReadUInt32Array("Value").ToArray());
case BuiltInType.Int64:
return () => new Variant(decoder.ReadInt64Array("Value").ToArray());
case BuiltInType.UInt64:
return () => new Variant(decoder.ReadUInt64Array("Value").ToArray());
case BuiltInType.Float:
return () => new Variant(decoder.ReadFloatArray("Value").ToArray());
case BuiltInType.Double:
return () => new Variant(decoder.ReadDoubleArray("Value").ToArray());
case BuiltInType.String:
return () => new Variant(decoder.ReadStringArray("Value").ToArray());
case BuiltInType.DateTime:
return () => new Variant(decoder.ReadDateTimeArray("Value").ToArray());
case BuiltInType.Guid:
return () => new Variant(decoder.ReadGuidArray("Value").ToArray());
case BuiltInType.ByteString:
return () => new Variant(decoder.ReadByteStringArray("Value").ToArray());
case BuiltInType.XmlElement:
return () => new Variant(decoder.ReadXmlElementArray("Value").ToArray());
case BuiltInType.NodeId:
return () => new Variant(decoder.ReadNodeIdArray("Value").ToArray());
case BuiltInType.ExpandedNodeId:
return () => new Variant(decoder.ReadExpandedNodeIdArray("Value").ToArray());
case BuiltInType.StatusCode:
return () => new Variant(decoder.ReadStatusCodeArray("Value").ToArray());
case BuiltInType.QualifiedName:
return () => new Variant(decoder.ReadQualifiedNameArray("Value").ToArray());
case BuiltInType.LocalizedText:
return () => new Variant(decoder.ReadLocalizedTextArray("Value").ToArray());
case BuiltInType.ExtensionObject:
return () => new Variant(decoder.ReadExtensionObjectArray("Value").ToArray());
case BuiltInType.DiagnosticInfo:
return () => new Variant(decoder.ReadDiagnosticInfoArray("Value").ToArray());
case BuiltInType.Enumeration:
return () => new Variant(decoder.ReadEnumerationArray("Value").ToArray());
default:
throw new NotImplementedException();
}
}
public static Func<Variant> GetDecodeMatrixDelegate(this BuiltInType builtIn, PlatformJsonDecoder decoder)
{
switch (builtIn)
{
case BuiltInType.Boolean:
return () => new Variant(new Matrix(decoder.ReadBooleanArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.SByte:
return () => new Variant(new Matrix(decoder.ReadSByteArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.Byte:
return () => new Variant(new Matrix(decoder.ReadSByteArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.Int16:
return () => new Variant(new Matrix(decoder.ReadInt16Array("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.UInt16:
return () => new Variant(new Matrix(decoder.ReadUInt16Array("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.Int32:
return () => new Variant(new Matrix(decoder.ReadInt32Array("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.UInt32:
return () => new Variant(new Matrix(decoder.ReadUInt32Array("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.Int64:
return () => new Variant(new Matrix(decoder.ReadInt64Array("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.UInt64:
return () => new Variant(new Matrix(decoder.ReadUInt64Array("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.Float:
return () => new Variant(new Matrix(decoder.ReadFloatArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.Double:
return () => new Variant(new Matrix(decoder.ReadDoubleArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.String:
return () => new Variant(new Matrix(decoder.ReadStringArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.DateTime:
return () => new Variant(new Matrix(decoder.ReadDateTimeArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.Guid:
return () => new Variant(new Matrix(decoder.ReadGuidArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.ByteString:
return () => new Variant(new Matrix(decoder.ReadByteStringArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.XmlElement:
return () => new Variant(new Matrix(decoder.ReadXmlElementArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.NodeId:
return () => new Variant(new Matrix(decoder.ReadNodeIdArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.ExpandedNodeId:
return () => new Variant(new Matrix(decoder.ReadExpandedNodeIdArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.StatusCode:
return () => new Variant(new Matrix(decoder.ReadStatusCodeArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.QualifiedName:
return () => new Variant(new Matrix(decoder.ReadQualifiedNameArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.LocalizedText:
return () => new Variant(new Matrix(decoder.ReadLocalizedTextArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.ExtensionObject:
return () => new Variant(new Matrix(decoder.ReadExtensionObjectArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.DiagnosticInfo:
return () => new Variant(new Matrix(decoder.ReadDiagnosticInfoArray("Value").ToArray(), builtIn, decoder.Dimensions));
case BuiltInType.Enumeration:
return () => new Variant(new Matrix(decoder.ReadEnumerationArray("Value").ToArray(), builtIn, decoder.Dimensions));
default:
throw new NotImplementedException();
}
}
}
public static class SessionExtensionMethods
{
public static (LocalizedText[] enumStrings, EnumValueType[] enumValues) GetEnumStrings(this Session session, NodeId dataTypeId)
{
var referenceCollection = session.GetPropertiesReferenceCollection(dataTypeId);
foreach (var dataValueCollection in referenceCollection
.Where(referenceDescription => referenceDescription.BrowseName.Name.Equals("EnumStrings"))
.Select(descr => ExpandedNodeId.ToNodeId(descr.NodeId, session.MessageContext.NamespaceUris))
.Select(enid => session.ReadNodeAttribute(enid, Attributes.Value)))
{
return (enumStrings: (LocalizedText[])dataValueCollection[0].Value, enumValues: null);
}
foreach (var dataValueCollection in referenceCollection
.Where(referenceDescription => referenceDescription.BrowseName.Name.Equals("EnumValues"))
.Select(descr => ExpandedNodeId.ToNodeId(descr.NodeId, session.MessageContext.NamespaceUris))
.Select(enid => session.ReadNodeAttribute(enid, Attributes.Value)))
{
var evs = ((ExtensionObject[])dataValueCollection[0].Value).Select(eo => eo.Body)
.Cast<EnumValueType>().ToArray();
return (enumStrings: null, enumValues: evs);
}
return (null, null);
}
public static ReferenceDescriptionCollection GetPropertiesReferenceCollection(this Session session, NodeId dataTypeId)
{
session.Browse(
null,
null,
dataTypeId,
0u,
BrowseDirection.Forward,
ReferenceTypeIds.HasProperty,
true,
(uint)NodeClass.Variable,
out _,
out var refDescriptionCollection);
return refDescriptionCollection;
}
public static DataValueCollection ReadNodeAttribute(this Session session, NodeId nodeId, uint attributeId)
{
var nodeToRead = new ReadValueIdCollection();
var vId = new ReadValueId()
{
NodeId = nodeId,
AttributeId = attributeId
};
nodeToRead.Add(vId);
session.Read(null,
0,
TimestampsToReturn.Both,
nodeToRead,
out var dataValueCollection,
out _
);
return dataValueCollection;
}
public static bool IsServerStatusGood(this Session session)
{
DataValue serverStatus;
try
{
serverStatus = session.ReadValue(new NodeId(2259, 0));
}
catch (Exception)
{
return false;
}
return DataValue.IsGood(serverStatus) && (int)serverStatus.Value == 0;
}
}
}

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