首次上传Blazor版本

This commit is contained in:
2248356998 qq.com
2023-03-04 18:41:11 +08:00
parent 22f06362b5
commit 63443d9ee2
577 changed files with 106590 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

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

368
.gitignore vendored Normal file
View File

@@ -0,0 +1,368 @@
## 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
/src/ThingsGateway.Web.Server/Default.db
/src/PluginPro*/
/src/TestResults*/
/src/ThingsGateway.Web.Server/ThingsGateway.db

BIN
Image/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
Image/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
Image/3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

BIN
Image/4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
Image/5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
Image/6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
Image/7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

BIN
Image/8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
Image/9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
Image/pay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

61
README.md Normal file
View File

@@ -0,0 +1,61 @@
<div align="center"><h1 align="center">ThingsGateway</a></h1></div>
<div align="center"><h3 align="center">边缘采集网关</h3></div>
#### 介绍
基于[ThingsBlazor](https://gitee.com/diego2098/ThingsBlazor)权限管理框架开发的跨平台边缘采集网关,支持南北端插件式开发,
动态更新插件,
并拥有较完善的北端Rpc权限管理。
#### 功能亮点
- Blazor Server架构开发部署更简单
- 支持时序数据库存储
- 实时/历史报警(Sql转储),支持布尔/高低限值
- Modbus 、OPC 采集插件
- Modbus Server、Mqtt Server(Client)、OPC UAServer插件,支持Rpc写入支持WebApi
- 采集/上传配置完全支持Excel导入导出
- 插件式驱动,方便驱动二次开发,并支持动态更新
#### 效果图
<table>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/1.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/2.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/3.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/4.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/5.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/6.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/7.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/8.png"/></td>
<td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/9.png"/></td>
</tr>
</table>
#### 文档
请查看Gitee Pages站点 https://diego2098.gitee.io/thingsgateway/
#### 补充说明
* OPCUAServer插件需联系OPC基金会进行授权
#### 支持作者
如果对您有帮助请点击右上角⭐Star关注或扫码捐赠感谢支持开源
<table>
<tr>
<td><img height=150 src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/pay.png"/></td>
</tr>
</table>
#### 联系作者
* QQ群605534569
* 邮箱2248356998@qq.com

20
handbook/.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
# Dependencies
node_modules
# Production
# build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

15
handbook/README.md Normal file
View File

@@ -0,0 +1,15 @@
文档基于 [https://www.docusaurus.io/](https://www.docusaurus.io/) 构建。
### 本地运行
```bash
npm install
npm run start
```
### 发布部署
```bash
npm run build
```

3
handbook/babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};

View File

@@ -0,0 +1,45 @@
---
id: 说明
title: 说明
sidebar_label: 01、说明
slug: /
---
import Tag from "@site/src/components/Tag.js";
## 使用前必要阅读
ThingsGateway 由作者Diego及其他贡献者开发所有版权归作者Diego所有程序集源代码在遵循 Apache License 2.0 的开源协议以及**附加协议**下,可**免费**供其他开发者二次开发或(商业)使用。
# 附加协议
#### 个人使用须知:
- 不得将程序集用作违法犯罪活动。
- 不得将程序集单独包装售卖,申请专利等。
- 不得擦除程序集所有有关作者的信息。
**以上内容必须全部符合,个人使用授权才成立。**
#### 二次开发须知:
- 不得将程序集用作违法犯罪活动。
- 不得将程序集单独包装售卖,申请专利等。
- 不得擦除程序集所有有关作者的信息。
- 二次开发完成后的作品必须附带源作品所有作者信息包括但不限于作者名、Gitee、Github 地址等。
- **完成后**的作品(仅 ThingsGateway 部分必须将发布时最新源代码提交一份给本作者QQ 邮箱2248356998@qq.com。
**以上内容必须全部符合,二次开发授权才成立。**
#### 盈利性(商业)用途使用须知:
- 不得将程序集用作违法犯罪活动。
- 不得将程序集单独包装售卖,申请专利等。
- **不得擦除程序集所有有关作者的信息,并必须于用户可见界面(如关于)中提名。**
**以上内容必须全部符合,使用授权才成立。**
# 免责申明
**在使用 ThingsGateway 之前请进行缜密的测试。在使用期间,由本程序集造成或间接造成的所有损失,均自己承担,与本程序集无关。**

View File

@@ -0,0 +1,48 @@
---
id: upgrade
title: 历史更新
sidebar_label: 02、历史更新
slug: /upgrade
---
import useBaseUrl from "@docusaurus/useBaseUrl";
import Tag from "@site/src/components/Tag.js";
:::tip `ThingsGateway` 框架升级/发版规则
**升级前重点关注可能造成【破坏性】的标签类型**<Tag>修复</Tag>、<Tag>调整</Tag>、<Tag>移除</Tag>、<Tag>升级</Tag>
版本号规则:`主版本号.次版本号.修订版本号`
- 只要【确认】为框架 `bug`,则当天修复,当天发版,修订版本号 `加 1`。
- 如果 `.csproj` 文件有变更,则当天发版,修订版本号 `加 1`。
- 其余情况,每年发布一个 `主版本`。
:::
## 开发计划
- &nbsp;<Tag>新增</Tag> 添加设备是否共用通道的选项当IP&端口一致时或者串口号一致时生效)
- &nbsp;<Tag>新增</Tag> 添加OPCDAClient采集插件
- &nbsp;<Tag>新增</Tag> 添加OPCUAClient采集插件
- &nbsp;<Tag>新增</Tag> 开发串口基础框架
##
## v1.0.0(已发布)
- &nbsp;<Tag>新增</Tag> 初始发布
##
<!--
-->

View File

@@ -0,0 +1,19 @@
---
id: 支持开源项目
title: 支持开源项目
sidebar_label: 03、支持开源项目
---
## 赞助ThingsGateway项目
> 您的支持就是我不懈努力的动力。
<br></br>如果对您有帮助请⭐Star关注或扫码捐赠感谢支持开源
#### 赞助途径
![](/img/pay.png)
#### 赞助名单(以下排名只按照赞助时间顺序)
1. 绢10¥

View File

@@ -0,0 +1,19 @@
---
id: quickstart
title: 快速启动
sidebar_label: 04、快速启动
---
### (一)、下载源码
[最新版本](https://gitee.com/diego2098/ThingsGateway)
### (二)、配置并启动系统
1、 vs2022打开解决方案设置ThingsGateway.Web.Server为启动项目
2、 直接debug运行
- 默认运行地址http://localhost:7100;
![](/img/login.png)

View File

@@ -0,0 +1,70 @@
---
id: collectdevice
title: 采集设备配置说明
sidebar_label: 5.1、采集设备
---
### (一)添加/修改采集设备
![](/img/addcollectdevice1.png)
<details>
<summary>设备基本属性</summary>
<div>
#### 名称
当前采集设备名称,全局唯一(采集设备)
#### 描述
当前采集设备描述
#### 设备驱动名称
当前采集设备选择的采集驱动插件名称,通过级联选择器选择对应驱动名称设置
#### 设备使能
启动/停用采集设备
#### 输出日志
启动/停用日志输出
</div>
</details>
<details>
<summary>设备附加属性</summary>
<div>
**对于不同的驱动,网关会将对应的插件配置规则保存在附加属性内**
**对应的属性描述可以查看输入框占位文本或者浮动Tips尝试理解或者查看第六节找到对应的驱动说明**
</div>
</details>
### (二)导入导出采集设备
#### 导出模板
![](/img/collectdevice2.png)
![](/img/collectdevice3.png)
可以看到采集设备的excel模板填入规则参考第一小节
#### 导入
![](/img/collectdevice4.png)
弹窗选择导入的excel文件后点击下一步进行校验
![](/img/collectdevice5.png)
根据红色字体提示,修改对应错误列字段
全部正常时可进行下一步
![](/img/collectdevice6.png)
点击上传,等待导入完成,完成后自动退出弹窗,否则提示错误

View File

@@ -0,0 +1,28 @@
---
id: variable
title: 变量配置说明
sidebar_label: 5.2、变量
---
### (二)变量配置
**选择设备**
**变量地址/执行参数**
不同驱动的变量地址规则查看对应文档
**表达式变换**
支持多种运算符,具体可查看 [ExpressionEvaluator WiKi](https://gitee.com/diego2098/ThingsGateway/releases/latest)
### (三)重启设备
**单个设备重启如图,或可按 [全部重启] 按钮,重启全部设备**
### (四)查看状态

View File

@@ -0,0 +1,43 @@
---
id: uploaddevice
title: 上传设备配置说明
sidebar_label: 5.3、上传设备
---
**上传设备配置与上传设备大致相同**
### (一)添加/修改上传设备
![](/img/adduploaddevice1.png)
<details>
<summary>设备基本属性</summary>
<div>
#### 名称
当前上传设备名称,全局唯一(上传设备)
#### 描述
当前上传设备描述
#### 设备驱动名称
当前上传设备选择的上传驱动插件名称,通过级联选择器选择对应驱动名称设置
#### 设备使能
启动/停用上传设备
#### 输出日志
启动/停用日志输出
</div>
</details>
<details>
<summary>设备附加属性</summary>
<div>
**对于不同的驱动,网关会将对应的插件配置规则保存在附加属性内**
**对应的属性描述可以查看输入框占位文本或者浮动Tips尝试理解或者查看第六节找到对应的驱动说明**
</div>
</details>

View File

@@ -0,0 +1,6 @@
---
id: plugincore
title: 插件配置说明
sidebar_label: 5.4、插件管理
---

View File

@@ -0,0 +1,6 @@
---
id: otherconfig
title: 其他配置说明
sidebar_label: 5.5、其他配置
---

View File

@@ -0,0 +1,39 @@
---
id: modbus
title: Modbus驱动
sidebar_label: 6.1、Modbus驱动
---
### (一)变量地址说明
- 1、基本地址
| 地址 | 功能码 | 说明 |
| ---------------| --------------|--------------------------|
| 4xxxxx | 03 | 读取03功能码 |
| 3xxxxx | 04 | 读取04功能码 |
| 1xxxxx | 02 | 读取02功能码 |
| 0xxxxx | 01 | 读取01功能码 |
- 2、站号(可选)
当需要指定站号地址时可使用,举例:
| 地址 | 说明 |
| ---------------| --------------------------|
| s=2;10001 | 读取02功能码 ,设备地址为2 |
| s=11;40001 | 读取03功能码 ,设备地址为11 |
- 3、写入功能码(可选)
当需要指定16/15功能码时可使用举例
| 地址 | 说明 |
| ---------------| --------------------------|
| w=15;10001 | 读取02功能码 ,写入15功能码 |
| w=16;40001 | 读取03功能码 ,写入16功能码 |
### (二)变量其他方法说明
- 无

View File

@@ -0,0 +1,39 @@
---
id: MqttServer
title: MqttServer
sidebar_label: 7.1、MqttServer
---
### (一)变量地址说明
- 1、基本地址
| 地址 | 功能码 | 说明 |
| ---------------| --------------|--------------------------|
| 4xxxxx | 03 | 读取03功能码 |
| 3xxxxx | 04 | 读取04功能码 |
| 1xxxxx | 02 | 读取02功能码 |
| 0xxxxx | 01 | 读取01功能码 |
- 2、站号(可选)
当需要指定站号地址时可使用,举例:
| 地址 | 说明 |
| ---------------| --------------------------|
| s=2;10001 | 读取02功能码 ,设备地址为2 |
| s=11;40001 | 读取03功能码 ,设备地址为11 |
- 3、写入功能码(可选)
当需要指定16/15功能码时可使用举例
| 地址 | 说明 |
| ---------------| --------------------------|
| w=15;10001 | 读取02功能码 ,写入15功能码 |
| w=16;40001 | 读取03功能码 ,写入16功能码 |
### (二)变量其他方法说明
- 无

View File

@@ -0,0 +1,39 @@
---
id: MqttClient
title: MqttClient
sidebar_label: 7.2、MqttClient
---
### (一)变量地址说明
- 1、基本地址
| 地址 | 功能码 | 说明 |
| ---------------| --------------|--------------------------|
| 4xxxxx | 03 | 读取03功能码 |
| 3xxxxx | 04 | 读取04功能码 |
| 1xxxxx | 02 | 读取02功能码 |
| 0xxxxx | 01 | 读取01功能码 |
- 2、站号(可选)
当需要指定站号地址时可使用,举例:
| 地址 | 说明 |
| ---------------| --------------------------|
| s=2;10001 | 读取02功能码 ,设备地址为2 |
| s=11;40001 | 读取03功能码 ,设备地址为11 |
- 3、写入功能码(可选)
当需要指定16/15功能码时可使用举例
| 地址 | 说明 |
| ---------------| --------------------------|
| w=15;10001 | 读取02功能码 ,写入15功能码 |
| w=16;40001 | 读取03功能码 ,写入16功能码 |
### (二)变量其他方法说明
- 无

View File

@@ -0,0 +1,39 @@
---
id: ModbusServer
title: ModbusServer
sidebar_label: 7.3、ModbusServer
---
### (一)变量地址说明
- 1、基本地址
| 地址 | 功能码 | 说明 |
| ---------------| --------------|--------------------------|
| 4xxxxx | 03 | 读取03功能码 |
| 3xxxxx | 04 | 读取04功能码 |
| 1xxxxx | 02 | 读取02功能码 |
| 0xxxxx | 01 | 读取01功能码 |
- 2、站号(可选)
当需要指定站号地址时可使用,举例:
| 地址 | 说明 |
| ---------------| --------------------------|
| s=2;10001 | 读取02功能码 ,设备地址为2 |
| s=11;40001 | 读取03功能码 ,设备地址为11 |
- 3、写入功能码(可选)
当需要指定16/15功能码时可使用举例
| 地址 | 说明 |
| ---------------| --------------------------|
| w=15;10001 | 读取02功能码 ,写入15功能码 |
| w=16;40001 | 读取03功能码 ,写入16功能码 |
### (二)变量其他方法说明
- 无

View File

@@ -0,0 +1,131 @@
module.exports = {
title: "ThingsGateway",
tagline: "ThingsGateway物联网关",
url: "https://diego2098.gitee.io",
baseUrl: "/thingsgateway",
onBrokenLinks: "throw",
onBrokenMarkdownLinks: "warn",
favicon: "img/favicon.ico",
projectName: "ThingsGateway",
scripts: [],
themeConfig: {
zoom: {
selector:
".markdown :not(em) > img,.markdown > img, article img[loading]",
background: {
light: "rgb(255, 255, 255)",
dark: "rgb(50, 50, 50)",
},
config: {},
},
docs: {
sidebar: {
hideable: true,
autoCollapseCategories: true,
},
},
prism: {
additionalLanguages: ["powershell", "csharp", "sql"],
},
navbar: {
title: "ThingsGateway",
logo: {
alt: "ThingsGateway Logo",
src: "img/thingsgatewaylogo.png",
},
hideOnScroll: true,
items: [
{
to: "docs",
activeBasePath: "docs",
label: "文档",
position: "left",
},
{
to: "/docs/upgrade",
activeBasePath: "docs",
label: "更新日志",
position: "left",
},
{
label: "源码",
position: "right",
href: "https://gitee.com/diego2098/ThingsGateway",
},
],
},
footer: {
style: "dark",
links: [
{
title: "文档",
items: [
{
label: "手册",
to: "docs",
},
],
},
{
title: "社区",
items: [
{
label: "讨论",
href: "https://gitee.com/diego2098/ThingsGateway/issues",
},
{
label: "看板",
href: "https://gitee.com/diego2098/ThingsGateway/board",
},
],
},
{
title: "更多",
items: [
{
label: "仓库",
href: "https://gitee.com/diego2098/ThingsGateway",
},
],
},
],
copyright: `Copyright © 2020-${new Date().getFullYear()} Diego.`,
},
},
presets: [
[
"@docusaurus/preset-classic",
{
docs: {
sidebarPath: require.resolve("./sidebars.js"),
editUrl: "https://gitee.com/diego2098/ThingsGateway/tree/master/handbook/",
showLastUpdateTime: true,
showLastUpdateAuthor: true,
sidebarCollapsible: true,
sidebarCollapsed: true,
// sidebarCollapsible: true,
},
blog: {
showReadingTime: true,
editUrl: "https://gitee.com/diego2098/ThingsGateway/tree/master/handbook/",
},
theme: {
customCss: require.resolve("./src/css/custom.css"),
},
},
],
],
plugins: [require.resolve("docusaurus-plugin-image-zoom")],
themes: [
[
"@easyops-cn/docusaurus-search-local",
{
hashed: true,
language: ["en", "zh"],
highlightSearchTermsOnTargetPage: true,
explicitSearchResultPath: true,
},
],
],
};

8
handbook/iconfont.json Normal file
View File

@@ -0,0 +1,8 @@
{
"symbol_url": "//at.alicdn.com/t/c/font_3276321_js2bwtaq9jc.js",
"use_typescript": false,
"save_dir": "./src/components/iconfonts",
"trim_icon_prefix": "icon",
"unit": "px",
"default_icon_size": 18
}

13179
handbook/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

50
handbook/package.json Normal file
View File

@@ -0,0 +1,50 @@
{
"name": "thingsgateway",
"version": "^1.2.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "^2.2.0",
"@docusaurus/preset-classic": "^2.2.0",
"@easyops-cn/docusaurus-search-local": "^0.33.5",
"@mdx-js/react": "^1.6.22",
"@svgr/webpack": "^6.5.1",
"animate.css": "^4.1.1",
"clsx": "^1.2.1",
"docusaurus-plugin-image-zoom": "^0.1.1",
"file-loader": "^6.2.0",
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"url-loader": "^4.1.1"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@docusaurus/module-type-aliases": "^2.2.0",
"react-iconfont-cli": "^2.0.2"
},
"engines": {
"node": ">=16.14"
}
}

33
handbook/sidebars.js Normal file
View File

@@ -0,0 +1,33 @@
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
// @ts-check
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
const sidebars = {
// By default, Docusaurus generates a sidebar from the docs folder structure
tutorialSidebar: [{ type: "autogenerated", dirName: "." }],
// But you can create a sidebar manually
/*
tutorialSidebar: [
'intro',
'hello',
{
type: 'category',
label: 'Tutorial',
items: ['tutorial-basics/create-a-document'],
},
],
*/
};
module.exports = sidebars;

View File

@@ -0,0 +1,10 @@
import React from "react";
export default function Humiliation({ name, links = [] }) {
const _urls = links.map((url, i) => <div key={i} ><a href={url} target="_blank">{url}</a></div>)
return <div style={{ marginBottom: 20 }}>
<div style={{ fontWeight: 'bold' }}>{name}</div>
<div>{_urls}</div>
</div>
}

View File

@@ -0,0 +1,64 @@
import React from "react";
import IconFont from "./iconfonts";
import classes from "./Tag.module.css";
export default function (props) {
const { children } = props;
const operates = {
最新版: {
icon: "xinzeng",
bgColor: "#39b54a",
},
新增: {
icon: "xinzeng",
bgColor: "#39b54a",
},
修复: {
icon: "bug",
bgColor: "#9c26b0",
},
文档: {
icon: "wendang",
bgColor: "rgb(79, 147, 255)",
},
更新: {
icon: "gengxin",
bgColor: "#0081ff",
},
调整: {
icon: "tiaozheng",
bgColor: "#333",
},
升级: {
icon: "shengji",
bgColor: "#e03997",
},
移除: {
icon: "shanchu",
bgColor: "#666",
},
答疑: {
icon: "dayi",
bgColor: "#bbb",
},
优化: {
icon: "youhua",
bgColor: "#38e550",
},
};
return (
<label
className={classes.label}
title={children}
style={{ backgroundColor: operates[children].bgColor }}
>
<IconFont
name={operates[children].icon}
color="white"
size={14}
className={classes.icon}
/>{" "}
{children}
</label>
);
}

View File

@@ -0,0 +1,17 @@
.label {
display: inline-flex;
align-items: center;
color: #fff;
padding: 4px 6px;
font-size: 12px;
color: #fff;
border-radius: 3px;
line-height: normal;
margin-left: -3px;
vertical-align: middle;
margin-right: 6px;
}
.icon {
margin-right: 4px;
}

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconBug: FunctionComponent<Props>;
export default IconBug;

View File

@@ -0,0 +1,31 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconBug = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M940 512H792V412c76.8 0 139-62.2 139-139 0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8 0 34.8-28.2 63-63 63H232c-34.8 0-63-28.2-63-63 0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8 0 76.8 62.2 139 139 139v100H84c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h148v96c0 6.5 0.2 13 0.7 19.3C164.1 728.6 116 796.7 116 876c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8 0-44.2 23.9-82.9 59.6-103.7 6 17.2 13.6 33.6 22.7 49 24.3 41.5 59 76.2 100.5 100.5S460.5 960 512 960s99.8-13.9 141.3-38.2c41.5-24.3 76.2-59 100.5-100.5 9.1-15.5 16.7-31.9 22.7-49C812.1 793.1 836 831.8 836 876c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8 0-79.3-48.1-147.4-116.7-176.7 0.4-6.4 0.7-12.8 0.7-19.3v-96h148c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM716 680c0 36.8-9.7 72-27.8 102.9-17.7 30.3-43 55.6-73.3 73.3-20.1 11.8-42 20-64.9 24.3V484c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v396.5c-22.9-4.3-44.8-12.5-64.9-24.3-30.3-17.7-55.6-43-73.3-73.3C317.7 752 308 716.8 308 680V412h408v268z"
fill={getIconColor(color, 0, '#333333')}
/>
<path
d="M304 280h56c4.4 0 8-3.6 8-8 0-28.3 5.9-53.2 17.1-73.5 10.6-19.4 26-34.8 45.4-45.4C450.9 142 475.7 136 504 136h16c28.3 0 53.2 5.9 73.5 17.1 19.4 10.6 34.8 26 45.4 45.4C650 218.9 656 243.7 656 272c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8 0-40-8.8-76.7-25.9-108.1-17.2-31.5-42.5-56.8-74-74C596.7 72.8 560 64 520 64h-16c-40 0-76.7 8.8-108.1 25.9-31.5 17.2-56.8 42.5-74 74C304.8 195.3 296 232 296 272c0 4.4 3.6 8 8 8z"
fill={getIconColor(color, 1, '#333333')}
/>
</svg>
);
};
IconBug.defaultProps = {
size: 18,
};
export default IconBug;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconDayi: FunctionComponent<Props>;
export default IconDayi;

