首次上传Blazor版本
63
.gitattributes
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
368
.gitignore
vendored
Normal 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
After Width: | Height: | Size: 135 KiB |
BIN
Image/2.png
Normal file
After Width: | Height: | Size: 125 KiB |
BIN
Image/3.png
Normal file
After Width: | Height: | Size: 217 KiB |
BIN
Image/4.png
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
Image/5.png
Normal file
After Width: | Height: | Size: 189 KiB |
BIN
Image/6.png
Normal file
After Width: | Height: | Size: 151 KiB |
BIN
Image/7.png
Normal file
After Width: | Height: | Size: 187 KiB |
BIN
Image/8.png
Normal file
After Width: | Height: | Size: 164 KiB |
BIN
Image/9.png
Normal file
After Width: | Height: | Size: 149 KiB |
BIN
Image/pay.png
Normal file
After Width: | Height: | Size: 326 KiB |
61
README.md
Normal 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
@@ -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
@@ -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
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
45
handbook/docs/01、说明.mdx
Normal 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 之前请进行缜密的测试。在使用期间,由本程序集造成或间接造成的所有损失,均自己承担,与本程序集无关。**
|
48
handbook/docs/02、历史更新.mdx
Normal 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`。
|
||||
- 其余情况,每年发布一个 `主版本`。
|
||||
|
||||
:::
|
||||
|
||||
|
||||
## 开发计划
|
||||
|
||||
- <Tag>新增</Tag> 添加设备是否共用通道的选项(当IP&端口一致时或者串口号一致时生效)
|
||||
|
||||
- <Tag>新增</Tag> 添加OPCDAClient采集插件
|
||||
|
||||
- <Tag>新增</Tag> 添加OPCUAClient采集插件
|
||||
|
||||
- <Tag>新增</Tag> 开发串口基础框架
|
||||
|
||||
##
|
||||
|
||||
## v1.0.0(已发布)
|
||||
|
||||
- <Tag>新增</Tag> 初始发布
|
||||
|
||||
|
||||
##
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
|
||||
-->
|
||||
|
19
handbook/docs/03、支持开源项目.mdx
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
id: 支持开源项目
|
||||
title: 支持开源项目
|
||||
sidebar_label: 03、支持开源项目
|
||||
---
|
||||
|
||||
## 赞助ThingsGateway项目
|
||||
> 您的支持就是我不懈努力的动力。
|
||||
<br></br>如果对您有帮助,请⭐Star关注或扫码捐赠,感谢支持开源!
|
||||
|
||||
#### 赞助途径
|
||||

|
||||
|
||||
|
||||
#### 赞助名单(以下排名只按照赞助时间顺序)
|
||||
|
||||
1. 绢(10¥)
|
||||
|
||||
|
19
handbook/docs/04、快速启动.mdx
Normal 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;
|
||||
|
||||

|
70
handbook/docs/05、网关配置/5.1、采集设备.mdx
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
id: collectdevice
|
||||
title: 采集设备配置说明
|
||||
sidebar_label: 5.1、采集设备
|
||||
---
|
||||
|
||||
### (一)添加/修改采集设备
|
||||
|
||||

|
||||
<details>
|
||||
<summary>设备基本属性</summary>
|
||||
<div>
|
||||
|
||||
#### 名称
|
||||
当前采集设备名称,全局唯一(采集设备)
|
||||
|
||||
#### 描述
|
||||
当前采集设备描述
|
||||
|
||||
#### 设备驱动名称
|
||||
当前采集设备选择的采集驱动插件名称,通过级联选择器选择对应驱动名称设置
|
||||
|
||||
#### 设备使能
|
||||
启动/停用采集设备
|
||||
|
||||
#### 输出日志
|
||||
启动/停用日志输出
|
||||
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>设备附加属性</summary>
|
||||
<div>
|
||||
|
||||
**对于不同的驱动,网关会将对应的插件配置规则保存在附加属性内**
|
||||
|
||||
**对应的属性描述可以查看输入框占位文本或者浮动Tips尝试理解;或者查看第六节,找到对应的驱动说明**
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
### (二)导入导出采集设备
|
||||
|
||||
#### 导出模板
|
||||

|
||||

|
||||
|
||||
可以看到采集设备的excel模板,填入规则参考第一小节
|
||||
|
||||
|
||||
#### 导入
|
||||
|
||||

|
||||
|
||||
弹窗选择导入的excel文件后,点击下一步进行校验
|
||||
|
||||

|
||||
|
||||
根据红色字体提示,修改对应错误列字段
|
||||
|
||||
全部正常时可进行下一步
|
||||
|
||||

|
||||
|
||||
点击上传,等待导入完成,完成后自动退出弹窗,否则提示错误
|
||||
|
28
handbook/docs/05、网关配置/5.2、变量.mdx
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
id: variable
|
||||
title: 变量配置说明
|
||||
sidebar_label: 5.2、变量
|
||||
---
|
||||
|
||||
### (二)变量配置
|
||||
|
||||
**选择设备**
|
||||
|
||||
|
||||
**变量地址/执行参数**
|
||||
|
||||
不同驱动的变量地址规则查看对应文档
|
||||
|
||||
|
||||
**表达式变换**
|
||||
|
||||
支持多种运算符,具体可查看 [ExpressionEvaluator WiKi](https://gitee.com/diego2098/ThingsGateway/releases/latest)
|
||||
|
||||
|
||||
### (三)重启设备
|
||||
|
||||
**单个设备重启如图,或可按 [全部重启] 按钮,重启全部设备**
|
||||
|
||||
|
||||
|
||||
### (四)查看状态
|
43
handbook/docs/05、网关配置/5.3、上传设备.mdx
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: uploaddevice
|
||||
title: 上传设备配置说明
|
||||
sidebar_label: 5.3、上传设备
|
||||
---
|
||||
**上传设备配置与上传设备大致相同**
|
||||
|
||||
### (一)添加/修改上传设备
|
||||
|
||||

|
||||
<details>
|
||||
<summary>设备基本属性</summary>
|
||||
<div>
|
||||
|
||||
#### 名称
|
||||
当前上传设备名称,全局唯一(上传设备)
|
||||
|
||||
#### 描述
|
||||
当前上传设备描述
|
||||
|
||||
#### 设备驱动名称
|
||||
当前上传设备选择的上传驱动插件名称,通过级联选择器选择对应驱动名称设置
|
||||
|
||||
#### 设备使能
|
||||
启动/停用上传设备
|
||||
|
||||
#### 输出日志
|
||||
启动/停用日志输出
|
||||
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>设备附加属性</summary>
|
||||
<div>
|
||||
|
||||
**对于不同的驱动,网关会将对应的插件配置规则保存在附加属性内**
|
||||
|
||||
**对应的属性描述可以查看输入框占位文本或者浮动Tips尝试理解;或者查看第六节,找到对应的驱动说明**
|
||||
|
||||
</div>
|
||||
</details>
|
6
handbook/docs/05、网关配置/5.4、插件管理.mdx
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: plugincore
|
||||
title: 插件配置说明
|
||||
sidebar_label: 5.4、插件管理
|
||||
---
|
||||
|
6
handbook/docs/05、网关配置/5.5、其他配置.mdx
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
id: otherconfig
|
||||
title: 其他配置说明
|
||||
sidebar_label: 5.5、其他配置
|
||||
---
|
||||
|
39
handbook/docs/06、采集驱动说明/Modbus.mdx
Normal 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功能码 |
|
||||
|
||||
### (二)变量其他方法说明
|
||||
|
||||
- 无
|
39
handbook/docs/07、上传驱动说明/7.1、MqttServer.mdx
Normal 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功能码 |
|
||||
|
||||
### (二)变量其他方法说明
|
||||
|
||||
- 无
|
39
handbook/docs/07、上传驱动说明/7.2、MqttClient.mdx
Normal 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功能码 |
|
||||
|
||||
### (二)变量其他方法说明
|
||||
|
||||
- 无
|
39
handbook/docs/07、上传驱动说明/7.3、ModbusServer.mdx
Normal 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功能码 |
|
||||
|
||||
### (二)变量其他方法说明
|
||||
|
||||
- 无
|
131
handbook/docusaurus.config.js
Normal 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
@@ -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
50
handbook/package.json
Normal 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
@@ -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;
|
10
handbook/src/components/Humiliation.js
Normal 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>
|
||||
}
|
64
handbook/src/components/Tag.js
Normal 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>
|
||||
);
|
||||
}
|
17
handbook/src/components/Tag.module.css
Normal 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;
|
||||
}
|
12
handbook/src/components/iconfonts/IconBug.d.ts
vendored
Normal 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;
|
31
handbook/src/components/iconfonts/IconBug.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconDayi.d.ts
vendored
Normal 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;
|
31
handbook/src/components/iconfonts/IconDayi.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconDown.d.ts
vendored
Normal 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;
|
27
handbook/src/components/iconfonts/IconDown.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconFuwu.d.ts
vendored
Normal 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;
|
27
handbook/src/components/iconfonts/IconFuwu.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconGengxin.d.ts
vendored
Normal 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;
|
27
handbook/src/components/iconfonts/IconGengxin.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconShanchu.d.ts
vendored
Normal 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;
|
31
handbook/src/components/iconfonts/IconShanchu.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconShengji.d.ts
vendored
Normal 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;
|
35
handbook/src/components/iconfonts/IconShengji.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconTiaozheng.d.ts
vendored
Normal 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;
|
27
handbook/src/components/iconfonts/IconTiaozheng.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconUp.d.ts
vendored
Normal 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;
|
27
handbook/src/components/iconfonts/IconUp.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconWendang.d.ts
vendored
Normal 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;
|
35
handbook/src/components/iconfonts/IconWendang.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconXinzeng.d.ts
vendored
Normal 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;
|
31
handbook/src/components/iconfonts/IconXinzeng.js
Normal 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;
|
12
handbook/src/components/iconfonts/IconYouhua.d.ts
vendored
Normal 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;
|
27
handbook/src/components/iconfonts/IconYouhua.js
Normal 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;
|
3
handbook/src/components/iconfonts/helper.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/* eslint-disable */
|
||||
|
||||
export declare const getIconColor: (color: string | string[] | undefined, index: number, defaultColor: string) => string;
|
17
handbook/src/components/iconfonts/helper.js
Normal 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;
|
||||
};
|
25
handbook/src/components/iconfonts/index.d.ts
vendored
Normal 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;
|
61
handbook/src/components/iconfonts/index.js
Normal 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;
|
25
handbook/src/css/custom.css
Normal 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);
|
||||
}
|
1
handbook/src/pages/android.svg
Normal 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 |
1
handbook/src/pages/docker.svg
Normal 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 |
540
handbook/src/pages/index.css
Normal 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
@@ -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;
|
25
handbook/src/pages/index.own.css
Normal 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;
|
||||
}
|
1
handbook/src/pages/kubernetes.svg
Normal file
After Width: | Height: | Size: 7.4 KiB |
1
handbook/src/pages/linux.svg
Normal file
After Width: | Height: | Size: 19 KiB |
1
handbook/src/pages/macos.svg
Normal 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 |
1
handbook/src/pages/windows.svg
Normal 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 |
0
handbook/static/.nojekyll
Normal file
BIN
handbook/static/img/addcollectdevice1.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
handbook/static/img/adduploaddevice1.png
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
handbook/static/img/collectdevice2.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
handbook/static/img/collectdevice3.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
handbook/static/img/collectdevice4.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
handbook/static/img/collectdevice5.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
handbook/static/img/collectdevice6.png
Normal file
After Width: | Height: | Size: 87 KiB |
BIN
handbook/static/img/favicon.ico
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
handbook/static/img/login.png
Normal file
After Width: | Height: | Size: 117 KiB |
BIN
handbook/static/img/pay.png
Normal file
After Width: | Height: | Size: 326 KiB |
BIN
handbook/static/img/thingsgatewaylogo.png
Normal file
After Width: | Height: | Size: 77 KiB |
7
handbook/static/script/baidutongji.js
Normal 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);
|
||||
})();
|
@@ -0,0 +1,4 @@
|
||||
global using System;
|
||||
|
||||
global using TouchSocket.Core;
|
||||
global using TouchSocket.Sockets;
|
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@@ -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; }
|
||||
|
||||
}
|
||||
}
|
@@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
}
|