Compare commits
72 Commits
35edd7dc43
...
1.1.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
03089adad6 | ||
![]() |
4a1fe746ab | ||
![]() |
aa52c05d2c | ||
![]() |
26407a43e7 | ||
![]() |
a02934bf19 | ||
![]() |
09c65fba09 | ||
![]() |
4305c727d0 | ||
![]() |
188339897f | ||
![]() |
4ecff9a707 | ||
![]() |
355aed49c6 | ||
![]() |
4717b6b0f0 | ||
![]() |
45ebe9048d | ||
![]() |
b2170c49a3 | ||
![]() |
dc2f4d6115 | ||
![]() |
1eb132440f | ||
![]() |
a464bbc37a | ||
![]() |
ed995697c2 | ||
![]() |
163cd84c7b | ||
![]() |
293d7cc292 | ||
![]() |
5de1b4e74c | ||
![]() |
7b474975da | ||
![]() |
beab51516b | ||
![]() |
fe8685a50c | ||
![]() |
f9af5d0885 | ||
![]() |
e8136a9720 | ||
![]() |
531e5d4556 | ||
![]() |
e66255963a | ||
![]() |
246aac8ee4 | ||
![]() |
23cfeff685 | ||
![]() |
a5e7e0d126 | ||
![]() |
5bebc30ba0 | ||
![]() |
0e7057f5b9 | ||
![]() |
7c6c365ba4 | ||
![]() |
424c9bb0c5 | ||
![]() |
9d0f26594c | ||
![]() |
99c17de079 | ||
![]() |
b1e3dd0af6 | ||
![]() |
261cb89530 | ||
![]() |
ff6773ba37 | ||
![]() |
bdfbbfcbbd | ||
![]() |
0c4cd56758 | ||
![]() |
4a36658321 | ||
![]() |
7aae938685 | ||
![]() |
3723401e7a | ||
![]() |
70631366a9 | ||
![]() |
0e40bbda3e | ||
![]() |
e9aa475398 | ||
![]() |
8d2a811184 | ||
![]() |
dd7f5b6700 | ||
![]() |
a4f6277737 | ||
![]() |
c2bfaacbb7 | ||
![]() |
a17cbfa2d4 | ||
![]() |
fb9a101555 | ||
![]() |
e319cf0200 | ||
![]() |
0a8395ef6a | ||
![]() |
38df5e01be | ||
![]() |
ebd891a868 | ||
![]() |
4ab2395cbe | ||
![]() |
5f1f989fc9 | ||
![]() |
44b709eee3 | ||
![]() |
d0d7726597 | ||
![]() |
054c342aeb | ||
![]() |
c79c33baf7 | ||
![]() |
23b00e35b2 | ||
![]() |
fe51079266 | ||
![]() |
0791b0bbee | ||
![]() |
dbf04c8eeb | ||
![]() |
6204256df8 | ||
![]() |
93cc8c2327 | ||
![]() |
68a2e5bbbc | ||
![]() |
72792153f2 | ||
![]() |
88b6ef1897 |
37
README.md
37
README.md
@@ -4,8 +4,7 @@
|
||||
#### 介绍
|
||||
|
||||
基于[ThingsBlazor](https://gitee.com/diego2098/ThingsBlazor)权限管理框架开发的跨平台边缘采集网关,支持南北端插件式开发,
|
||||
动态更新插件,
|
||||
并拥有较完善的北端Rpc权限管理。
|
||||
动态更新插件,并拥有较完善的北端Rpc权限管理。
|
||||
|
||||
|
||||
#### 功能亮点
|
||||
@@ -13,14 +12,29 @@
|
||||
- Blazor Server架构,开发部署更简单
|
||||
- 采集/上传配置完全支持Excel导入导出
|
||||
- 插件式驱动,方便驱动二次开发,并支持动态更新
|
||||
- Modbus、OPCDA、OPCUA采集插件(支持导入节点)
|
||||
- Modbus Server、Mqtt Server(Client)、OPC UAServer插件,支持Rpc写入,支持WebApi
|
||||
- 支持时序数据库存储
|
||||
- 实时/历史报警(Sql转储),支持布尔/高低限值
|
||||
|
||||
#### 社区版采集插件
|
||||
> 支持分包解析/订阅
|
||||
- Modbus(Rtu/Tcp/Udp)
|
||||
- OPCDAClient(支持导入节点)
|
||||
- OPCUAClient(支持导入节点)
|
||||
- 西门子S7协议
|
||||
|
||||
#### 社区版上传插件
|
||||
> 支持Rpc写入
|
||||
- Modbus Server
|
||||
- Mqtt Server
|
||||
- Mqtt Client
|
||||
|
||||
> 不支持Rpc
|
||||
- RabbitMQ
|
||||
|
||||
|
||||
#### nuget
|
||||
|
||||
- Modbus库,支持ModbusTcp、ModbusRtuOverTcp、ModbusUdp、ModbusServer等
|
||||
- Modbus库,支持ModbusTcp、ModbusRtu、ModbusRtuOverTcp、ModbusUdp、ModbusServer等
|
||||
``` powershell
|
||||
dotnet add package ThingsGateway.Foundation.Adapter.Modbus
|
||||
```
|
||||
@@ -32,6 +46,12 @@
|
||||
``` powershell
|
||||
dotnet add package ThingsGateway.Foundation.Adapter.OPCUA
|
||||
```
|
||||
|
||||
- S7库
|
||||
``` powershell
|
||||
dotnet add package ThingsGateway.Foundation.Adapter.Siemens
|
||||
```
|
||||
|
||||
#### 效果图
|
||||
<table>
|
||||
<tr>
|
||||
@@ -59,6 +79,7 @@
|
||||
|
||||
#### 补充说明
|
||||
* 使用OPC相关插件时请遵循OPC基金会的授权规则
|
||||
* 使用OPCDA插件时,需安装OPC核心库,[文件地址](https://gitee.com/diego2098/ThingsGateway/attach_files)
|
||||
|
||||
#### 开源协议
|
||||
|
||||
@@ -66,11 +87,11 @@
|
||||
|
||||
|
||||
|
||||
|
||||
#### 支持作者
|
||||
如果对您有帮助,请点击右上角⭐Star关注,感谢支持开源!
|
||||
若希望捐赠项目,请跳转到文档站点查看捐赠码或使用Gitee捐赠功能
|
||||
|
||||
|
||||
若希望捐赠项目,请[点击](https://diego2098.gitee.io/thingsgateway/docs/03%E3%80%81%E6%94%AF%E6%8C%81%E9%A1%B9%E7%9B%AE%E4%B8%8EPro%E7%89%88%E8%AF%B4%E6%98%8E/%E6%94%AF%E6%8C%81%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/)查看捐赠码或使用Gitee捐赠功能
|
||||
|
||||
|
||||
#### 联系作者
|
||||
* QQ群:605534569
|
||||
|
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
1
handbook/build/assets/js/186a3eb8.24a97c3d.js
Normal file
1
handbook/build/assets/js/186a3eb8.24a97c3d.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/19b7f836.7e71f890.js
Normal file
1
handbook/build/assets/js/19b7f836.7e71f890.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/39afbd6e.73bcb94a.js
Normal file
1
handbook/build/assets/js/39afbd6e.73bcb94a.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/46d61d03.b084f227.js
Normal file
1
handbook/build/assets/js/46d61d03.b084f227.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/5ae411f8.5d1d50cd.js
Normal file
1
handbook/build/assets/js/5ae411f8.5d1d50cd.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/6dde87d0.641bb477.js
Normal file
1
handbook/build/assets/js/6dde87d0.641bb477.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/84ef20df.b6365e51.js
Normal file
1
handbook/build/assets/js/84ef20df.b6365e51.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/879054ab.f2f9317e.js
Normal file
1
handbook/build/assets/js/879054ab.f2f9317e.js
Normal file
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/935f2afb.30ea2125.js
Normal file
1
handbook/build/assets/js/935f2afb.30ea2125.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkthingsgateway=self.webpackChunkthingsgateway||[]).push([[564],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function c(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},i=Object.keys(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n<i.length;n++)r=i[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),s=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},p=function(e){var t=s(e.components);return n.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,l=e.parentName,p=c(e,["components","mdxType","originalType","parentName"]),d=s(r),f=a,g=d["".concat(l,".").concat(f)]||d[f]||u[f]||i;return r?n.createElement(g,o(o({ref:t},p),{},{components:r})):n.createElement(g,o({ref:t},p))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=r.length,o=new Array(i);o[0]=d;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:a,o[1]=c;for(var s=2;s<i;s++)o[s]=r[s];return n.createElement.apply(null,o)}return n.createElement.apply(null,r)}d.displayName="MDXCreateElement"},5771:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>u,frontMatter:()=>i,metadata:()=>c,toc:()=>s});var n=r(7462),a=(r(7294),r(3905));const i={id:"quickstart",title:"\u5feb\u901f\u542f\u52a8",sidebar_label:"04\u3001\u5feb\u901f\u542f\u52a8"},o=void 0,c={unversionedId:"quickstart",id:"quickstart",title:"\u5feb\u901f\u542f\u52a8",description:"\uff08\u4e00\uff09\u3001\u4e0b\u8f7d\u6e90\u7801",source:"@site/docs/04\u3001\u5feb\u901f\u542f\u52a8.mdx",sourceDirName:".",slug:"/quickstart",permalink:"/thingsgateway/docs/quickstart",draft:!1,editUrl:"https://gitee.com/diego2098/ThingsGateway/tree/master/handbook/docs/04\u3001\u5feb\u901f\u542f\u52a8.mdx",tags:[],version:"current",frontMatter:{id:"quickstart",title:"\u5feb\u901f\u542f\u52a8",sidebar_label:"04\u3001\u5feb\u901f\u542f\u52a8"},sidebar:"tutorialSidebar",previous:{title:"03\u3001\u652f\u6301\u5f00\u6e90\u9879\u76ee",permalink:"/thingsgateway/docs/\u652f\u6301\u5f00\u6e90\u9879\u76ee"},next:{title:"5.1\u3001\u91c7\u96c6\u8bbe\u5907",permalink:"/thingsgateway/docs/05\u3001\u7f51\u5173\u914d\u7f6e/collectdevice"}},l={},s=[{value:"\uff08\u4e00\uff09\u3001\u4e0b\u8f7d\u6e90\u7801",id:"\u4e00\u4e0b\u8f7d\u6e90\u7801",level:3},{value:"\uff08\u4e8c\uff09\u3001\u914d\u7f6e\u5e76\u542f\u52a8\u7cfb\u7edf",id:"\u4e8c\u914d\u7f6e\u5e76\u542f\u52a8\u7cfb\u7edf",level:3}],p={toc:s};function u(e){let{components:t,...i}=e;return(0,a.kt)("wrapper",(0,n.Z)({},p,i,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\u4e00\u4e0b\u8f7d\u6e90\u7801"},"\uff08\u4e00\uff09\u3001\u4e0b\u8f7d\u6e90\u7801"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://gitee.com/diego2098/ThingsGateway"},"\u6700\u65b0\u7248\u672c")," "),(0,a.kt)("h3",{id:"\u4e8c\u914d\u7f6e\u5e76\u542f\u52a8\u7cfb\u7edf"},"\uff08\u4e8c\uff09\u3001\u914d\u7f6e\u5e76\u542f\u52a8\u7cfb\u7edf"),(0,a.kt)("p",null,"1\u3001 vs2022\u6253\u5f00\u89e3\u51b3\u65b9\u6848\uff0c\u8bbe\u7f6eThingsGateway.Web.Server\u4e3a\u542f\u52a8\u9879\u76ee"),(0,a.kt)("p",null,"2\u3001 \u76f4\u63a5debug\u8fd0\u884c"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u9ed8\u8ba4\u8fd0\u884c\u5730\u5740\uff1ahttp://localhost:7100;")),(0,a.kt)("p",null,(0,a.kt)("img",{src:r(2405).Z,width:"2558",height:"1459"})))}u.isMDXComponent=!0},2405:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/login-ec50fefb8cb6a0b5ed67912fc045f617.png"}}]);
|
1
handbook/build/assets/js/976454a3.a7b325a9.js
Normal file
1
handbook/build/assets/js/976454a3.a7b325a9.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self.webpackChunkthingsgateway=self.webpackChunkthingsgateway||[]).push([[564],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>f});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?o(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):o(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function c(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},o=Object.keys(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)r=o[n],t.indexOf(r)>=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var l=n.createContext({}),s=function(e){var t=n.useContext(l),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},p=function(e){var t=s(e.components);return n.createElement(l.Provider,{value:t},e.children)},u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,o=e.originalType,l=e.parentName,p=c(e,["components","mdxType","originalType","parentName"]),d=s(r),f=a,g=d["".concat(l,".").concat(f)]||d[f]||u[f]||o;return r?n.createElement(g,i(i({ref:t},p),{},{components:r})):n.createElement(g,i({ref:t},p))}));function f(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=r.length,i=new Array(o);i[0]=d;var c={};for(var l in t)hasOwnProperty.call(t,l)&&(c[l]=t[l]);c.originalType=e,c.mdxType="string"==typeof e?e:a,i[1]=c;for(var s=2;s<o;s++)i[s]=r[s];return n.createElement.apply(null,i)}return n.createElement.apply(null,r)}d.displayName="MDXCreateElement"},5771:(e,t,r)=>{r.r(t),r.d(t,{assets:()=>l,contentTitle:()=>i,default:()=>u,frontMatter:()=>o,metadata:()=>c,toc:()=>s});var n=r(7462),a=(r(7294),r(3905));const o={id:"quickstart",title:"\u5feb\u901f\u542f\u52a8",sidebar_label:"04\u3001\u5feb\u901f\u542f\u52a8"},i=void 0,c={unversionedId:"quickstart",id:"quickstart",title:"\u5feb\u901f\u542f\u52a8",description:"\uff08\u4e00\uff09\u3001\u4e0b\u8f7d\u6e90\u7801",source:"@site/docs/04\u3001\u5feb\u901f\u542f\u52a8.mdx",sourceDirName:".",slug:"/quickstart",permalink:"/thingsgateway/docs/quickstart",draft:!1,editUrl:"https://gitee.com/diego2098/ThingsGateway/tree/master/handbook/docs/04\u3001\u5feb\u901f\u542f\u52a8.mdx",tags:[],version:"current",frontMatter:{id:"quickstart",title:"\u5feb\u901f\u542f\u52a8",sidebar_label:"04\u3001\u5feb\u901f\u542f\u52a8"},sidebar:"tutorialSidebar",previous:{title:"3.2\u3001Pro\u7248\u8bf4\u660e",permalink:"/thingsgateway/docs/03\u3001\u652f\u6301\u9879\u76ee\u4e0ePro\u7248\u8bf4\u660e/Pro"},next:{title:"5.1\u3001\u91c7\u96c6\u8bbe\u5907",permalink:"/thingsgateway/docs/05\u3001\u7f51\u5173\u914d\u7f6e/collectdevice"}},l={},s=[{value:"\uff08\u4e00\uff09\u3001\u4e0b\u8f7d\u6e90\u7801",id:"\u4e00\u4e0b\u8f7d\u6e90\u7801",level:3},{value:"\uff08\u4e8c\uff09\u3001\u914d\u7f6e\u5e76\u542f\u52a8\u7cfb\u7edf",id:"\u4e8c\u914d\u7f6e\u5e76\u542f\u52a8\u7cfb\u7edf",level:3}],p={toc:s};function u(e){let{components:t,...o}=e;return(0,a.kt)("wrapper",(0,n.Z)({},p,o,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h3",{id:"\u4e00\u4e0b\u8f7d\u6e90\u7801"},"\uff08\u4e00\uff09\u3001\u4e0b\u8f7d\u6e90\u7801"),(0,a.kt)("p",null,(0,a.kt)("a",{parentName:"p",href:"https://gitee.com/diego2098/ThingsGateway"},"\u6700\u65b0\u7248\u672c")," "),(0,a.kt)("h3",{id:"\u4e8c\u914d\u7f6e\u5e76\u542f\u52a8\u7cfb\u7edf"},"\uff08\u4e8c\uff09\u3001\u914d\u7f6e\u5e76\u542f\u52a8\u7cfb\u7edf"),(0,a.kt)("p",null,"1\u3001 vs2022\u6253\u5f00\u89e3\u51b3\u65b9\u6848\uff0c\u8bbe\u7f6eThingsGateway.Web.Server\u4e3a\u542f\u52a8\u9879\u76ee"),(0,a.kt)("p",null,"2\u3001 \u76f4\u63a5debug\u8fd0\u884c"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"\u9ed8\u8ba4\u8fd0\u884c\u5730\u5740\uff1ahttp://localhost:7100;")),(0,a.kt)("p",null,(0,a.kt)("img",{src:r(2405).Z,width:"2558",height:"1459"})))}u.isMDXComponent=!0},2405:(e,t,r)=>{r.d(t,{Z:()=>n});const n=r.p+"assets/images/login-ec50fefb8cb6a0b5ed67912fc045f617.png"}}]);
|
1
handbook/build/assets/js/9a8d6690.1f1d08eb.js
Normal file
1
handbook/build/assets/js/9a8d6690.1f1d08eb.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
handbook/build/assets/js/b8d02a9c.25449686.js
Normal file
1
handbook/build/assets/js/b8d02a9c.25449686.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
handbook/build/assets/js/main.7870138f.js
Normal file
2
handbook/build/assets/js/main.7870138f.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
(()=>{"use strict";var e,t,a,r,f,o={},n={};function c(e){var t=n[e];if(void 0!==t)return t.exports;var a=n[e]={exports:{}};return o[e].call(a.exports,a,a.exports,c),a.exports}c.m=o,e=[],c.O=(t,a,r,f)=>{if(!a){var o=1/0;for(b=0;b<e.length;b++){a=e[b][0],r=e[b][1],f=e[b][2];for(var n=!0,i=0;i<a.length;i++)(!1&f||o>=f)&&Object.keys(c.O).every((e=>c.O[e](a[i])))?a.splice(i--,1):(n=!1,f<o&&(o=f));if(n){e.splice(b--,1);var d=r();void 0!==d&&(t=d)}}return t}f=f||0;for(var b=e.length;b>0&&e[b-1][2]>f;b--)e[b]=e[b-1];e[b]=[a,r,f]},c.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return c.d(t,{a:t}),t},a=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,c.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var f=Object.create(null);c.r(f);var o={};t=t||[null,a({}),a([]),a(a)];for(var n=2&r&&e;"object"==typeof n&&!~t.indexOf(n);n=a(n))Object.getOwnPropertyNames(n).forEach((t=>o[t]=()=>e[t]));return o.default=()=>e,c.d(f,o),f},c.d=(e,t)=>{for(var a in t)c.o(t,a)&&!c.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},c.f={},c.e=e=>Promise.all(Object.keys(c.f).reduce(((t,a)=>(c.f[a](e,t),t)),[])),c.u=e=>"assets/js/"+({22:"879054ab",53:"935f2afb",175:"c361d1eb",189:"186a3eb8",195:"c4f5d8e4",217:"b8d02a9c",242:"26929101",261:"cb4f4ca9",282:"96f6c49e",320:"b86d2c6e",373:"760264af",397:"13a5cec0",514:"1be78505",526:"fd321996",544:"a9d01d88",556:"39afbd6e",564:"976454a3",623:"5ae411f8",676:"2ae4ef1c",866:"ec4c2846",875:"444af13b",889:"7f2059f1",916:"f21858e6",918:"17896441",920:"1a4e3797",966:"4ab8a817",987:"8606c6a1",994:"53c1042e",996:"19b7f836"}[e]||e)+"."+{22:"39368577",53:"e90da8ff",175:"ee8bab92",189:"80ad5d61",195:"3eff4979",217:"bdf25ce5",242:"143c32d8",261:"983c3141",282:"b182a2f0",320:"82fd5222",373:"3ed61cd5",397:"36c4e731",443:"a35c493a",514:"79ca6d8a",523:"bb83377d",525:"ee7ff280",526:"f5d30c71",544:"0aefd47f",556:"81d00235",564:"7efa7a24",623:"9d713392",676:"152595fe",866:"de5833c1",875:"a37fbda4",889:"e7147378",916:"b12940cb",918:"e52e3593",920:"83cee253",966:"7d0118d3",972:"a79306f2",987:"f55d8fd1",994:"e1e7c39f",996:"98239486"}[e]+".js",c.miniCssF=e=>{},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),c.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},f="thingsgateway:",c.l=(e,t,a,o)=>{if(r[e])r[e].push(t);else{var n,i;if(void 0!==a)for(var d=document.getElementsByTagName("script"),b=0;b<d.length;b++){var u=d[b];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==f+a){n=u;break}}n||(i=!0,(n=document.createElement("script")).charset="utf-8",n.timeout=120,c.nc&&n.setAttribute("nonce",c.nc),n.setAttribute("data-webpack",f+a),n.src=e),r[e]=[t];var l=(t,a)=>{n.onerror=n.onload=null,clearTimeout(s);var f=r[e];if(delete r[e],n.parentNode&&n.parentNode.removeChild(n),f&&f.forEach((e=>e(a))),t)return t(a)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:n}),12e4);n.onerror=l.bind(null,n.onerror),n.onload=l.bind(null,n.onload),i&&document.head.appendChild(n)}},c.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.p="/thingsgateway/",c.gca=function(e){return e={17896441:"918",26929101:"242","879054ab":"22","935f2afb":"53",c361d1eb:"175","186a3eb8":"189",c4f5d8e4:"195",b8d02a9c:"217",cb4f4ca9:"261","96f6c49e":"282",b86d2c6e:"320","760264af":"373","13a5cec0":"397","1be78505":"514",fd321996:"526",a9d01d88:"544","39afbd6e":"556","976454a3":"564","5ae411f8":"623","2ae4ef1c":"676",ec4c2846:"866","444af13b":"875","7f2059f1":"889",f21858e6:"916","1a4e3797":"920","4ab8a817":"966","8606c6a1":"987","53c1042e":"994","19b7f836":"996"}[e]||e,c.p+c.u(e)},(()=>{var e={303:0,532:0};c.f.j=(t,a)=>{var r=c.o(e,t)?e[t]:void 0;if(0!==r)if(r)a.push(r[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var f=new Promise(((a,f)=>r=e[t]=[a,f]));a.push(r[2]=f);var o=c.p+c.u(t),n=new Error;c.l(o,(a=>{if(c.o(e,t)&&(0!==(r=e[t])&&(e[t]=void 0),r)){var f=a&&("load"===a.type?"missing":a.type),o=a&&a.target&&a.target.src;n.message="Loading chunk "+t+" failed.\n("+f+": "+o+")",n.name="ChunkLoadError",n.type=f,n.request=o,r[1](n)}}),"chunk-"+t,t)}},c.O.j=t=>0===e[t];var t=(t,a)=>{var r,f,o=a[0],n=a[1],i=a[2],d=0;if(o.some((t=>0!==e[t]))){for(r in n)c.o(n,r)&&(c.m[r]=n[r]);if(i)var b=i(c)}for(t&&t(a);d<o.length;d++)f=o[d],c.o(e,f)&&e[f]&&e[f][0](),e[f]=0;return c.O(b)},a=self.webpackChunkthingsgateway=self.webpackChunkthingsgateway||[];a.forEach(t.bind(null,0)),a.push=t.bind(null,a.push.bind(a))})()})();
|
1
handbook/build/assets/js/runtime~main.66237d79.js
Normal file
1
handbook/build/assets/js/runtime~main.66237d79.js
Normal file
@@ -0,0 +1 @@
|
||||
(()=>{"use strict";var e,t,a,r,f,o={},d={};function n(e){var t=d[e];if(void 0!==t)return t.exports;var a=d[e]={exports:{}};return o[e].call(a.exports,a,a.exports,n),a.exports}n.m=o,e=[],n.O=(t,a,r,f)=>{if(!a){var o=1/0;for(b=0;b<e.length;b++){a=e[b][0],r=e[b][1],f=e[b][2];for(var d=!0,c=0;c<a.length;c++)(!1&f||o>=f)&&Object.keys(n.O).every((e=>n.O[e](a[c])))?a.splice(c--,1):(d=!1,f<o&&(o=f));if(d){e.splice(b--,1);var i=r();void 0!==i&&(t=i)}}return t}f=f||0;for(var b=e.length;b>0&&e[b-1][2]>f;b--)e[b]=e[b-1];e[b]=[a,r,f]},n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},a=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,n.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var f=Object.create(null);n.r(f);var o={};t=t||[null,a({}),a([]),a(a)];for(var d=2&r&&e;"object"==typeof d&&!~t.indexOf(d);d=a(d))Object.getOwnPropertyNames(d).forEach((t=>o[t]=()=>e[t]));return o.default=()=>e,n.d(f,o),f},n.d=(e,t)=>{for(var a in t)n.o(t,a)&&!n.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce(((t,a)=>(n.f[a](e,t),t)),[])),n.u=e=>"assets/js/"+({22:"879054ab",53:"935f2afb",175:"c361d1eb",189:"186a3eb8",195:"c4f5d8e4",217:"b8d02a9c",242:"26929101",261:"cb4f4ca9",282:"96f6c49e",320:"b86d2c6e",373:"760264af",397:"13a5cec0",514:"1be78505",526:"fd321996",544:"a9d01d88",556:"39afbd6e",564:"976454a3",593:"84ef20df",623:"5ae411f8",705:"46d61d03",866:"ec4c2846",874:"6dde87d0",875:"444af13b",889:"7f2059f1",916:"f21858e6",918:"17896441",920:"1a4e3797",946:"9a8d6690",966:"4ab8a817",987:"8606c6a1",994:"53c1042e",996:"19b7f836"}[e]||e)+"."+{22:"f2f9317e",53:"30ea2125",175:"ee8bab92",189:"24a97c3d",195:"3eff4979",217:"25449686",242:"143c32d8",261:"983c3141",282:"b182a2f0",320:"903ca3f1",373:"3ed61cd5",397:"36c4e731",443:"a35c493a",514:"79ca6d8a",523:"bb83377d",525:"ee7ff280",526:"f5d30c71",544:"0aefd47f",556:"73bcb94a",564:"a7b325a9",593:"b6365e51",623:"5d1d50cd",705:"b084f227",866:"de5833c1",874:"641bb477",875:"a37fbda4",889:"de3ea03b",916:"79872004",918:"e52e3593",920:"83cee253",946:"1f1d08eb",966:"7d0118d3",972:"a79306f2",987:"f55d8fd1",994:"8816391f",996:"7e71f890"}[e]+".js",n.miniCssF=e=>{},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},f="thingsgateway:",n.l=(e,t,a,o)=>{if(r[e])r[e].push(t);else{var d,c;if(void 0!==a)for(var i=document.getElementsByTagName("script"),b=0;b<i.length;b++){var u=i[b];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==f+a){d=u;break}}d||(c=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,n.nc&&d.setAttribute("nonce",n.nc),d.setAttribute("data-webpack",f+a),d.src=e),r[e]=[t];var l=(t,a)=>{d.onerror=d.onload=null,clearTimeout(s);var f=r[e];if(delete r[e],d.parentNode&&d.parentNode.removeChild(d),f&&f.forEach((e=>e(a))),t)return t(a)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),c&&document.head.appendChild(d)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/thingsgateway/",n.gca=function(e){return e={17896441:"918",26929101:"242","879054ab":"22","935f2afb":"53",c361d1eb:"175","186a3eb8":"189",c4f5d8e4:"195",b8d02a9c:"217",cb4f4ca9:"261","96f6c49e":"282",b86d2c6e:"320","760264af":"373","13a5cec0":"397","1be78505":"514",fd321996:"526",a9d01d88:"544","39afbd6e":"556","976454a3":"564","84ef20df":"593","5ae411f8":"623","46d61d03":"705",ec4c2846:"866","6dde87d0":"874","444af13b":"875","7f2059f1":"889",f21858e6:"916","1a4e3797":"920","9a8d6690":"946","4ab8a817":"966","8606c6a1":"987","53c1042e":"994","19b7f836":"996"}[e]||e,n.p+n.u(e)},(()=>{var e={303:0,532:0};n.f.j=(t,a)=>{var r=n.o(e,t)?e[t]:void 0;if(0!==r)if(r)a.push(r[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var f=new Promise(((a,f)=>r=e[t]=[a,f]));a.push(r[2]=f);var o=n.p+n.u(t),d=new Error;n.l(o,(a=>{if(n.o(e,t)&&(0!==(r=e[t])&&(e[t]=void 0),r)){var f=a&&("load"===a.type?"missing":a.type),o=a&&a.target&&a.target.src;d.message="Loading chunk "+t+" failed.\n("+f+": "+o+")",d.name="ChunkLoadError",d.type=f,d.request=o,r[1](d)}}),"chunk-"+t,t)}},n.O.j=t=>0===e[t];var t=(t,a)=>{var r,f,o=a[0],d=a[1],c=a[2],i=0;if(o.some((t=>0!==e[t]))){for(r in d)n.o(d,r)&&(n.m[r]=d[r]);if(c)var b=c(n)}for(t&&t(a);i<o.length;i++)f=o[i],n.o(e,f)&&e[f]&&e[f][0](),e[f]=0;return n.O(b)},a=self.webpackChunkthingsgateway=self.webpackChunkthingsgateway||[];a.forEach(t.bind(null,0)),a.push=t.bind(null,a.push.bind(a))})()})();
|
16
handbook/build/docs/03、支持项目与Pro版说明/Pro/index.html
Normal file
16
handbook/build/docs/03、支持项目与Pro版说明/Pro/index.html
Normal file
File diff suppressed because one or more lines are too long
16
handbook/build/docs/03、支持项目与Pro版说明/支持开源项目/index.html
Normal file
16
handbook/build/docs/03、支持项目与Pro版说明/支持开源项目/index.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
16
handbook/build/docs/06、采集驱动说明/Siemens/index.html
Normal file
16
handbook/build/docs/06、采集驱动说明/Siemens/index.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
16
handbook/build/docs/07、上传驱动说明/RabbitMQClient/index.html
Normal file
16
handbook/build/docs/07、上传驱动说明/RabbitMQClient/index.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
handbook/build/img/QuestdbSet.jpg
Normal file
BIN
handbook/build/img/QuestdbSet.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://diego2098.gitee.io/thingsgateway/search</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/collectdevice</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/otherconfig</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/plugincore</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/uploaddevice</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/variable</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/06%E3%80%81%E9%87%87%E9%9B%86%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/modbus</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/06%E3%80%81%E9%87%87%E9%9B%86%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/opcda</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/06%E3%80%81%E9%87%87%E9%9B%86%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/opcua</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/07%E3%80%81%E4%B8%8A%E4%BC%A0%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/ModbusServer</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/07%E3%80%81%E4%B8%8A%E4%BC%A0%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/MqttClient</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/07%E3%80%81%E4%B8%8A%E4%BC%A0%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/MqttServer</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/modbusdemo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/modbusserverdemo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/mqttclientdemo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/mqttserverdemo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/opcdademo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/opcuademo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/%E6%94%AF%E6%8C%81%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/quickstart</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/upgrade</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url></urlset>
|
||||
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://diego2098.gitee.io/thingsgateway/search</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/03%E3%80%81%E6%94%AF%E6%8C%81%E9%A1%B9%E7%9B%AE%E4%B8%8EPro%E7%89%88%E8%AF%B4%E6%98%8E/%E6%94%AF%E6%8C%81%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/03%E3%80%81%E6%94%AF%E6%8C%81%E9%A1%B9%E7%9B%AE%E4%B8%8EPro%E7%89%88%E8%AF%B4%E6%98%8E/Pro</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/collectdevice</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/otherconfig</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/plugincore</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/uploaddevice</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/05%E3%80%81%E7%BD%91%E5%85%B3%E9%85%8D%E7%BD%AE/variable</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/06%E3%80%81%E9%87%87%E9%9B%86%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/modbus</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/06%E3%80%81%E9%87%87%E9%9B%86%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/opcda</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/06%E3%80%81%E9%87%87%E9%9B%86%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/opcua</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/06%E3%80%81%E9%87%87%E9%9B%86%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/Siemens</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/07%E3%80%81%E4%B8%8A%E4%BC%A0%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/ModbusServer</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/07%E3%80%81%E4%B8%8A%E4%BC%A0%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/MqttClient</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/07%E3%80%81%E4%B8%8A%E4%BC%A0%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/MqttServer</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/07%E3%80%81%E4%B8%8A%E4%BC%A0%E9%A9%B1%E5%8A%A8%E8%AF%B4%E6%98%8E/RabbitMQClient</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/modbusdemo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/modbusserverdemo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/mqttclientdemo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/mqttserverdemo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/opcdademo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/08%E3%80%81Demo/opcuademo</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/quickstart</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/docs/upgrade</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://diego2098.gitee.io/thingsgateway/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url></urlset>
|
@@ -12,9 +12,7 @@ import Tag from "@site/src/components/Tag.js";
|
||||
ThingsGateway 由作者Diego及其他贡献者开发,所有版权归作者Diego所有,程序集源代码在遵循 Apache License 2.0 的开源协议以及**附加协议**下,可**免费**供其他开发者二次开发或(商业)使用。
|
||||
|
||||
|
||||
# 附加协议
|
||||
|
||||
#### 个人使用须知:
|
||||
### 个人使用须知:
|
||||
|
||||
- 不得将程序集用作违法犯罪活动。
|
||||
- 不得将程序集单独包装售卖,申请专利等。
|
||||
@@ -22,7 +20,7 @@ ThingsGateway 由作者Diego及其他贡献者开发,所有版权归作者Dieg
|
||||
|
||||
**以上内容必须全部符合,个人使用授权才成立。**
|
||||
|
||||
#### 二次开发须知:
|
||||
### 二次开发须知:
|
||||
|
||||
- 不得将程序集用作违法犯罪活动。
|
||||
- 不得将程序集单独包装售卖,申请专利等。
|
||||
@@ -32,13 +30,22 @@ ThingsGateway 由作者Diego及其他贡献者开发,所有版权归作者Dieg
|
||||
|
||||
**以上内容必须全部符合,二次开发授权才成立。**
|
||||
|
||||
#### 盈利性(商业)用途使用须知:
|
||||
### 盈利性(商业)用途使用须知:
|
||||
|
||||
- 不得将程序集用作违法犯罪活动。
|
||||
- 不得将程序集单独包装售卖,申请专利等。
|
||||
- **不得擦除程序集所有有关作者的信息,并必须于用户可见界面(如关于)中提名。**
|
||||
|
||||
**以上内容必须全部符合,使用授权才成立。**
|
||||
**以上内容必须全部符合,商业使用授权才成立。**
|
||||
|
||||
|
||||
## ThingsGatewayPro 商用许可
|
||||
|
||||
ThingsGatewayPro 软件框架与 ThingsGateway 一致,另包含其他定制插件。所有版权归作者Diego所有。
|
||||
|
||||
ThingsGatewayPro 不公开开源,需要付费购买。
|
||||
|
||||
|
||||
|
||||
# 免责申明
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: 支持开源项目
|
||||
title: 支持开源项目
|
||||
sidebar_label: 03、支持开源项目
|
||||
sidebar_label: 3.1、支持开源项目
|
||||
---
|
||||
|
||||
## 赞助ThingsGateway项目
|
||||
@@ -14,8 +14,9 @@ sidebar_label: 03、支持开源项目
|
||||
|
||||
#### 赞助名单(以下排名只按照赞助时间顺序)
|
||||
|
||||
1. 绢(10¥)
|
||||
2. 小蚂蚁 (18.8¥)
|
||||
1. 绢 (10¥)
|
||||
2. 小蚂蚁 (18.8¥+66.66¥)
|
||||
3. 伟 (18.8¥)
|
||||
|
||||
|
||||
|
45
handbook/docs/03、支持项目与Pro版说明/3.2、Pro版说明.mdx
Normal file
45
handbook/docs/03、支持项目与Pro版说明/3.2、Pro版说明.mdx
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
id: Pro
|
||||
title: Pro版说明
|
||||
sidebar_label: 3.2、Pro版说明
|
||||
---
|
||||
|
||||
### 一、说明
|
||||
|
||||
ThingsGatewayPro是ThingsGateway的加强版本,基础功能与ThingsGateway一致,并添加一些额外功能插件
|
||||
|
||||
同时ThingsGatewayPro提供企业定制服务及必要的远程协助,具体收费可以咨询**作者Diego,联系方式:QQ:2248356998**
|
||||
|
||||
### 二、授权
|
||||
|
||||
#### ThingsGatewayPro分为个人授权与企业授权,区别在于授权归属
|
||||
授权后获得证书凭据(图片形式)
|
||||
|
||||
#### 2.1 个人授权
|
||||
|
||||
- **授权归属于购买者个人所有**
|
||||
- 价格 998¥
|
||||
|
||||
#### 2.2 企业授权
|
||||
|
||||
- **授权归属于企业所有,仅企业主体享有授权**
|
||||
- 价格 998¥
|
||||
|
||||
|
||||
### 三、插件差异
|
||||
|
||||
| 插件名称 | 说明 |
|
||||
| ---------------| --------------------------|
|
||||
| 待更新......| 待更新......|
|
||||
|
||||
|
||||
|
||||
### 四、购买
|
||||
|
||||
#### 购买途径
|
||||

|
||||
# 请备注 - ThingsGatewayPro
|
||||
|
||||
|
||||
|
||||
|
@@ -54,3 +54,5 @@ PORT=5432;DATABASE=SqlSugar4xTest;HOST=localhost;PASSWORD=haosql;USER ID=postgre
|
||||
```
|
||||
host=localhost;port=8812;username=admin;password=quest;database=qdb;ServerCompatibilityMode=NoTypeLoading;
|
||||
```
|
||||
QQ群里下载questdb.zip和QuestDB win启动bat文件,解压两个文件,更改QuestDB win启动bat里的对应路径,最后启动服务,重启官网就可查的历史数据
|
||||

|
@@ -5,6 +5,8 @@ sidebar_label: 6.1、Modbus驱动
|
||||
---
|
||||
### (一)设备配置
|
||||
|
||||
1、Socket链路
|
||||
|
||||
| 属性 | 说明 | 默认值/备注|
|
||||
| ---------------| --------------------------| ---|
|
||||
| 连接超时时间 | 建立socket通讯的超时时间(ms) | 3000|
|
||||
@@ -16,6 +18,19 @@ sidebar_label: 6.1、Modbus驱动
|
||||
| 连接id检测 | 是否检查modbus的事务标识符和协议标识符为一致 | Flase |
|
||||
| crc检测 | 是否检查modbus的事务标识符和协议标识符为一致 | True|
|
||||
|
||||
2、串口链路
|
||||
|
||||
| 属性 | 说明 | 默认值/备注|
|
||||
| ---------------| --------------------------| ---|
|
||||
| 默认解析顺序 | 4个字节以上的数据解析,可设置为ABCD/BADC/CDBA/DCBA,默认ABCD |ABCD:Big-Endian<br></br>BADC:Big-Endian Byte Swap<br></br>CDAB:Little-Endian Byte Swap<br></br>DCBA:Little-Endian |
|
||||
| COM口 | COM口 | COM1|
|
||||
| 波特率 | | 9600|
|
||||
| 数据位 | | 8|
|
||||
| 校验位 | | None|
|
||||
| 停止位 | | One|
|
||||
| 默认站号 | 默认的mudbus站号id | 1|
|
||||
| 读写超时时间 | 读取等待中的超时时间(ms) | 3000|
|
||||
| crc检测 | 是否检查modbus的事务标识符和协议标识符为一致 | True|
|
||||
|
||||
### (二)变量配置
|
||||
#### 变量地址说明
|
||||
|
@@ -3,6 +3,13 @@ id: opcda
|
||||
title: OPCDA驱动
|
||||
sidebar_label: 6.2、OPCDA驱动
|
||||
---
|
||||
|
||||
:::tip `OPCDA驱动注意`
|
||||
|
||||
使用OPCDA插件时,需安装OPC核心库,[文件地址](https://gitee.com/diego2098/ThingsGateway/attach_files)
|
||||
|
||||
:::
|
||||
|
||||
### (一)设备配置
|
||||
|
||||
| 属性 | 说明 | 默认值/备注|
|
||||
|
38
handbook/docs/06、采集驱动说明/Siemens.mdx
Normal file
38
handbook/docs/06、采集驱动说明/Siemens.mdx
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
id: Siemens
|
||||
title: Siemens驱动
|
||||
sidebar_label: 6.4、Siemens驱动
|
||||
---
|
||||
### (一)设备配置
|
||||
|
||||
| 属性 | 说明 | 默认值/备注|
|
||||
| ---------------| --------------------------| ---|
|
||||
| 连接超时时间 | 建立socket通讯的超时时间(ms) | 3000|
|
||||
| 默认解析顺序 | 4个字节以上的数据解析,可设置为ABCD/BADC/CDBA/DCBA,默认ABCD |ABCD:Big-Endian<br></br>BADC:Big-Endian Byte Swap<br></br>CDAB:Little-Endian Byte Swap<br></br>DCBA:Little-Endian |
|
||||
| IP | 连接IP | 127.0.0.1|
|
||||
| 端口 | 连接端口 | 102|
|
||||
| 读写超时时间 | 读取等待中的超时时间(ms) | 3000|
|
||||
| LocalTSAP | | 0 |
|
||||
| DestTSAP | | 0|
|
||||
|
||||
|
||||
### (二)变量配置
|
||||
#### 变量地址说明
|
||||
|
||||
- 1、基本地址
|
||||
|
||||
| 地址 | 说明 |
|
||||
| ---------------| --------------------------|
|
||||
| Txxxxx | Timer寄存器,例如T100/T100.1 |
|
||||
| Cxxxxx | Counter寄存器,例如C100/C100.1 |
|
||||
| AIxxxxx | AI寄存器,例如AI100/AI100.1 |
|
||||
| AQxxxxx | AQ寄存器,例如AQ100/AQ100.1 |
|
||||
| Ixxxxx | I寄存器,例如I100/I100.1 |
|
||||
| Qxxxxx | Q寄存器,例如Q100/Q100.1 |
|
||||
| Mxxxxx | M寄存器,例如M100/M100.1 |
|
||||
| DBxxxxx | DB寄存器,例如DB100.1/DB100.1.1 |
|
||||
|
||||
|
||||
#### 变量特殊方法说明
|
||||
|
||||
无特殊方法
|
@@ -76,7 +76,6 @@ Rpc返回Json:
|
||||
"activeTime": "2023-03-08T08:46:39.2708927+08:00",
|
||||
"deviceStatus": 1,
|
||||
"deviceOffMsg": "",
|
||||
"intervalTime": 0,
|
||||
"name": "ModbusTest",
|
||||
"description": null,
|
||||
"enable": true,
|
||||
|
@@ -72,7 +72,6 @@ Rpc返回Json:
|
||||
"activeTime": "2023-03-08T08:46:39.2708927+08:00",
|
||||
"deviceStatus": 1,
|
||||
"deviceOffMsg": "",
|
||||
"intervalTime": 0,
|
||||
"name": "ModbusTest",
|
||||
"description": null,
|
||||
"enable": true,
|
||||
|
68
handbook/docs/07、上传驱动说明/7.4、RabbitMQClient.mdx
Normal file
68
handbook/docs/07、上传驱动说明/7.4、RabbitMQClient.mdx
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: RabbitMQClient
|
||||
title: RabbitMQClient
|
||||
sidebar_label: 7.4、RabbitMQClient
|
||||
---
|
||||
|
||||
|
||||
### (一)设备配置
|
||||
|
||||
| 属性 | 说明 | 默认值/备注|
|
||||
| ---------------| --------------------------| ---|
|
||||
| 账号 | 登录账号 | guest|
|
||||
| 密码 | 登录密码 | guest|
|
||||
| IP | 连接IP | 127.0.0.1|
|
||||
| 端口 | 连接端口 | 5672|
|
||||
| 虚拟Host | | / |
|
||||
|路由名称 | |TG|
|
||||
|变量队列名称| |ThingsGateway/Variable|
|
||||
|设备队列名称| |ThingsGateway/Device|
|
||||
|是否发布List| |False|
|
||||
|是否声明队列| |False|
|
||||
|
||||
|
||||
变量Json:
|
||||
```
|
||||
|
||||
[
|
||||
{
|
||||
"deviceName": "ModbusTest",
|
||||
"rawValue": "12345",
|
||||
"value": "12345",
|
||||
"changeTime": "2023-03-08T08:42:36.1979048+08:00",
|
||||
"collectTime": "2023-03-08T08:46:39.3070467+08:00",
|
||||
"quality": 192,
|
||||
"readExpressions": null,
|
||||
"writeExpressions": null,
|
||||
"intervalTime": 1000,
|
||||
"otherMethod": null,
|
||||
"variableAddress": "40001",
|
||||
"name": "test40001",
|
||||
"description": null,
|
||||
"initialValue": null,
|
||||
"protectTypeEnum": 1,
|
||||
"dataTypeEnum": 7
|
||||
}
|
||||
]
|
||||
```
|
||||
设备Json:
|
||||
```
|
||||
[
|
||||
{
|
||||
"pluginName": "ThingsGateway.Modbus.ModbusTcp",
|
||||
"deviceVariablesNum": 1,
|
||||
"activeTime": "2023-03-08T08:46:39.2708927+08:00",
|
||||
"deviceStatus": 1,
|
||||
"deviceOffMsg": "",
|
||||
"name": "ModbusTest",
|
||||
"description": null,
|
||||
"enable": true,
|
||||
"createTime": "2023-03-06T17:31:23.0562652",
|
||||
"updateTime": "0001-01-01T00:00:00"
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
### (二)变量配置
|
||||
无
|
BIN
handbook/static/img/QuestdbSet.jpg
Normal file
BIN
handbook/static/img/QuestdbSet.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
@@ -57,8 +57,16 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
if (response[1] >= 0x80)//错误码
|
||||
return new OperResult<byte[]>(GetDescriptionByErrorCode(response[2]));
|
||||
if (send.Length == 0)
|
||||
{
|
||||
var result = OperResult.CreateSuccessResult(GenericHelper.ArrayRemoveBegin(response, 3));
|
||||
result.Message = "接收数据正确,但主机并没有主动请求数据";
|
||||
result.ResultCode = ResultCode.Canceled;
|
||||
return result;
|
||||
}
|
||||
if (send[0] != response[0])
|
||||
return new OperResult<byte[]>(string.Format("站号不一致", send[0], response[0]));
|
||||
if (send[1] != response[1])
|
||||
|
@@ -0,0 +1,109 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ThingsGateway.Foundation.Serial;
|
||||
|
||||
using TouchSocket.Resources;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
{
|
||||
public class ModbusRtu : ReadWriteDevicesSerialClientBase
|
||||
{
|
||||
public ModbusRtuDataHandleAdapter DataHandleAdapter = new();
|
||||
|
||||
public ModbusRtu(SerialClient serialClient) : base(serialClient)
|
||||
{
|
||||
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 OpenAsync();
|
||||
var commandResult = ModbusHelper.GetReadModbusCommand(address, length, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var item = commandResult.Content;
|
||||
var result = await SerialClient.GetTGWaitingClient(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;
|
||||
SerialClient.SetDataHandlingAdapter(DataHandleAdapter);
|
||||
}
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await OpenAsync();
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = await SerialClient.GetTGWaitingClient(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 OpenAsync();
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = await SerialClient.GetTGWaitingClient(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 ModbusRtuDataHandleAdapter : ReadWriteDevicesSerialDataHandleAdapter<ModbusRtuMessage>
|
||||
{
|
||||
public bool Crc16CheckEnable { get; set; } = true;
|
||||
|
||||
public override byte[] PackCommand(byte[] command)
|
||||
{
|
||||
return ModbusHelper.AddCrc(command);
|
||||
}
|
||||
|
||||
protected override ModbusRtuMessage GetInstance()
|
||||
{
|
||||
return new ModbusRtuMessage();
|
||||
}
|
||||
|
||||
protected override OperResult<byte[]> UnpackResponse(
|
||||
byte[] send, byte[] response)
|
||||
{
|
||||
return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
|
||||
}
|
||||
}
|
||||
}
|
@@ -28,7 +28,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var item = commandResult.Content;
|
||||
var result = await TcpClient.GetWaitingClient(new()).SendThenResponseAsync(item, TimeOut, token);
|
||||
var result = await TcpClient.GetTGWaitingClient(new()).SendThenResponseAsync(item, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
@@ -60,7 +60,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = await TcpClient.GetWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
|
||||
var result = await TcpClient.GetTGWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
@@ -86,7 +86,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = await TcpClient.GetWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
|
||||
var result = await TcpClient.GetTGWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
|
@@ -28,7 +28,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var item = commandResult.Content;
|
||||
var result = await UdpSession.GetWaitingClient(new()).SendThenResponseAsync(item, TimeOut, token);
|
||||
var result = await UdpSession.GetTGWaitingClient(new()).SendThenResponseAsync(item, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
@@ -60,7 +60,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = await UdpSession.GetWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
|
||||
var result = await UdpSession.GetTGWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
@@ -86,7 +86,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = await UdpSession.GetWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
|
||||
var result = await UdpSession.GetTGWaitingClient(new()).SendThenResponseAsync(commandResult.Content, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
|
@@ -27,7 +27,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var item = commandResult.Content;
|
||||
var result = TcpClient.GetWaitingClient(new()).SendThenResponse(item, TimeOut, token);
|
||||
var result = TcpClient.GetTGWaitingClient(new()).SendThenResponse(item, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
@@ -60,7 +60,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = TcpClient.GetWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
var result = TcpClient.GetTGWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
@@ -87,7 +87,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = TcpClient.GetWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
var result = TcpClient.GetTGWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
|
@@ -27,7 +27,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var item = commandResult.Content;
|
||||
var result = UdpSession.GetWaitingClient(new()).SendThenResponse(item, TimeOut, token);
|
||||
var result = UdpSession.GetTGWaitingClient(new()).SendThenResponse(item, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
@@ -60,7 +60,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
var commandResult = ModbusHelper.GetWriteModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = UdpSession.GetWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
var result = UdpSession.GetTGWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
@@ -87,7 +87,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
var commandResult = ModbusHelper.GetWriteBoolModbusCommand(address, value, Station);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = UdpSession.GetWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
var result = UdpSession.GetTGWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
return collectMessage;
|
||||
|
@@ -3,11 +3,11 @@
|
||||
<PropertyGroup>
|
||||
<LangVersion>latestMajor</LangVersion>
|
||||
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
|
||||
<Version>1.0.3</Version>
|
||||
<Version>1.0.5</Version>
|
||||
<Title>ThingsGateway.Foundation.Adapter.Modbus</Title>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<Authors>Diego</Authors>
|
||||
<Description>Modbus通讯类库,支持MdobusTcp;ModbusRtuOverTcp;ModbusTcpServer</Description>
|
||||
<Description>Modbus通讯类库,支持MdobusTcp;ModbusRtu;ModbusRtuOverTcp;ModbusTcpServer</Description>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild>
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using OpcDaClient.Da;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -9,8 +10,6 @@ using System.Timers;
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
using ThingsGateway.Foundation.Extension.Json;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
//部分非托管交互代码来自https://gitee.com/Zer0Day/opc-client与OPC基金会opcnet库,更改部分逻辑
|
||||
@@ -29,7 +28,7 @@ namespace ThingsGateway.Foundation.Adapter.OPCDA
|
||||
private List<OpcGroup> Groups = new();
|
||||
private int IsQuit = 1;
|
||||
private OpcServer m_server;
|
||||
private IntelligentConcurrentQueue<ItemReadResult> results = new IntelligentConcurrentQueue<ItemReadResult>(100000);
|
||||
private ConcurrentQueue<ItemReadResult> results = new ConcurrentQueue<ItemReadResult>();
|
||||
private Dictionary<string, List<OpcItem>> Tags = new();
|
||||
//定义组对象(订阅者)
|
||||
public OPCDAClient(ILog logger)
|
||||
@@ -285,7 +284,7 @@ namespace ThingsGateway.Foundation.Adapter.OPCDA
|
||||
while (!DisposedValue)
|
||||
{
|
||||
if (results.Count > 0)
|
||||
DataChangedHandler?.Invoke(results.ToListWithDequeue(results.Count));
|
||||
DataChangedHandler?.Invoke(results.ToListWithDequeue());
|
||||
if (OPCNode == null)
|
||||
await Task.Delay(1000);
|
||||
else
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<LangVersion>latestMajor</LangVersion>
|
||||
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
|
||||
<Version>1.0.3</Version>
|
||||
<Version>1.0.5</Version>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
<OutputType>Library</OutputType>
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using Opc.Ua;
|
||||
using Opc.Ua.Client;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -21,7 +22,7 @@ public class OPCUAClient : DisposableObject
|
||||
|
||||
private EventHandler m_ConnectComplete;
|
||||
|
||||
private IntelligentConcurrentQueue<(MonitoredItem, MonitoredItemNotification)> m_data = new(100000);
|
||||
private ConcurrentQueue<(MonitoredItem, MonitoredItemNotification)> m_data = new();
|
||||
private bool m_IsConnected;
|
||||
|
||||
private EventHandler m_KeepAliveComplete;
|
||||
@@ -1415,7 +1416,7 @@ public class OPCUAClient : DisposableObject
|
||||
while (!DisposedValue)
|
||||
{
|
||||
if (m_data.Count > 0)
|
||||
DataChangedHandler?.Invoke(m_data.ToListWithDequeue(m_data.Count));
|
||||
DataChangedHandler?.Invoke(m_data.ToListWithDequeue());
|
||||
if (OPCNode == null)
|
||||
await Task.Delay(1000);
|
||||
else
|
||||
|
@@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<LangVersion>latestMajor</LangVersion>
|
||||
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
|
||||
<Version>1.0.3</Version>
|
||||
<Version>1.0.5</Version>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
<OutputType>Library</OutputType>
|
||||
|
||||
|
@@ -0,0 +1 @@
|
||||
global using System;
|
@@ -0,0 +1,225 @@
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
public enum S7Area : byte
|
||||
{
|
||||
PE = 0x81,
|
||||
PA = 0x82,
|
||||
MK = 0x83,
|
||||
DB = 0x84,
|
||||
CT = 0x1C,
|
||||
TM = 0x1D,
|
||||
AI = 0X06,
|
||||
AQ = 0x07,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 西门子PLC地址数据信息
|
||||
/// </summary>
|
||||
public class SiemensAddress : DeviceAddressBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据块代码
|
||||
/// </summary>
|
||||
public byte DataCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// DB块数据信息
|
||||
/// </summary>
|
||||
public ushort DbBlock { get; set; }
|
||||
|
||||
|
||||
public static int GetAddressStart(string address, bool isCounterOrTimer = false)
|
||||
{
|
||||
if (address.IndexOf('.') < 0)
|
||||
{
|
||||
return isCounterOrTimer ? Convert.ToInt32(address) : Convert.ToInt32(address) * 8;
|
||||
}
|
||||
|
||||
string[] strArray = address.Split('.');
|
||||
return (Convert.ToInt32(strArray[0]) * 8) + Convert.ToInt32(strArray[1]);
|
||||
}
|
||||
|
||||
|
||||
public static OperResult<SiemensAddress> ParseFrom(string address)
|
||||
{
|
||||
return ParseFrom(address, 0);
|
||||
}
|
||||
|
||||
public static OperResult<SiemensAddress> ParseFrom(string address, int length)
|
||||
{
|
||||
SiemensAddress s7AddressData = new SiemensAddress();
|
||||
try
|
||||
{
|
||||
address = address.ToUpper();
|
||||
s7AddressData.Length = length;
|
||||
s7AddressData.DbBlock = 0;
|
||||
if (address.StartsWith("AI"))
|
||||
{
|
||||
s7AddressData.DataCode = (byte)S7Area.AI;
|
||||
if (address.StartsWith("AIX") || address.StartsWith("AIB") || address.StartsWith("AIW") || address.StartsWith("AID"))
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(3));
|
||||
}
|
||||
else
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(2));
|
||||
}
|
||||
}
|
||||
else if (address.StartsWith("AQ"))
|
||||
{
|
||||
s7AddressData.DataCode = (byte)S7Area.AQ;
|
||||
if (address.StartsWith("AQX") || address.StartsWith("AQB") || address.StartsWith("AQW") || address.StartsWith("AQD"))
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(3));
|
||||
}
|
||||
else
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(2));
|
||||
}
|
||||
}
|
||||
else if (address[0] == 'I')
|
||||
{
|
||||
s7AddressData.DataCode = (byte)S7Area.PE;
|
||||
if (address.StartsWith("IX") || address.StartsWith("IB") || address.StartsWith("IW") || address.StartsWith("ID"))
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(1));
|
||||
}
|
||||
}
|
||||
else if (address[0] == 'Q')
|
||||
{
|
||||
s7AddressData.DataCode = (byte)S7Area.PA;
|
||||
if (address.StartsWith("QX") || address.StartsWith("QB") || address.StartsWith("QW") || address.StartsWith("QD"))
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(1));
|
||||
}
|
||||
}
|
||||
else if (address[0] == 'M')
|
||||
{
|
||||
s7AddressData.DataCode = (byte)S7Area.MK;
|
||||
if (address.StartsWith("MX") || address.StartsWith("MB") || address.StartsWith("MW") || address.StartsWith("MD"))
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(1));
|
||||
}
|
||||
}
|
||||
else if (address[0] == 'D' || address.Substring(0, 2) == "DB")
|
||||
{
|
||||
s7AddressData.DataCode = (byte)S7Area.DB;
|
||||
string[] strArray = address.Split('.');
|
||||
s7AddressData.DbBlock = address[1] != 'B' ? Convert.ToUInt16(strArray[0].Substring(1)) : Convert.ToUInt16(strArray[0].Substring(2));
|
||||
string address1 = address.Substring(address.IndexOf('.') + 1);
|
||||
if (address1.StartsWith("DBX") || address1.StartsWith("DBB") || address1.StartsWith("DBW") || address1.StartsWith("DBD"))
|
||||
{
|
||||
address1 = address1.Substring(3);
|
||||
}
|
||||
|
||||
s7AddressData.AddressStart = GetAddressStart(address1);
|
||||
}
|
||||
else if (address[0] == 'T')
|
||||
{
|
||||
s7AddressData.DataCode = (byte)S7Area.TM;
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(1), true);
|
||||
}
|
||||
else if (address[0] == 'C')
|
||||
{
|
||||
s7AddressData.DataCode = (byte)S7Area.CT;
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(1), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (address[0] != 'V')
|
||||
{
|
||||
return new OperResult<SiemensAddress>("不支持的类型");
|
||||
}
|
||||
|
||||
s7AddressData.DataCode = (byte)S7Area.DB;
|
||||
s7AddressData.DbBlock = 1;
|
||||
if (address.StartsWith("VB") || address.StartsWith("VW") || address.StartsWith("VD") || address.StartsWith("VX"))
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
s7AddressData.AddressStart = GetAddressStart(address.Substring(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<SiemensAddress>(ex.Message);
|
||||
}
|
||||
return OperResult.CreateSuccessResult<SiemensAddress>(s7AddressData);
|
||||
}
|
||||
|
||||
public override void Parse(string address, int length)
|
||||
{
|
||||
OperResult<SiemensAddress> from = ParseFrom(address, length);
|
||||
if (!from.IsSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddressStart = from.Content.AddressStart;
|
||||
Length = from.Content.Length;
|
||||
DataCode = from.Content.DataCode;
|
||||
DbBlock = from.Content.DbBlock;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (DataCode == (byte)S7Area.TM)
|
||||
{
|
||||
return "T" + AddressStart.ToString();
|
||||
}
|
||||
if (DataCode == (byte)S7Area.CT)
|
||||
{
|
||||
return "C" + AddressStart.ToString();
|
||||
}
|
||||
|
||||
if (DataCode == (byte)S7Area.AI)
|
||||
{
|
||||
return "AI" + GetStringAddress(AddressStart);
|
||||
}
|
||||
|
||||
if (DataCode == (byte)S7Area.AQ)
|
||||
{
|
||||
return "AQ" + GetStringAddress(AddressStart);
|
||||
}
|
||||
|
||||
if (DataCode == (byte)S7Area.PE)
|
||||
{
|
||||
return "I" + GetStringAddress(AddressStart);
|
||||
}
|
||||
|
||||
if (DataCode == (byte)S7Area.PA)
|
||||
{
|
||||
return "Q" + GetStringAddress(AddressStart);
|
||||
}
|
||||
|
||||
if (DataCode == (byte)S7Area.MK)
|
||||
{
|
||||
return "M" + GetStringAddress(AddressStart);
|
||||
}
|
||||
|
||||
return DataCode == (byte)S7Area.DB ? "DB" + DbBlock.ToString() + "." + GetStringAddress(AddressStart) : AddressStart.ToString();
|
||||
}
|
||||
|
||||
private static string GetStringAddress(int addressStart)
|
||||
{
|
||||
return addressStart % 8 == 0 ? (addressStart / 8).ToString() : string.Format("{0}.{1}", addressStart / 8, addressStart % 8);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
/// <summary>
|
||||
/// https://github.com/S7NetPlus/s7netplus/blob/develop/S7.Net/Types/DateTime.cs
|
||||
/// Contains the methods to convert between <see cref="T:System.DateTime"/> and S7 representation of datetime values.
|
||||
/// </summary>
|
||||
public static class DateTime
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum <see cref="T:System.DateTime"/> value supported by the specification.
|
||||
/// </summary>
|
||||
public static readonly System.DateTime SpecMinimumDateTime = new System.DateTime(1990, 1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum <see cref="T:System.DateTime"/> value supported by the specification.
|
||||
/// </summary>
|
||||
public static readonly System.DateTime SpecMaximumDateTime = new System.DateTime(2089, 12, 31, 23, 59, 59, 999);
|
||||
|
||||
/// <summary>
|
||||
/// Parses a <see cref="T:System.DateTime"/> value from bytes.
|
||||
/// </summary>
|
||||
/// <param name="bytes">Input bytes read from PLC.</param>
|
||||
/// <returns>A <see cref="T:System.DateTime"/> object representing the value read from PLC.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the length of
|
||||
/// <paramref name="bytes"/> is not 8 or any value in <paramref name="bytes"/>
|
||||
/// is outside the valid range of values.</exception>
|
||||
public static System.DateTime FromByteArray(byte[] bytes)
|
||||
{
|
||||
return FromByteArrayImpl(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an array of <see cref="T:System.DateTime"/> values from bytes.
|
||||
/// </summary>
|
||||
/// <param name="bytes">Input bytes read from PLC.</param>
|
||||
/// <returns>An array of <see cref="T:System.DateTime"/> objects representing the values read from PLC.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the length of
|
||||
/// <paramref name="bytes"/> is not a multiple of 8 or any value in
|
||||
/// <paramref name="bytes"/> is outside the valid range of values.</exception>
|
||||
public static System.DateTime[] ToArray(byte[] bytes)
|
||||
{
|
||||
if (bytes.Length % 8 != 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length,
|
||||
$"Parsing an array of DateTime requires a multiple of 8 bytes of input data, input data is '{bytes.Length}' long.");
|
||||
|
||||
var cnt = bytes.Length / 8;
|
||||
var result = new System.DateTime[bytes.Length / 8];
|
||||
|
||||
for (var i = 0; i < cnt; i++)
|
||||
result[i] = FromByteArrayImpl(new ArraySegment<byte>(bytes, i * 8, 8));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static System.DateTime FromByteArrayImpl(IList<byte> bytes)
|
||||
{
|
||||
if (bytes.Count != 8)
|
||||
throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Count,
|
||||
$"Parsing a DateTime requires exactly 8 bytes of input data, input data is {bytes.Count} bytes long.");
|
||||
|
||||
int DecodeBcd(byte input) => 10 * (input >> 4) + (input & 0b00001111);
|
||||
|
||||
int ByteToYear(byte bcdYear)
|
||||
{
|
||||
var input = DecodeBcd(bcdYear);
|
||||
if (input < 90) return input + 2000;
|
||||
if (input < 100) return input + 1900;
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(bcdYear), bcdYear,
|
||||
$"Value '{input}' is higher than the maximum '99' of S7 date and time representation.");
|
||||
}
|
||||
|
||||
int AssertRangeInclusive(int input, byte min, byte max, string field)
|
||||
{
|
||||
if (input < min)
|
||||
throw new ArgumentOutOfRangeException(nameof(input), input,
|
||||
$"Value '{input}' is lower than the minimum '{min}' allowed for {field}.");
|
||||
if (input > max)
|
||||
throw new ArgumentOutOfRangeException(nameof(input), input,
|
||||
$"Value '{input}' is higher than the maximum '{max}' allowed for {field}.");
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
var year = ByteToYear(bytes[0]);
|
||||
var month = AssertRangeInclusive(DecodeBcd(bytes[1]), 1, 12, "month");
|
||||
var day = AssertRangeInclusive(DecodeBcd(bytes[2]), 1, 31, "day of month");
|
||||
var hour = AssertRangeInclusive(DecodeBcd(bytes[3]), 0, 23, "hour");
|
||||
var minute = AssertRangeInclusive(DecodeBcd(bytes[4]), 0, 59, "minute");
|
||||
var second = AssertRangeInclusive(DecodeBcd(bytes[5]), 0, 59, "second");
|
||||
var hsec = AssertRangeInclusive(DecodeBcd(bytes[6]), 0, 99, "first two millisecond digits");
|
||||
var msec = AssertRangeInclusive(bytes[7] >> 4, 0, 9, "third millisecond digit");
|
||||
var dayOfWeek = AssertRangeInclusive(bytes[7] & 0b00001111, 1, 7, "day of week");
|
||||
|
||||
return new System.DateTime(year, month, day, hour, minute, second, hsec * 10 + msec);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="T:System.DateTime"/> value to a byte array.
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The DateTime value to convert.</param>
|
||||
/// <returns>A byte array containing the S7 date time representation of <paramref name="dateTime"/>.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the value of
|
||||
/// <paramref name="dateTime"/> is before <see cref="P:SpecMinimumDateTime"/>
|
||||
/// or after <see cref="P:SpecMaximumDateTime"/>.</exception>
|
||||
public static byte[] ToByteArray(System.DateTime dateTime)
|
||||
{
|
||||
byte EncodeBcd(int value)
|
||||
{
|
||||
return (byte)((value / 10 << 4) | value % 10);
|
||||
}
|
||||
|
||||
if (dateTime < SpecMinimumDateTime)
|
||||
throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime,
|
||||
$"Date time '{dateTime}' is before the minimum '{SpecMinimumDateTime}' supported in S7 date time representation.");
|
||||
|
||||
if (dateTime > SpecMaximumDateTime)
|
||||
throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime,
|
||||
$"Date time '{dateTime}' is after the maximum '{SpecMaximumDateTime}' supported in S7 date time representation.");
|
||||
|
||||
byte MapYear(int year) => (byte)(year < 2000 ? year - 1900 : year - 2000);
|
||||
|
||||
int DayOfWeekToInt(DayOfWeek dayOfWeek) => (int)dayOfWeek + 1;
|
||||
|
||||
return new[]
|
||||
{
|
||||
EncodeBcd(MapYear(dateTime.Year)),
|
||||
EncodeBcd(dateTime.Month),
|
||||
EncodeBcd(dateTime.Day),
|
||||
EncodeBcd(dateTime.Hour),
|
||||
EncodeBcd(dateTime.Minute),
|
||||
EncodeBcd(dateTime.Second),
|
||||
EncodeBcd(dateTime.Millisecond / 10),
|
||||
(byte) (dateTime.Millisecond % 10 << 4 | DayOfWeekToInt(dateTime.DayOfWeek))
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an array of <see cref="T:System.DateTime"/> values to a byte array.
|
||||
/// </summary>
|
||||
/// <param name="dateTimes">The DateTime values to convert.</param>
|
||||
/// <returns>A byte array containing the S7 date time representations of <paramref name="dateTime"/>.</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when any value of
|
||||
/// <paramref name="dateTimes"/> is before <see cref="P:SpecMinimumDateTime"/>
|
||||
/// or after <see cref="P:SpecMaximumDateTime"/>.</exception>
|
||||
public static byte[] ToByteArray(System.DateTime[] dateTimes)
|
||||
{
|
||||
var bytes = new List<byte>(dateTimes.Length * 8);
|
||||
foreach (var dateTime in dateTimes) bytes.AddRange(ToByteArray(dateTime));
|
||||
|
||||
return bytes.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,369 @@
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
internal partial class SiemensHelper
|
||||
{
|
||||
#region 验证
|
||||
//internal static OperResult<byte[]> AnalysisReadBit(byte[] content)
|
||||
//{
|
||||
// int length = 1;
|
||||
// if (content.Length < 21 || content[20] != 1)
|
||||
// return new OperResult<byte[]>("数据块长度校验失败");
|
||||
// byte[] numArray = new byte[length];
|
||||
// if (content[21] == byte.MaxValue && content[22] == 3)//Bit:3;Byte:4;Counter或者Timer:9
|
||||
// {
|
||||
// numArray[0] = content[25];//+4
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21]));
|
||||
// }
|
||||
// return OperResult.CreateSuccessResult<byte[]>(numArray);
|
||||
//}
|
||||
|
||||
internal static OperResult<byte[]> AnalysisReadByte(byte[] sends, byte[] content)
|
||||
{
|
||||
int length = 0;
|
||||
int itemLen = (sends.Length - 19) / 12;
|
||||
|
||||
for (int index = 0; index < itemLen; index++)
|
||||
{
|
||||
if (sends[22 + (index * 12)] >= (byte)S7WordLength.Word)
|
||||
{
|
||||
length += ((sends[23 + (index * 12)] * 256) + sends[24 + (index * 12)]) * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
length += (sends[23 + (index * 12)] * 256) + sends[24 + (index * 12)];
|
||||
}
|
||||
}
|
||||
|
||||
if (content.Length < 21 || content[20] != itemLen)
|
||||
{
|
||||
return new OperResult<byte[]>("数据块长度校验失败");
|
||||
}
|
||||
|
||||
byte[] dataArray = new byte[length];
|
||||
int index1 = 0;
|
||||
int dataIndex = 0;
|
||||
for (int index2 = 21; index2 < content.Length; index2++)
|
||||
{
|
||||
if (index2 + 1 < content.Length)
|
||||
{
|
||||
int s7len = 0;
|
||||
if (sends[22 + (index1 * 12)] >= (byte)S7WordLength.Word)
|
||||
{
|
||||
s7len = ((sends[23 + (index1 * 12)] * 256) + sends[24 + (index1 * 12)]) * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
s7len = (sends[23 + (index1 * 12)] * 256) + sends[24 + (index1 * 12)];
|
||||
}
|
||||
if (content[index2] == byte.MaxValue && content[index2 + 1] == 4)//Bit:3;Byte:4;Counter或者Timer:9
|
||||
{
|
||||
Array.Copy(content, index2 + 4, dataArray, dataIndex, s7len);
|
||||
index2 += s7len + 3;
|
||||
dataIndex += s7len;
|
||||
index1++;
|
||||
}
|
||||
else if (content[index2] == byte.MaxValue && content[index2 + 1] == 9)//Counter或者Timer:9
|
||||
{
|
||||
int num = (content[index2 + 2] * 256) + content[index2 + 3];
|
||||
if (num % 3 == 0)
|
||||
{
|
||||
for (int index3 = 0; index3 < num / 3; index3++)
|
||||
{
|
||||
Array.Copy(content, index2 + 5 + (3 * index3), dataArray, dataIndex, 2);
|
||||
dataIndex += 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index4 = 0; index4 < num / 5; index4++)
|
||||
{
|
||||
Array.Copy(content, index2 + 7 + (5 * index4), dataArray, dataIndex, 2);
|
||||
dataIndex += 2;
|
||||
}
|
||||
}
|
||||
index2 += num + 4;
|
||||
index1++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult<byte[]>((int)content[index2] + GetCpuError(content[index2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return OperResult.CreateSuccessResult(dataArray);
|
||||
|
||||
}
|
||||
|
||||
internal static OperResult<byte[]> AnalysisWrite(byte[] content)
|
||||
{
|
||||
if (content.Length < 22)
|
||||
{
|
||||
return new OperResult<byte[]>(content) { Message = "未知错误" };
|
||||
}
|
||||
|
||||
byte err = content[21];
|
||||
if (err == byte.MaxValue)
|
||||
{
|
||||
return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21]));
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperResult.CreateSuccessResult(content);
|
||||
}
|
||||
|
||||
}
|
||||
private static string GetCpuError(ushort Error)
|
||||
{
|
||||
switch (Error)
|
||||
{
|
||||
case 0x05: return "地址超限";
|
||||
case 0x06: return "返回长度无效";
|
||||
case 0x07: return "数据大小不匹配";
|
||||
case 0x0a:
|
||||
case 0xd209: return "数据块不存在";
|
||||
case 0x8500: return "超出PDU大小";
|
||||
case 0xdc01: return "无效的值";
|
||||
case 0x8104: return "功能不可用";
|
||||
case 0xd241: return "需要密码";
|
||||
case 0xd602: return "无效密码";
|
||||
case 0xd604:
|
||||
case 0xd605: return "没有设置密码或已清除";
|
||||
default:
|
||||
return "未知错误";
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#region 获取报文
|
||||
internal static OperResult<byte[]> GetReadCommand(SiemensAddress[] siemensAddress)
|
||||
{
|
||||
if (siemensAddress == null)
|
||||
{
|
||||
return new OperResult<byte[]>("地址为null");
|
||||
}
|
||||
int len = siemensAddress.Length <= 19 ? siemensAddress.Length : throw new Exception("读取数量大于19!");
|
||||
int telegramLen = len * 12 + 19;
|
||||
int parameterLen = len * 12 + 2;
|
||||
|
||||
byte[] numArray = new byte[telegramLen];
|
||||
|
||||
Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
|
||||
numArray[2] = (byte)(telegramLen / 256);
|
||||
numArray[3] = (byte)(telegramLen % 256);
|
||||
numArray[13] = (byte)(parameterLen / 256);
|
||||
numArray[14] = (byte)(parameterLen % 256);
|
||||
numArray[18] = (byte)len;
|
||||
|
||||
for (int index = 0; index < len; index++)
|
||||
{
|
||||
Array.Copy(S7_MULRD_ITEM, 0, numArray, 19 + (index * 12), S7_MULRD_ITEM.Length);
|
||||
if (siemensAddress[index].DataCode == (byte)S7WordLength.Counter || siemensAddress[index].DataCode == (byte)S7WordLength.Timer)
|
||||
{
|
||||
numArray[22 + (index * 12)] = siemensAddress[index].DataCode;
|
||||
numArray[23 + (index * 12)] = (byte)(siemensAddress[index].Length / 256);
|
||||
numArray[24 + (index * 12)] = (byte)(siemensAddress[index].Length % 256);
|
||||
}
|
||||
else
|
||||
{
|
||||
numArray[22 + (index * 12)] = (byte)S7WordLength.Byte;
|
||||
numArray[23 + (index * 12)] = (byte)(siemensAddress[index].Length / 256);
|
||||
numArray[24 + (index * 12)] = (byte)(siemensAddress[index].Length % 256);
|
||||
}
|
||||
numArray[25 + (index * 12)] = (byte)(siemensAddress[index].DbBlock / 256U);
|
||||
numArray[26 + (index * 12)] = (byte)(siemensAddress[index].DbBlock % 256U);
|
||||
numArray[27 + (index * 12)] = siemensAddress[index].DataCode;
|
||||
numArray[28 + (index * 12)] = (byte)(siemensAddress[index].AddressStart / 256 / 256 % 256);
|
||||
numArray[29 + (index * 12)] = (byte)(siemensAddress[index].AddressStart / 256 % 256);
|
||||
numArray[30 + (index * 12)] = (byte)(siemensAddress[index].AddressStart % 256);
|
||||
}
|
||||
return OperResult.CreateSuccessResult(numArray);
|
||||
}
|
||||
|
||||
internal static OperResult<byte[]> GetWriteBitCommand(SiemensAddress address, bool data)
|
||||
{
|
||||
int len = 1;
|
||||
int telegramLen = 16 + 19 + len;//最后的1是写入值的byte数量
|
||||
int parameterLen = 12 + 2;
|
||||
|
||||
byte[] numArray = new byte[telegramLen];
|
||||
|
||||
Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
|
||||
numArray[2] = (byte)(telegramLen / 256);
|
||||
numArray[3] = (byte)(telegramLen % 256);
|
||||
numArray[13] = (byte)(parameterLen / 256);
|
||||
numArray[14] = (byte)(parameterLen % 256);
|
||||
numArray[15] = (byte)((4 + len) / 256);
|
||||
numArray[16] = (byte)((4 + len) % 256);
|
||||
numArray[17] = 5;
|
||||
numArray[18] = (byte)1;
|
||||
//写入Item与读取大致相同
|
||||
numArray[19] = (byte)18;
|
||||
numArray[20] = (byte)10;
|
||||
numArray[21] = (byte)16;
|
||||
numArray[22] = (byte)S7WordLength.Bit;
|
||||
numArray[23] = (byte)(len / 256);
|
||||
numArray[24] = (byte)(len % 256);
|
||||
numArray[25] = (byte)(address.DbBlock / 256U);
|
||||
numArray[26] = (byte)(address.DbBlock % 256U);
|
||||
numArray[27] = (byte)address.DataCode;
|
||||
numArray[28] = (byte)(address.AddressStart / 256 / 256);
|
||||
numArray[29] = (byte)(address.AddressStart / 256);
|
||||
numArray[30] = (byte)(address.AddressStart % 256);
|
||||
//后面跟的是写入的数据信息
|
||||
numArray[31] = 0;
|
||||
numArray[32] = 3;//Bit:3;Byte:4;Counter或者Timer:9
|
||||
numArray[33] = (byte)(len / 256);
|
||||
numArray[34] = (byte)(len % 256);
|
||||
numArray[35] = (byte)(data ? 1 : 0);
|
||||
|
||||
return OperResult.CreateSuccessResult(numArray);
|
||||
|
||||
}
|
||||
|
||||
internal static OperResult<byte[]> GetWriteByteCommand(SiemensAddress address, byte[] data)
|
||||
{
|
||||
int len = data.Length;
|
||||
int telegramLen = 16 + 19 + len;//最后的1是写入值的byte数量
|
||||
int parameterLen = 12 + 2;
|
||||
|
||||
byte[] numArray = new byte[telegramLen];
|
||||
|
||||
Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
|
||||
numArray[2] = (byte)(telegramLen / 256);
|
||||
numArray[3] = (byte)(telegramLen % 256);
|
||||
numArray[13] = (byte)(parameterLen / 256);
|
||||
numArray[14] = (byte)(parameterLen % 256);
|
||||
numArray[15] = (byte)((4 + len) / 256);
|
||||
numArray[16] = (byte)((4 + len) % 256);
|
||||
numArray[17] = 5;
|
||||
numArray[18] = (byte)1;
|
||||
//写入Item与读取大致相同
|
||||
numArray[19] = (byte)18;
|
||||
numArray[20] = (byte)10;
|
||||
numArray[21] = (byte)16;
|
||||
numArray[22] = (byte)S7WordLength.Byte;
|
||||
numArray[23] = (byte)(len / 256);
|
||||
numArray[24] = (byte)(len % 256);
|
||||
|
||||
numArray[25] = (byte)(address.DbBlock / 256U);
|
||||
numArray[26] = (byte)(address.DbBlock % 256U);
|
||||
numArray[27] = (byte)address.DataCode;
|
||||
numArray[28] = (byte)(address.AddressStart / 256 / 256);
|
||||
numArray[29] = (byte)(address.AddressStart / 256);
|
||||
numArray[30] = (byte)(address.AddressStart % 256);
|
||||
//后面跟的是写入的数据信息
|
||||
numArray[31] = 0;
|
||||
numArray[32] = 4;//Bit:3;Byte:4;Counter或者Timer:9
|
||||
numArray[33] = (byte)(len * 8 / 256);
|
||||
numArray[34] = (byte)(len * 8 % 256);
|
||||
data.CopyTo(numArray, 35);
|
||||
|
||||
return OperResult.CreateSuccessResult(numArray);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 字符串
|
||||
|
||||
internal static async Task<OperResult<string>> ReadString(
|
||||
SiemensS7PLC plc, string address, Encoding encoding)
|
||||
{
|
||||
//先读取一次获取长度,再读取实际值
|
||||
if (plc.CurrentPlc != SiemensEnum.S200Smart)
|
||||
{
|
||||
var result1 = await plc.ReadAsync(address, 2);
|
||||
if (!result1.IsSuccess)
|
||||
{
|
||||
return result1.Copy<string>();
|
||||
}
|
||||
if (result1.Content[0] == (byte)0 || result1.Content[0] == byte.MaxValue)
|
||||
{
|
||||
return new OperResult<string>("在PLC中不是字符串类型");
|
||||
}
|
||||
var result2 = await plc.ReadAsync(address, 2 + result1.Content[1]);
|
||||
if (!result2.IsSuccess)
|
||||
{
|
||||
return result2.Copy<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 2, result2.Content.Length - 2));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result1 = await plc.ReadAsync(address, 1);
|
||||
if (!result1.IsSuccess)
|
||||
return result1.Copy<string>();
|
||||
var result2 = await plc.ReadAsync(address, 1 + result1.Content[0]);
|
||||
if (!result2.IsSuccess)
|
||||
{
|
||||
return result2.Copy<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 1, result2.Content.Length - 1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal static async Task<OperResult> Write(SiemensS7PLC plc, string address, string value, Encoding encoding)
|
||||
{
|
||||
if (value == null)
|
||||
value = string.Empty;
|
||||
byte[] inBytes = encoding.GetBytes(value);
|
||||
//if (encoding == Encoding.Unicode)
|
||||
// inBytes = inBytes.BytesReverseByWord();
|
||||
if (plc.CurrentPlc != SiemensEnum.S200Smart)
|
||||
{
|
||||
OperResult<byte[]> result = await plc.ReadAsync(address, 2);
|
||||
if (!result.IsSuccess) return result;
|
||||
if (result.Content[0] == byte.MaxValue) return new OperResult<string>("在PLC中不是字符串类型");
|
||||
if (result.Content[0] == 0) result.Content[0] = 254;
|
||||
if (value.Length > result.Content[0]) return new OperResult<string>("写入值长度超限");
|
||||
return await plc.WriteAsync(
|
||||
address,
|
||||
DataHelper.SpliceArray(new byte[2] { result.Content[0], (byte)value.Length },
|
||||
inBytes
|
||||
));
|
||||
}
|
||||
return await plc.WriteAsync(address, DataHelper.SpliceArray<byte>(new byte[1]
|
||||
{
|
||||
(byte) value.Length
|
||||
}, inBytes));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
public enum S7WordLength : byte
|
||||
{
|
||||
Bit = 0x01,
|
||||
Byte = 0x02,
|
||||
Char = 0x03,
|
||||
Word = 0x04,
|
||||
Int = 0x05,
|
||||
DWord = 0x06,
|
||||
DInt = 0x07,
|
||||
Real = 0x08,
|
||||
Counter = 0x1C,
|
||||
Timer = 0x1D,
|
||||
}
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
internal partial class SiemensHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// S7连读写请求头(包含ISO头和COTP头)
|
||||
/// </summary>
|
||||
static byte[] S7_MULRW_HEADER = {
|
||||
0x03,0x00,
|
||||
0x00,0x1f, // 报文长度(item.len*12+19,注意:根据传入读取item数量更改)
|
||||
0x02,0xf0, 0x80, //COTP信息
|
||||
0x32, // S7协议ID
|
||||
0x01, // 类型,请求命令
|
||||
0x00,0x00, // 冗余识别
|
||||
0x00,0x01, // 序列号
|
||||
0x00,0x0e, // parameter长度(item.len*12+2,注意:根据传入读取item数量更改)
|
||||
0x00,0x00, // Data Length+4 ,写入时填写,读取时为0
|
||||
0x04, // 4 Read Var, 5 Write Var ,注意更改
|
||||
0x01, // Item数量(item.len,注意:根据传入读取item数量更改)
|
||||
};
|
||||
|
||||
// S7变量多读Item
|
||||
static byte[] S7_MULRD_ITEM = {
|
||||
0x12, // Var 规范.
|
||||
0x0a, // 剩余的字节长度
|
||||
0x10, // Syntax ID
|
||||
(byte)S7WordLength.Byte, // 相关的数据长度代码(注意:根据传入的变量更改)
|
||||
0x00,0x01, // 数据长度 (注意:根据传入的变量更改)
|
||||
0x00,0x00, // DB编号 (注意:根据传入的变量更改)
|
||||
0x84, // 数据块类型 (注意:根据传入的变量更改)
|
||||
0x00,0x00,0x00 // 数据块偏移量 (注意:根据传入的变量更改)
|
||||
};
|
||||
|
||||
|
||||
// ISO连接请求报文(也包含ISO头和COTP头)
|
||||
internal static byte[] ISO_CR = {
|
||||
// TPKT (RFC1006 Header)
|
||||
0x03, // RFC 1006 ID (3)
|
||||
0x00, // 保留 0
|
||||
0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
|
||||
0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
|
||||
// COTP (ISO 8073 Header)
|
||||
0x11, // PDU Size Length
|
||||
0xE0, // CR -连接请求ID
|
||||
0x00, 0x00, // Dst Reference
|
||||
0x00, 0x01, // Src Reference
|
||||
0x00, // Class + Options Flags
|
||||
//对于S7200/Smart ,下面参数也需要重写
|
||||
0xC0, // PDU Max Length ID
|
||||
0x01, 0x0A, // PDU Max Length
|
||||
0xC1, // Src TSAP Identifier
|
||||
0x02, // Src TSAP Length (2 bytes)
|
||||
0x01, 0x00, // Src TSAP (需重写)
|
||||
0xC2, // Dst TSAP Identifier
|
||||
0x02, // Dst TSAP Length (2 bytes)
|
||||
0x01, 0x02 // Dst TSAP (需重写)
|
||||
};
|
||||
// ISO连接请求报文(也包含ISO头和COTP头)
|
||||
internal static byte[] ISO_CR200 = {
|
||||
// TPKT (RFC1006 Header)
|
||||
0x03, // RFC 1006 ID (3)
|
||||
0x00, // 保留 0
|
||||
0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
|
||||
0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
|
||||
// COTP (ISO 8073 Header)
|
||||
0x11, // PDU Size Length
|
||||
0xE0, // CR -连接请求ID
|
||||
0x00, 0x00, // Dst Reference
|
||||
0x00, 0x01, // Src Reference
|
||||
0x00, // Class + Options Flags
|
||||
|
||||
//对于S7200/Smart
|
||||
0xC1,0x02,
|
||||
0x4D,0x57, //LOCALTASP
|
||||
0xC2,
|
||||
0x02,
|
||||
0x4D,0x57, //DESTTASP
|
||||
0xC0,
|
||||
0x01,0x09
|
||||
|
||||
};
|
||||
// ISO连接请求报文(也包含ISO头和COTP头)
|
||||
internal static byte[] ISO_CR200SMART = {
|
||||
// TPKT (RFC1006 Header)
|
||||
0x03, // RFC 1006 ID (3)
|
||||
0x00, // 保留 0
|
||||
0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
|
||||
0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
|
||||
// COTP (ISO 8073 Header)
|
||||
0x11, // PDU Size Length
|
||||
0xE0, // CR -连接请求ID
|
||||
0x00, 0x00, // Dst Reference
|
||||
0x00, 0x01, // Src Reference
|
||||
0x00, // Class + Options Flags
|
||||
|
||||
//对于S7200/Smart
|
||||
0xC1,0x02,
|
||||
0x10,0x00,//LOCALTASP
|
||||
0xC2,
|
||||
0x02,
|
||||
0x03,0x00,//DESTTASP
|
||||
0xC0,
|
||||
0x01,0x0A
|
||||
};
|
||||
// PDU获取报文(也包含ISO头和COTP头)
|
||||
internal static byte[] S7_PN = {
|
||||
0x03, 0x00, 0x00, 0x19,
|
||||
0x02, 0xf0, 0x80, // TPKT + COTP
|
||||
0x32, 0x01, 0x00, 0x00,
|
||||
|
||||
//这里对于S7200/Smart需要重写
|
||||
0x04, 0x00, 0x00, 0x08,
|
||||
|
||||
0x00, 0x00, 0xf0, 0x00,
|
||||
0x00, 0x01, 0x00, 0x01,
|
||||
0x01,0xE0 // PDU Length Requested 这里默认480字节,对于S7200/Smart 960字节
|
||||
};
|
||||
// PDU获取报文(也包含ISO头和COTP头)
|
||||
internal static byte[] S7200_PN = {
|
||||
0x03, 0x00, 0x00, 0x19,
|
||||
0x02, 0xf0, 0x80, // TPKT + COTP
|
||||
0x32, 0x01, 0x00, 0x00,
|
||||
|
||||
0x00,0x00,0x00,0x08,
|
||||
|
||||
0x00, 0x00, 0xf0, 0x00,
|
||||
0x00, 0x01, 0x00, 0x01,
|
||||
0x01,0xE0 // PDU Length Requested 这里默认960字节
|
||||
};
|
||||
internal static byte[] S7200SMART_PN = {
|
||||
0x03, 0x00, 0x00, 0x19,
|
||||
0x02, 0xf0, 0x80, // TPKT + COTP
|
||||
0x32, 0x01, 0x00, 0x00,
|
||||
|
||||
0xCC,0xC1,0x00,0x08,
|
||||
|
||||
0x00, 0x00, 0xf0, 0x00,
|
||||
0x00, 0x01, 0x00, 0x01,
|
||||
0x01,0xE0 // PDU Length Requested 这里默认960字节
|
||||
};
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
public class SiemensMessage : MessageBase, IMessage
|
||||
{
|
||||
public override int HeadBytesLength => 4;
|
||||
|
||||
public override bool CheckHeadBytes(byte[] token)
|
||||
{
|
||||
HeadBytes = token;
|
||||
byte[] headBytes = HeadBytes;
|
||||
if (headBytes == null || headBytes.Length < 4)
|
||||
BodyLength = 0;
|
||||
int length = (HeadBytes[2] * 256) + HeadBytes[3] - 4;
|
||||
if (length < 0)
|
||||
length = 0;
|
||||
BodyLength = length;
|
||||
return HeadBytes != null && HeadBytes[0] == 3 && HeadBytes[1] == 0;
|
||||
}
|
||||
|
||||
protected override void SendBytesThen()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
public enum SiemensEnum
|
||||
{
|
||||
S200,
|
||||
S200Smart,
|
||||
|
||||
S300,
|
||||
S400,
|
||||
|
||||
S1200,
|
||||
S1500,
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,335 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Resources;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
/// <summary>
|
||||
/// 相关命令含义源自网络资料/Shrap7/s7netplus
|
||||
/// </summary>
|
||||
public partial class SiemensS7PLC : ReadWriteDevicesTcpClientBase
|
||||
{
|
||||
|
||||
public SiemensS7PLCDataHandleAdapter DataHandleAdapter = new();
|
||||
private SiemensEnum _currentPlc = SiemensEnum.S1200;
|
||||
private int pdu_length = 0;
|
||||
private byte plc_rack = 0;
|
||||
private byte plc_slot = 0;
|
||||
private byte[] ISO_CR;
|
||||
private byte[] S7_PN;
|
||||
public SiemensEnum CurrentPlc => _currentPlc;
|
||||
/// <summary>
|
||||
/// 传入PLC类型,程序内会改变相应PLC类型的S7协议LocalTSAP, RemoteTSAP等
|
||||
/// </summary>
|
||||
/// <param name="tcpClient"></param>
|
||||
/// <param name="siemensPLCEnum"></param>
|
||||
public SiemensS7PLC(TcpClient tcpClient, SiemensEnum siemensPLCEnum) : base(tcpClient)
|
||||
{
|
||||
_currentPlc = siemensPLCEnum;
|
||||
RegisterByteLength = 1;
|
||||
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
|
||||
ISO_CR = new byte[22];
|
||||
S7_PN = new byte[25];
|
||||
Array.Copy(SiemensHelper.ISO_CR, ISO_CR, ISO_CR.Length);
|
||||
Array.Copy(SiemensHelper.S7_PN, S7_PN, S7_PN.Length);
|
||||
switch (siemensPLCEnum)
|
||||
{
|
||||
case SiemensEnum.S1200:
|
||||
ISO_CR[21] = 0x00;
|
||||
break;
|
||||
case SiemensEnum.S300:
|
||||
ISO_CR[21] = 0x02;
|
||||
break;
|
||||
case SiemensEnum.S400:
|
||||
ISO_CR[21] = 0x03;
|
||||
ISO_CR[17] = 0x00;
|
||||
break;
|
||||
case SiemensEnum.S1500:
|
||||
ISO_CR[21] = 0x00;
|
||||
break;
|
||||
case SiemensEnum.S200Smart:
|
||||
ISO_CR = SiemensHelper.ISO_CR200SMART;
|
||||
S7_PN = SiemensHelper.S7200SMART_PN;
|
||||
break;
|
||||
case SiemensEnum.S200:
|
||||
ISO_CR = SiemensHelper.ISO_CR200;
|
||||
S7_PN = SiemensHelper.S7200_PN;
|
||||
break;
|
||||
|
||||
}
|
||||
tcpClient.Connected += Connected;
|
||||
}
|
||||
|
||||
|
||||
#region 设置
|
||||
|
||||
/// <summary>
|
||||
/// 远程TSAP,需重新连接
|
||||
/// </summary>
|
||||
public int DestTSAP
|
||||
{
|
||||
get
|
||||
{
|
||||
return
|
||||
_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart ?
|
||||
(ISO_CR[17] * 256) + ISO_CR[18] :
|
||||
(ISO_CR[20] * 256) + ISO_CR[21];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
|
||||
{
|
||||
ISO_CR[17] = BitConverter.GetBytes(value)[1];
|
||||
ISO_CR[18] = BitConverter.GetBytes(value)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
ISO_CR[20] = BitConverter.GetBytes(value)[1];
|
||||
ISO_CR[21] = BitConverter.GetBytes(value)[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地TSAP,需重新连接
|
||||
/// </summary>
|
||||
public int LocalTSAP
|
||||
{
|
||||
get
|
||||
{
|
||||
return
|
||||
_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart ?
|
||||
(ISO_CR[13] * 256) + ISO_CR[14] :
|
||||
(ISO_CR[16] * 256) + ISO_CR[17];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
|
||||
{
|
||||
ISO_CR[13] = BitConverter.GetBytes(value)[1];
|
||||
ISO_CR[14] = BitConverter.GetBytes(value)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
ISO_CR[16] = BitConverter.GetBytes(value)[1];
|
||||
ISO_CR[17] = BitConverter.GetBytes(value)[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int PDULength => pdu_length;
|
||||
|
||||
/// <summary>
|
||||
/// 机架号,需重新连接
|
||||
/// </summary>
|
||||
public byte Rack
|
||||
{
|
||||
get => plc_rack;
|
||||
set
|
||||
{
|
||||
plc_rack = value;
|
||||
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ISO_CR[21] = (byte)((plc_rack * 0x20) + plc_slot);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 槽号,需重新连接
|
||||
/// </summary>
|
||||
public byte Slot
|
||||
{
|
||||
get => plc_slot;
|
||||
set
|
||||
{
|
||||
plc_slot = value;
|
||||
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ISO_CR[21] = (byte)((plc_rack * 0x20) + plc_slot);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync();
|
||||
var commandResult = GetReadByteCommand(address, length);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
List<byte> bytes = new();
|
||||
foreach (var item in commandResult.Content)
|
||||
{
|
||||
var result = TcpClient.GetTGWaitingClient(new()).SendThenResponse(item, TimeOut, token);
|
||||
if (result.RequestInfo is MessageBase collectMessage)
|
||||
{
|
||||
if (collectMessage.IsSuccess)
|
||||
bytes.AddRange(collectMessage.Content);
|
||||
else
|
||||
return OperResult.CreateFailedResult<byte[]>(collectMessage);
|
||||
}
|
||||
}
|
||||
return bytes.Count > 0 ? OperResult.CreateSuccessResult(bytes.ToArray()) : new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return OperResult.CreateFailedResult<byte[]>(commandResult);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<byte[]>(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取变长字符串
|
||||
/// </summary>
|
||||
public async Task<OperResult<string>> ReadString(string address, Encoding encoding)
|
||||
{
|
||||
return await SiemensHelper.ReadString(this, address, encoding);
|
||||
}
|
||||
|
||||
public async Task<OperResult<System.DateTime>> ReadDateTime(string address)
|
||||
{
|
||||
return ByteTransformHelper.GetResultFromBytes(await ReadAsync(address, 8), ThingsGateway.Foundation.Adapter.Siemens.DateTime.FromByteArray);
|
||||
}
|
||||
|
||||
public async Task<OperResult<System.DateTime>> ReadDate(string address)
|
||||
{
|
||||
return (await this.ReadAsync(address, 2)).
|
||||
Then(m => OperResult.CreateSuccessResult(ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime.AddDays(
|
||||
ThingsGatewayBitConverter.ToUInt16(m, 0)))
|
||||
);
|
||||
}
|
||||
|
||||
public override void SetDataAdapter()
|
||||
{
|
||||
DataHandleAdapter = new();
|
||||
TcpClient.SetDataHandlingAdapter(DataHandleAdapter);
|
||||
}
|
||||
|
||||
|
||||
public override Task<OperResult> WriteAsync(string address, string value, Encoding encoding, CancellationToken token = default)
|
||||
{
|
||||
return SiemensHelper.Write(this, address, value, encoding);
|
||||
}
|
||||
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ConnectAsync();
|
||||
var commandResult = GetWriteByteCommand(address, value);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
List<ResponsedData> bytes = new();
|
||||
foreach (var item in commandResult.Content)
|
||||
{
|
||||
ResponsedData result = TcpClient.GetTGWaitingClient(new()).SendThenResponse(item, TimeOut, token);
|
||||
bytes.Add(result);
|
||||
}
|
||||
return OperResult.CreateSuccessResult(bytes.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperResult.CreateFailedResult<bool[]>(commandResult);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<bool[]>(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
|
||||
{
|
||||
if (value.Length > 1)
|
||||
{
|
||||
return new OperResult("不支持多写");
|
||||
}
|
||||
try
|
||||
{
|
||||
await ConnectAsync();
|
||||
|
||||
var commandResult = GetWriteBitCommand(address, value[0]);
|
||||
if (commandResult.IsSuccess)
|
||||
{
|
||||
var result = TcpClient.GetTGWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
|
||||
return OperResult.CreateSuccessResult(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return OperResult.CreateFailedResult<bool[]>(commandResult);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult<bool[]>(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void Connected(ITcpClient client, MsgEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result1 = SendThenResponse(ISO_CR);
|
||||
if (!result1.IsSuccess)
|
||||
{
|
||||
Logger?.Error(client.GetIPPort() + "ISO初始化失败");
|
||||
return;
|
||||
}
|
||||
var result2 = SendThenResponse(S7_PN);
|
||||
if (!result2.IsSuccess)
|
||||
{
|
||||
Logger?.Error(client.GetIPPort() + "初始化失败");
|
||||
return;
|
||||
}
|
||||
pdu_length = ThingsGatewayBitConverter.ToUInt16(result2.Content.SelectLast(2), 0);
|
||||
pdu_length = pdu_length < 200 ? 200 : pdu_length;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Exception(ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<OperResult> WriteDateTime(string address, System.DateTime dateTime)
|
||||
{
|
||||
return await WriteAsync(address, ThingsGateway.Foundation.Adapter.Siemens.DateTime.ToByteArray(dateTime));
|
||||
}
|
||||
|
||||
public async Task<OperResult> WriteDate(string address, System.DateTime dateTime)
|
||||
{
|
||||
return await base.WriteAsync(address, Convert.ToUInt16((dateTime - ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime).TotalDays));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
public class SiemensS7PLCDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<SiemensMessage>
|
||||
{
|
||||
public override byte[] PackCommand(byte[] command)
|
||||
{
|
||||
return command;
|
||||
}
|
||||
protected override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
protected override OperResult<byte[]> UnpackResponse(
|
||||
byte[] send,
|
||||
byte[] response)
|
||||
{
|
||||
if (response[2] * 256 + response[3] == 7)
|
||||
{
|
||||
return new OperResult<byte[]>(response);
|
||||
}
|
||||
else
|
||||
{
|
||||
//已请求方为准,分开返回类型校验
|
||||
switch (send[17])
|
||||
{
|
||||
case 0x04:
|
||||
return SiemensHelper.AnalysisReadByte(send, response);
|
||||
case 0x05:
|
||||
return SiemensHelper.AnalysisWrite(response);
|
||||
}
|
||||
return OperResult.CreateSuccessResult(response);
|
||||
}
|
||||
}
|
||||
|
||||
protected override SiemensMessage GetInstance()
|
||||
{
|
||||
return new SiemensMessage();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
|
||||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
{
|
||||
public partial class SiemensS7PLC : ReadWriteDevicesTcpClientBase
|
||||
{
|
||||
private static OperResult<byte[]> GetWriteBitCommand(string address, bool data)
|
||||
{
|
||||
OperResult<SiemensAddress> result = SiemensAddress.ParseFrom(address);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return result.Copy<byte[]>();
|
||||
}
|
||||
return SiemensHelper.GetWriteBitCommand(result.Content, data);
|
||||
}
|
||||
private OperResult<List<byte[]>> GetReadByteCommand(string address, int length)
|
||||
{
|
||||
OperResult<SiemensAddress> from = SiemensAddress.ParseFrom(address, length);
|
||||
if (!from.IsSuccess) return from.Copy<List<byte[]>>();
|
||||
ushort num1 = 0;
|
||||
var listBytes = new List<byte[]>();
|
||||
while (num1 < length)
|
||||
{
|
||||
//pdu长度,重复生成报文,直至全部生成
|
||||
ushort num2 = (ushort)Math.Min(length - num1, pdu_length);
|
||||
from.Content.Length = num2;
|
||||
var result = GetReadByteCommand(new SiemensAddress[1] { from.Content });
|
||||
if (!result.IsSuccess) return result.Copy<List<byte[]>>();
|
||||
listBytes.AddRange(result.Content);
|
||||
num1 += num2;
|
||||
if (from.Content.DataCode == (byte)S7WordLength.Timer || from.Content.DataCode == (byte)S7WordLength.Counter)
|
||||
{
|
||||
from.Content.AddressStart += num2 / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
from.Content.AddressStart += num2 * 8;
|
||||
}
|
||||
}
|
||||
return OperResult.CreateSuccessResult(listBytes);
|
||||
}
|
||||
|
||||
private OperResult<List<byte[]>> GetReadByteCommand(SiemensAddress[] siemensAddress)
|
||||
{
|
||||
if (siemensAddress.Length <= 19)
|
||||
{
|
||||
return ByteTransformHelper.GetResultFromBytes(SiemensHelper.GetReadCommand(siemensAddress), m => new List<byte[]>() { m });
|
||||
}
|
||||
|
||||
List<byte[]> byteList = new List<byte[]>();
|
||||
List<SiemensAddress[]> s7AddressDataArrayList = siemensAddress.ArraySplitByLength(19);
|
||||
for (int index = 0; index < s7AddressDataArrayList.Count; ++index)
|
||||
{
|
||||
var result = GetReadByteCommand(s7AddressDataArrayList[index]);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
byteList.AddRange(result.Content);
|
||||
}
|
||||
return OperResult.CreateSuccessResult(byteList);
|
||||
}
|
||||
|
||||
private OperResult<List<byte[]>> GetWriteByteCommand(string address, byte[] value)
|
||||
{
|
||||
return SiemensAddress.ParseFrom(address).Then(m => GetWriteByteCommand(m, value));
|
||||
}
|
||||
|
||||
private OperResult<List<byte[]>> GetWriteByteCommand(SiemensAddress address, byte[] value)
|
||||
{
|
||||
int length1 = value.Length;
|
||||
ushort index = 0;
|
||||
List<byte[]> bytes = new List<byte[]>();
|
||||
while (index < length1)
|
||||
{
|
||||
//pdu长度,重复生成报文,直至全部生成
|
||||
ushort length2 = (ushort)Math.Min(length1 - index, pdu_length);
|
||||
byte[] data = ThingsGatewayBitConverter.ToByte(value, index, length2);
|
||||
OperResult<byte[]> result1 = SiemensHelper.GetWriteByteCommand(address, data);
|
||||
if (!result1.IsSuccess)
|
||||
{
|
||||
return result1.Copy<List<byte[]>>();
|
||||
}
|
||||
bytes.Add(result1.Content);
|
||||
index += length2;
|
||||
address.AddressStart += length2 * 8;
|
||||
}
|
||||
return OperResult.CreateSuccessResult(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<LangVersion>latestMajor</LangVersion>
|
||||
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
|
||||
<Version>1.0.5</Version>
|
||||
<Title>ThingsGateway.Foundation.Adapter.Siemens</Title>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<Authors>Diego</Authors>
|
||||
<Description>Siemens通讯类库</Description>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
<RunAnalyzersDuringBuild>True</RunAnalyzersDuringBuild>
|
||||
<PackageProjectUrl>https://diego2098.gitee.io/thingsgateway/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
|
||||
<PackageOutputPath>$(SolutionDir)</PackageOutputPath>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\..\README.md">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@@ -1,122 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using ThingsGateway.Foundation.Adapter.Modbus;
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace ThingsGateway.Foundation.Tests
|
||||
{
|
||||
public class Modbus
|
||||
{
|
||||
TcpClient client;
|
||||
private TouchSocketConfig config;
|
||||
private ModbusRtuOverTcp RtuTcpClient;
|
||||
private ModbusRtuOverUdp RtuUdpClient;
|
||||
private ModbusTcp TcpClient;
|
||||
private ModbusUdp UdpClient;
|
||||
|
||||
[Fact]
|
||||
public async Task RtuTcpReadTest()
|
||||
{
|
||||
ModbusRtuTcpClient();
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
string address = $"4{i * 2 + 1};DATA=ABCD;";
|
||||
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, RtuTcpClient.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
|
||||
var test = await RtuTcpClient.ReadAsync(address, 1);
|
||||
var data = byteConverter.ToInt16(test.Content, 0);
|
||||
Console.WriteLine(data.ToJson());
|
||||
}
|
||||
|
||||
for (int i = 100; i < 110; i++)
|
||||
{
|
||||
string address = $"4{i * 2 + 1};TEXT=UTF8;LEN=4";
|
||||
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, RtuTcpClient.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
|
||||
var test = await RtuTcpClient.ReadAsync(address, length / RtuTcpClient.RegisterByteLength);
|
||||
var data = byteConverter.ToString(test.Content, 0, length);
|
||||
Console.WriteLine(data.ToJson());
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine("<22>ܺ<EFBFBD>ʱ<EFBFBD><CAB1>" + stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TcpReadTest()
|
||||
{
|
||||
ModbusTcpClient();
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
string address = $"0{i + 1};";
|
||||
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, TcpClient.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
|
||||
var test = await TcpClient.ReadAsync(address, 1);
|
||||
var data = byteConverter.ToBoolean(test.Content, 0);
|
||||
Console.WriteLine(data.ToJson());
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine("<22>ܺ<EFBFBD>ʱ<EFBFBD><CAB1>" + stopwatch.Elapsed.TotalSeconds);
|
||||
|
||||
}
|
||||
|
||||
private void ModbusRtuTcpClient()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
config.SetRemoteIPHost(new IPHost("127.0.0.1:5021"))
|
||||
.SetBufferLength(300);
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
client = config.Container.Resolve<TcpClient>();
|
||||
client.Setup(config);
|
||||
RtuTcpClient = new(client);
|
||||
RtuTcpClient.ConnectTimeOut = 5000;
|
||||
RtuTcpClient.Station = 1;
|
||||
RtuTcpClient.TimeOut = 5000;
|
||||
}
|
||||
|
||||
private void ModbusRtuUdpClient()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
config.SetRemoteIPHost(new IPHost("127.0.0.1:5022")).SetBindIPHost(new IPHost(0))
|
||||
.SetBufferLength(3000);
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
var client1 = config.BuildWithUdpSession<UdpSession>();
|
||||
RtuUdpClient = new(client1);
|
||||
RtuUdpClient.ConnectTimeOut = 5000;
|
||||
RtuUdpClient.Station = 1;
|
||||
RtuUdpClient.TimeOut = 5000;
|
||||
}
|
||||
|
||||
private void ModbusTcpClient()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
config.SetRemoteIPHost(new IPHost("127.0.0.1:5023"))
|
||||
.SetBufferLength(300);
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
client = config.Container.Resolve<TcpClient>();
|
||||
client.Setup(config);
|
||||
TcpClient = new(client);
|
||||
TcpClient.ConnectTimeOut = 5000;
|
||||
TcpClient.Station = 1;
|
||||
TcpClient.TimeOut = 5000;
|
||||
}
|
||||
private void ModbusUdpClient()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
config.SetRemoteIPHost(new IPHost("127.0.0.1:5024")).SetBindIPHost(new IPHost(0))
|
||||
.SetBufferLength(3000);
|
||||
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
var client1 = config.BuildWithUdpSession<UdpSession>();
|
||||
UdpClient = new(client1);
|
||||
UdpClient.ConnectTimeOut = 5000;
|
||||
UdpClient.Station = 1;
|
||||
UdpClient.TimeOut = 5000;
|
||||
}
|
||||
}
|
||||
}
|
BIN
src/Plugins/ThingsGateway.Foundation.Tests/Modbus/Mbslave1.mbs
Normal file
BIN
src/Plugins/ThingsGateway.Foundation.Tests/Modbus/Mbslave1.mbs
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user