View File

@@ -0,0 +1,31 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconDayi = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M143.872 768a51.2 51.2 0 0 1-15.36-2.56 51.2 51.2 0 0 1-35.328-51.2V283.136a148.992 148.992 0 0 1 141.824-153.6h450.56a148.992 148.992 0 0 1 141.824 153.6V512a148.992 148.992 0 0 1-141.824 153.6H244.224l-60.928 80.896a51.2 51.2 0 0 1-39.424 21.504zM235.008 180.224a97.792 97.792 0 0 0-90.624 102.4v430.592L218.624 614.4h466.944a97.792 97.792 0 0 0 90.624-102.4V283.136a97.792 97.792 0 0 0-90.624-102.4z"
fill={getIconColor(color, 0, '#333333')}
/>
<path
d="M880.128 875.52a51.2 51.2 0 0 1-39.424-20.48l-60.928-80.896h-243.2a25.6 25.6 0 0 1 0-51.2h268.8l76.288 102.4v-295.936a25.6 25.6 0 0 1 25.6-25.6 25.6 25.6 0 0 1 25.6 25.6v293.888a51.2 51.2 0 0 1-51.2 51.2z"
fill={getIconColor(color, 1, '#333333')}
/>
</svg>
);
};
IconDayi.defaultProps = {
size: 18,
};
export default IconDayi;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconDown: FunctionComponent<Props>;
export default IconDown;

