Compare commits

...

131 Commits

Author SHA1 Message Date
Kimdiego2098
affe9a44e0 优化 ModbusServer 内存占用 2023-10-20 01:53:48 +08:00
Kimdiego2098
43730fa519 3.0.0.21 2023-10-20 01:19:18 +08:00
Kimdiego2098
d39aa22b09 优化OPCUAServer,取消注册,提供多url写入 2023-10-20 01:19:05 +08:00
Kimdiego2098
e232a6b6ea 更新赞助名单 2023-10-19 21:15:35 +08:00
Kimdiego2098
71ebb36fe9 更新文档 2023-10-19 20:36:28 +08:00
Kimdiego2098
78a0b86327 更新版本:3.0.0.20 2023-10-19 20:27:31 +08:00
Kimdiego2098
2636c16a97 优化modbusServer内存管理 2023-10-19 20:24:39 +08:00
Kimdiego2098
fd77c0242d update touchsocket 2023-10-19 20:23:43 +08:00
Kimdiego2098
e74819a900 update toucksocket 2023-10-18 21:51:49 +08:00
Kimdiego2098
9b7f696c9b 更新文档 2023-10-18 20:42:10 +08:00
Kimdiego2098
0230d614e7 发布驱动包 2023-10-18 18:04:37 +08:00
Diego2098
252d99ad78 !12 【轻量级 PR】:修正心跳事件中的参数
Merge pull request !12 from youthalan/N/A
2023-10-18 06:59:17 +00:00
youthalan
1ffc200350 修正心跳事件中的参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 06:58:03 +00:00
Kimdiego2098
807d89b2b2 更新版本号 2023-10-18 13:13:03 +08:00
Kimdiego2098
4013afa1f1 初始化 采集/上传线程时 直接返回线程控制 2023-10-18 13:11:59 +08:00
Kimdiego2098
a580927ceb 更新OPCUAClient类库 2023-10-18 13:09:25 +08:00
Kimdiego2098
bf2cf52034 更新OPCUAClient类库 2023-10-18 12:45:26 +08:00
Kimdiego2098
81bb8b7c31 更新OPCUAClient类库 2023-10-18 12:44:58 +08:00
Kimdiego2098
a825007fb5 更新版本号与nuget发布 2023-10-18 12:39:42 +08:00
Kimdiego2098
988124d96a 更新OPCUAClient类库 2023-10-18 12:38:20 +08:00
Diego2098
f0de815296 !11 补充OPCClient类库事件的缺失文件
Merge pull request !11 from youthalan/N/A
2023-10-18 04:34:34 +00:00
youthalan
0e2d58c887 补充OPCClient类库事件的缺失文件
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 04:32:39 +00:00
Diego2098
b155382626 !10 添加连接或断开事件
Merge pull request !10 from youthalan/N/A
2023-10-18 04:20:22 +00:00
youthalan
f362d740af 修改OPCUAClient类添加连接或断开事件,修改注入时不需要带参数
Signed-off-by: youthalan <youthalan@126.com>
2023-10-18 03:48:05 +00:00
Kimdiego2098
4a85e31a4f update 3.0.0.16 2023-10-18 00:38:36 +08:00
Kimdiego2098
302c270ad5 opcuaClient浏览空间 添加是否显示子变量的选项 2023-10-18 00:36:44 +08:00
Kimdiego2098
3c1517d0f3 更改日志输出内容 2023-10-18 00:15:07 +08:00
Kimdiego2098
f9fb222044 update 3.0.0.15 2023-10-18 00:07:24 +08:00
Kimdiego2098
e8edc02ba3 增加不支持单文件发布的说明 2023-10-17 23:47:24 +08:00
Kimdiego2098
95a44e3053 update tdengineDB plugin 2023-10-17 23:43:48 +08:00
Kimdiego2098
74a9fe9a87 update touchsocket 2023-10-17 23:12:19 +08:00
Kimdiego2098
4d03f9ea1a TD时序库插件,创建时间更改为主键 2023-10-17 23:08:09 +08:00
Kimdiego2098
67c96ca991 update touchsocket 2023-10-17 23:06:21 +08:00
Kimdiego2098
88fb793c68 更新nuget包 2023-10-17 21:00:50 +08:00
Kimdiego2098
d6d02d8cc5 update SQLDB 2023-10-16 20:50:10 +08:00
Kimdiego2098
c5a3f8e2e3 update touchsocket and other 2023-10-16 20:36:51 +08:00
Kimdiego2098
27e8653a1a 3.0.0.13 2023-10-16 17:44:09 +08:00
Kimdiego2098
863beda82c 增加关系库存储插件; 2023-10-16 17:40:17 +08:00
Kimdiego2098
bac84c3ecd 增加时序库存储插件; 2023-10-16 17:40:13 +08:00
Kimdiego2098
2fca2ad9f8 更新pro用户列表 2023-10-16 17:39:14 +08:00
Kimdiego2098
dd75286fe0 3.0.0.12 2023-10-16 08:47:39 +08:00
Kimdiego2098
7f91792cf1 opcuaclient添加是否加载服务端数据类型的选项 2023-10-16 08:46:46 +08:00
Kimdiego2098
0e0ccad311 fix:tcpservice dispose err 2023-10-16 08:46:32 +08:00
Diego2098
0691f72e67 !9 增加可选择安全订阅
Merge pull request !9 from youthalan/N/A
2023-10-16 00:33:19 +00:00
youthalan
7e38a51720 增加可选择安全订阅,以加快订阅速度
Signed-off-by: youthalan <youthalan@126.com>
2023-10-16 00:26:59 +00:00
Kimdiego2098
34ca8243a3 更新3.0.0.11 2023-10-15 20:26:18 +08:00
Diego2098
112fea7632 读取数据类型方法改为批量 2023-10-15 20:21:23 +08:00
Kimdiego2098
378763e4ee 同步Pro版本 2023-10-13 20:14:35 +08:00
Kimdiego2098
517bd0394d update touchsocket 2023-10-13 19:16:12 +08:00
Kimdiego2098
70adb97fb5 update nuget 2023-10-13 16:14:42 +08:00
Kimdiego2098
623d44cabe update 3.0.0.7 2023-10-13 11:54:27 +08:00
Kimdiego2098
0d479ca00b 添加三菱mc 3e帧二进制通讯协议文档 2023-10-13 11:51:51 +08:00
Kimdiego2098
8bc49ef437 update demo 2023-10-12 18:52:44 +08:00
Kimdiego2098
f83fcec786 参数名称修改 2023-10-12 14:57:58 +08:00
Kimdiego2098
93690ce40d fix:mqtt保留消息未更新/更新错误 2023-10-12 14:57:36 +08:00
Kimdiego2098
f82c5f2f27 3.0.0.6 2023-10-11 16:35:10 +08:00
Kimdiego2098
a83c1c3899 update nuget 2023-10-11 16:28:18 +08:00
Kimdiego2098
91d6aed109 调整代码执行顺序 2023-10-11 16:19:34 +08:00
Kimdiego2098
db8f8fe51d update ManageGateway 2023-10-11 10:59:33 +08:00
Kimdiego2098
4596004b17 update ManageGateway 2023-10-11 10:41:09 +08:00
Kimdiego2098
d5540906cb update 3.0.0.5 2023-10-10 21:09:34 +08:00
Kimdiego2098
90796a979d fix:modbusRtu写入返回报文缓存逻辑修复 2023-10-10 21:08:42 +08:00
Kimdiego2098
2190a87772 update touchsocket 2023-10-10 20:03:40 +08:00
Kimdiego2098
c5953b83f8 update touchsocket 2023-10-10 20:02:15 +08:00
Kimdiego2098
24bc60abf0 fix:signalR razor dispose接口 2023-10-09 18:11:23 +08:00
Kimdiego2098
31eee6b009 update uaparser 2023-10-09 10:56:22 +08:00
Kimdiego2098
c5da565a8f 添加MqttRpcDemo 2023-10-07 12:04:17 +08:00
Kimdiego2098
947cd712e1 添加清理日志任务配置参数 2023-10-06 18:28:06 +08:00
Kimdiego2098
edc208f96b update 3.0.0.2 2023-10-05 15:32:50 +08:00
Diego2098
1fb0296ee7 update Directory.Build.props 2023-10-05 15:21:51 +08:00
Kimdiego2098
6488d3df87 修复重启共享通道中的单个设备时,导致通道内其他设备变量异常 2023-10-05 00:34:12 +08:00
Kimdiego2098
56189d78e0 update opcuaclient 2023-10-04 17:12:47 +08:00
Kimdiego2098
bff18127b8 update opcuaclient 2023-10-04 16:59:15 +08:00
Kimdiego2098
363206e0ba update nuget 2023-10-04 02:04:09 +08:00
Kimdiego2098
fd3e378501 update 3.0.0.1 2023-10-04 01:42:27 +08:00
Kimdiego2098
4ba2fe4c9d 实时报警列表线程同步 2023-10-04 01:27:03 +08:00
Kimdiego2098
2c499626ad modbusRtu 适配器 过滤干扰头部数据 2023-10-04 01:26:44 +08:00
Kimdiego2098
2b581a03c3 更新种子数据 2023-10-04 01:24:34 +08:00
Kimdiego2098
450c15210a update windowsService bat 2023-10-03 19:11:42 +08:00
Kimdiego2098
65fed8cc93 update demo 2023-10-03 18:54:01 +08:00
Kimdiego2098
4b64771ea2 rpc调用提示优化 2023-10-03 18:20:39 +08:00
Kimdiego2098
f39977a6ff fix:rpc 特殊方法分类错误 2023-10-03 17:05:49 +08:00
Kimdiego2098
933b535caa update demo 2023-10-02 22:37:50 +08:00
Kimdiego2098
8abc5d2f20 update driverDebugPage 2023-10-02 18:34:55 +08:00
Kimdiego2098
d8783cd994 update opcuaClient 2023-10-02 18:30:58 +08:00
Diego2098
d5d087feb5 add s7 wstring addressSign 2023-10-01 16:54:42 +08:00
Diego2098
6ba3399df7 add s7 wstring addressSign 2023-10-01 16:49:07 +08:00
Diego2098
65124b3aa8 更新demo 2023-10-01 13:33:25 +08:00
Kimdiego2098
98597f4726 update demo csproj 2023-10-01 00:33:33 +08:00
Kimdiego2098
e7981f0d8e add EncodingMapper 2023-10-01 00:25:04 +08:00
Kimdiego2098
cf654427c3 更新文档 2023-09-30 23:28:50 +08:00
Kimdiego2098
ff2f628282 默认不启用远程更新 2023-09-30 23:23:44 +08:00
Kimdiego2098
ae818ca265 更新readme 2023-09-30 23:09:48 +08:00
Kimdiego2098
0f2aed458e 同步3.0.0版本代码 2023-09-30 23:05:53 +08:00
Kimdiego2098
d486c44ff6 更新文档 2023-09-26 20:03:58 +08:00
Kimdiego2098
ca7b9980bf 2.1.0.15 2023-09-20 11:50:06 +08:00
Kimdiego2098
3c71e6a8e3 2.1.0.15 2023-09-20 11:47:10 +08:00
Kimdiego2098
542442864c 添加kafka/mq/iotsharp的间隔上传 2023-09-20 11:46:17 +08:00
Diego2098
5edb64fa85 fix:上传设备选择驱动时没有正确刷新 2023-09-18 00:10:02 +08:00
Kimdiego2098
8dc1c898a3 2.1.0.14 2023-09-15 14:02:15 +08:00
Kimdiego2098
1ed35726b0 ExpressionEvaluator改为多实例 2023-09-15 14:01:54 +08:00
Kimdiego2098
27fae9ebaa 2.1.0.13 2023-09-15 13:23:59 +08:00
Kimdiego2098
b103f25c94 add dispose() 2023-09-15 13:23:39 +08:00
Kimdiego2098
abff450274 2.1.0.12 2023-09-15 13:20:29 +08:00
Kimdiego2098
c260736a11 更新多个依赖包版本 2023-09-15 13:19:04 +08:00
Kimdiego2098
166ac2307a 修复因重复注册cancellationToken.Register导致的内存暴涨! 2023-09-15 13:17:36 +08:00
Kimdiego2098
b21a4e1a4d CancellationToken代替CancellationTokenSource传入 2023-09-15 13:16:56 +08:00
Kimdiego2098
f7dc943fa3 2.1.0.11 2023-09-11 09:35:24 +08:00
Kimdiego2098
bfbd2693ec feat:ManageGatewayWorker part 2023-09-11 09:31:26 +08:00
Kimdiego2098
819e71c993 feat:ManageGatewayWorker part 2023-09-11 09:09:48 +08:00
Kimdiego2098
9fd0b489a2 feat:ManageGatewayWorker part 2023-09-11 09:09:03 +08:00
Kimdiego2098
f5fe9f8dae TGAPPInfp更名APPInfo 2023-09-11 08:57:13 +08:00
Kimdiego2098
f9ffc18145 Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-09-11 08:54:01 +08:00
Kimdiego2098
08db5b983a feat:ManageGatewayWorker part 2023-09-11 08:53:52 +08:00
Diego2098
5b3b4c8c50 !7 修复无法修改变量值问题
Merge pull request !7 from 如阳如木/master
2023-09-07 10:14:45 +00:00
如阳如木
73f914ffc4 删除&& LastSetValue?.ToString() != data?.ToString()
Signed-off-by: 如阳如木 <970143933@qq.com>
2023-09-07 08:42:49 +00:00
Kimdiego2098
d6bdd73ed6 ManageGatewayConfig.json 2023-09-06 17:29:58 +08:00
Kimdiego2098
7370ee7349 fix:_mqttServer null error 2023-09-06 17:27:36 +08:00
Kimdiego2098
4574596bac 2.1.0.10 2023-09-06 17:16:28 +08:00
Kimdiego2098
4d16855e36 从数据库取原属性 2023-09-06 17:14:24 +08:00
Kimdiego2098
13a0d4d282 更改变量 最近一次值 为 上次值 2023-09-06 16:44:58 +08:00
Kimdiego2098
b9cd06b829 更改 变量最近一次值 为 上次值 2023-09-06 16:44:42 +08:00
Kimdiego2098
5b460e8fa2 2.1.0.9 2023-09-06 16:30:50 +08:00
Kimdiego2098
41087edf17 fix:串口断连/拔出/断电等情况,重新连接 2023-09-06 16:29:48 +08:00
Kimdiego2098
2afcc38e38 feat:ManageGatewayWorker part 2023-09-06 16:15:38 +08:00
Kimdiego2098
e59ccce25f feat:ManageGatewayWorker part 2023-09-06 16:10:29 +08:00
Kimdiego2098
d7425890e8 feat:ManageGatewayWorker part 2023-09-05 23:59:18 +08:00
Kimdiego2098
a989a837fb feat:ManageGatewayWorker part 2023-09-05 23:37:02 +08:00
Kimdiego2098
db1221da50 feat:ManageGatewayWorker part 2023-09-05 23:33:02 +08:00
Kimdiego2098
cf794569ed 2.1.0.8 2023-09-05 09:19:58 +08:00
Kimdiego2098
51e5bbab0d fix:PeriodicTimer dispose 2023-09-05 09:17:26 +08:00
1616 changed files with 119161 additions and 35959 deletions