View File

@@ -0,0 +1,27 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconDown = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M942.4615936 284.62787926c-14.30911886-14.12709945-37.31996786-14.05468217-51.48229632 0.21920654L517.97142983 661.27810333 139.75544149 286.45003606c-14.30911886-14.16232846-37.31996786-14.05468217-51.51948344 0.21920654-14.16232846 14.30911886-14.05468217 37.35519687 0.21920654 51.51948345l401.99014627 398.34974663c0.61847666 0.61847666 1.41897273 0.76526706 2.03940637 1.34655658 0.14483342 0.14483342 0.18201941 0.32685283 0.32685283 0.47364324 7.09877874 7.02636259 16.38375538 10.55911595 25.63154489 10.55911595 9.35739278 0 18.75001458-3.60516949 25.85075143-10.77636551l398.34974663-401.99014628C956.84312974 321.8382427 956.73548345 298.7921647 942.4615936 284.62787926z"
fill={getIconColor(color, 0, '#333333')}
/>
</svg>
);
};
IconDown.defaultProps = {
size: 18,
};
export default IconDown;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconFuwu: FunctionComponent<Props>;
export default IconFuwu;

View File

@@ -0,0 +1,27 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconFuwu = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M384 896h-64v-70.4c0-15.2-10.4-28-24.8-31.2C159.2 768 64 644.8 64 496v-32h64v32c0 118.4 73.6 215.2 179.2 236 44.8 8.8 76.8 48 76.8 94.4v69.6zM704 896h-64v-70.4c0-45.6 32-85.6 76.8-94.4C822.4 711.2 896 614.4 896 496v-32h64v32c0 148.8-95.2 272-231.2 298.4-14.4 3.2-24.8 16-24.8 31.2v70.4zM512.8 640l-41.6-37.6c-147.2-133.6-244-208-244-316.8 0-88 68.8-156.8 156.8-156.8 49.6 0 97.6 23.2 128.8 60C544 152 592 128.8 641.6 128.8c88 0 156.8 68.8 156.8 156.8 0 108-96.8 183.2-244 316.8L512.8 640z"
fill={getIconColor(color, 0, '#333333')}
/>
</svg>
);
};
IconFuwu.defaultProps = {
size: 18,
};
export default IconFuwu;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconGengxin: FunctionComponent<Props>;
export default IconGengxin;

View File

@@ -0,0 +1,27 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconGengxin = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1172 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M870.0416 250.4704a38.4 38.4 0 0 0-8.96 53.5552c13.056 18.2784 24.4224 37.8368 33.7408 58.112a38.4512 38.4512 0 0 0 50.944 18.8928 38.4512 38.4512 0 0 0 18.8416-50.944 436.0192 436.0192 0 0 0-40.96-70.6048 38.3488 38.3488 0 0 0-53.6064-9.0112zM181.4528 566.016a35.9936 35.9936 0 0 0 25.5488-10.5984L351.7952 410.624a36.096 36.096 0 1 0-51.0976-51.0976L217.6 442.5728C250.0096 278.1184 395.264 153.6 569.1392 153.6c50.7904 0 99.8912 10.3936 145.92 30.9248a38.4 38.4 0 1 0 31.232-70.0928 431.36 431.36 0 0 0-177.152-37.632c-214.6816 0-393.1136 156.416-428.4416 361.216L62.1568 359.4752a36.1984 36.1984 0 0 0-51.0976 51.0976l144.8448 144.7936a36.0448 36.0448 0 0 0 25.5488 10.6496zM978.5344 463.104a36.1984 36.1984 0 0 0-51.0976 0l-144.8448 144.7936a36.096 36.096 0 1 0 51.0976 51.0976l88.6272-88.576C894.3104 740.2496 746.8032 870.4 569.1392 870.4a357.7856 357.7856 0 0 1-325.2736-207.7184 38.4 38.4 0 1 0-69.7344 32.3072 434.3808 434.3808 0 0 0 394.9568 252.2112c215.1936 0 393.984-157.184 428.6464-362.7008l74.496 74.496a35.9936 35.9936 0 0 0 51.0976 0 36.096 36.096 0 0 0 0-51.0976l-144.7936-144.7936z"
fill={getIconColor(color, 0, '#333333')}
/>
</svg>
);
};
IconGengxin.defaultProps = {
size: 18,
};
export default IconGengxin;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconShanchu: FunctionComponent<Props>;
export default IconShanchu;

View File

@@ -0,0 +1,31 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconShanchu = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M512 883.2A371.2 371.2 0 1 0 140.8 512 371.2 371.2 0 0 0 512 883.2z m0 64a435.2 435.2 0 1 1 435.2-435.2 435.2 435.2 0 0 1-435.2 435.2z"
fill={getIconColor(color, 0, '#333333')}
/>
<path
d="M557.056 512l122.368 122.368a31.744 31.744 0 1 1-45.056 45.056L512 557.056l-122.368 122.368a31.744 31.744 0 1 1-45.056-45.056L466.944 512 344.576 389.632a31.744 31.744 0 1 1 45.056-45.056L512 466.944l122.368-122.368a31.744 31.744 0 1 1 45.056 45.056z"
fill={getIconColor(color, 1, '#333333')}
/>
</svg>
);
};
IconShanchu.defaultProps = {
size: 18,
};
export default IconShanchu;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconShengji: FunctionComponent<Props>;
export default IconShengji;

View File

@@ -0,0 +1,35 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconShengji = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M223.425605 449.2744l161.632237 0 0 253.65714c0 16.954137 13.745049 30.699186 30.699186 30.699186 16.95516 0 30.699186-13.745049 30.699186-30.699186l0-284.356326c0-16.95516-13.744026-30.699186-30.699186-30.699186L291.035446 387.876028l217.23665-248.51605L733.039255 387.580293 607.104031 387.580293c-16.954137 0-30.699186 13.745049-30.699186 30.699186l0 284.652062c0 16.954137 13.745049 30.699186 30.699186 30.699186s30.699186-13.745049 30.699186-30.699186L637.803217 448.978664l164.448376 0c12.140505 0 23.140023-7.154957 28.063149-18.251689 4.922103-11.097756 2.841721-24.053835-5.307889-33.05279L530.62315 72.570829c-5.881964-6.495948-14.273075-10.134825-23.024389-10.091846-8.763594 0.076748-17.076934 3.895727-22.844288 10.494005L200.312188 398.371056c-7.92653 9.067516-9.818623 21.931498-4.839215 32.896224S211.383338 449.2744 223.425605 449.2744z"
fill={getIconColor(color, 0, '#333333')}
/>
<path
d="M222.354204 829.113381l581.732178 0c16.954137 0 30.699186-13.745049 30.699186-30.699186s-13.745049-30.699186-30.699186-30.699186L222.354204 767.715009c-16.954137 0-30.699186 13.745049-30.699186 30.699186S205.400067 829.113381 222.354204 829.113381z"
fill={getIconColor(color, 1, '#333333')}
/>
<path
d="M804.086381 896.729361 222.354204 896.729361c-16.954137 0-30.699186 13.745049-30.699186 30.699186s13.745049 30.699186 30.699186 30.699186l581.732178 0c16.954137 0 30.699186-13.745049 30.699186-30.699186S821.041542 896.729361 804.086381 896.729361z"
fill={getIconColor(color, 2, '#333333')}
/>
</svg>
);
};
IconShengji.defaultProps = {
size: 18,
};
export default IconShengji;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconTiaozheng: FunctionComponent<Props>;
export default IconTiaozheng;

View File

@@ -0,0 +1,27 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconTiaozheng = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M380.15463648 874.54223633c0 18.12744166-14.83154297 32.95898463-32.95898463 32.95898463s-32.95898463-14.83154297-32.95898462-32.95898463V228.9152832L172.71078883 370.86962865a33.04467773 33.04467773 0 0 1-46.60400416 0 33.04467773 33.04467773 0 0 1 0-46.6040034l197.55615234-198.14941406A32.76782227 32.76782227 0 0 1 347.0967749 116.52514674c0.03295924 0 0.06591772-0.03295924 0.09887695-0.03295924 1.54907201 0 2.90039088 0.69213867 4.41650366 0.88989258 2.66967773 0.39550781 5.40527318 0.59326172 7.94311548 1.61499049 12.03002904 4.94384766 20.59936549 16.71020508 20.59936549 30.45410156v725.0910642z m320.15698192 23.34155248a32.85351537 32.85351537 0 0 1-23.43383789 9.59106445c-0.03295924 0-0.06591772 0.03295924-0.09887696 0.03295924-1.54907201 0-2.90039088-0.69213867-4.41650365-0.92285182-2.70263697-0.36254857-5.40527318-0.56030248-7.94311549-1.61498972-12.03002904-4.91088842-20.59936549-16.67724584-20.59936473-30.42114309V149.45776367c0-18.12744166 14.83154297-32.95898463 32.95898387-32.95898463s32.95898463 14.83154297 32.95898463 32.95898463v645.60058619l141.52587916-141.92138697c12.81445313-12.82104467 33.81591797-12.82104467 46.63037109 0 12.78808619 12.81445313 12.78808619 33.77636719 0 46.60400416L700.3116184 897.88378881z"
fill={getIconColor(color, 0, '#333333')}
/>
</svg>
);
};
IconTiaozheng.defaultProps = {
size: 18,
};
export default IconTiaozheng;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconUp: FunctionComponent<Props>;
export default IconUp;

View File

@@ -0,0 +1,27 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconUp = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M81.5384064 739.37212074c14.30911886 14.12709945 37.31996786 14.05468217 51.48229632-0.21920654L506.02857017 362.72189667 884.24455851 737.54996394c14.30911886 14.16232846 37.31996786 14.05468217 51.51948344-0.21920654 14.16232846-14.30911886 14.05468217-37.35519687-0.21920654-51.51948345l-401.99014627-398.34974663c-0.61847666-0.61847666-1.41897273-0.76526706-2.03940637-1.34655658-0.14483342-0.14483342-0.18201941-0.32685283-0.32685282-0.47364324-7.09877874-7.02636259-16.38375538-10.55911595-25.6315449-10.55911595-9.35739278 0-18.75001458 3.60516949-25.85075143 10.77636551l-398.34974663 401.99014628C67.15687026 702.1617573 67.26451655 725.2078353 81.5384064 739.37212074z"
fill={getIconColor(color, 0, '#333333')}
/>
</svg>
);
};
IconUp.defaultProps = {
size: 18,
};
export default IconUp;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconWendang: FunctionComponent<Props>;
export default IconWendang;

View File

@@ -0,0 +1,35 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconWendang = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M302 332a30 30 0 1 1 0-60h420a30 30 0 0 1 0 60H302zM302 542a30 30 0 0 1 0-60h420a30 30 0 0 1 0 60H302zM302 752a30 30 0 0 1 0-60h120a30 30 0 0 1 0 60H302z"
fill={getIconColor(color, 0, '#333333')}
/>
<path
d="M789.47 784.1a30 30 0 0 1 39.36 45.3l-144.24 125.25a30 30 0 0 1-19.68 7.35H214.85C163.4 962 122 919.46 122 867.38V156.62C122 104.54 163.4 62 214.85 62h594.3C860.6 62 902 104.54 902 156.62v529.05a30 30 0 1 1-60 0V156.62C842 137.3 827.09 122 809.15 122H214.85C196.91 122 182 137.3 182 156.62v710.76C182 886.7 196.91 902 214.85 902h438.84l135.78-117.9z"
fill={getIconColor(color, 1, '#333333')}
/>
<path
d="M692 931.19a30 30 0 1 1-60 0v-174.6C632 704.57 673.4 662 724.85 662h147.78a30 30 0 0 1 0 60h-147.78c-17.94 0-32.85 15.3-32.85 34.62v174.6z"
fill={getIconColor(color, 2, '#333333')}
/>
</svg>
);
};
IconWendang.defaultProps = {
size: 18,
};
export default IconWendang;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconXinzeng: FunctionComponent<Props>;
export default IconXinzeng;

View File

@@ -0,0 +1,31 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconXinzeng = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M512 71.68c-242.688 0-440.32 197.632-440.32 440.32s197.632 440.32 440.32 440.32 440.32-197.632 440.32-440.32-197.632-440.32-440.32-440.32z m0 819.2c-208.896 0-378.88-169.984-378.88-378.88s169.984-378.88 378.88-378.88 378.88 169.984 378.88 378.88-169.984 378.88-378.88 378.88z"
fill={getIconColor(color, 0, '#333333')}
/>
<path
d="M542.72 261.12H481.28v220.16H261.12v61.44h220.16v220.16h61.44v-220.16h220.16V481.28h-220.16z"
fill={getIconColor(color, 1, '#333333')}
/>
</svg>
);
};
IconXinzeng.defaultProps = {
size: 18,
};
export default IconXinzeng;

View File

@@ -0,0 +1,12 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
size?: number;
color?: string | string[];
}
declare const IconYouhua: FunctionComponent<Props>;
export default IconYouhua;

View File

@@ -0,0 +1,27 @@
/* eslint-disable */
import React from 'react';
import { getIconColor } from './helper';
const DEFAULT_STYLE = {
display: 'block',
};
const IconYouhua = ({ size, color, style: _style, ...rest }) => {
const style = _style ? { ...DEFAULT_STYLE, ..._style } : DEFAULT_STYLE;
return (
<svg viewBox="0 0 1024 1024" width={size + 'px'} height={size + 'px'} style={style} {...rest}>
<path
d="M856.4 292.8c-63.3-63.6-126.6-127.1-190.2-190.3-15.3-15.2-32.7-16.1-48.1-0.8-64.3 63.6-128.1 127.6-191.8 191.9-14 14.2-16.3 31.6-1.7 46 14.8 14.7 31.5 10.6 46.1-2.7 5.1-4.6 9.8-9.7 14.7-14.7 39.2-39.7 78.5-79.5 122.8-124.4 0 170 3 332.2-1.1 494-2.4 96.4-91.2 174.6-187.4 176.6-110.6 2.3-198.6-84.4-199-197.4-0.6-136.3-0.2-272.6-0.1-408.9 0-21.8-7.9-37.4-31.2-39.9-18.9-2-33.2 13.2-33.1 37.5 0 145.8-3.4 291.7 2.4 437.2 6 152.1 160.4 263.5 309.5 230.5C591.8 900 672.8 797.2 673.6 664.6c0.8-144 0.2-288.1 0.2-432.1v-33.3c11.2 10.2 17.6 15.4 23.3 21.3 38.5 38.4 76.7 77 115.3 115.2 14.8 14.6 32.2 19.2 47.8 2.9 13.8-14.8 10.3-31.7-3.8-45.8z"
fill={getIconColor(color, 0, '#333333')}
/>
</svg>
);
};
IconYouhua.defaultProps = {
size: 18,
};
export default IconYouhua;

View File

@@ -0,0 +1,3 @@
/* eslint-disable */
export declare const getIconColor: (color: string | string[] | undefined, index: number, defaultColor: string) => string;

View File

@@ -0,0 +1,17 @@
/* eslint-disable */
/**
* @param {string | string[] | undefined} color
* @param {number} index
* @param {string} defaultColor
* @return {string}
*/
export const getIconColor = (color, index, defaultColor) => {
return color
? (
typeof color === 'string'
? color
: color[index] || defaultColor
)
: defaultColor;
};

View File

@@ -0,0 +1,25 @@
/* eslint-disable */
import { SVGAttributes, FunctionComponent } from 'react';
export { default as IconYouhua } from './IconYouhua';
export { default as IconDayi } from './IconDayi';
export { default as IconShengji } from './IconShengji';
export { default as IconTiaozheng } from './IconTiaozheng';
export { default as IconGengxin } from './IconGengxin';
export { default as IconWendang } from './IconWendang';
export { default as IconShanchu } from './IconShanchu';
export { default as IconBug } from './IconBug';
export { default as IconXinzeng } from './IconXinzeng';
export { default as IconFuwu } from './IconFuwu';
export { default as IconDown } from './IconDown';
export { default as IconUp } from './IconUp';
interface Props extends Omit<SVGAttributes<SVGElement>, 'color'> {
name: 'youhua' | 'dayi' | 'shengji' | 'tiaozheng' | 'gengxin' | 'wendang' | 'shanchu' | 'bug' | 'xinzeng' | 'fuwu' | 'down' | 'up';
size?: number;
color?: string | string[];
}
declare const IconFont: FunctionComponent<Props>;
export default IconFont;

View File

@@ -0,0 +1,61 @@
/* eslint-disable */
import React from 'react';
import IconYouhua from './IconYouhua';
import IconDayi from './IconDayi';
import IconShengji from './IconShengji';
import IconTiaozheng from './IconTiaozheng';
import IconGengxin from './IconGengxin';
import IconWendang from './IconWendang';
import IconShanchu from './IconShanchu';
import IconBug from './IconBug';
import IconXinzeng from './IconXinzeng';
import IconFuwu from './IconFuwu';
import IconDown from './IconDown';
import IconUp from './IconUp';
export { default as IconYouhua } from './IconYouhua';
export { default as IconDayi } from './IconDayi';
export { default as IconShengji } from './IconShengji';
export { default as IconTiaozheng } from './IconTiaozheng';
export { default as IconGengxin } from './IconGengxin';
export { default as IconWendang } from './IconWendang';
export { default as IconShanchu } from './IconShanchu';
export { default as IconBug } from './IconBug';
export { default as IconXinzeng } from './IconXinzeng';
export { default as IconFuwu } from './IconFuwu';
export { default as IconDown } from './IconDown';
export { default as IconUp } from './IconUp';
const IconFont = ({ name, ...rest }) => {
switch (name) {
case 'youhua':
return <IconYouhua {...rest} />;
case 'dayi':
return <IconDayi {...rest} />;
case 'shengji':
return <IconShengji {...rest} />;
case 'tiaozheng':
return <IconTiaozheng {...rest} />;
case 'gengxin':
return <IconGengxin {...rest} />;
case 'wendang':
return <IconWendang {...rest} />;
case 'shanchu':
return <IconShanchu {...rest} />;
case 'bug':
return <IconBug {...rest} />;
case 'xinzeng':
return <IconXinzeng {...rest} />;
case 'fuwu':
return <IconFuwu {...rest} />;
case 'down':
return <IconDown {...rest} />;
case 'up':
return <IconUp {...rest} />;
}
return null;
};
export default IconFont;

View File