View File

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

View File

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

63
framework/.gitattributes vendored Normal file
View File

@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

364
framework/.gitignore vendored Normal file
View File

@@ -0,0 +1,364 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd

View File

@@ -0,0 +1,15 @@
<Project>
<PropertyGroup>
<Version>3.0.0.22</Version>
<LangVersion>latest</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Authors>Diego</Authors>
<Product>ThingsGateway</Product>
<Copyright>© 2023-present Diego</Copyright>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
</PropertyGroup>
</Project>

View File

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

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<ApplicationIcon>favicon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<None Remove="favicon.ico" />
</ItemGroup>
<ItemGroup>
<Content Include="favicon.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Photino.Blazor" Version="2.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,26 @@
@*
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Foundation.Demo
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>

View File

@@ -0,0 +1,148 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Microsoft.AspNetCore.Components;
using ThingsGateway.Components;
namespace ThingsGateway.Foundation.Demo;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
/// <summary>
/// 调试UI
/// </summary>
public abstract class DriverDebugUIBase : ComponentBase, IDisposable
{
/// <summary>
/// 日志缓存
/// </summary>
public ConcurrentLinkedList<(LogLevel level, string message)> Messages = new();
private PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(1));
/// <inheritdoc/>
~DriverDebugUIBase()
{
this.SafeDispose();
}
/// <summary>
/// 变量地址
/// </summary>
public virtual string Address { get; set; } = "40001";
/// <summary>
/// 长度
/// </summary>
public virtual int Length { get; set; } = 1;
/// <summary>
/// 默认读写设备
/// </summary>
public virtual IReadWrite Plc { get; set; }
/// <summary>
/// 写入值
/// </summary>
public virtual string WriteValue { get; set; }
/// <summary>
/// 数据类型
/// </summary>
protected virtual DataTypeEnum DataTypeEnum { get; set; } = DataTypeEnum.Int16;
/// <summary>
/// <inheritdoc/>
/// </summary>
[Inject]
public InitTimezone InitTimezone { get; set; }
/// <inheritdoc/>
public virtual void Dispose()
{
_periodicTimer?.Dispose();
}
/// <inheritdoc/>
public void LogOut(ThingsGateway.Foundation.Core.LogLevel logLevel, object source, string message, Exception exception)
{
Messages.Add(((LogLevel)logLevel,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {message} {exception}"));
if (Messages.Count > 2500)
{
Messages.Clear();
}
}
/// <inheritdoc/>
public virtual async Task ReadAsync()
{
try
{
var data = await Plc.ReadAsync(Address, Length, DataTypeEnum);
if (data.IsSuccess)
{
Messages.Add((LogLevel.Information,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 对应类型值:{Environment.NewLine}{data.Content.ToJsonString(true)} "));
}
else
{
Messages.Add((LogLevel.Warning,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
}
}
catch (Exception ex)
{
Messages.Add((LogLevel.Error,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 错误:{ex}"));
}
}
/// <inheritdoc/>
public virtual async Task WriteAsync()
{
try
{
var data = await Plc.WriteAsync(Address, WriteValue, Length, DataTypeEnum);
if (data.IsSuccess)
{
Messages.Add((LogLevel.Information,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
}
else
{
Messages.Add((LogLevel.Warning,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - {data.Message}"));
}
}
catch (Exception ex)
{
Messages.Add((LogLevel.Error,
$"{DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset)} - 写入前失败:{ex}"));
}
}
/// <inheritdoc/>
protected override void OnInitialized()
{
_ = RunTimerAsync();
base.OnInitialized();
}
private async Task RunTimerAsync()
{
while (await _periodicTimer.WaitForNextTickAsync())
{
await InvokeAsync(StateHasChanged);
}
}
}

View File

@@ -0,0 +1,205 @@
@*
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
*@
@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@using Microsoft.JSInterop;
@using ThingsGateway.Foundation.Core;
@using Masa.Blazor;
@namespace ThingsGateway.Foundation.Demo
@inherits DriverDebugUIBase
<MCard Elevation="1" Rounded="false" Class=" pa-2" Style="width:100%">
<MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters>
<MCol Md="5">
<MTabs @bind-Value="tab" Class="ma-2">
<MTab Value=1> 读写测试 </MTab>
<MTab Value=2> 特殊功能 </MTab>
<MTab Value=3> 代码示例 </MTab>
</MTabs>
<MTabsItems Value="tab">
<MTabItem Value="1">
@if (tab == 1)
{
@if (ReadWriteContent == null)
{
<div class="my-1 py-1">
<MTooltip Bottom Context="tip">
<ActivatorContent>
<MTextarea Class="mx-1 my-1" Label="变量地址" @attributes="@tip.Attrs" Dense Outlined HideDetails="@("auto")" @bind-Value=@Address />
</ActivatorContent>
<ChildContent>
<span style="white-space: pre-wrap;">@Plc?.GetAddressDescription()</span>
</ChildContent>
</MTooltip>
<MRow Class="my-1" Justify="JustifyTypes.Start" Align="AlignTypes.Start" NoGutters>
<MTextField Class="mx-1 my-1" Style="max-width:200px" Label="读取长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@Length />
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="DataTypeEnum" Outlined Label="数据类型"
Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.Description)
ItemValue=@(u =>(DataTypeEnum)u.Value)
HideDetails=@("auto") Height="30"
Dense>
</MSelect>
</MRow>
<MButton Class="mx-1 my-1" Color="primary" OnClick="ReadAsync">
读取
</MButton>
<MTextarea Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@WriteValue />
<MButton Class="mx-1 my-1" Color="primary" OnClick="WriteAsync">
写入
</MButton>
</div>
}
else
{
@ReadWriteContent
}
}
</MTabItem>
<MTabItem Value="2">
@if (tab == 2)
{
@if (ShowDefaultOtherContent)
{
<MSubheader>
连读打包
</MSubheader>
<MContainer>
@foreach (var item in DeviceVariableRunTimes)
{
<MRow Dense Align="AlignTypes.Center">
<MTextField Class="ma-1" Outlined Style="min-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.VariableAddress)) Dense HideDetails="@("auto")" @bind-Value=@item.VariableAddress></MTextField>
<MSelect Class="mx-1 my-1" Style="max-width:120px" @bind-Value="item.DataTypeEnum" Outlined Label=@(item.DescriptionWithOutSugar(x => x.DataTypeEnum))
Items=@(typeof(DataTypeEnum).GetEnumListWithOutSugar())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.Description)
ItemValue=@(u =>(DataTypeEnum)u.Value)
HideDetails=@("auto") Height="30"
Dense>
</MSelect>
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=@(item.DescriptionWithOutSugar(x => x.IntervalTime)) Dense HideDetails="@("auto")" @bind-Value=@item.IntervalTime></MTextField>
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label=实时值 Readonly ClearIcon="" Dense HideDetails="@("auto")" Value=item.Value?.ToJsonString()></MTextField>
</MRow>
}
<MRow Dense>
<MTextField Class="ma-1" Outlined Style="max-width:100px" Label="打包长度" Dense HideDetails="@("auto")" @bind-Value=@MaxPack></MTextField>
<MButton Class="ma-1" Color="primary" OnClick="MulReadAsync">
读取
</MButton>
</MRow>
</MContainer>
}
@if (OtherContent != null)
{
<MSheet Style="height:100%;overflow-y:auto">
@OtherContent
</MSheet>
}
}
</MTabItem>
<MTabItem Value="3">
@if (tab == 3)
{
@if (CodeContent != null)
@CodeContent
else
{
<MRow Align="AlignTypes.Center">
<MContainer>
<MItemGroup @bind-Value="_selected" Class="shrink mr-6" Mandatory>
@{
int index = 0;
}
<MRow>
@foreach (var item in Sections)
{
<MItem Value="@(index++)">
<div>
<MButton IsActive="@context.Active" Icon OnClick="@context.Toggle">
<MIcon>mdi-record</MIcon>
</MButton>
</div>
</MItem>
}
</MRow>
</MItemGroup>
</MContainer>
<MCol>
<MWindow Value="_selected" Vertical Class="elevation-1 grey lighten-5 rounded-b" Style=@($"height:450px;overflow:auto")>
@{
int index = 0;
}
@foreach (var item in Sections)
{
<MWindowItem Value="@(index++)">
<AppCode RoundedTop0 Code="@item.Code" Language="@item.Language" />
</MWindowItem>
}
</MWindow>
</MCol>
</MRow>
}
}
</MTabItem>
</MTabsItems>
</MCol>
<MCol Md="7">
<MCard Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
<ConsoleTxt Messages="Messages" Height=500></ConsoleTxt>
</MCard>
</MCol>
</MRow>
</MCard>
@code {
StringNumber tab;
}

View File

@@ -0,0 +1,236 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using BlazorComponent;
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace ThingsGateway.Foundation.Demo;
/// <inheritdoc/>
public partial class DriverDebugUIPage : DriverDebugUIBase
{
/// <summary>
/// DeviceVariableRunTimes
/// </summary>
public List<DeviceVariableRunTime> DeviceVariableRunTimes;
/// <summary>
/// MaxPack
/// </summary>
public int MaxPack = 100;
/// <summary>
/// MulReadAsync
/// </summary>
/// <returns></returns>
public async Task MulReadAsync()
{
var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack);
foreach (var item in deviceVariableSourceReads)
{
var result = await Plc.ReadAsync(item.VariableAddress, item.Length);
if (result.IsSuccess)
{
try
{
item.DeviceVariableRunTimes.PraseStructContent(Plc, result.Content);
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Information, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Content.ToHexString(' ')));
}
catch (Exception ex)
{
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + ex));
}
}
else
Messages.Add((Microsoft.Extensions.Logging.LogLevel.Warning, DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + result.Message));
}
}
private StringNumber _selected = 0;
/// <summary>
/// Sections
/// </summary>
[Parameter]
public List<(string Code, string Language)> Sections { get; set; } = new();
/// <summary>
/// ShowDefaultOtherContent
/// </summary>
[Parameter]
public bool ShowDefaultOtherContent { get; set; } = true;
/// <inheritdoc/>
protected override void OnInitialized()
{
DeviceVariableRunTimes = new()
{
new DeviceVariableRunTime()
{
DataTypeEnum=DataTypeEnum.Int16,
VariableAddress="40001",
IntervalTime=1000,
},
new DeviceVariableRunTime()
{
DataTypeEnum=DataTypeEnum.Int16,
VariableAddress="40011",
IntervalTime=1000,
},
new DeviceVariableRunTime()
{
DataTypeEnum=DataTypeEnum.Int16,
VariableAddress="40031",
IntervalTime=1000,
},
new DeviceVariableRunTime()
{
DataTypeEnum=DataTypeEnum.Int16,
VariableAddress="40101",
IntervalTime=1000,
},
};
Sections.Add((
"""
/// <inheritdoc/>
public class DeviceVariableSourceRead : IDeviceVariableSourceRead<DeviceVariableRunTime>
{
/// <inheritdoc/>
public TimerTick TimerTick { get; set; }
/// <inheritdoc/>
public string VariableAddress { get; set; }
/// <inheritdoc/>
public int Length { get; set; }
/// <inheritdoc/>
public List<DeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<DeviceVariableRunTime>();
}
/// <inheritdoc/>
public class DeviceVariableRunTime : IDeviceVariableRunTime
{
/// <inheritdoc/>
[Description("读取间隔")]
public int IntervalTime { get; set; }
/// <inheritdoc/>
[Description("变量地址")]
public string VariableAddress { get; set; }
/// <inheritdoc/>
public int Index { get; set; }
/// <inheritdoc/>
public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
/// <inheritdoc/>
[Description("数据类型")]
public DataTypeEnum DataTypeEnum { get; set; }
/// <inheritdoc/>
[Description("实时值")]
public object Value { get; set; }
/// <inheritdoc/>
public OperResult SetValue(object value)
{
Value = value;
return OperResult.CreateSuccessResult();
}
}
public List<DeviceVariableRunTime> DeviceVariableRunTimes;
private static async Task ModbusClientAsync(IReadWrite plc)
{
DeviceVariableRunTimes = new()
{
new DeviceVariableRunTime()
{
DataTypeEnum=DataTypeEnum.Int16,
VariableAddress="40001",
IntervalTime=1000,
},
new DeviceVariableRunTime()
{
DataTypeEnum=DataTypeEnum.Int16,
VariableAddress="40011",
IntervalTime=1000,
},
new DeviceVariableRunTime()
{
DataTypeEnum=DataTypeEnum.Int16,
VariableAddress="40031",
IntervalTime=1000,
},
new DeviceVariableRunTime()
{
DataTypeEnum=DataTypeEnum.Int16,
VariableAddress="40101",
IntervalTime=1000,
},
};
#region
var deviceVariableSourceReads = Plc.LoadSourceRead<DeviceVariableSourceRead, DeviceVariableRunTime>(DeviceVariableRunTimes, MaxPack);
foreach (var item in deviceVariableSourceReads)
{
var result = await Plc.ReadAsync(item.VariableAddress, item.Length);
if (result.IsSuccess)
{
item.DeviceVariableRunTimes.PraseStructContent(result.Content);
}
}
#endregion
}
""", "csharp"));
base.OnInitialized();
}
/// <summary>
/// 自定义模板
/// </summary>
[Parameter]
public RenderFragment ReadWriteContent { get; set; }
/// <summary>
/// 自定义模板
/// </summary>
[Parameter]
public RenderFragment OtherContent { get; set; }
/// <summary>
/// 自定义模板
/// </summary>
[Parameter]
public RenderFragment CodeContent { get; set; }
/// <inheritdoc/>
~DriverDebugUIPage()
{
this.SafeDispose();
}
/// <summary>
/// <inheritdoc/>
/// </summary>
public override IReadWrite Plc { get; set; }
/// <inheritdoc/>
public override void Dispose()
{
Plc?.SafeDispose();
base.Dispose();
}
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="firstRender"></param>
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
}
}

View File

@@ -0,0 +1,47 @@
@*
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Foundation.Demo
@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@using System.IO.Ports;
@using Masa.Blazor
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
<div class="mb-4">通道配置</div>
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.PortName)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.PortName />
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.BaudRate)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.BaudRate />
<MTextField Style="max-width:100px" Class="ma-1" Outlined Label=@(serialProperty.DescriptionWithOutSugar(x => x.DataBits)) Dense HideDetails="@("auto")" @bind-Value=@serialProperty.DataBits />
<MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.Parity" Label="@(serialProperty.DescriptionWithOutSugar(x => x.Parity))"
Items=@(typeof(Parity).GetEnumListWithOutSugar())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.Description)
ItemValue=@(u =>(Parity)u.Value)
HideDetails=@("auto") Height="30"
Dense>
</MSelect>
<MSelect Class="ma-1 " Style="max-width:200px" Outlined @bind-Value="serialProperty.StopBits" Label="@(serialProperty.DescriptionWithOutSugar(x => x.StopBits))"
Items=@(typeof(StopBits).GetEnumListWithOutSugar())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.Description)
ItemValue=@(u =>(StopBits)u.Value)
HideDetails=@("auto") Height="30"
Dense>
</MSelect>
<MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
连接
</MButton>
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
断开
</MButton>
</MRow>
</MCard>