@@ -0,0 +1,25 @@
/* stylelint-disable docusaurus/copyright-header */
/**
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: rgb(33, 175, 144);
--ifm-color-primary-darker: rgb(31, 165, 136);
--ifm-color-primary-darkest: rgb(26, 136, 112);
--ifm-color-primary-light: rgb(70, 203, 174);
--ifm-color-primary-lighter: rgb(102, 212, 189);
--ifm-color-primary-lightest: rgb(146, 224, 208);
--ifm-code-font-size: 95%;
}
.docusaurus-highlight-code-line {
background-color: rgb(72, 77, 91);
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
}

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1673488195730" class="icon" viewBox="0 0 1026 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2727" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.390625" height="200"><path d="M2.438 0h975.238v975.238H2.438z" fill="#2FC2B0" fill-opacity="0" p-id="2728"></path><path d="M266.386 774.095c0 20.675 18.725 37.547 41.594 37.547h41.643V943.25c0 31.208 27.843 56.369 62.415 56.369 34.524 0 62.415-25.21 62.415-56.369V811.69h83.188v131.56c0 31.208 27.892 56.369 62.415 56.369 34.524 0 62.416-25.21 62.416-56.369V811.69h41.594c22.918 0 41.593-16.92 41.593-37.546V398.19H266.386v375.856zM162.377 398.238c-34.523 0-62.415 25.21-62.415 56.37v263.118c0 31.208 27.892 56.37 62.415 56.37 34.524 0 62.415-25.162 62.415-56.37V454.607c0-31.207-27.891-56.369-62.415-56.369z m707.292 0c-34.524 0-62.416 25.21-62.416 56.37v263.118c0 31.208 27.892 56.37 62.416 56.37 34.523 0 62.415-25.162 62.415-56.37V454.607c0-31.207-27.892-56.369-62.415-56.369z m-206.75-219.477l54.271-49.054a17.457 17.457 0 0 0 0-26.478 22.333 22.333 0 0 0-29.306 0l-61.586 55.393a269.897 269.897 0 0 0-110.25-23.454c-39.986 0-77.386 8.63-110.885 23.65l-61.782-55.784a22.333 22.333 0 0 0-29.354 0 17.457 17.457 0 0 0 0 26.478l54.515 49.25c-61.78 41.154-102.156 107.275-102.156 181.881h499.322c0-74.752-40.618-140.922-102.79-181.882zM432.81 285.501h-41.594v-37.547h41.594v37.547z m208.018 0h-41.594v-37.547h41.594v37.547z" fill="#2FC2B0" p-id="2729"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1611298937091" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="862" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M944.9 471.640625a141.946875 141.946875 0 0 0-105.046875-9.50625 139.640625 139.640625 0 0 0-56.784375-88.959375l-11.25-8.859375-9.534375 10.6875a117.675 117.675 0 0 0-22.21875 84.65625 108.590625 108.590625 0 0 0 21.20625 56.86875 164.925 164.925 0 0 1-30.54375 13.44375A204.271875 204.271875 0 0 1 667.75625 540.125H73.925l-1.265625 13.303125a248.428125 248.428125 0 0 0 20.896875 129.375l8.128125 16.115625 0.928125 1.51875c55.828125 92.19375 167.203125 139.95 274.05 139.95 206.8875 0 364.190625-98.746875 442.575-288.478125 52.36875 2.671875 105.946875-12.403125 131.56875-61.003125l6.525-12.403125-12.43125-6.975zM246.978125 711.6875a46.434375 46.434375 0 1 1 47.8125-46.434375 47.165625 47.165625 0 0 1-47.8125 46.434375z" fill="#1296db" p-id="863"></path><path d="M246.978125 640.7a24.440625 24.440625 0 1 0 25.14375 24.46875 24.80625 24.80625 0 0 0-25.14375-24.46875M118.25 427.625h84.375v84.375H118.25z m112.5 0h84.375v84.375h-84.375z m0-112.5h84.375v84.375h-84.375z m112.5 0h84.375v84.375h-84.375z m0 112.5h84.375v84.375h-84.375z m112.5 0h84.375v84.375h-84.375z m112.5 0h84.375v84.375h-84.375zM455.75 315.125h84.375v84.375h-84.375z m0-112.5h84.375v84.375h-84.375z" fill="#1296db" p-id="864"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,540 @@
.ThingsGateway-banner {
padding: 4rem 2rem;
align-items: center;
background-color: #211b50;
color: #fff;
}
.ThingsGateway-banner-container {
display: flex;
justify-content: space-between;
max-width: 1140px;
margin: 0 auto;
}
.ThingsGateway-banner-item {
flex: 1;
}
.ThingsGateway-banner-project {
font-size: 1.5em;
font-weight: 700;
}
.ThingsGateway-banner-description {
margin: 24px 0;
font-size: 2.5em;
font-weight: 700;
line-height: 1.25;
background-image: linear-gradient(81deg, #8759ff, #3fc4fe, #42ffac);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.ThingsGateway-banner-spec {
padding: 0;
opacity: 0.7;
font-family: Muli;
font-size: 1em;
font-weight: 500;
line-height: 1.33;
}
.ThingsGateway-banner-spec li {
list-style: none;
position: relative;
padding-left: 1em;
margin-bottom: 1em;
}
.ThingsGateway-banner-spec li::before {
content: "";
position: absolute;
top: 0.5em;
left: 0;
width: 4px;
height: 4px;
background-color: rgb(135, 89, 255);
}
.ThingsGateway-support-platform {
font-size: 0.85em;
line-height: 2;
margin-top: 3em;
font-weight: 500;
opacity: 0.6;
color: white;
font-family: Muli;
}
.ThingsGateway-support-icons {
display: flex;
margin-top: 12px;
}
.ThingsGateway-support-icons span {
margin-right: 20px;
}
.ThingsGateway-get-start,
.ThingsGateway-try-demo {
margin-top: 4em;
border-radius: 2em;
min-width: 145px;
color: #fff;
background: #8759ff;
position: relative;
line-height: 1.5;
text-align: center;
padding: 8px 32px;
text-decoration: none;
display: inline-block;
white-space: nowrap;
}
.ThingsGateway-try-demo {
background-color: rgb(33, 176, 145);
margin-left: 20px;
}
.ThingsGateway-get-start:hover {
background: rgba(135, 89, 255, 0.9);
}
.ThingsGateway-try-demo:hover {
opacity: 0.9;
}
.ThingsGateway-banner-item .system-window {
width: 34rem;
}
.ThingsGateway-get-start:hover,
.ThingsGateway-try-demo:hover {
color: #fff;
text-decoration: none;
}
.system-top-bar {
background-image: linear-gradient(to right,
rgba(136, 89, 255, 0.2),
rgba(63, 196, 254, 0.2) 90%,
rgba(66, 255, 172, 0.2));
padding: 0.25em 1em;
}
.system-top-bar-circle {
display: inline-block;
width: 0.5em;
height: 0.5em;
margin-left: 0.3em;
border-radius: 50%;
filter: brightness(100%);
}
.system-window {
--ifm-leading: 0;
width: 95%;
padding: 0;
border-radius: 1em;
overflow: hidden;
background: rgb(33, 27, 80);
}
.system-window iframe {
border-radius: unset;
}
.system-window pre {
margin-bottom: 0 !important;
}
.blue-accent {
--uni-border-color: #3fbbfe;
--uni-box-shadow-color: rgba(63, 187, 254, 0.1);
--ifm-menu-color-active: #3fbbfe;
}
.preview-border {
box-shadow: 0 6px 58px 0 rgba(63, 187, 254, 0.1);
border: solid 1px #3fbbfe;
}
.ThingsGateway-content {
margin-top: 4em;
margin-bottom: 4em;
text-align: center;
}
.ThingsGateway-small-title {
color: #412a94;
font-family: Muli;
font-size: 1em;
font-weight: 600;
letter-spacing: 1px;
opacity: 0.6;
}
.ThingsGateway-small-title.dark {
color: #f5f6f7;
}
.ThingsGateway-big-title {
color: #412a94;
font-family: Poppins;
font-size: 2em;
font-weight: 700;
line-height: 1.31;
margin-bottom: 2em;
}
.ThingsGateway-big-title.dark {
color: #f5f6f7;
}
.ThingsGateway-gitee-log {
display: flex;
justify-content: center;
flex-wrap: nowrap;
}
.ThingsGateway-log-item {
width: 260px;
height: 173px;
box-sizing: border-box;
margin-right: 65px;
position: relative;
}
.ThingsGateway-log-jiao {
width: 100px;
height: 100px;
background: #fff;
position: absolute;
top: 0;
right: 0;
top: -6px;
right: -6px;
border-top: 1px dashed #a795e8;
border-right: 1px dashed #a795e8;
}
.ThingsGateway-log-jiao.dark {
background: #18191a;
}
.ThingsGateway-log-item:last-child {
margin-right: 0;
}
.ThingsGateway-log-number {
position: relative;
z-index: 2;
height: 100%;
display: flex;
width: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
}
.ThingsGateway-log-number div {
font-size: 3em;
font-weight: 700;
}
.ThingsGateway-log-number span {
font-family: Poppins, sans-serif;
font-stretch: normal;
font-style: normal;
letter-spacing: normal;
line-height: normal;
color: #1c1e21;
}
.ThingsGateway-log-number span.dark {
color: #f5f6f7;
}
.ThingsGateway-remark {
display: flex;
justify-content: center;
}
.ThingsGateway-remark-item {
margin: 2em;
max-width: 320px;
height: 100%;
padding: 4em 2em;
border-style: solid;
border-width: 6px;
border-image-source: linear-gradient(var(--uni-border-gradient-degrees),
#8759ff,
#3fc4fe 51%,
#42ffac);
border-image-slice: 1;
}
.ThingsGateway-remark-item:first-child {
border-top: 0;
border-right: 0;
--uni-border-gradient-degrees: 41deg;
}
.ThingsGateway-remark-item:nth-child(2) {
--uni-border-gradient-degrees: 100deg;
border-right: 0;
border-left: 0;
border-top: 0;
border-bottom: 0;
}
.ThingsGateway-remark-item:last-child {
--uni-border-gradient-degrees: 221deg;
border-bottom: 0;
border-left: 0;
}
.ThingsGateway-remark-p {
height: 150px;
}
.ThingsGateway-remark-p h1 {
font-size: 24px;
}
.ThingsGateway-remark-p p {
color: rgb(71, 71, 71);
font-family: Muli;
font-size: 1em;
line-height: 1.75;
opacity: 0.8;
text-align: center;
}
.ThingsGateway-remark-p p.dark {
color: #f5f6f7;
}
.ThingsGateway-whouse {
align-items: center;
background-color: #412a94 !important;
color: #fff;
display: flex;
padding: 5rem 0;
}
.ThingsGateway-who-custom {
background-color: #fff;
min-height: 500px;
width: 60%;
box-sizing: border-box;
padding: 6rem;
justify-content: flex-end;
align-items: center;
color: #723cff;
text-align: right;
display: flex;
flex-wrap: wrap;
}
.ThingsGateway-custom-img {
text-decoration: none;
color: transparent;
margin-left: 3em;
}
.ThingsGateway-who-des {
padding: 0 5rem;
box-sizing: border-box;
}
.ThingsGateway-who-des p {
color: #fff;
font-family: Muli;
font-size: 1em;
line-height: 1.75;
margin-bottom: 0.8em;
opacity: 0.8;
}
.footer {
background-color: #211b50 !important;
}
.ThingsGateway-links {
margin: 4em;
text-align: center;
}
.ThingsGateway-links-content a {
display: inline-block;
margin: 0 1em;
font-size: 20px;
font-weight: 600;
}
.ThingsGateway-proccesson {
margin: 4em 0;
text-align: center;
}
#dotnet-china {
height: 100px;
}
.ThingsGateway-contributors {
margin: 4em 0;
text-align: center;
}
.ThingsGateway-contributor-item {
display: inline-block;
margin: 10px 5px;
text-align: center;
background-color: #f3f3f3;
color: #333;
padding: 10px;
width: 130px;
overflow: hidden;
border-radius: 4px;
height: 170px;
box-shadow: 4px 3px 16px -3px #0009;
box-sizing: border-box;
position: relative;
}
.ThingsGateway-contributor-extra {
position: absolute;
top: -9px;
right: 0;
padding: 2px 5px;
background-color: #412a94;
color: #fff;
font-size: 12px;
border-radius: 4px;
text-align: left;
}
.ThingsGateway-contributor-item a {
text-decoration: none;
color: #333;
font-weight: bold;
display: block;
font-size: 10pt;
}
.ThingsGateway-contributor-item div {
margin-top: 10px;
}
.ThingsGateway-contributor-item img {
display: block;
width: 100%;
}
.ThingsGateway-contributor-item.dark {
background: #333;
}
.ThingsGateway-contributor-item.dark a {
color: #f5f6f7;
}
.ThingsGateway-get-start-btn {
position: relative;
display: flex;
}
.ThingsGateway-version {
position: absolute;
z-index: 10;
right: 0;
top: -10px;
color: yellow;
font-size: 16px;
}
@media screen and (max-width: 1024px) {
.ThingsGateway-banner-container {
justify-content: unset;
flex-direction: column;
}
.ThingsGateway-get-start-btn {
text-align: center;
}
#dotnet-china {
height: 45px;
}
.ThingsGateway-banner-item .system-window {
width: 100%;
margin-top: 3rem;
}
.ThingsGateway-gitee-log {
justify-content: center;
flex-wrap: unset;
flex-direction: column;
align-items: center;
padding: 20px;
}
.ThingsGateway-log-item {
width: 100%;
height: 173px;
margin-right: 0;
margin-top: 25px;
}
.ThingsGateway-big-title {
margin-bottom: 1em;
}
.ThingsGateway-remark {
flex-direction: column;
}
.ThingsGateway-whouse {
flex-direction: column;
padding-bottom: 1em;
}
.ThingsGateway-who-des {
padding-top: 1em;
padding-bottom: 2em;
}
.ThingsGateway-remark-item {
border: none;
margin: 0;
width: 100%;
max-width: unset;
padding-bottom: 0;
}
.ThingsGateway-custom-img {
margin-left: 0;
margin-bottom: 2em;
}
.ThingsGateway-custom-img img {
max-width: unset;
}
.ThingsGateway-who-custom {
justify-content: center;
align-items: center;
width: 100%;
text-align: center;
}
.ThingsGateway-contributors {
margin: 4em 0;
}
}

159
handbook/src/pages/index.js Normal file
View File

@@ -0,0 +1,159 @@
import Link from "@docusaurus/Link";
import { useColorMode } from "@docusaurus/theme-common";
import useBaseUrl from "@docusaurus/useBaseUrl";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import Layout from "@theme/Layout";
import components from "@theme/MDXComponents";
import React from "react";
import AndroidIcon from "./android.svg";
import DockerIcon from "./docker.svg";
import "./index.css";
import "./index.own.css";
import KubernetesIcon from "./kubernetes.svg";
import LinuxIcon from "./linux.svg";
import MacOSIcon from "./macos.svg";
import WindowIcon from "./windows.svg";
function Home() {
const context = useDocusaurusContext();
const { siteConfig = {} } = context;
React.useEffect(() => {}, []);
return (
<Layout
title={`ThingsGateway ${siteConfig.title}`}
description="ThingsGateway物联网关"
>
<Banner />
<Gitee />
</Layout>
);
}
function Banner() {
return (
<div className="ThingsGateway-banner">
<div className="ThingsGateway-banner-container">
<div className="ThingsGateway-banner-item">
<div className="ThingsGateway-banner-project">
ThingsGateway{" "}
<span
style={{ fontSize: 14, fontWeight: "normal", color: "#8759ff" }}
></span>
</div>
<div className="ThingsGateway-banner-description">
基于NetCore的跨平台物联网关
</div>
<ul className="ThingsGateway-banner-spec">
<li> Apache-2.0 宽松开源协议商业免费授权</li>
<li>支持 .NET/6/7+ 平台</li>
</ul>
<div className="ThingsGateway-support-platform">受支持平台</div>
<div className="ThingsGateway-support-icons">
<span>
<WindowIcon height="39" width="39" />
</span>
<span>
<LinuxIcon height="39" width="39" />
</span>
<span>
<AndroidIcon height="39" width="39" />
</span>
<span>
<MacOSIcon height="39" width="39" />
</span>
<span>
<DockerIcon height="39" width="39" />
</span>
<span>
<KubernetesIcon height="39" width="39" />
</span>
</div>
<div className="ThingsGateway-get-start-btn">
<Link className="ThingsGateway-get-start" to={useBaseUrl("docs")}>
入门指南
<span className="ThingsGateway-version">^1.2.0</span>
</Link>
</div>
</div>
</div>
</div>
);
}
function Gitee() {
const { colorMode, setLightTheme, setDarkTheme } = useColorMode();
const isDarkTheme = colorMode === "dark";
return (
<div className="ThingsGateway-content">
<p className={"ThingsGateway-small-title" + (isDarkTheme ? " dark" : "")}>
开源免费/商业免费授权
</p>
<h1 className={"ThingsGateway-big-title" + (isDarkTheme ? " dark" : "")}>
Apache-2.0 开源协议代码在 Gitee 平台托管
</h1>
</div>
);
}
function CodeSection(props) {
let { language, replace, section, source } = props;
source = source.replace(/\/\/ <.*?\n/g, "");
if (replace) {
for (const [pattern, value] of Object.entries(replace)) {
source = source.replace(new RegExp(pattern, "gs"), value);
}
}
source = source.trim();
if (!source.includes("\n")) {
source += "\n";
}
return (
<components.pre>
<components.code
children={source}
className={`language-${language}`}
mdxType="code"
originalType="code"
parentName="pre"
/>
</components.pre>
);
}
function SystemWindow(systemWindowProps) {
const { children, className, ...props } = systemWindowProps;
return (
<div
{...props}
className={"system-window blue-accent preview-border " + className}
>
<div className="system-top-bar">
<span
className="system-top-bar-circle"
style={{ backgroundColor: "#8759ff" }}
/>
<span
className="system-top-bar-circle"
style={{ backgroundColor: "#3fc4fe" }}
/>
<span
className="system-top-bar-circle"
style={{ backgroundColor: "#42ffac" }}
/>
</div>
{children}
</div>
);
}
export default Home;

View File

@@ -0,0 +1,25 @@
.navbar {
background-color: #211b50;
}
.navbar__brand {
color: #fff;
}
.navbar__link {
color: #fff;
}
.navbar__link:hover,
.navbar__link--active {
color: yellow;
}
.navbar__items {
color: #fff;
}
.menu__list-item .navbar__link--active,
.menu__list-item .navbar__link:hover {
color: #743dff;
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1611298499508" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1165" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M878.3 721.7c-1.35 3.6-18.9 65.25-62.1 128.25-37.8 54.9-76.5 109.8-137.7 110.7-60.3 0.9-79.65-36-148.5-36s-90.45 35.1-147.6 36.9c-58.95 2.25-104.4-59.85-142.2-114.3C163.25 736.55 103.85 531.8 183.5 395c39.15-68.4 109.8-112.05 186.3-113.4 57.6-0.9 112.95 39.15 148.05 39.15 36 0 102.6-48.6 172.8-41.4 29.25 1.35 111.6 12.15 164.25 89.1-4.05 2.7-98.1 58.5-97.2 171.45 0.9 136.35 119.25 181.35 120.6 181.8m-234-515.25c31.5-38.7 52.65-91.8 46.8-144.45-45 1.8-100.35 30.15-132.75 68.4-28.8 33.3-54.45 87.3-47.25 139.5 49.95 2.7 101.7-26.55 133.2-63.45" fill="#ffffff" p-id="1166"></path></svg>

After

Width:  |  Height:  |  Size: 965 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1611298487411" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1027" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M459.5 485.75V182.09375l-314.90625 67.5V485.75H459.5zM512 485.75h367.3125V92.1875l-367.3125 78.75V485.75zM459.5 538.25H144.6875v236.15625l314.90625 67.5V538.25zM512 538.25v314.90625l367.3125 78.75V538.25H512z" fill="#00adef" p-id="1028"></path></svg>

After

Width:  |  Height:  |  Size: 627 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
handbook/static/img/pay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -0,0 +1,7 @@
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?91e98d00c5024e8d87b0bb3c10281d59";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();

View File

@@ -0,0 +1,4 @@
global using System;
global using TouchSocket.Core;
global using TouchSocket.Sockets;

View File

@@ -0,0 +1,135 @@
using System.Text;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
/// <summary>
/// Modbus协议地址
/// </summary>
public class ModbusAddress : DeviceAddressBase
{
public ModbusAddress()
{
}
public ModbusAddress(string address, ushort len)
{
Station = -1;
AddressStart = 0;
Parse(address, len);
}
public ModbusAddress(string address, byte station)
{
Station = station;
AddressStart = 0;
Parse(address, 0);
}
/// <summary>
/// 读取功能码
/// </summary>
public int ReadFunction { get; set; }
/// <summary>
/// 站号信息
/// </summary>
public int Station { get; set; }
/// <summary>
/// 写入功能码
/// </summary>
public int WriteFunction { get; set; }
public override void Parse(string address, int length)
{
Length = length;
if (address.IndexOf(';') < 0)
{
Address(address);
}
else
{
string[] strArray = address.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
for (int index = 0; index < strArray.Length; ++index)
{
if (strArray[index].ToUpper().StartsWith("S="))
{
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
Station = byte.Parse(strArray[index].Substring(2));
}
else if (strArray[index].ToUpper().StartsWith("W="))
{
if (Convert.ToInt16(strArray[index].Substring(2)) > 0)
this.WriteFunction = (int)byte.Parse(strArray[index].Substring(2));
}
else if (!strArray[index].Contains("="))
{
Address(strArray[index]);
}
}
}
void Address(string address)
{
var readF = ushort.Parse(address.Substring(0, 1));
if (readF > 4)
throw new("功能码错误");
GetFunction(readF);
AddressStart = int.Parse(address.Substring(1)) - 1;
}
}
public override string ToString()
{
StringBuilder stringGeter = new StringBuilder();
if (Station > 0)
{
stringGeter.Append("s=" + Station.ToString() + ";");
}
if (WriteFunction > 0)
{
stringGeter.Append("w=" + WriteFunction.ToString() + ";");
}
stringGeter.Append(GetFunctionString(ReadFunction) + (AddressStart + 1).ToString());
return stringGeter.ToString();
}
private void GetFunction(ushort readF)
{
switch (readF)
{
case 0:
ReadFunction = 1;
break;
case 1:
ReadFunction = 2;
break;
case 3:
ReadFunction = 4;
break;
case 4:
ReadFunction = 3;
break;
}
}
private string GetFunctionString(int readF)
{
switch (readF)
{
case 1:
return "0";
case 2:
return "1";
case 3:
return "4";
case 4:
return "3";
}
return "4";
}
}
}

View File

@@ -0,0 +1,269 @@
using ThingsGateway.Foundation.Extension;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
internal class ModbusHelper
{
/// <summary>
/// 添加Crc16
/// </summary>
internal static byte[] AddCrc(byte[] command)
{
return EasyCRC16.CRC16(command);
}
/// <summary>
/// 添加ModbusTcp报文头
/// </summary>
internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
{
byte[] tcp = new byte[modbus.Length + 6];
tcp[0] = BitConverter.GetBytes(id)[1];
tcp[1] = BitConverter.GetBytes(id)[0];
tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
modbus.CopyTo(tcp, 6);
return tcp;
}
/// <summary>
/// 通过错误码来获取到对应的文本消息
/// </summary>
internal static string GetDescriptionByErrorCode(byte code)
{
switch (code)
{
case 1:
return "不支持的功能码";
case 2:
return "读取寄存器越界";
case 3:
return "读取长度超限";
case 4:
return "读写异常";
default:
return "未知错误";
}
}
/// <summary>
/// 获取modbus数据区内容返回数据需去除Crc和报文头例如01 03 02 00 01发送数据需报文头
/// </summary>
/// <param name="send">发送数据</param>
/// <param name="response">返回数据</param>
/// <returns></returns>
internal static OperResult<byte[]> GetModbusData(byte[] send, byte[] response)
{
try
{
if (response[1] >= 0x80)//错误码
return new OperResult<byte[]>(GetDescriptionByErrorCode(response[2]));
if (send[0] != response[0])
return new OperResult<byte[]>(string.Format("站号不一致", send[0], response[0]));
if (send[1] != response[1])
return new OperResult<byte[]>(response) { Message = "功能码不一致" };
if (response.Length > 3)
return OperResult.CreateSuccessResult(GenericHelper.ArrayRemoveBegin(response, 3));
else
return new OperResult<byte[]>(response) { Message = "数据长度为0" };
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 去除Crc返回modbus数据区
/// </summary>
/// <param name="send"></param>
/// <param name="response"></param>
/// <param name="crcCheck"></param>
/// <returns></returns>
internal static OperResult<byte[]> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
{
if (response.Length < 5)
return new OperResult<byte[]>("数据长度不足" + response.ToHexString());
if (crcCheck && !EasyCRC16.CheckCRC16(response))
return new OperResult<byte[]>("Crc校验失败" + DataHelper.ByteToHexString(response, ' '));
return GetModbusData(send, response.RemoveLast(2));
}
/// <summary>
/// 获取读取报文
/// </summary>
internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
{
try
{
ModbusAddress mAddress = new ModbusAddress(address, station);
return GetReadModbusCommand(mAddress, length);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取写入布尔量报文,根据地址识别功能码
/// </summary>
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
{
try
{
ModbusAddress mAddress = new ModbusAddress(address, station);
//功能码或实际长度
if (values?.Length > 1 || mAddress.WriteFunction == 15)
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
else
return GetWriteBoolModbusCommand(address, values[0], station);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取写入字报文,根据地址识别功能码
/// </summary>
internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
{
try
{
ModbusAddress mAddress = new ModbusAddress(address, station);
//功能码或实际长度
if (value?.Length > 2 || mAddress.WriteFunction == 16)
return GetWriteModbusCommand(mAddress, value);
else
return GetWriteOneModbusCommand(mAddress, value);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取读取报文
/// </summary>
private static OperResult<byte[]> GetReadModbusCommand(ModbusAddress mAddress, int length)
{
byte[] array = new byte[6]
{
(byte) mAddress.Station,
(byte) mAddress.ReadFunction,
BitConverter.GetBytes(mAddress.AddressStart)[1],
BitConverter.GetBytes(mAddress.AddressStart)[0],
BitConverter.GetBytes(length)[1],
BitConverter.GetBytes(length)[0]
};
return OperResult.CreateSuccessResult(array);
}
/// <summary>
/// 获取05写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
{
try
{
if (address.IndexOf('.') <= 0)
{
ModbusAddress mAddress = new ModbusAddress(address, station);
return GetWriteBoolModbusCommand(mAddress, value);
}
return new("不支持写入字寄存器的某一位");
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取05写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
{
byte[] array = new byte[6]
{
(byte) mAddress.Station,
(byte)5,
BitConverter.GetBytes(mAddress.AddressStart)[1],
BitConverter.GetBytes(mAddress.AddressStart)[0],
0,
0
};
if (value)
{
array[4] = 0xFF;
array[5] = 0;
}
else
{
array[4] = 0;
array[5] = 0;
}
return OperResult.CreateSuccessResult(array);
}
/// <summary>
/// 获取15写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
{
try
{
byte[] numArray1 = values.BoolArrayToByte();
byte[] numArray2 = new byte[7 + numArray1.Length];
numArray2[0] = (byte)mAddress.Station;
numArray2[1] = (byte)15;
numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
numArray2[4] = (byte)(length / 256);
numArray2[5] = (byte)(length % 256);
numArray2[6] = (byte)numArray1.Length;
numArray1.CopyTo(numArray2, 7);
return OperResult.CreateSuccessResult(numArray2);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取16写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
{
byte[] numArray = new byte[7 + values.Length];
numArray[0] = (byte)mAddress.Station;
numArray[1] = (byte)16;
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
numArray[4] = (byte)(values.Length / 2 / 256);
numArray[5] = (byte)(values.Length / 2 % 256);
numArray[6] = (byte)values.Length;
values.CopyTo(numArray, 7);
return OperResult.CreateSuccessResult(numArray);
}
/// <summary>
/// 获取6写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
{
byte[] numArray = new byte[4 + values.Length];
numArray[0] = (byte)mAddress.Station;
numArray[1] = (byte)6;
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
values.CopyTo(numArray, 4);
return OperResult.CreateSuccessResult(numArray);
}
}
}

View File

@@ -0,0 +1,18 @@
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuMessage : MessageBase, IMessage
{
public override int HeadBytesLength => -1;
public override bool CheckHeadBytes(byte[] head)
{
return true;
}
protected override void SendBytesThen()
{
BodyLength = -1;
}
}
}

View File

@@ -0,0 +1,107 @@
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Resources;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase
{
public ModbusRtuOverTcpDataHandleAdapter DataHandleAdapter = new();
public ModbusRtuOverTcp(TcpClient tcpClient) : base(tcpClient)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
public bool Crc16CheckEnable { get; set; }
public byte Station { get; set; } = 1;
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
var result = await TcpClient.GetWaitingClient(new()).SendThenResponseAsync(item, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable;
TcpClient.SetDataHandlingAdapter(DataHandleAdapter);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
var result = await TcpClient.GetWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
var result = await TcpClient.GetWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
}
}

View File

@@ -0,0 +1,23 @@
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuOverTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusRtuMessage>
{
public bool Crc16CheckEnable { get; set; } = true;
public override byte[] PackCommand(byte[] command)
{
return ModbusHelper.AddCrc(command);
}
protected override ModbusRtuMessage GetInstance()
{
return new ModbusRtuMessage();
}
protected override OperResult<byte[]> UnpackResponse(
byte[] send, byte[] response)
{
return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
}
}
}

View File

@@ -0,0 +1,107 @@
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Resources;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusRtuOverUdp : ReadWriteDevicesUdpClientBase
{
public ModbusRtuOverUdpDataHandleAdapter DataHandleAdapter = new();
public ModbusRtuOverUdp(UdpSession udpSession) : base(udpSession)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
public bool Crc16CheckEnable { get => DataHandleAdapter.Crc16CheckEnable; set => DataHandleAdapter.Crc16CheckEnable = value; }
public byte Station { get; set; } = 1;
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
var result = await UdpSession.GetWaitingClient(new()).SendThenResponseAsync(item, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable;
UdpSession.SetDataHandlingAdapter(DataHandleAdapter);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
var result = await UdpSession.GetWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
var result = await UdpSession.GetWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
}
}

View File

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

View File

@@ -0,0 +1,375 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ThingsGateway.Foundation.Extension;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusServer : ReadWriteDevicesTcpServerBase
{
public ModbusServerDataHandleAdapter DataHandleAdapter = new();
public ModbusServer(TcpService tcpService) : base(tcpService)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
/// <summary>
/// 默认站点
/// </summary>
public byte Station { get; set; } = 1;
/// <summary>
/// 多站点
/// </summary>
public bool MulStation { get; set; }
public override void SetDataAdapter(SocketClient client)
{
DataHandleAdapter = new();
client.SetDataHandlingAdapter(DataHandleAdapter);
}
/// <summary>
/// 保持寄存器
/// </summary>
public Dictionary<int, ByteBlock> ModbusServer04ByteBlocks = new();
/// <summary>
/// 输入寄存器
/// </summary>
public Dictionary<int, ByteBlock> ModbusServer03ByteBlocks = new();
/// <summary>
/// 开关输入
/// </summary>
public Dictionary<int, ByteBlock> ModbusServer02ByteBlocks = new();
/// <summary>
/// 继电器
/// </summary>
public Dictionary<int, ByteBlock> ModbusServer01ByteBlocks = new();
public override Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
ModbusAddress mAddress = null;
try
{
mAddress = new ModbusAddress(address, Station);
}
catch (Exception ex)
{
return Task.FromResult(new OperResult<byte[]>(ex));
}
if (MulStation)
{
ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
}
else
{
if (Station != mAddress.Station)
{
return Task.FromResult(new OperResult<byte[]>("地址错误"));
}
ModbusServer01ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer02ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer03ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer04ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer01ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer02ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer03ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer04ByteBlocks[Station].SetLength(1024 * 128);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
int len = mAddress.ReadFunction == 2 || mAddress.ReadFunction == 1 ? length : length * RegisterByteLength;
switch (mAddress.ReadFunction)
{
case 1:
byte[] bytes0 = new byte[len];
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Read(bytes0);
return Task.FromResult(OperResult.CreateSuccessResult(bytes0));
case 2:
byte[] bytes1 = new byte[len];
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Read(bytes1);
return Task.FromResult(OperResult.CreateSuccessResult(bytes1));
case 3:
byte[] bytes3 = new byte[len];
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Read(bytes3);
return Task.FromResult(OperResult.CreateSuccessResult(bytes3));
case 4:
byte[] bytes4 = new byte[len];
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Read(bytes4);
return Task.FromResult(OperResult.CreateSuccessResult(bytes4));
}
return Task.FromResult(new OperResult<byte[]>("功能码错误"));
}
public override Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
ModbusAddress mAddress = null;
try
{
mAddress = new ModbusAddress(address, Station);
}
catch (Exception ex)
{
return Task.FromResult(new OperResult(ex));
}
if (MulStation)
{
ModbusServer03ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer04ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer03ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer04ByteBlocks[mAddress.Station].SetLength(1024 * 128);
}
else
{
if (Station != mAddress.Station)
{
return Task.FromResult(new OperResult("地址错误"));
}
ModbusServer03ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer04ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer03ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer04ByteBlocks[Station].SetLength(1024 * 128);
}
var ModbusServer03ByteBlock = ModbusServer03ByteBlocks[mAddress.Station];
var ModbusServer04ByteBlock = ModbusServer04ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 3:
ModbusServer03ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer03ByteBlock.Write(value);
return Task.FromResult(OperResult.CreateSuccessResult());
case 4:
ModbusServer04ByteBlock.Pos = mAddress.AddressStart * RegisterByteLength;
ModbusServer04ByteBlock.Write(value);
return Task.FromResult(OperResult.CreateSuccessResult());
}
return Task.FromResult(new OperResult("功能码错误"));
}
public override Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
ModbusAddress mAddress = null;
try
{
mAddress = new ModbusAddress(address, Station);
}
catch (Exception ex)
{
return Task.FromResult(new OperResult(ex));
}
if (MulStation)
{
ModbusServer01ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer02ByteBlocks.TryAdd(mAddress.Station, new(1024 * 128));
ModbusServer01ByteBlocks[mAddress.Station].SetLength(1024 * 128);
ModbusServer02ByteBlocks[mAddress.Station].SetLength(1024 * 128);
}
else
{
if (Station != mAddress.Station)
{
return Task.FromResult(new OperResult("地址错误"));
}
ModbusServer01ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer02ByteBlocks.TryAdd(Station, new(1024 * 128));
ModbusServer01ByteBlocks[Station].SetLength(1024 * 128);
ModbusServer02ByteBlocks[Station].SetLength(1024 * 128);
}
var ModbusServer01ByteBlock = ModbusServer01ByteBlocks[mAddress.Station];
var ModbusServer02ByteBlock = ModbusServer02ByteBlocks[mAddress.Station];
switch (mAddress.ReadFunction)
{
case 1:
ModbusServer01ByteBlock.Pos = mAddress.AddressStart;
ModbusServer01ByteBlock.Write(value.BoolArrayToByte());
return Task.FromResult(OperResult.CreateSuccessResult());
case 2:
ModbusServer02ByteBlock.Pos = mAddress.AddressStart;
ModbusServer02ByteBlock.Write(value.BoolArrayToByte());
return Task.FromResult(OperResult.CreateSuccessResult());
}
return Task.FromResult(new OperResult("功能码错误"));
}
protected override async Task Received(SocketClient client, IRequestInfo requestInfo)
{
if (requestInfo is ModbusServerMessage modbusServerMessage)
{
if (modbusServerMessage.CurModbusAddress == null)
{
WriteError(client, modbusServerMessage);
}
if (modbusServerMessage.CurModbusAddress.WriteFunction == 0)
{
var data = await ReadAsync(modbusServerMessage.CurModbusAddress.ToString(), modbusServerMessage.CurModbusAddress.Length);
if (data.IsSuccess)
{
var coreData = data.Content;
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
{
coreData = data.Content.Select(m => m > 0).ToArray().BoolArrayToByte().SelectMiddle(0, (int)Math.Ceiling((double)modbusServerMessage.CurModbusAddress.Length / 8.0));
}
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
.SpliceArray(new byte[] { (byte)coreData.Length }, coreData);
sendData[5] = (byte)(sendData.Length - 6);
client.Send(sendData);
}
else
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
sendData[5] = (byte)(sendData.Length - 6);
sendData[7] = (byte)(sendData[7] + 128);
client.Send(sendData);
}
}
else
{
var coreData = modbusServerMessage.Content;
if (modbusServerMessage.CurModbusAddress.ReadFunction == 1 || modbusServerMessage.CurModbusAddress.ReadFunction == 2)
{
//写入继电器
if (Write != null)
{
if ((await Write(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
{
var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.CurModbusAddress.Length));
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
}
else
{
var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData.ByteToBoolArray(modbusServerMessage.CurModbusAddress.Length));
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
}
else
{
//写入寄存器
if (Write != null)
{
if ((await Write(modbusServerMessage.CurModbusAddress, modbusServerMessage.Content, ThingsGatewayBitConverter, client)).IsSuccess)
{
var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData);
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
else
{
WriteError(client, modbusServerMessage);
}
}
else
{
var result = await WriteAsync(modbusServerMessage.CurModbusAddress.ToString(), coreData);
if (result.IsSuccess)
{
WriteSuccess03(client, modbusServerMessage);
}
else
{
WriteError(client, modbusServerMessage);
}
}
}
}
}
static void WriteError(SocketClient client, ModbusServerMessage modbusServerMessage)
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 8)
.SpliceArray(new byte[] { (byte)1 });//01 lllegal function
sendData[5] = (byte)(sendData.Length - 6);
sendData[7] = (byte)(sendData[7] + 128);
client.Send(sendData);
}
}
private static void WriteSuccess03(SocketClient client, ModbusServerMessage modbusServerMessage)
{
var sendData = modbusServerMessage.ReceivedBytes.SelectMiddle(0, 12);
sendData[5] = (byte)(sendData.Length - 6);
client.Send(sendData);
}
public Func<ModbusAddress, byte[], IThingsGatewayBitConverter, SocketClient, Task<OperResult>> Write;
protected override void Dispose(bool disposing)
{
foreach (var item in ModbusServer01ByteBlocks)
{
item.Value.Dispose();
}
foreach (var item in ModbusServer02ByteBlocks)
{
item.Value.Dispose();
}
foreach (var item in ModbusServer03ByteBlocks)
{
item.Value.Dispose();
}
foreach (var item in ModbusServer04ByteBlocks)
{
item.Value.Dispose();
}
ModbusServer01ByteBlocks.Clear();
ModbusServer02ByteBlocks.Clear();
ModbusServer03ByteBlocks.Clear();
ModbusServer04ByteBlocks.Clear();
Stop();
base.Dispose(disposing);
}
}
}

View File

@@ -0,0 +1,132 @@
using ThingsGateway.Foundation.Extension;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusServerDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusServerMessage>
{
public override byte[] PackCommand(byte[] command)
{
return command;
}
protected override ModbusServerMessage GetInstance()
{
return new ModbusServerMessage();
}
protected override OperResult<byte[]> UnpackResponse(
byte[] send,
byte[] response)
{
return GetModbusData(response.RemoveBegin(6));
}
public ThingsGatewayBitConverter ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
protected override FilterResult GetResponse(ByteBlock byteBlock, ModbusServerMessage request, byte[] allBytes, byte[] bytes)
{
var unpackbytes = UnpackResponse(request.SendBytes, bytes);
request.Message = unpackbytes.Message;
request.ResultCode = unpackbytes.ResultCode;
if (unpackbytes.IsSuccess)
{
request.ReceivedBytes = bytes;
//解析01 03 00 00 00 0A
var station = ThingsGatewayBitConverter.ToByte(bytes, 6);
var function = ThingsGatewayBitConverter.ToByte(bytes, 7);
int addressStart = ThingsGatewayBitConverter.ToInt16(bytes, 8);
if (addressStart == -1)
{
addressStart = 65535;
}
if (function > 4)
{
if (function > 6)
{
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
WriteFunction = function,
ReadFunction = function == 16 ? 3 : function == 15 ? 1 : 3,
Length = ThingsGatewayBitConverter.ToByte(bytes, 11),
};
request.Content = unpackbytes.Content.RemoveBegin(7);
}
else
{
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
WriteFunction = function,
ReadFunction = function == 6 ? 3 : function == 5 ? 1 : 3,
Length = 1,
};
request.Content = unpackbytes.Content.RemoveBegin(4);
}
}
else
{
request.CurModbusAddress = new ModbusAddress()
{
Station = station,
AddressStart = addressStart,
ReadFunction = function,
Length = ThingsGatewayBitConverter.ToByte(bytes, 11),
};
}
return FilterResult.Success;
}
else
{
byteBlock.Pos = byteBlock.Len;
request.ReceivedBytes = allBytes;
return FilterResult.Success;
}
}
/// <summary>
/// 获取modbus写入数据区内容
/// </summary>
/// <param name="send">发送数据</param>
/// <param name="response">返回数据</param>
/// <returns></returns>
internal OperResult<byte[]> GetModbusData(byte[] response)
{
try
{
var func = ThingsGatewayBitConverter.ToByte(response, 1);
if (func == 1 || func == 2 || func == 3 || func == 4 || func == 5 || func == 6)
{
if (response.Length == 6)
return OperResult.CreateSuccessResult(response);
}
else if (func == 15 || func == 16)
{
var length = ThingsGatewayBitConverter.ToByte(response, 6);
if (response.Length == 7 + length)
{
return OperResult.CreateSuccessResult(response);
}
}
return new OperResult<byte[]>(response) { Message = $"数据长度{response.Length}错误" };
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
}
}

View File

@@ -0,0 +1,20 @@
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusServerMessage : MessageBase, IMessage
{
public override int HeadBytesLength => 6;
public override bool CheckHeadBytes(byte[] head)
{
if (head == null || head.Length != 6) return false;
HeadBytes = head;
int num = (HeadBytes[4] * 256) + HeadBytes[5];
BodyLength = num;
return true;
}
public ModbusAddress CurModbusAddress { get; set; }
}
}

View File

@@ -0,0 +1,109 @@
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Resources;
namespace ThingsGateway.Foundation.Adapter.Modbus
{
public class ModbusTcp : ReadWriteDevicesTcpClientBase
{
public ModbusTcpDataHandleAdapter DataHandleAdapter = new();
public ModbusTcp(TcpClient tcpClient) : base(tcpClient)
{
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
RegisterByteLength = 2;
}
public bool IsCheckMessageId { get; set; }
public byte Station { get; set; } = 1;
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
if (commandResult.IsSuccess)
{
var item = commandResult.Content;
var result = TcpClient.GetWaitingClient(new()).SendThenResponse(item, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
DataHandleAdapter.IsCheckMessageId = IsCheckMessageId;
TcpClient.SetDataHandlingAdapter(DataHandleAdapter);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
var result = TcpClient.GetWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
if (commandResult.IsSuccess)
{
var result = TcpClient.GetWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
return collectMessage;
}
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
return new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
}
}

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