View File

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

View File

@@ -10,21 +10,19 @@
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Blazor
@namespace ThingsGateway.Foundation.Demo
@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@using System.IO.Ports;
@using System.Collections.Concurrent;
@using ThingsGateway.Foundation;
@using ThingsGateway.Foundation.Serial;
@using ThingsGateway.Foundation.Core;
@using Masa.Blazor
@using TouchSocket.Core;
@using TouchSocket.Sockets;
@implements IDisposable
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start">
<MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP />
<MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port />
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
<div class="mb-4">通道配置</div>
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP />
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port />
<MButton Class="ma-1" OnClick=@ConnectAsync Color="primary">
连接
@@ -33,7 +31,4 @@
断开
</MButton>
</MRow>
</MCard>

View File

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

View File

@@ -10,21 +10,20 @@
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Blazor
@namespace ThingsGateway.Foundation.Demo
@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@using System.IO.Ports;
@using System.Collections.Concurrent;
@using ThingsGateway.Foundation;
@using ThingsGateway.Foundation.Serial;
@using ThingsGateway.Foundation.Core;
@using Masa.Blazor
@using TouchSocket.Core;
@using TouchSocket.Sockets;
@implements IDisposable
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start">
<MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
<MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
<div class="mb-4">通道配置</div>
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
<MButton Class="ma-1" OnClick=@Connect Color="primary">
连接
@@ -32,8 +31,8 @@
<MButton Class="ma-1" OnClick=@DisConnect Color="red">
断开
</MButton>
</MRow>
</MCard>
</MCard>

View File

@@ -10,13 +10,10 @@
//------------------------------------------------------------------------------
#endregion
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace ThingsGateway.Blazor;
namespace ThingsGateway.Foundation.Demo;
/// <inheritdoc/>
public partial class TcpServerPage
public partial class TcpServerPage : IDisposable
{
/// <summary>
/// 日志输出
@@ -31,11 +28,6 @@ public partial class TcpServerPage
private TcpService TcpServer { get; set; } = new();
/// <inheritdoc/>
public void Dispose()
{
TcpServer.SafeDispose();
}
private void Connect()
{
try
@@ -68,30 +60,49 @@ public partial class TcpServerPage
public TcpService GetTcpServer()
{
config ??= new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
//载入配置
TcpServer.Setup(config);
return TcpServer;
}
/// <inheritdoc/>
protected override void OnInitialized()
{
config ??= new TouchSocketConfig();
base.OnInitialized();
}
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnInitialized()
/// <param name="firstRender"></param>
protected override void OnAfterRender(bool firstRender)
{
config?.Dispose();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
TcpServer = new TcpService();
TcpServer.Setup(config);
base.OnInitialized();
if (firstRender)
{
var LogMessage = new LoggerGroup() { LogLevel = LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
TcpServer.Setup(config);
}
base.OnAfterRender(firstRender);
}
private void LogOut(LogLevel logLevel, object source, string message, Exception exception) => LogAction?.Invoke(logLevel, source, message, exception);
/// <summary>
/// <inheritdoc/>
/// </summary>
public void Dispose()
{
TcpServer.SafeDispose();
}
internal void StateHasChangedAsync()
{
StateHasChanged();
}
}

View File

@@ -10,21 +10,20 @@
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Blazor
@namespace ThingsGateway.Foundation.Demo
@using BlazorComponent;
@using Microsoft.AspNetCore.Components.Web;
@using System.IO.Ports;
@using System.Collections.Concurrent;
@using ThingsGateway.Foundation;
@using ThingsGateway.Foundation.Serial;
@using ThingsGateway.Foundation.Core;
@using Masa.Blazor
@using TouchSocket.Core;
@using TouchSocket.Sockets;
@implements IDisposable
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Start">
<MTextField Class="ma-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
<MTextField Class="ma-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
<MCard Elevation="1" Rounded="false" Class="pa-2" Style="width:100%">
<div class="mb-4">通道配置</div>
<MRow Justify="JustifyTypes.Start" Align="AlignTypes.Center">
<MTextField Class="ma-1" Style="max-width:100px" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@IP />
<MTextField Class="ma-1" Style="max-width:100px" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@Port />
<MButton Class="ma-1" OnClick=Connect Color="primary">
连接
@@ -32,8 +31,7 @@
<MButton Class="ma-1" OnClick=DisConnect Color="red">
断开
</MButton>
</MRow>
</MCard>

View File

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

View File

@@ -0,0 +1,42 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Foundation.Demo;
/// <inheritdoc/>
public class DeviceVariableRunTime : IDeviceVariableRunTime
{
/// <inheritdoc/>
[Description("读取间隔")]
public int IntervalTime { get; set; }
/// <inheritdoc/>
[Description("变量地址")]
public string VariableAddress { get; set; }
/// <inheritdoc/>
public int Index { get; set; }
/// <inheritdoc/>
public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; set; }
/// <inheritdoc/>
[Description("数据类型")]
public DataTypeEnum DataTypeEnum { get; set; }
/// <inheritdoc/>
[Description("实时值")]
public object Value { get; set; }
/// <inheritdoc/>
public OperResult SetValue(object value, DateTime dateTime = default, bool isOnline = true)
{
Value = value;
return OperResult.CreateSuccessResult();
}
}

View File

@@ -0,0 +1,28 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
namespace ThingsGateway.Foundation.Demo;
/// <inheritdoc/>
public class DeviceVariableSourceRead : IDeviceVariableSourceRead<IDeviceVariableRunTime>
{
/// <inheritdoc/>
public TimerTick TimerTick { get; set; }
/// <inheritdoc/>
public string VariableAddress { get; set; }
/// <inheritdoc/>
public int Length { get; set; }
/// <inheritdoc/>
public List<IDeviceVariableRunTime> DeviceVariableRunTimes { get; set; } = new List<IDeviceVariableRunTime>();
}

View File

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

View File

@@ -0,0 +1,36 @@
@*
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
*@
@page "/"
@layout BaseLayout
@inject NavigationManager NavigationManager
@namespace ThingsGateway.Foundation.Demo
@using Microsoft.AspNetCore.Authorization;
@code {
/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="firstRender"></param>
/// <returns></returns>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
NavigationManager.NavigateTo("index");
}
await base.OnAfterRenderAsync(firstRender);
}
}

View File

@@ -0,0 +1,57 @@
@*
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
*@
@page "/index"
<div class="ml-2">
<div class="my-6 ">
<MLabel Class="text-h3" Color="primary">ThingsGateway</MLabel>
</div>
<div>
<strong class="text--lighten-1 text-h5 my-1">文档</strong>
</div>
<div class="my-2 ml-4">
<PCopyableText>
https://diego2098.gitee.io/thingsgateway-docs/
</PCopyableText>
</div>
<div>
<strong class="text--lighten-1 text-h5 my-1">协议</strong>
</div>
<div class="my-2 ml-4">
<PCopyableText Text="https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh">
Apache-2.0开源协议
</PCopyableText>
</div>
<div>
<strong class="text--lighten-1 text-h5 my-1">赞助</strong>
</div>
<div class="my-2 ml-4">
<PCopyableText Text="https://diego2098.gitee.io/thingsgateway-docs/docs/donate">
ThingsGateway赞助途径
</PCopyableText>
</div>
<div>
<strong class="text--lighten-1 text-h5 my-1">社区</strong>
</div>
<div class="my-2 ml-4">
<PCopyableText Text="605534569">
QQ群605534569
</PCopyableText>
</div>
</div>

View File

@@ -0,0 +1,42 @@
@*
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Foundation.Demo
@inherits LayoutComponentBase
<CascadingValue Value="IsMobile" Name="IsMobile">
<MApp>
<MErrorHandler>
@Body
</MErrorHandler>
</MApp>
</CascadingValue>
@code {
public bool IsMobile { get; set; }
[Inject]
public MasaBlazor MasaBlazor { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
MasaBlazor.BreakpointChanged += BreakpointOnOnUpdate;
}
private void BreakpointOnOnUpdate(object sender, BreakpointChangedEventArgs e)
{
IsMobile = MasaBlazor.Breakpoint.Mobile;
if (e.MobileChanged)
{
StateHasChanged();
}
}
}

View File

@@ -10,34 +10,18 @@
//------------------------------------------------------------------------------
*@
@namespace ThingsGateway.Admin.Blazor
@using Masa.Blazor.Presets
@using ThingsGateway.Admin.Blazor.Core
@namespace ThingsGateway.Foundation.Demo
@using System.Text;
@inherits LayoutComponentBase
@layout BaseLayout
@if (UserManager.UserId > 0)
{
<SysSignalR></SysSignalR>
}
<PPageTabsProvider>
<CascadingValue Value="@this" IsFixed>
<CascadingValue Value="@Changed" Name="Changed">
<MNavigationDrawer Color="barcolor" @bind-Value="_drawerOpen" App Width="200">
@if (IsMobile)
{
<MSystemBar Color="barcolor" Height="@(BlazorResourceConst.PageTabsHeight)">
<MButton Icon OnClick=@(()=> _drawerOpen = !_drawerOpen)>
<MIcon>
mdi-close-thick
</MIcon>
</MButton>
<MSpacer />
<AppbarButtons />
</MSystemBar>
}
<Logo CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE HeightInt=@(IsMobile?BlazorResourceConst.AppBarHeight:BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight) />
<Logo CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_TITLE=@CONFIG_TITLE HeightInt=@(IsMobile?BlazorResourceConst.AppBarHeight:BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight) />
<AppList ClassString="overflow-y-auto" Routable
StyleString=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight}px);")
Items="Navs" />
@@ -48,18 +32,16 @@
<MButton Class="mr-0" Icon Small=IsMobile OnClick=@(() => _drawerOpen = !_drawerOpen)>
<MIcon>mdi-menu</MIcon>
</MButton>
<AppBarItems CONFIG_COPYRIGHT=@CONFIG_COPYRIGHT CONFIG_COPYRIGHT_URL=@CONFIG_COPYRIGHT_URL CONFIG_TITLE=@CONFIG_TITLE>
</AppBarItems>
</MAppBar>
<MMain Style=@($"{(!(IsMobile||_drawerOpen!=true)? "padding-left:200px;":"")}")>
<div class="full-width">
<PageTabs @ref="_pageTabs" />
<PageTabs @ref="_pageTabs" PageTabItems="pageTabItems" />
</div>
<MDivider Center></MDivider>
<MCard Flat Class="overflow-y-auto overflow-x-hidden ma-auto pa-0 rounded-0" Style=@($"height: calc(100vh - {BlazorResourceConst.AppBarHeight+BlazorResourceConst.PageTabsHeight+BlazorResourceConst.FooterHeight}px);")>
<PPageContainer PageTabs="@_pageTabs?.PPageTabs" SelfPatterns="@selfPatterns">
<PPageContainer PageTabs="@_pageTabs?.PPageTabs">
@Body
</PPageContainer>
</MCard>
@@ -71,5 +53,25 @@
</CascadingValue>
</PPageTabsProvider>
@code {
bool Changed { get; set; }
private bool? _drawerOpen = true;
private PageTabs _pageTabs;
/// <summary>
/// IsMobile
/// </summary>
[CascadingParameter(Name = "IsMobile")]
public bool IsMobile { get; set; }
}
@code{
private string CONFIG_COPYRIGHT = "Diego";
private string CONFIG_COPYRIGHT_URL = "https://gitee.com/diego2098/ThingsGateway";
private string CONFIG_TITLE = "ThingsGateway";
}

View File

@@ -0,0 +1,232 @@
#region copyright
//------------------------------------------------------------------------------
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4><EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://diego2098.gitee.io/thingsgateway-docs/
// QQȺ<51><C8BA>605534569
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
namespace ThingsGateway.Foundation.Demo;
public partial class MainLayout
{
private List<NavItem> Navs { get; set; } = new();
private List<PageTabItem> pageTabItems { get; set; } = new();
protected override void OnInitialized()
{
var dataString =
"""
[
{
"Href": "/index",
"Title": "<22><>ҳ"
},
{
"Title": "Modbus",
"Children": [
{
"Href": "/ModbusRtu",
"Title": "ModbusRtu"
},
{
"Href": "/ModbusTcp",
"Title": "ModbusTcp"
},
{
"Href": "/ModbusRtuOverTcp",
"Title": "ModbusRtuOverTcp"
},
{
"Href": "/ModbusRtuOverUdp",
"Title": "ModbusRtuOverUdp"
},
{
"Href": "/ModbusUdp",
"Title": "ModbusUdp"
},
{
"Href": "/ModbusTcpDtu",
"Title": "ModbusTcpDtu"
},
{
"Href": "/ModbusTcpServer",
"Title": "ModbusTcpServer"
},
{
"Href": "/ModbusSerialServer",
"Title": "ModbusSerialServer"
}
]
},
{
"Title": "Siemens",
"Children": [
{
"Href": "/S7_1500",
"Title": "S7_1500"
},
{
"Href": "/S7_1200",
"Title": "S7_1200"
},
{
"Href": "/S7_200",
"Title": "S7_200"
},
{
"Href": "/S7_200SMART",
"Title": "S7_200SMART"
},
{
"Href": "/S7_300",
"Title": "S7_400"
},
{
"Href": "/S7_400",
"Title": "S7_400"
}
]
},
{
"Title": "DLT645",
"Children": [
{
"Href": "/DLT645_2007",
"Title": "DLT645_2007"
},
{
"Href": "/DLT645_2007OverTcp",
"Title": "DLT645_2007OverTcp"
}
]
},
{
"Title": "OPCDA",
"Children": [
{
"Href": "/OPCDAClient",
"Title": "OPCDAClient"
}
]
},
{
"Title": "OPCUA",
"Children": [
{
"Href": "/OPCUAClient",
"Title": "OPCUAClient"
}
]
},
{
"Title": "Mqtt",
"Children": [
{
"Href": "/MqttClient",
"Title": "MqttClient"
}
]
}
]
""";
Navs = dataString.FromJsonString<List<NavItem>>();
#if Pro
var dataStringPro =
"""
[
{
"Title": "Melsec",
"Children": [
{
"Href": "/QnA3E_Binary",
"Title": "QnA3E_Binary"
}
]
},
{
"Title": "ABCIP",
"Children": [
{
"Href": "/ABCIPTCP",
"Title": "ABCIPTCP"
}
]
},
{
"Title": "Omron",
"Children": [
{
"Href": "/OmronFinsTcp",
"Title": "OmronFinsTcp"
},
{
"Href": "/OmronFinsUdp",
"Title": "OmronFinsUdp"
}
]
},
{
"Title": "Secs",
"Children": [
{
"Href": "/SecsTcp",
"Title": "SecsTcp"
}
]
},
{
"Title": "TS550",
"Children": [
{
"Href": "/TS550",
"Title": "TS550"
}
]
},
{
"Title": "Vigor",
"Children": [
{
"Href": "/VigorSerial",
"Title": "VigorSerial"
},
{
"Href": "/VigorSerialOverTcp",
"Title": "VigorSerialOverTcp"
}
]
},
{
"Title": "GasCustom",
"Children": [
{
"Href": "/GasCustomSerial",
"Title": "GasCustomSerial"
},
{
"Href": "/GasCustomSerialOverTcp",
"Title": "GasCustomSerialOverTcp"
}
]
}
]
""";
Navs.AddRange(dataStringPro.FromJsonString<List<NavItem>>());
#endif
pageTabItems = Navs.PasePageTabItem();
base.OnInitialized();
}
}

View File

@@ -0,0 +1,152 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
<DefineConstants>Pro</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(SolutionName)'=='ThingsGateway - Pro'">
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Melsec\Page\QnA3E_BinaryDebugPage.razor.cs" Link="Pages\Melsec\QnA3E_BinaryDebugPage.razor.cs" />
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Melsec\Page\QnA3E_BinaryDebugPage.razor" Link="Pages\Melsec\QnA3E_BinaryDebugPage.razor" />
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Melsec\ThingsGateway.Foundation.Adapter.Melsec.csproj" />
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor.cs" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor.cs" />
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.AllenBradleyCip\Page\AllenBradleyCipTcpDebugPage.razor" Link="Pages\ABCIP\AllenBradleyCipTcpDebugPage.razor" />
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.AllenBradleyCip\ThingsGateway.Foundation.Adapter.AllenBradleyCip.csproj" />
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor.cs" />
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor.cs" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor.cs" />
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsTcpDebugPage.razor" Link="Pages\OmronFins\OmronFinsTcpDebugPage.razor" />
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Omron\Page\OmronFinsUdpDebugPage.razor" Link="Pages\OmronFins\OmronFinsUdpDebugPage.razor" />
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Omron\ThingsGateway.Foundation.Adapter.Omron.csproj" />
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor.cs" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor.cs" />
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Secs\Page\SecsHsmsTcpDebugPage.razor" Link="Pages\Secs\SecsHsmsTcpDebugPage.razor" />
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Secs\ThingsGateway.Foundation.Adapter.Secs.csproj" />
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor.cs" Link="Pages\TS550\TS550DebugPage.razor.cs" />
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.TS550\Page\TS550DebugPage.razor" Link="Pages\TS550\TS550DebugPage.razor" />
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.TS550\ThingsGateway.Foundation.Adapter.TS550.csproj" />
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialDebugPage.razor.cs" />
<Compile Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor.cs" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor.cs" />
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialDebugPage.razor" Link="Pages\Vigor\VigorSerialDebugPage.razor" />
<Content Include="..\..\PluginPro\ThingsGateway.Plugin.Vigor\Page\VigorSerialOverTcpDebugPage.razor" Link="Pages\Vigor\VigorSerialOverTcpDebugPage.razor" />
<ProjectReference Include="..\..\FoundationPro\ThingsGateway.Foundation.Adapter.Vigor\ThingsGateway.Foundation.Adapter.Vigor.csproj" />
<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor.cs" />
<Compile Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor.cs" />
<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialDebugPage.razor" />
<Content Include="..\..\PluginProAF2021\ThingsGateway.Plugin.HZW_QTJC_01\Page\HZW_QTJC_01SerialOverTcpDebugPage.razor" Link="Pages\HZW_QTJC_01\HZW_QTJC_01SerialOverTcpDebugPage.razor" />
<ProjectReference Include="..\..\PluginProAF2021\ThingsGateway.Foundation.Adapter.HZW_QTJC_01\ThingsGateway.Foundation.Adapter.HZW_QTJC_01.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007DebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\MqttRpcNameVaueWithId.cs" Link="Pages\Mqtt\MqttRpcNameVaueWithId.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor.cs" Link="Pages\Mqtt\MqttClientDebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor.cs" Link="Pages\Mqtt\MqttClientPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\PrivateLogger.cs" Link="Pages\Mqtt\PrivateLogger.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClient.cs" Link="Pages\Mqtt\MqttRpcClient.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcClientExtensions.cs" Link="Pages\Mqtt\MqttRpcClientExtensions.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\RpcClass\MqttRpcTopicPair.cs" Link="Pages\Mqtt\MqttRpcTopicPair.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007DebugPage.razor" Link="Pages\DLT645\DLT645_2007DebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor.cs" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.DLT645\Page\DLT645_2007OverTcpDebugPage.razor" Link="Pages\DLT645\DLT645_2007OverTcpDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuDebugPage.razor" Link="Pages\Modbus\ModbusRtuDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverTcpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverTcpDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusRtuOverUdpDebugPage.razor" Link="Pages\Modbus\ModbusRtuOverUdpDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusSerialServerDebugPage.razor" Link="Pages\Modbus\ModbusSerialServerDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDebugPage.razor" Link="Pages\Modbus\ModbusTcpDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpDtuDebugPage.razor" Link="Pages\Modbus\ModbusTcpDtuDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor.cs" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusTcpServerDebugPage.razor" Link="Pages\Modbus\ModbusTcpServerDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor.cs" Link="Pages\Modbus\ModbusUdpDebugPage.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Modbus\Page\ModbusUdpDebugPage.razor" Link="Pages\Modbus\ModbusUdpDebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor.cs" Link="Pages\OPCDA\OPCDAClientDebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor.cs" Link="Pages\OPCDA\OPCDAClientPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor.cs" Link="Pages\OPCDA\OPCDAImportVariable.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientDebugPage.razor" Link="Pages\OPCDA\OPCDAClientDebugPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAClientPage.razor" Link="Pages\OPCDA\OPCDAClientPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCDA\Page\OPCDAImportVariable.razor" Link="Pages\OPCDA\OPCDAImportVariable.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor" Link="Pages\OPCUA\OPCUAClientDebugPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor" Link="Pages\OPCUA\OPCUAClientPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor" Link="Pages\OPCUA\OPCUAImportVariable.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientDebugPage.razor.cs" Link="Pages\OPCUA\OPCUAClientDebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAClientPage.razor.cs" Link="Pages\OPCUA\OPCUAClientPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.OPCUA\Page\OPCUAImportVariable.razor.cs" Link="Pages\OPCUA\OPCUAImportVariable.razor.cs" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor" Link="Pages\Siemens\S7_1200DebugPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor" Link="Pages\Siemens\S7_1500DebugPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor" Link="Pages\Siemens\S7_200DebugPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor" Link="Pages\Siemens\S7_200SMARTDebugPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor" Link="Pages\Siemens\S7_300DebugPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor" Link="Pages\Siemens\S7_400DebugPage.razor" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1200DebugPage.razor.cs" Link="Pages\Siemens\S7_1200DebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_1500DebugPage.razor.cs" Link="Pages\Siemens\S7_1500DebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200DebugPage.razor.cs" Link="Pages\Siemens\S7_200DebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_200SMARTDebugPage.razor.cs" Link="Pages\Siemens\S7_200SMARTDebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_300DebugPage.razor.cs" Link="Pages\Siemens\S7_300DebugPage.razor.cs" />
<Compile Include="..\..\Plugin\ThingsGateway.Plugin.Siemens\Page\S7_400DebugPage.razor.cs" Link="Pages\Siemens\S7_400DebugPage.razor.cs" />
</ItemGroup>
<ItemGroup >
<!--<PackageReference Include="ThingsGateway.Foundation.Adapter.DLT645" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.Modbus" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCDA" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.OPCUA" Version="*" />
<PackageReference Include="ThingsGateway.Foundation.Adapter.Siemens" Version="*" />-->
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
</ItemGroup>
<ItemGroup >
<ProjectReference Include="..\..\Web\ThingsGateway.Components\ThingsGateway.Components.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientDebugPage.razor" Link="Pages\Mqtt\MqttClientDebugPage.razor" />
<Content Include="..\..\Plugin\ThingsGateway.Plugin.Mqtt\Page\MqttClientPage.razor" Link="Pages\Mqtt\MqttClientPage.razor" />
<PackageReference Include="MQTTnet" Version="4.3.1.873" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,29 @@
@*
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
*@
@using System.Net.Http
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorComponent
@using Masa.Blazor
@using Masa.Blazor.Presets
@using ThingsGateway.Foundation.Core;
@using ThingsGateway.Components;
@using ThingsGateway.Core;
@using System.Net.Http.Json
@using System.IO;
@using System.Text.Json;
@using ThingsGateway.Foundation.Serial;
@using ThingsGateway.Foundation.Sockets;

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>ThingsGateway.Foundation.Demo</title>
<base href="/" />
<link rel="icon" href="favicon.ico" type="image/x-icon">
<link href="_content/Masa.Blazor/css/masa-blazor.min.css" rel="stylesheet" />
<link href="_content/ThingsGateway.Components/css/materialdesign/v7.1.96/css/materialdesignicons.min.css" rel="stylesheet">
<link href="_content/ThingsGateway.Components/css/material/icons.css" rel="stylesheet">
<link href="_content/ThingsGateway.Components/css/fontawesome/v6.4.0/css/all.min.css" rel="stylesheet">
<link href="_content/ThingsGateway.Components/style/custom.css" rel="stylesheet">
<link href="_content/ThingsGateway.Components/prism/prism-material-dark-for-masa.css" rel="stylesheet">
<link href="_content/ThingsGateway.Components/prism/prism-line-highlight.min.css" rel="stylesheet">
</head>
<body>
<div id="app"></div>
<div id="blazor-error-ui">
<span>
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
</span>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webview.js" autostart="true"></script>
<script src="_content/ThingsGateway.Components/prism/prism.min.js"></script>
<script src="_content/BlazorComponent/js/blazor-component.js"></script>
</body>
</html>

View File

@@ -1,7 +1,12 @@
<Project>
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>2.1.0.7</Version>
<Version>3.0.0.22</Version>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<LangVersion>latest</LangVersion>
<TargetFrameworks>net45;netstandard2.0;net6.0;net7.0</TargetFrameworks>
<Description>
ThingsGateway.Foundation是工业设备通讯类库归属于ThingsGateway边缘网关项目说明文档https://diego2098.gitee.io/thingsgateway-docs/
</Description>
<Authors>Diego</Authors>
<Product>ThingsGateway</Product>
<Copyright>© 2023-present Diego</Copyright>
@@ -11,38 +16,38 @@
<EmbedAllSources>true</EmbedAllSources>
<RepositoryType>Gitee</RepositoryType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReadmeFile>./README.md</PackageReadmeFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageIcon>icon.png</PackageIcon>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway-docs/</PackageProjectUrl>
<PackageIcon>icon.png</PackageIcon>
<PackageTags>ThingsGateway;Diego;dotNET China;Blazor;设备采集;边缘网关</PackageTags>
<PackageOutputPath>../../../nupkgs</PackageOutputPath>
<SignAssembly>True</SignAssembly>
<DelaySign>False</DelaySign>
<AssemblyOriginatorKeyFile>..\..\..\..\snks\ThingsGateway.snk</AssemblyOriginatorKeyFile>
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
<PackageOutputPath>../../nupkgs</PackageOutputPath>
<AssemblyOriginatorKeyFile>../../../snks/ThingsGateway.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup>
<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\..\..\README.md" Pack="true" PackagePath="\" />
<None Include="..\..\..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\..\icon.png">
<None Include="..\..\..\icon.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugSymbols>True</DebugSymbols>
<DebugType>Embedded</DebugType>
<EmbedAllSources>True</EmbedAllSources>
</PropertyGroup>
</Project>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,109 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Text;
using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Foundation.Adapter.DLT645;
/// <summary>
/// DLT645_2007Address
/// </summary>
public class DLT645_2007Address : DeviceAddressBase
{
/// <summary>
/// <inheritdoc/>
/// </summary>
public DLT645_2007Address()
{
}
/// <summary>
/// 数据标识
/// </summary>
public byte[] DataId { get; set; } = new byte[0];
/// <summary>
/// 反转解析
/// </summary>
public bool Reverse { get; set; } = true;
/// <summary>
/// 站号信息
/// </summary>
public byte[] Station { get; set; } = new byte[0];
/// <summary>
/// 解析地址
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public static DLT645_2007Address ParseFrom(string address)
{
DLT645_2007Address dLT645_2007Address = new();
byte[] array;
array = new byte[0];
if (address.IndexOf(';') < 0)
{
array = address.ByHexStringToBytes().Reverse().ToArray();
}
else
{
string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
for (int index = 0; index < strArray.Length; ++index)
{
if (strArray[index].ToUpper().StartsWith("S="))
{
var station = strArray[index].Substring(2);
if (station.IsNullOrEmpty()) station = string.Empty;
if (station.Length < 12)
station = station.PadLeft(12, '0');
dLT645_2007Address.Station = station.ByHexStringToBytes().Reverse().ToArray();
}
else if (strArray[index].Contains("r="))
{
dLT645_2007Address.Reverse = strArray[index].Substring(2).GetBoolValue();
}
else if (!strArray[index].Contains("="))
{
array = strArray[index].ByHexStringToBytes().Reverse().ToArray();
}
}
}
dLT645_2007Address.DataId = array;
return dLT645_2007Address;
}
/// <inheritdoc/>
public override string ToString()
{
StringBuilder stringGeter = new();
if (Station.Length > 0)
{
stringGeter.Append($"s={Station.Reverse().ToArray().ToHexString()};");
}
if (DataId.Length > 0)
{
stringGeter.Append($"{DataId.Reverse().ToArray().ToHexString()};");
}
if (!Reverse)
{
stringGeter.Append($"s={Reverse.ToString()};");
}
return stringGeter.ToString();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,175 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.Text;
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.String;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <summary>
/// Modbus协议地址
/// </summary>
public class ModbusAddress : DeviceAddressBase
{
/// <summary>
/// <inheritdoc/>
/// </summary>
public ModbusAddress()
{
}
/// <summary>
/// 读取功能码
/// </summary>
public ushort AddressStart => Address.ToUShort();
/// <summary>
/// 读取功能码
/// </summary>
public byte ReadFunction { get; set; }
/// <summary>
/// 站号信息
/// </summary>
public byte Station { get; set; }
/// <summary>
/// 写入功能码
/// </summary>
public byte WriteFunction { get; set; }
/// <summary>
/// 打包临时写入,需要读取的字节长度
/// </summary>
public int ByteLength { get; set; }
/// <summary>
/// BitIndex
/// </summary>
public int BitIndex => (int)(Address.SplitDot().LastOrDefault().ToInt());
/// <summary>
/// 读取功能码
/// </summary>
public ushort AddressEnd => (ushort)(AddressStart + (Math.Ceiling(ByteLength / 2.0) > 0 ? Math.Ceiling(ByteLength / 2.0) : 1));
/// <summary>
/// 作为Slave时需提供的SocketId用于分辨Socket客户端通常对比的是初始链接时的注册包
/// </summary>
public string SocketId { get; set; }
/// <summary>
/// 解析地址
/// </summary>
public static ModbusAddress ParseFrom(string address, byte station)
{
ModbusAddress modbusAddress = new()
{
Station = station
};
return ParseFrom(address, modbusAddress);
}
/// <summary>
/// 解析地址
/// </summary>
public static ModbusAddress ParseFrom(string address, ModbusAddress modbusAddress = null)
{
modbusAddress ??= new();
if (address.IndexOf(';') < 0)
{
Address(address);
}
else
{
string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
for (int index = 0; index < strArray.Length; ++index)
{
if (strArray[index].ToUpper().StartsWith("S="))
{
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
modbusAddress.Station = byte.Parse(strArray[index].Substring(2));
}
else if (strArray[index].ToUpper().StartsWith("W="))
{
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
modbusAddress.WriteFunction = byte.Parse(strArray[index].Substring(2));
}
else if (strArray[index].ToUpper().StartsWith("ID="))
{
modbusAddress.SocketId = strArray[index].Substring(3);
}
else if (!strArray[index].Contains("="))
{
Address(strArray[index]);
}
}
}
return modbusAddress;
void Address(string address)
{
var readF = ushort.Parse(address.Substring(0, 1));
if (readF > 4)
throw new("功能码错误");
switch (readF)
{
case 0:
modbusAddress.ReadFunction = 1;
break;
case 1:
modbusAddress.ReadFunction = 2;
break;
case 3:
modbusAddress.ReadFunction = 4;
break;
case 4:
modbusAddress.ReadFunction = 3;
break;
}
modbusAddress.Address = (double.Parse(address.Substring(1)) - 1).ToString();
}
}
/// <inheritdoc/>
public override string ToString()
{
StringBuilder stringGeter = new();
if (Station > 0)
{
stringGeter.Append($"s={Station.ToString()};");
}
if (WriteFunction > 0)
{
stringGeter.Append($"w={WriteFunction.ToString()};");
}
if (!string.IsNullOrEmpty(SocketId))
{
stringGeter.Append($"id={SocketId};");
}
stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString());
return stringGeter.ToString();
}
private string GetFunctionString(int readF)
{
return readF switch
{
1 => "0",
2 => "1",
3 => "4",
4 => "3",
_ => "4",
};
}
}

View File

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

View File

@@ -0,0 +1,191 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <summary>
/// ModbusRtu
/// </summary>
public class ModbusRtu : ReadWriteDevicesSerialSessionBase
{
/// <summary>
/// ModbusRtu
/// </summary>
/// <param name="serialSession"></param>
public ModbusRtu(SerialSession serialSession) : base(serialSession)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// Crc校验
/// </summary>
[Description("Crc校验")]
public bool Crc16CheckEnable { get; set; } = true;
/// <summary>
/// 站号
/// </summary>
[Description("站号")]
public byte Station { get; set; } = 1;
/// <inheritdoc/>
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
/// <inheritdoc/>
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
{
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override void SetDataAdapter(object socketClient = null)
{
ModbusRtuDataHandleAdapter dataHandleAdapter = new()
{
Crc16CheckEnable = Crc16CheckEnable,
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
SerialSession.SetDataHandlingAdapter(dataHandleAdapter);
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
if (FrameTime != 0)
Thread.Sleep(FrameTime);
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
await Task.Delay(FrameTime, cancellationToken);
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,185 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <inheritdoc/>
public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase
{
/// <inheritdoc/>
public ModbusRtuOverTcp(TcpClient tcpClient) : base(tcpClient)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// Crc校验
/// </summary>
[Description("Crc校验")]
public bool Crc16CheckEnable { get; set; } = true;
/// <summary>
/// 站号
/// </summary>
[Description("站号")]
public byte Station { get; set; } = 1;
/// <inheritdoc/>
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
{
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
/// <inheritdoc/>
public override void SetDataAdapter(object socketClient = null)
{
ModbusRtuDataHandleAdapter dataHandleAdapter = new()
{
Crc16CheckEnable = Crc16CheckEnable,
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
if (FrameTime != 0)
Thread.Sleep(FrameTime);
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
await Task.Delay(FrameTime, cancellationToken);
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
}

View File

@@ -0,0 +1,184 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <inheritdoc/>
public class ModbusRtuOverUdp : ReadWriteDevicesUdpSessionBase
{
/// <inheritdoc/>
public ModbusRtuOverUdp(UdpSession udpSession) : base(udpSession)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// Crc校验
/// </summary>
[Description("Crc校验")]
public bool Crc16CheckEnable { get; set; }
/// <summary>
/// 站号
/// </summary>
[Description("站号")]
public byte Station { get; set; } = 1;
/// <inheritdoc/>
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override void SetDataAdapter(object socketClient = null)
{
ModbusRtuOverUdpDataHandleAdapter dataHandleAdapter = new()
{
Crc16CheckEnable = Crc16CheckEnable,
};
UdpSession.Config.SetUdpDataHandlingAdapter(() =>
{
return dataHandleAdapter;
});
UdpSession.Setup(UdpSession.Config);
}
/// <inheritdoc/>
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
{
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
if (FrameTime != 0)
Thread.Sleep(FrameTime);
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
await Task.Delay(FrameTime, cancellationToken);
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,40 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <summary>
/// <inheritdoc/>
/// </summary>
public class ModbusSerialServerMessage : MessageBase, IMessage
{
/// <summary>
/// 当前关联的地址
/// </summary>
public ModbusAddress CurModbusAddress { get; set; }
/// <summary>
/// 当前读写的数据长度
/// </summary>
public int Length { get; set; }
/// <inheritdoc/>
public override int HeadBytesLength => -1;
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[] heads)
{
BodyLength = -1;
return true;
}
}

View File

@@ -0,0 +1,184 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <inheritdoc/>
public class ModbusTcp : ReadWriteDevicesTcpClientBase
{
/// <inheritdoc/>
public ModbusTcp(TcpClient tcpClient) : base(tcpClient)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// 检测事务标识符
/// </summary>
[Description("检测事务标识符")]
public bool IsCheckMessageId { get; set; }
/// <summary>
/// 站号
/// </summary>
[Description("站号")]
public byte Station { get; set; } = 1;
/// <inheritdoc/>
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
/// <inheritdoc/>
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
{
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
var data = SendThenReturn(commandResult, cancellationToken);
return data;
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
var data = await SendThenReturnAsync(commandResult, cancellationToken);
return data;
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override void SetDataAdapter(object socketClient = null)
{
ModbusTcpDataHandleAdapter dataHandleAdapter = new()
{
IsCheckMessageId = IsCheckMessageId,
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
};
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
if (FrameTime != 0)
Thread.Sleep(FrameTime);
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
await Task.Delay(FrameTime, cancellationToken);
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,20 +15,24 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
/// <summary>
/// <inheritdoc/>
/// </summary>
public class ModbusServerMessage : MessageBase, IMessage
public class ModbusTcpServerMessage : MessageBase, IMessage
{
/// <summary>
/// 当前关联的地址
/// </summary>
public ModbusAddress CurModbusAddress { get; set; }
/// <summary>
/// 当前读写的数据长度
/// </summary>
public int Length { get; set; }
/// <inheritdoc/>
public override int HeadBytesLength => 6;
/// <inheritdoc/>
public override bool CheckHeadBytes(byte[] head)
public override bool CheckHeadBytes(byte[] heads)
{
if (head == null || head.Length != 6) return false;
HeadBytes = head;
if (heads == null || heads.Length != 6) return false;
HeadBytes = heads;
int num = (HeadBytes[4] * 256) + HeadBytes[5];
BodyLength = num;

View File

@@ -0,0 +1,187 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <inheritdoc/>
public class ModbusUdp : ReadWriteDevicesUdpSessionBase
{
/// <inheritdoc/>
public ModbusUdp(UdpSession udpSession) : base(udpSession)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// 检测事务标识符
/// </summary>
[Description("检测事务标识符")]
public bool IsCheckMessageId { get; set; }
/// <summary>
/// 站号
/// </summary>
[Description("站号")]
public byte Station { get; set; } = 1;
/// <inheritdoc/>
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
{
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
}
/// <inheritdoc/>
public override string GetAddressDescription()
{
return base.GetAddressDescription() + Environment.NewLine + ModbusHelper.GetAddressDescription();
}
/// <inheritdoc/>
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
}
/// <inheritdoc/>
public override void SetDataAdapter(object socketClient = null)
{
ModbusUdpDataHandleAdapter dataHandleAdapter = new()
{
IsCheckMessageId = IsCheckMessageId
};
UdpSession.Config.SetUdpDataHandlingAdapter(() =>
{
return dataHandleAdapter;
});
UdpSession.Setup(UdpSession.Config);
}
/// <inheritdoc/>
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
Connect(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return SendThenReturn(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
/// <inheritdoc/>
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
{
try
{
await ConnectAsync(cancellationToken);
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
return await SendThenReturnAsync(commandResult, cancellationToken);
}
catch (Exception ex)
{
return new OperResult(ex);
}
}
private OperResult<byte[]> SendThenReturn(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
if (FrameTime != 0)
Thread.Sleep(FrameTime);
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
private async Task<OperResult<byte[]>> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken cancellationToken)
{
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
await Task.Delay(FrameTime, cancellationToken);
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
return (MessageBase)result.RequestInfo;
}
else
{
return new OperResult<byte[]>(commandResult.Message);
}
}
}

View File

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

View File

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

View File

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

View File

@@ -14,11 +14,10 @@
<inheritdoc/>
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor(System.String,System.UInt16)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.#ctor(System.String,System.Byte)">
<inheritdoc/>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressStart">
<summary>
读取功能码
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ReadFunction">
<summary>
@@ -35,16 +34,28 @@
写入功能码
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Parse(System.String,System.Int32)">
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ByteLength">
<summary>
打包临时写入,需要读取的字节长度
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.AddressEnd">
<summary>
读取功能码
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.Parse(System.String)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,System.Int32)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,System.Byte)">
<summary>
解析地址
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ParseFrom(System.String,ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress)">
<summary>
解析地址
</summary>
<param name="address"></param>
<param name="length"></param>
<returns></returns>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusAddress.ToString">
<inheritdoc/>
@@ -135,24 +146,14 @@
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.TcpClientEx)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.CacheTimeout">
<summary>
组包缓存时间/ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Crc16CheckEnable">
<summary>
Crc校验
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Station">
<summary>
站号
@@ -161,10 +162,22 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.GetAddressDescription">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SetDataAdapter">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SetDataAdapter(System.Object)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
@@ -173,13 +186,10 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(TouchSocket.Sockets.UdpSession)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Crc16CheckEnable">
@@ -187,11 +197,6 @@
Crc校验
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Station">
<summary>
站号
@@ -200,10 +205,22 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.GetAddressDescription">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SetDataAdapter">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SetDataAdapter(System.Object)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
@@ -212,9 +229,6 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdpDataHandleAdapter">
<summary>
<inheritdoc/>
@@ -239,27 +253,17 @@
ModbusRtu
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.#ctor(ThingsGateway.Foundation.Serial.SerialsSession)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.#ctor(ThingsGateway.Foundation.Serial.SerialSession)">
<summary>
ModbusRtu
</summary>
<param name="serialSession"></param>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.CacheTimeout">
<summary>
组包缓存时间/ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Crc16CheckEnable">
<summary>
Crc校验
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Station">
<summary>
站号
@@ -268,10 +272,22 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.GetAddressDescription">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Read(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.SetDataAdapter">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.SetDataAdapter(System.Object)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
@@ -347,7 +363,7 @@
接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(TouchSocket.Sockets.TcpService)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.#ctor(ThingsGateway.Foundation.Sockets.TcpService)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.MulStation">
@@ -360,13 +376,28 @@
默认站点
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Dispose">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.GetAddressDescription">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Read(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.SetDataAdapter(TouchSocket.Sockets.SocketClient)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.SetDataAdapter(System.Object)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
@@ -375,10 +406,7 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Dispose(System.Boolean)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Received(TouchSocket.Sockets.SocketClient,TouchSocket.Core.IRequestInfo)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusServer.Received(ThingsGateway.Foundation.Sockets.SocketClient,ThingsGateway.Foundation.Core.IRequestInfo)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerDataHandleAdapter">
@@ -414,6 +442,11 @@
当前关联的地址
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.Length">
<summary>
当前读写的数据长度
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusServerMessage.HeadBytesLength">
<inheritdoc/>
</member>
@@ -423,19 +456,9 @@
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.TcpClientEx)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.#ctor(ThingsGateway.Foundation.Sockets.TcpClient)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.CacheTimeout">
<summary>
组包缓存时间/ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.IsCheckMessageId">
<summary>
检测事务标识符
@@ -449,10 +472,22 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.GetAddressDescription">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SetDataAdapter">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SetDataAdapter(System.Object)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
@@ -461,9 +496,6 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter">
<summary>
ModbusTcpDataHandleAdapter
@@ -502,14 +534,9 @@
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(TouchSocket.Sockets.UdpSession)">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.#ctor(ThingsGateway.Foundation.Sockets.UdpSession)">
<inheritdoc/>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.FrameTime">
<summary>
帧前时间ms
</summary>
</member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.IsCheckMessageId">
<summary>
检测事务标识符
@@ -520,13 +547,25 @@
站号
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.LoadSourceRead``2(System.Collections.Generic.List{``1},System.Int32)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.GetAddressDescription">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Read(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.ReadAsync(System.String,System.Int32,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SetDataAdapter">
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SetDataAdapter(System.Object)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Byte[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.Write(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Byte[],System.Threading.CancellationToken)">
@@ -535,9 +574,6 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.WriteAsync(System.String,System.Boolean[],System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp.SendThenReturnAsync(ThingsGateway.Foundation.OperResult{System.Byte[]},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter">
<summary>
<inheritdoc/>
@@ -557,5 +593,19 @@
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.ModbusUdpDataHandleAdapter.UnpackResponse(System.Byte[],System.Byte[])">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.PackHelper">
<summary>
PackHelper
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Adapter.Modbus.PackHelper.ModbusLoadSourceRead``2(ThingsGateway.Foundation.Core.IReadWrite,System.Collections.Generic.List{``1},System.Int32)">
<summary>
打包变量,添加到<see href="deviceVariableSourceReads"></see>
</summary>
<param name="device"></param>
<param name="deviceVariables"></param>
<param name="MaxPack">最大打包长度</param>
<returns></returns>
</member>
</members>
</doc>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,9 +10,6 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Linq;
namespace ThingsGateway.Foundation.Adapter.OPCDA;
internal static class CollectionExtensions
@@ -23,11 +20,30 @@ internal static class CollectionExtensions
/// <typeparam name="T"></typeparam>
/// <param name="this"></param>
/// <param name="where"></param>
public static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
internal static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
{
foreach (var obj in @this.Where(where).ToList())
{
@this.Remove(obj);
}
}
/// <summary>
/// 将项目列表分解为特定大小的块
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="chunksize"></param>
/// <returns></returns>
internal static List<List<T>> ChunkTrivialBetter<T>(this IEnumerable<T> source, int chunksize)
{
var pos = 0;
List<List<T>> n = new();
while (source.Skip(pos).Any())
{
n.Add(source.Skip(pos).Take(chunksize).ToList());
pos += chunksize;
}
return n;
}
}

View File

@@ -0,0 +1,123 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
//------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页http://rrqm_home.gitee.io/touchsocket/
// 交流QQ群234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
using System.Collections.Concurrent;
namespace ThingsGateway.Foundation.Adapter.OPCDA;
/// <summary>
/// DictionaryExtension
/// </summary>
internal static class DictionaryExtension
{
#region
/// <summary>
/// 移除满足条件的项目。
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="pairs"></param>
/// <param name="func"></param>
/// <returns></returns>
internal static int RemoveWhen<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> pairs, Func<KeyValuePair<TKey, TValue>, bool> func)
{
var list = new List<TKey>();
foreach (var item in pairs)
{
if (func?.Invoke(item) == true)
{
list.Add(item.Key);
}
}
var count = 0;
foreach (var item in list)
{
if (pairs.TryRemove(item, out _))
{
count++;
}
}
return count;
}
#if NET45_OR_GREATER || NETSTANDARD2_0_OR_GREATER
/// <summary>
/// 尝试添加
/// </summary>
/// <typeparam name="Tkey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="dictionary"></param>
/// <param name="tkey"></param>
/// <param name="value"></param>
/// <returns></returns>
internal static bool TryAdd<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
{
if (dictionary.ContainsKey(tkey))
{
return false;
}
dictionary.Add(tkey, value);
return true;
}
#endif
/// <summary>
/// 尝试添加
/// </summary>
/// <typeparam name="Tkey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="dictionary"></param>
/// <param name="tkey"></param>
/// <param name="value"></param>
/// <returns></returns>
internal static void AddOrUpdate<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
{
if (dictionary.ContainsKey(tkey))
{
dictionary[tkey] = value;
}
else
{
dictionary.Add(tkey, value);
}
}
/// <summary>
/// 获取值。如果键不存在,则返回默认值。
/// </summary>
/// <typeparam name="Tkey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="dictionary"></param>
/// <param name="tkey"></param>
/// <returns></returns>
internal static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey)
{
return dictionary.TryGetValue(tkey, out var value) ? value : default;
}
#endregion
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
#region copyright
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
@@ -70,6 +70,11 @@ public class OPCNode
/// </summary>
[Description("安全策略")]
public bool IsUseSecurity { get; set; } = false;
/// <summary>
/// 加载服务端数据类型
/// </summary>
[Description("加载服务端数据类型")]
public bool LoadType { get; set; } = true;
/// <inheritdoc/>
public override string ToString()
{

View File

@@ -1,4 +1,5 @@
#region copyright
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
@@ -8,6 +9,7 @@
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Newtonsoft.Json.Linq;
@@ -17,29 +19,23 @@ using Opc.Ua.Client;
using Opc.Ua.Client.ComplexTypes;
using Opc.Ua.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ThingsGateway.Foundation.Extension;
//修改自https://github.com/dathlin/OpcUaHelper 与OPC基金会net库
namespace ThingsGateway.Foundation.Adapter.OPCUA;
/// <summary>
/// 订阅委托
/// </summary>
/// <param name="value"></param>
public delegate void DataChangedEventHandler((VariableNode variableNode, DataValue dataValue, JToken jToken) value);
/// <summary>
/// OPCUAClient
/// </summary>
public class OPCUAClient : DisposableObject
public class OPCUAClient : IDisposable
{
#region
/// <summary>
/// 当前配置
/// </summary>
@@ -59,28 +55,31 @@ public class OPCUAClient : DisposableObject
/// 当前的变量名称/OPC变量节点
/// </summary>
private readonly Dictionary<string, VariableNode> _variableDicts = new();
private readonly EasyLock checkLock = new();
private readonly object checkLock = new();
/// <summary>
/// 当前的订阅组,组名称/组
/// </summary>
private readonly Dictionary<string, Subscription> dic_subscriptions = new();
private readonly ILog Log;
private readonly ApplicationInstance m_application = new();
private readonly ApplicationConfiguration m_configuration;
/// <summary>
/// SessionReconnectHandler
/// </summary>
public SessionReconnectHandler ReConnectHandler => m_reConnectHandler;
private SessionReconnectHandler m_reConnectHandler;
private EventHandler m_ReconnectComplete;
private EventHandler m_ReconnectStarting;
private EventHandler<KeepAliveEventArgs> m_KeepAliveComplete;
private EventHandler<bool> m_ConnectComplete;
private EventHandler<OpcUaStatusEventArgs> m_OpcStatusChange;
private ISession m_session;
/// <summary>
/// 默认的构造函数实例化一个新的OPC UA类
/// </summary>
public OPCUAClient(ILog log)
public OPCUAClient()
{
Log = log;
var certificateValidator = new CertificateValidator();
certificateValidator.CertificateValidation += CertificateValidation;
@@ -99,7 +98,6 @@ public class OPCUAClient : DisposableObject
MaxMessageQueueSize = 1000000,
MaxNotificationQueueSize = 1000000,
MaxPublishRequestCount = 10000000,
},
SecurityConfiguration = new SecurityConfiguration
@@ -142,8 +140,6 @@ public class OPCUAClient : DisposableObject
StoreType = CertificateStoreType.Directory,
StorePath = AppContext.BaseDirectory + @"OPCUAClientCertificate\pki\trustedUser",
}
},
TransportQuotas = new TransportQuotas
@@ -169,8 +165,6 @@ public class OPCUAClient : DisposableObject
m_configuration.Validate(ApplicationType.Client);
m_application.ApplicationConfiguration = m_configuration;
}
/// <summary>
@@ -193,6 +187,56 @@ public class OPCUAClient : DisposableObject
/// </summary>
public string OPCUAName { get; set; } = "ThingsGateway";
/// <summary>
/// SessionReconnectHandler
/// </summary>
public SessionReconnectHandler ReConnectHandler => m_reConnectHandler;
/// <summary>
/// Raised when a good keep alive from the server arrives.
/// </summary>
public event EventHandler<KeepAliveEventArgs> KeepAliveComplete
{
add { m_KeepAliveComplete += value; }
remove { m_KeepAliveComplete -= value; }
}
/// <summary>
/// Raised when a reconnect operation starts.
/// </summary>
public event EventHandler ReconnectStarting
{
add { m_ReconnectStarting += value; }
remove { m_ReconnectStarting -= value; }
}
/// <summary>
/// Raised when a reconnect operation completes.
/// </summary>
public event EventHandler ReconnectComplete
{
add { m_ReconnectComplete += value; }
remove { m_ReconnectComplete -= value; }
}
/// <summary>
/// Raised after successfully connecting to or disconnecing from a server.
/// </summary>
public event EventHandler<bool> ConnectComplete
{
add { m_ConnectComplete += value; }
remove { m_ConnectComplete -= value; }
}
/// <summary>
/// Raised after the client status change
/// </summary>
public event EventHandler<OpcUaStatusEventArgs> OpcStatusChange
{
add { m_OpcStatusChange += value; }
remove { m_OpcStatusChange -= value; }
}
/// <summary>
/// 当前活动会话。
/// </summary>
@@ -205,7 +249,7 @@ public class OPCUAClient : DisposableObject
/// <summary>
/// 新增订阅需要指定订阅组名称订阅的tag名数组
/// </summary>
public async Task AddSubscriptionAsync(string subscriptionName, string[] items)
public async Task AddSubscriptionAsync(string subscriptionName, string[] items, bool loadType = true)
{
Subscription m_subscription = new(m_session.DefaultSubscription)
{
@@ -218,14 +262,14 @@ public class OPCUAClient : DisposableObject
DisplayName = subscriptionName
};
List<MonitoredItem> monitoredItems = new();
var variableNodes = loadType ? await ReadNodesAsync(items) : null;
for (int i = 0; i < items.Length; i++)
{
try
{
var variableNode = await ReadNodeAsync(items[i], false);
var item = new MonitoredItem
{
StartNodeId = variableNode.NodeId,
StartNodeId = loadType ? variableNodes[i].NodeId : items[i],
AttributeId = Attributes.Value,
DisplayName = items[i],
Filter = OPCNode.DeadBand == 0 ? null : new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.Absolute, Trigger = DataChangeTrigger.StatusValue },
@@ -236,7 +280,7 @@ public class OPCUAClient : DisposableObject
}
catch (Exception ex)
{
Log.Error($"初始化{items[i]}变量订阅失败" + ex.Message);
UpdateStatus(3, DateTime.Now, $"初始化{items[i]}变量订阅失败,错误原因:{ex}");
}
}
m_subscription.AddItems(monitoredItems);
@@ -249,10 +293,12 @@ public class OPCUAClient : DisposableObject
}
m_subscription.ApplyChanges();
var iserror = m_subscription.MonitoredItems.Any(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode));
if (iserror)
var isError = m_subscription.MonitoredItems.Any(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode));
if (isError)
{
Log.Error("创建以下变量订阅失败" + m_subscription.MonitoredItems.Where(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode)).Select(a => a.StartNodeId.ToString() + "" + a.Status.Error.ToString()).ToJsonString());
UpdateStatus(3, DateTime.Now, $"创建以下变量订阅失败:{Environment.NewLine}{m_subscription.MonitoredItems.Where(
a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode))
.Select(a => $"{a.StartNodeId.ToString()}{a.Status.Error.ToString()}").ToJsonString()}");
}
lock (dic_subscriptions)
@@ -262,7 +308,7 @@ public class OPCUAClient : DisposableObject
// remove
dic_subscriptions[subscriptionName].Delete(true);
m_session.RemoveSubscription(dic_subscriptions[subscriptionName]);
dic_subscriptions[subscriptionName].SafeDispose();
try { dic_subscriptions[subscriptionName].Dispose(); } catch { }
dic_subscriptions[subscriptionName] = m_subscription;
}
else
@@ -283,7 +329,7 @@ public class OPCUAClient : DisposableObject
{
item.Value.Delete(true);
m_session.RemoveSubscription(item.Value);
item.Value.SafeDispose();
try { item.Value.Dispose(); } catch { }
}
dic_subscriptions.Clear();
}
@@ -302,42 +348,44 @@ public class OPCUAClient : DisposableObject
// remove
dic_subscriptions[subscriptionName].Delete(true);
m_session.RemoveSubscription(dic_subscriptions[subscriptionName]);
dic_subscriptions[subscriptionName].SafeDispose();
try { dic_subscriptions[subscriptionName].Dispose(); } catch { }
dic_subscriptions.RemoveWhere(a => a.Key == subscriptionName);
}
}
}
private async void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs)
private void Callback(MonitoredItem monitoreditem, MonitoredItemNotificationEventArgs monitoredItemNotificationEventArgs)
{
try
{
var variableNode = await ReadNodeAsync(monitoreditem.StartNodeId.ToString(), false);
foreach (var value in monitoreditem.DequeueValues())
if (m_session != null)
{
if (value.Value != null)
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false);
foreach (var value in monitoreditem.DequeueValues())
{
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
if (data == null && value.Value != null)
if (value.Value != null)
{
Log.Warning($"{monitoreditem.StartNodeId}转换出错原始值String为{value.Value}");
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
if (data == null && value.Value != null)
{
UpdateStatus(3, DateTime.Now, $"{monitoreditem.StartNodeId}转换出错原始值String为{value.Value}");
var data1 = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
}
DataChangedHandler?.Invoke((variableNode, value, data));
}
else
{
var data = JValue.CreateNull();
DataChangedHandler?.Invoke((variableNode, value, data));
}
DataChangedHandler?.Invoke((variableNode, value, data));
}
else
{
var data = JValue.CreateNull();
DataChangedHandler?.Invoke((variableNode, value, data));
}
}
}
catch (Exception ex)
{
Log.Warning($"{monitoreditem.StartNodeId}订阅事件出错:{ex.Message}");
UpdateStatus(3, DateTime.Now, $"{monitoreditem.StartNodeId}订阅处理错误,错误原因:" + ex);
}
}
#endregion
@@ -417,9 +465,9 @@ public class OPCUAClient : DisposableObject
/// <param name="end">结束时间</param>
/// <param name="count">读取的个数</param>
/// <param name="containBound">是否包含边界</param>
/// <param name="token">token</param>
/// <param name="cancellationToken">cancellationToken</param>
/// <returns>读取的数据列表</returns>
public async Task<List<DataValue>> ReadHistoryRawDataValues(string tag, DateTime start, DateTime end, uint count = 1, bool containBound = false, CancellationToken token = default)
public async Task<List<DataValue>> ReadHistoryRawDataValues(string tag, DateTime start, DateTime end, uint count = 1, bool containBound = false, CancellationToken cancellationToken = default)
{
HistoryReadValueId m_nodeToContinue = new()
{
@@ -446,7 +494,7 @@ public class OPCUAClient : DisposableObject
TimestampsToReturn.Both,
false,
nodesToRead,
token);
cancellationToken);
var results = result.Results;
var diagnosticInfos = result.DiagnosticInfos;
ClientBase.ValidateResponse(results, nodesToRead);
@@ -463,8 +511,10 @@ public class OPCUAClient : DisposableObject
#endregion
#region
private ComplexTypeSystem typeSystem;
/// <summary>
/// 连接到服务器
/// </summary>
@@ -482,11 +532,10 @@ public class OPCUAClient : DisposableObject
// disconnect any existing session.
if (m_session != null)
{
Log.Debug("断开连接");
m_session = null;
}
}
private ComplexTypeSystem typeSystem;
/// <summary>
/// Creates a new session.
/// </summary>
@@ -504,7 +553,7 @@ public class OPCUAClient : DisposableObject
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(m_configuration);
ConfiguredEndpoint endpoint = new(null, endpointDescription, endpointConfiguration);
UserIdentity userIdentity;
if (!OPCNode.UserName.IsNullOrEmpty())
if (!string.IsNullOrEmpty(OPCNode.UserName))
{
userIdentity = new UserIdentity(OPCNode.UserName, OPCNode.Password);
}
@@ -515,33 +564,38 @@ public class OPCUAClient : DisposableObject
//创建本地证书
await m_application.CheckApplicationInstanceCertificate(true, 0, 1200);
m_session = await Opc.Ua.Client.Session.Create(
m_configuration,
endpoint,
false,
OPCNode.CheckDomain,
(string.IsNullOrEmpty(OPCUAName)) ? m_configuration.ApplicationName : OPCUAName,
60000,
userIdentity,
Array.Empty<string>());
m_configuration,
endpoint,
false,
OPCNode.CheckDomain,
(string.IsNullOrEmpty(OPCUAName)) ? m_configuration.ApplicationName : OPCUAName,
60000,
userIdentity,
Array.Empty<string>());
typeSystem = new ComplexTypeSystem(m_session);
Log.Debug("连接成功");
m_session.KeepAliveInterval = OPCNode.KeepAliveInterval == 0 ? 60000 : OPCNode.KeepAliveInterval;
m_session.KeepAlive += new KeepAliveEventHandler(Session_KeepAlive);
// raise an event.
DoConnectComplete(true);
UpdateStatus(2, DateTime.UtcNow, "Connected");
//如果是订阅模式,连接时添加订阅组
if (OPCNode.ActiveSubscribe)
await AddSubscriptionAsync(Guid.NewGuid().ToString(), Variables.ToArray());
await AddSubscriptionAsync(Guid.NewGuid().ToString(), Variables.ToArray(), OPCNode.LoadType);
return m_session;
}
private void PrivateDisconnect()
{
// stop any reconnect operation.
bool state = m_session?.Connected == true;
if (m_reConnectHandler != null)
{
m_reConnectHandler.SafeDispose();
try { m_reConnectHandler.Dispose(); } catch { }
m_reConnectHandler = null;
}
if (m_session != null)
@@ -550,29 +604,33 @@ public class OPCUAClient : DisposableObject
m_session.Close(10000);
}
if (state)
{
UpdateStatus(2, DateTime.UtcNow, "Disconnected");
DoConnectComplete(false);
}
}
#endregion
#region /
/// <summary>
/// 从服务器读取值
/// </summary>
public async Task<List<(string, DataValue, JToken)>> ReadJTokenValueAsync(string[] tags, CancellationToken token = default)
public async Task<List<(string, DataValue, JToken)>> ReadJTokenValueAsync(string[] tags, CancellationToken cancellationToken = default)
{
var result = await ReadJTokenValueAsync(tags.Select(a => new NodeId(a)).ToArray(), token);
var result = await ReadJTokenValueAsync(tags.Select(a => new NodeId(a)).ToArray(), cancellationToken);
return result;
}
/// <summary>
/// 异步写opc标签
/// </summary>
public async Task<Dictionary<string, OperResult>> WriteNodeAsync(Dictionary<string, JToken> writeInfoLists, CancellationToken token = default)
public async Task<Dictionary<string, Tuple<bool, string>>> WriteNodeAsync(Dictionary<string, JToken> writeInfoLists, CancellationToken cancellationToken = default)
{
Dictionary<string, OperResult> results = new();
Dictionary<string, Tuple<bool, string>> results = new();
try
{
WriteValueCollection valuesToWrite = new();
@@ -583,7 +641,7 @@ public class OPCUAClient : DisposableObject
NodeId = new NodeId(item.Key),
AttributeId = Attributes.Value,
};
var variableNode = await ReadNodeAsync(item.Key, false, token);
var variableNode = await ReadNodeAsync(item.Key, false, cancellationToken);
var dataValue = JsonUtils.Decode(
m_session.MessageContext,
variableNode.DataType,
@@ -596,23 +654,21 @@ public class OPCUAClient : DisposableObject
valuesToWrite.Add(valueToWrite);
}
var result = await m_session.WriteAsync(
requestHeader: null,
nodesToWrite: valuesToWrite, token);
nodesToWrite: valuesToWrite, cancellationToken);
ClientBase.ValidateResponse(result.Results, valuesToWrite);
ClientBase.ValidateDiagnosticInfos(result.DiagnosticInfos, valuesToWrite);
var keys = writeInfoLists.Keys.ToList();
for (int i = 0; i < keys.Count; i++)
{
if (!StatusCode.IsGood(result.Results[i]))
results.Add(keys[i], new(result.Results[i].ToString()));
results.Add(keys[i], Tuple.Create(true, result.Results[i].ToString()));
else
{
results.Add(keys[i], OperResult.CreateSuccessResult());
results.Add(keys[i], Tuple.Create(false, "成功"));
}
}
@@ -623,17 +679,16 @@ public class OPCUAClient : DisposableObject
var keys = writeInfoLists.Keys.ToList();
foreach (var item in keys)
{
results.Add(item, new(ex));
results.Add(item, Tuple.Create(true, ex.Message));
}
return results;
}
}
/// <summary>
/// 从服务器读取值
/// </summary>
private async Task<List<(string, DataValue, JToken)>> ReadJTokenValueAsync(NodeId[] nodeIds, CancellationToken token = default)
private async Task<List<(string, DataValue, JToken)>> ReadJTokenValueAsync(NodeId[] nodeIds, CancellationToken cancellationToken = default)
{
if (m_session == null)
{
@@ -655,7 +710,7 @@ public class OPCUAClient : DisposableObject
0,
TimestampsToReturn.Neither,
nodesToRead,
token);
cancellationToken);
var results = result.Results;
var diagnosticInfos = result.DiagnosticInfos;
ClientBase.ValidateResponse(results, nodesToRead);
@@ -663,7 +718,7 @@ public class OPCUAClient : DisposableObject
List<(string, DataValue, JToken)> jTokens = new();
for (int i = 0; i < results.Count; i++)
{
var variableNode = await ReadNodeAsync(nodeIds[i].ToString(), false, token);
var variableNode = await ReadNodeAsync(nodeIds[i].ToString(), false, cancellationToken);
var type = TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable);
var jToken = JsonUtils.Encode(m_session.MessageContext, type, results[i].Value);
jTokens.Add((variableNode.NodeId.ToString(), results[i], jToken));
@@ -674,7 +729,7 @@ public class OPCUAClient : DisposableObject
/// <summary>
/// 从服务器或缓存读取节点
/// </summary>
private async Task<VariableNode> ReadNodeAsync(string nodeIdStr, bool isOnlyServer = true, CancellationToken token = default)
private async Task<VariableNode> ReadNodeAsync(string nodeIdStr, bool isOnlyServer = true, CancellationToken cancellationToken = default)
{
if (!isOnlyServer)
{
@@ -683,22 +738,68 @@ public class OPCUAClient : DisposableObject
return value;
}
}
NodeId nodeToRead = new(nodeIdStr);
var node = (VariableNode)await m_session.ReadNodeAsync(nodeToRead, token);
await typeSystem.LoadType(node.DataType, true, true);
var node = (VariableNode)await m_session.ReadNodeAsync(nodeToRead, NodeClass.Variable, false, cancellationToken);
if (OPCNode.LoadType)
await typeSystem.LoadType(node.DataType);
_variableDicts.AddOrUpdate(nodeIdStr, node);
return node;
}
#endregion
/// <summary>
/// 从服务器或缓存读取节点
/// </summary>
private VariableNode ReadNode(string nodeIdStr, bool isOnlyServer = true)
{
if (!isOnlyServer)
{
if (_variableDicts.TryGetValue(nodeIdStr, out var value))
{
return value;
}
}
NodeId nodeToRead = new(nodeIdStr);
var node = (VariableNode)m_session.ReadNode(nodeToRead, NodeClass.Variable, false);
_variableDicts.AddOrUpdate(nodeIdStr, node);
return node;
}
/// <summary>
/// 从服务器读取节点
/// </summary>
private async Task<List<Node>> ReadNodesAsync(string[] nodeIdStrs, CancellationToken cancellationToken = default)
{
List<NodeId> nodeIds = new List<NodeId>();
foreach (var item in nodeIdStrs)
{
NodeId nodeToRead = new(item);
nodeIds.Add(nodeToRead);
}
(IList<Node>, IList<ServiceResult>) nodes = await m_session.ReadNodesAsync(nodeIds, NodeClass.Variable, false, cancellationToken);
for (int i = 0; i < nodes.Item1.Count; i++)
{
if (StatusCode.IsGood(nodes.Item2[i].StatusCode))
{
var node = ((VariableNode)nodes.Item1[i]);
await typeSystem.LoadType(node.DataType);
_variableDicts.AddOrUpdate(nodeIdStrs[i], node);
}
else
{
UpdateStatus(3, DateTime.Now, $"获取服务器节点信息失败{nodes.Item2[i]}");
}
}
return nodes.Item1.ToList();
}
#endregion
#region
/// <summary>
/// 读取一个节点的所有属性
/// </summary>
public async Task<OperResult<List<OPCNodeAttribute>>> ReadNoteAttributeAsync(string tag, uint attributesId, CancellationToken token = default)
public async Task<List<OPCNodeAttribute>> ReadNoteAttributeAsync(string tag, uint attributesId, CancellationToken cancellationToken = default)
{
BrowseDescriptionCollection nodesToBrowse = new();
ReadValueIdCollection nodesToRead = new();
@@ -721,16 +822,16 @@ public class OPCUAClient : DisposableObject
};
nodesToBrowse.Add(nodeToBrowse);
var result1 = await ReadNoteAttributeAsync(nodesToBrowse, nodesToRead, token);
var result2 = result1.Copy<List<OPCNodeAttribute>>();
result2.Content = result1.Content?.Values?.FirstOrDefault()?.ToList();
var result1 = await ReadNoteAttributeAsync(nodesToBrowse, nodesToRead, cancellationToken);
var result2 = result1.Values.FirstOrDefault();
return result2;
}
/// <summary>
/// 读取节点的所有属性
/// </summary>
public async Task<OperResult<Dictionary<string, List<OPCNodeAttribute>>>> ReadNoteAttributeAsync(List<string> tags, CancellationToken token)
public async Task<Dictionary<string, List<OPCNodeAttribute>>> ReadNoteAttributeAsync(List<string> tags, CancellationToken cancellationToken)
{
BrowseDescriptionCollection nodesToBrowse = new();
ReadValueIdCollection nodesToRead = new();
@@ -757,10 +858,9 @@ public class OPCUAClient : DisposableObject
ResultMask = (uint)BrowseResultMask.All
};
nodesToBrowse.Add(nodeToBrowse);
}
return await ReadNoteAttributeAsync(nodesToBrowse, nodesToRead, token);
return await ReadNoteAttributeAsync(nodesToBrowse, nodesToRead, cancellationToken);
}
/// <summary>
@@ -920,17 +1020,13 @@ public class OPCUAClient : DisposableObject
return nodeAttribute.ToArray();
}
#endregion
/// <inheritdoc/>
protected override void Dispose(bool disposing)
public void Dispose()
{
Disconnect();
checkLock.SafeDispose();
base.Dispose(disposing);
}
#region
@@ -945,16 +1041,15 @@ public class OPCUAClient : DisposableObject
throw new Exception(string.Format("验证证书失败,错误代码:{0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo));
}
private async Task<OperResult<Dictionary<string, List<OPCNodeAttribute>>>> ReadNoteAttributeAsync(BrowseDescriptionCollection nodesToBrowse, ReadValueIdCollection nodesToRead, CancellationToken token)
private async Task<Dictionary<string, List<OPCNodeAttribute>>> ReadNoteAttributeAsync(BrowseDescriptionCollection nodesToBrowse, ReadValueIdCollection nodesToRead, CancellationToken cancellationToken)
{
int startOfProperties = nodesToRead.Count;
ReferenceDescriptionCollection references = await FormUtils.BrowseAsync(m_session, nodesToBrowse, false, token);
ReferenceDescriptionCollection references = await FormUtils.BrowseAsync(m_session, nodesToBrowse, false, cancellationToken);
if (references == null)
{
return new OperResult<Dictionary<string, List<OPCNodeAttribute>>>("浏览失败");
throw new("浏览失败");
}
for (int ii = 0; ii < references.Count; ii++)
@@ -976,7 +1071,7 @@ public class OPCUAClient : DisposableObject
null,
0,
TimestampsToReturn.Neither,
nodesToRead, token);
nodesToRead, cancellationToken);
ClientBase.ValidateResponse(result.Results, nodesToRead);
ClientBase.ValidateDiagnosticInfos(result.DiagnosticInfos, nodesToRead);
@@ -1022,7 +1117,6 @@ public class OPCUAClient : DisposableObject
}
}
if (nodeAttributes.ContainsKey(nodeToRead.NodeId.ToString()))
{
nodeAttributes[nodeToRead.NodeId.ToString()].Add(item);
@@ -1032,8 +1126,7 @@ public class OPCUAClient : DisposableObject
nodeAttributes.Add(nodeToRead.NodeId.ToString(), new() { item });
}
}
return OperResult.CreateSuccessResult(nodeAttributes);
return nodeAttributes;
}
/// <summary>
@@ -1041,22 +1134,46 @@ public class OPCUAClient : DisposableObject
/// </summary>
private void Server_ReconnectComplete(object sender, EventArgs e)
{
if (!Object.ReferenceEquals(sender, m_reConnectHandler))
try
{
return;
if (!Object.ReferenceEquals(sender, m_reConnectHandler))
{
return;
}
m_session = m_reConnectHandler.Session;
m_reConnectHandler.Dispose();
m_reConnectHandler = null;
// raise any additional notifications.
m_ReconnectComplete?.Invoke(this, e);
}
catch (Exception)
{
throw;
}
}
m_session = m_reConnectHandler.Session;
m_reConnectHandler.SafeDispose();
m_reConnectHandler = null;
/// <summary>
/// Report the client status
/// </summary>
/// <param name="logLevel">Whether the status represents an error. </param>
/// <param name="time">The time associated with the status.</param>
/// <param name="status">The status message.</param>
/// <param name="args">Arguments used to format the status message.</param>
private void UpdateStatus(int logLevel, DateTime time, string status, params object[] args)
{
m_OpcStatusChange?.Invoke(this, new OpcUaStatusEventArgs()
{
LogLevel = logLevel,
Time = time.ToLocalTime(),
Text = String.Format(status, args),
});
}
private void Session_KeepAlive(ISession session, KeepAliveEventArgs e)
{
if (checkLock.IsWaitting) { return; }
checkLock.Wait();
try
lock (checkLock)
{
if (!Object.ReferenceEquals(session, m_session))
{
@@ -1065,26 +1182,39 @@ public class OPCUAClient : DisposableObject
if (ServiceResult.IsBad(e.Status))
{
Log.Warning($"心跳检测错误:{e.Status}");
if (m_session.KeepAliveInterval <= 0)
{
UpdateStatus(3, e.CurrentTime, "Communication Error ({0})", e.Status);
return;
}
UpdateStatus(3, e.CurrentTime, "Reconnecting in {0}s", m_session.KeepAliveInterval / 1000);
if (m_reConnectHandler == null)
{
m_ReconnectStarting?.Invoke(this, e);
m_reConnectHandler = new SessionReconnectHandler();
m_reConnectHandler.BeginReconnect(m_session, m_session.KeepAliveInterval, Server_ReconnectComplete);
}
return;
}
Log.Trace($"心跳检测正常 [{session.Endpoint.EndpointUrl}]");
}
finally
{
checkLock.Release();
// update status.
UpdateStatus(0, e.CurrentTime, "Session_KeepAlive Connected [{0}]", session.Endpoint.EndpointUrl);
// raise any additional notifications.
m_KeepAliveComplete?.Invoke(this, e);
}
}
/// <summary>
/// Raises the connect complete event on the main GUI thread.
/// </summary>
private void DoConnectComplete(bool state)
{
m_ConnectComplete?.Invoke(this, state);
}
#endregion
}

View File

@@ -0,0 +1,69 @@
#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 Opc.Ua;
namespace ThingsGateway.Foundation.Adapter.OPCUA;
/// <summary>
/// OPC UA的状态更新消息
/// </summary>
public class OpcUaStatusEventArgs
{
/// <summary>
/// 日志等级,<br></br>
/// 更为详细的步骤型日志输出 Trace = 0,<br></br>
/// 调试信息日志Debug = 1,<br></br>
/// 消息类日志输出 Info = 2,<br></br>
/// 警告类日志输出 Warning = 3,<br></br>
/// 错误类日志输出 Error = 4,<br></br>
/// 不可控中断类日输出Critical = 5,
/// </summary>
public int LogLevel { get; set; }
/// <summary>
/// 时间
/// </summary>
public DateTime Time { get; set; }
/// <summary>
/// 文本
/// </summary>
public string Text { get; set; }
}
/// <summary>
/// 读取属性过程中用于描述的
/// </summary>
public class OPCNodeAttribute
{
/// <summary>
/// 属性的名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 操作结果状态描述
/// </summary>
public StatusCode StatusCode { get; set; }
/// <summary>
/// 属性的类型描述
/// </summary>
public string Type { get; set; }
/// <summary>
/// 属性的值,如果读取错误,返回文本描述
/// </summary>
public object Value { get; set; }
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.4.372.56" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.4.372.56" />
</ItemGroup>
</Project>

View File

@@ -10,10 +10,6 @@
//------------------------------------------------------------------------------
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ThingsGateway.Foundation.Adapter.OPCUA;
internal static class CollectionExtensions
@@ -24,7 +20,7 @@ internal static class CollectionExtensions
/// <typeparam name="T"></typeparam>
/// <param name="this"></param>
/// <param name="where"></param>
public static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
internal static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
{
foreach (var obj in @this.Where(where).ToList())
{
@@ -39,7 +35,7 @@ internal static class CollectionExtensions
/// <param name="source"></param>
/// <param name="selector"></param>
/// <returns></returns>
public static Task<TResult[]> SelectAsync<T, TResult>(this IEnumerable<T> source, Func<T, Task<TResult>> selector)
internal static Task<TResult[]> SelectAsync<T, TResult>(this IEnumerable<T> source, Func<T, Task<TResult>> selector)
{
return Task.WhenAll(source.Select(selector));
}

View File

@@ -0,0 +1,123 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
//------------------------------------------------------------------------------
// 此代码版权除特别声明或在XREF结尾的命名空间的代码归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议若本仓库没有设置则按MIT开源协议授权
// CSDN博客https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频https://space.bilibili.com/94253567
// Gitee源代码仓库https://gitee.com/RRQM_Home
// Github源代码仓库https://github.com/RRQM
// API首页http://rrqm_home.gitee.io/touchsocket/
// 交流QQ群234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
using System.Collections.Concurrent;
namespace ThingsGateway.Foundation.Adapter.OPCUA;
/// <summary>
/// DictionaryExtension
/// </summary>
internal static class DictionaryExtension
{
#region
/// <summary>
/// 移除满足条件的项目。
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="pairs"></param>
/// <param name="func"></param>
/// <returns></returns>
internal static int RemoveWhen<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> pairs, Func<KeyValuePair<TKey, TValue>, bool> func)
{
var list = new List<TKey>();
foreach (var item in pairs)
{
if (func?.Invoke(item) == true)
{
list.Add(item.Key);
}
}
var count = 0;
foreach (var item in list)
{
if (pairs.TryRemove(item, out _))
{
count++;
}
}
return count;
}
#if NET45_OR_GREATER || NETSTANDARD2_0_OR_GREATER
/// <summary>
/// 尝试添加
/// </summary>
/// <typeparam name="Tkey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="dictionary"></param>
/// <param name="tkey"></param>
/// <param name="value"></param>
/// <returns></returns>
internal static bool TryAdd<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
{
if (dictionary.ContainsKey(tkey))
{
return false;
}
dictionary.Add(tkey, value);
return true;
}
#endif
/// <summary>
/// 尝试添加
/// </summary>
/// <typeparam name="Tkey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="dictionary"></param>
/// <param name="tkey"></param>
/// <param name="value"></param>
/// <returns></returns>
internal static void AddOrUpdate<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey, TValue value)
{
if (dictionary.ContainsKey(tkey))
{
dictionary[tkey] = value;
}
else
{
dictionary.Add(tkey, value);
}
}
/// <summary>
/// 获取值。如果键不存在,则返回默认值。
/// </summary>
/// <typeparam name="Tkey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="dictionary"></param>
/// <param name="tkey"></param>
/// <returns></returns>
internal static TValue GetValue<Tkey, TValue>(this Dictionary<Tkey, TValue> dictionary, Tkey tkey)
{
return dictionary.TryGetValue(tkey, out var value) ? value : default;
}
#endregion
}

View File

@@ -13,11 +13,7 @@
using Opc.Ua;
using Opc.Ua.Client;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ThingsGateway.Foundation.Adapter.OPCUA;
/// <summary>
@@ -152,35 +148,16 @@ public class FormUtils
}
}
/// <summary>
/// Create the continuation point collection from the browse result
/// collection for the BrowseNext service.
/// </summary>
/// <param name="browseResultCollection">The browse result collection to use.</param>
/// <returns>The collection of continuation points for the BrowseNext service.</returns>
private static ByteStringCollection PrepareBrowseNext(BrowseResultCollection browseResultCollection)
{
var continuationPoints = new ByteStringCollection();
foreach (var browseResult in browseResultCollection)
{
if (browseResult.ContinuationPoint != null)
{
continuationPoints.Add(browseResult.ContinuationPoint);
}
}
return continuationPoints;
}
/// <summary>
/// 浏览地址空间
/// </summary>
/// <param name="session"></param>
/// <param name="nodesToBrowse"></param>
/// <param name="throwOnError"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="ServiceResultException"></exception>
public static async Task<ReferenceDescriptionCollection> BrowseAsync(ISession session, BrowseDescriptionCollection nodesToBrowse, bool throwOnError, CancellationToken token = default)
public static async Task<ReferenceDescriptionCollection> BrowseAsync(ISession session, BrowseDescriptionCollection nodesToBrowse, bool throwOnError, CancellationToken cancellationToken = default)
{
try
{
@@ -195,7 +172,7 @@ public class FormUtils
null,
null,
0,
nodesToBrowse, token);
nodesToBrowse, cancellationToken);
var results = result.Results;
var diagnosticInfos = result.DiagnosticInfos;
ClientBase.ValidateResponse(results, nodesToBrowse);
@@ -241,7 +218,7 @@ public class FormUtils
null,
false,
continuationPoints
, token);
, cancellationToken);
results = nextResult.Results;
diagnosticInfos = nextResult.DiagnosticInfos;
ClientBase.ValidateResponse(results, continuationPoints);
@@ -297,10 +274,10 @@ public class FormUtils
/// <param name="session"></param>
/// <param name="nodeToBrowse"></param>
/// <param name="throwOnError"></param>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="ServiceResultException"></exception>
public static async Task<ReferenceDescriptionCollection> BrowseAsync(ISession session, BrowseDescription nodeToBrowse, bool throwOnError, CancellationToken token = default)
public static async Task<ReferenceDescriptionCollection> BrowseAsync(ISession session, BrowseDescription nodeToBrowse, bool throwOnError, CancellationToken cancellationToken = default)
{
try
{
@@ -319,7 +296,7 @@ public class FormUtils
null,
null,
0,
nodesToBrowse, token);
nodesToBrowse, cancellationToken);
var results = result.Results;
var diagnosticInfos = result.DiagnosticInfos;
ClientBase.ValidateResponse(results, nodesToBrowse);
@@ -354,7 +331,7 @@ public class FormUtils
var nextResult = await session.BrowseNextAsync(
null,
false,
continuationPoints, token);
continuationPoints, cancellationToken);
results = nextResult.Results;
diagnosticInfos = nextResult.DiagnosticInfos;
ClientBase.ValidateResponse(results, continuationPoints);
@@ -827,7 +804,7 @@ public class FormUtils
public static async Task<List<NodeId>> TranslateBrowsePaths(
ISession session,
NodeId startNodeId,
NamespaceTable namespacesUris, CancellationToken token,
NamespaceTable namespacesUris, CancellationToken cancellationToken,
params string[] relativePaths)
{
// build the list of browse paths to follow by parsing the relative paths.
@@ -858,7 +835,7 @@ public class FormUtils
var result = await session.TranslateBrowsePathsToNodeIdsAsync(
null,
browsePaths,
token);
cancellationToken);
BrowsePathResultCollection results = result.Results;
DiagnosticInfoCollection diagnosticInfos = result.DiagnosticInfos;
// ensure that the server returned valid results.
@@ -1129,4 +1106,23 @@ public class FormUtils
_ => valueRank.ToString(),
};
}
/// <summary>
/// Create the continuation point collection from the browse result
/// collection for the BrowseNext service.
/// </summary>
/// <param name="browseResultCollection">The browse result collection to use.</param>
/// <returns>The collection of continuation points for the BrowseNext service.</returns>
private static ByteStringCollection PrepareBrowseNext(BrowseResultCollection browseResultCollection)
{
var continuationPoints = new ByteStringCollection();
foreach (var browseResult in browseResultCollection)
{
if (browseResult.ContinuationPoint != null)
{
continuationPoints.Add(browseResult.ContinuationPoint);
}
}
return continuationPoints;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ using Opc.Ua;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
namespace ThingsGateway.Foundation.Adapter.OPCUA;
@@ -123,7 +123,7 @@ public static class JsonUtils
case BuiltInType.UInt64: { return decoder.ReadUInt64(fieldName); }
case BuiltInType.Float: { return decoder.ReadFloat(fieldName); }
case BuiltInType.Double: { return decoder.ReadDouble(fieldName); }
case BuiltInType.String: { return decoder.ReadField(fieldName, out var token) ? token?.ToString() : null; }
case BuiltInType.String: { return decoder.ReadField(fieldName, out var cancellationToken) ? cancellationToken?.ToString() : null; }
case BuiltInType.DateTime: { return decoder.ReadDateTime(fieldName); }
case BuiltInType.Guid: { return decoder.ReadGuid(fieldName); }
case BuiltInType.ByteString: { return decoder.ReadByteString(fieldName); }
@@ -163,7 +163,7 @@ public static class JsonUtils
/// <param name="type"></param>
/// <param name="value"></param>
/// <returns></returns>
public static JToken Encode(
internal static JToken Encode(
IServiceMessageContext Context,
BuiltInType type,
object value
@@ -172,7 +172,7 @@ public static class JsonUtils
//对于IntegerInt64Number等会转化为string JValue
using var encoder = CreateEncoder(Context, null, false);
Encode(encoder, type, "Value", value, null);
Encode(encoder, type, "Value", value);
var textbuffer = encoder.CloseAndReturnText();
using var stringReader = new StringReader(textbuffer);
using var jsonReader = new JsonTextReader(stringReader);
@@ -180,7 +180,28 @@ public static class JsonUtils
return jToken["Value"];
}
private static void Encode(JsonEncoder encoder, BuiltInType builtInType, string fieldName, object value, ByteBlock byteBlock)
/// <summary>
/// CreateEncoder
/// </summary>
/// <returns></returns>
private static OPCUAJsonEncoder CreateEncoder(
IServiceMessageContext context,
Stream stream,
bool useReversibleEncoding = false,
bool topLevelIsArray = false,
bool includeDefaultValues = true,
bool includeDefaultNumbers = true
)
{
return new OPCUAJsonEncoder(context, useReversibleEncoding, topLevelIsArray, stream)
{
IncludeDefaultValues = includeDefaultValues,
IncludeDefaultNumberValues = includeDefaultNumbers
};
}
private static void Encode(OPCUAJsonEncoder encoder, BuiltInType builtInType, string fieldName, object value)
{
bool isArray = (value?.GetType().IsArray ?? false) && (builtInType != BuiltInType.ByteString);
bool isCollection = (value is IList) && (builtInType != BuiltInType.ByteString);
@@ -337,26 +358,6 @@ public static class JsonUtils
encoder.WriteArray(fieldName, c, c.Rank, builtInType);
}
}
/// <summary>
/// CreateEncoder
/// </summary>
/// <returns></returns>
private static JsonEncoder CreateEncoder(
IServiceMessageContext context,
Stream stream,
bool useReversibleEncoding = false,
bool topLevelIsArray = false,
bool includeDefaultValues = true,
bool includeDefaultNumbers = true
)
{
return new JsonEncoder(context, useReversibleEncoding, topLevelIsArray, stream)
{
IncludeDefaultValues = includeDefaultValues,
IncludeDefaultNumberValues = includeDefaultNumbers
};
}
#endregion
#region json
@@ -366,7 +367,7 @@ public static class JsonUtils
/// </summary>
/// <param name="jToken"></param>
/// <returns></returns>
public static int CalculateActualValueRank(this JToken jToken)
internal static int CalculateActualValueRank(this JToken jToken)
{
if (jToken.Type != JTokenType.Array)
return -1;
@@ -381,12 +382,6 @@ public static class JsonUtils
}
return numDimensions;
}
private static JTokenType GetElementsType(this JToken[] jTokens)
{
if (!jTokens.ElementsHasSameType())
throw new Exception("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;
@@ -394,6 +389,13 @@ public static class JsonUtils
.Select(x => (x.Type == JTokenType.Integer) ? JTokenType.Float : x.Type)
.All(t => t == checkType);
}
private static JTokenType GetElementsType(this JToken[] jTokens)
{
if (!jTokens.ElementsHasSameType())
throw new Exception("The array sent must have the same type of element in each dimension");
return jTokens.First().Type;
}
private static Type GetSystemType(JTokenType jsonType)
{
return jsonType switch
@@ -421,4 +423,112 @@ public static class JsonUtils
}
#endregion
#region Json序列化和反序列化
/// <summary>
/// 从字符串到json
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
internal static object FromJsonString(this string json, Type type)
{
return Newtonsoft.Json.JsonConvert.DeserializeObject(json, type);
}
/// <summary>
/// 从字符串到json
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json"></param>
/// <returns></returns>
internal static T FromJsonString<T>(this string json)
{
return (T)FromJsonString(json, typeof(T));
}
/// <summary>
/// Json反序列化
/// </summary>
/// <typeparam name="T">反序列化类型</typeparam>
/// <param name="datas">数据</param>
/// <returns></returns>
internal static T JsonDeserializeFromBytes<T>(byte[] datas)
{
return (T)JsonDeserializeFromBytes(datas, typeof(T));
}
/// <summary>
/// Json反序列化
/// </summary>
/// <param name="datas"></param>
/// <param name="type"></param>
/// <returns></returns>
internal static object JsonDeserializeFromBytes(byte[] datas, Type type)
{
return FromJsonString(Encoding.UTF8.GetString(datas), type);
}
/// <summary>
/// Json反序列化
/// </summary>
/// <typeparam name="T">反序列化类型</typeparam>
/// <param name="path">文件路径</param>
/// <returns></returns>
internal static T JsonDeserializeFromFile<T>(string path)
{
return JsonDeserializeFromString<T>(File.ReadAllText(path));
}
/// <summary>
/// Json反序列化
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="json">json字符串</param>
/// <returns></returns>
internal static T JsonDeserializeFromString<T>(string json)
{
return FromJsonString<T>(json);
}
/// <summary>
/// Json序列化数据对象
/// </summary>
/// <param name="obj">数据对象</param>
/// <returns></returns>
internal static byte[] JsonSerializeToBytes(object obj)
{
return Encoding.UTF8.GetBytes(ToJsonString(obj));
}
/// <summary>
/// Json序列化至文件
/// </summary>
/// <param name="obj"></param>
/// <param name="path"></param>
internal static void JsonSerializeToFile(object obj, string path)
{
using (var fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
var date = JsonSerializeToBytes(obj);
fileStream.Write(date, 0, date.Length);
fileStream.Close();
}
}
/// <summary>
/// 转换为Json
/// </summary>
/// <param name="item"></param>
/// <param name="isIndented"></param>
/// <returns></returns>
internal static string ToJsonString(this object item, bool isIndented = false)
{
if (isIndented)
return Newtonsoft.Json.JsonConvert.SerializeObject(item, Newtonsoft.Json.Formatting.Indented);
else
return Newtonsoft.Json.JsonConvert.SerializeObject(item);
}
#endregion Json序列化和反序列化
}

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