diff --git a/README.md b/README.md index 1863d57..e69de29 100644 --- a/README.md +++ b/README.md @@ -1,390 +0,0 @@ -# EdgeManager(SCADA系统) - -- [EdgeManager(SCADA系统)](#edgemanagerscada系统) - - [API](#api) - - [class `EDataCapture`](#class-edatacapture) - - [0. HTTP POST: `set_node_data`](#0-http-post-set_node_data) - - [使用指北](#使用指北) - - [后端](#后端) - - [前端](#前端) - - [开发环境](#开发环境) - - [调试环境](#调试环境) - - [技术细节](#技术细节) - - [0. `EdgeManager\EDataCapture\EDataCapture -> set_data()`为什么是以`6710885`为大小chunked的?](#0-edgemanageredatacaptureedatacapture---set_data为什么是以6710885为大小chunked的) - -## API - -> API设计最大程度遵循了已有的MES规范。 - -### class `EDataCapture` - -#### 0. HTTP POST: `set_node_data` - -**请求** - -> 上位机程序中请单次传入尽量多的数据使得性能最大化。 -> -> 但仍需兼顾传输速率和超时时间。 -> -> 无需指定数值类型,服务端会自动根据已添加的节点信息检查,若不符则会报错。 - - -**上传设备状态** - -> 数据节点的数据类型在后台设定为设备状态时,该节点上传的值只能上传int或string两种类型。 -> -> 并且int或string这两种类型,只能上传下方表格中int或string指定的值,若不符则会报错。 -> -> 上传对应的设备状态parent_device_code为必传字段 - -| int类型 | string类型 | 描述 | -| ----- | ----- | ----- | -| 1 | IDLE | 设备已经初始化,再等待工作 | -| 2 | RUN | 设备在工作 | -| 3 | FINISH | 工序结束后,托盘还没取出时,设备为FINISH,取出后变为IDLE状态 | -| 4 | TROUBLE | 设备有问题,需要维修,这时msg有相应的关键字 | -| 5 | PAUSE | 设备通过手工操作变成暂停状态 | -| 6 | OFFLINE | 设备处于离线状态 | - -```json -{ - "action": "set_node_data", - "param": { - "working_subclass": , // 可选 - "data": [ - { - "code": , - // value的类型需与type对应 - "value": , - "device_code": [string], // 必需字段 - "parent_device_code": [string], // 可选字段,对应MES中的code - "batch": [string] // 可选字段 - }, - { - //code的值在后台的数据类别为设备状态类型时 - "code": , - // value的类型需与type对应 - "value": , - "device_code": [string], // 必需字段 - "parent_device_code": [string], // 必需字段,对应MES中的code - "batch": [string] // 可选字段 - }, - { - "code": , - "value": , - "device_code": [string], - "parent_device_code": [string], - "batch": [string] - }, - ... - ] - } -} -``` - -**返回** - -操作成功: - -```json -{ - "action": "result_set_node_data", - "errcode": 0, - "errmsg": "" -} -``` - -操作失败(**工序单元尚未登记**): - -```json -{ - "action": "result_set_node_data", - "errcode": 4002, - "errmsg": "未登记过的工序单元!" -} -``` - -操作失败(**数值类型错误**): - -```json -{ - "action": "result_set_node_data", - "errcode": 4002, - "errmsg": "节点编码和数值类型不匹配!" -} -``` - -操作失败(不明原因): - -```json -{ - "action": "result_set_node_data", - "errcode": 4002, - "errmsg": "ROLLBACKed: Bad data received (structure and/or values)" -} -``` - - -## 使用指北 - -本项目可独立运行,也可作为MES的插件使用。 - -下文简要介绍如何将代码合并入MES中。 - -需要保证`timescaledb`的版本在**2.2.0或以上**。 - -### 后端 - -添加pg扩展(Ubuntu): - -```bash -sudo apt install php-pgsql -``` - -添加pg扩展(CentOS 7): - -```bash -sudo yum install php-pgsql -``` - -添加pg扩展(CentOS8): - -```bash -sudo dnf install php-pgsql -``` - -目录结构: - -```pre -📦EdgeManager - ┣ 📂EDataCapture - ┃ ┣ 📜EDataCapture.php - ┃ ┗ 📜ENodeConfigure.php - ┣ 📜Init.php - ┗ 📜Utils.php -``` - -建议使用composer autoload引入。 - -将目录`EdgeManager`复制至项目后在composer.json内写入: - -```json -"autoload": { - "psr-4": { - "EdgeManager\\": "EdgeManager/" - }, - "files": [ - "EdgeManager/Utils.php", - "EdgeManager/Init.php" - ] -}, -``` - -运行`composer dump-autoload`即可正常调用EdgeManager的代码。 - -### 前端 - -添加依赖(**使用npm**): - -```bash -npm i element-ui \ - @d2-projects/d2-crud \ - vue-cheetah-grid \ - @d2-projects/vue-table-export \ - @d2-projects/vue-table-import \ - github-markdown-css \ - marked@^2.0.0 \ - jschardet -S -``` - -添加依赖(**使用yarn**): -```bash -yarn add element-ui \ - @d2-projects/d2-crud \ - vue-cheetah-grid \ - @d2-projects/vue-table-export \ - @d2-projects/vue-table-import \ - github-markdown-css \ - marked@^2.0.0 \ - jschardet -``` - -需要在`main.js`中增加(全局引入组件): - -```js -// D2-Crud -import ElementUI from 'element-ui' -import 'element-ui/lib/theme-chalk/index.css' -import D2Crud from '@d2-projects/d2-crud' -// Cheetah-Grid -import vueCheetahGrid from 'vue-cheetah-grid' -// 表格导出插件 -import pluginExport from '@d2-projects/vue-table-export' -import pluginImport from '@d2-projects/vue-table-import' - -Vue.use(ElementUI) -Vue.use(D2Crud) -Vue.use(vueCheetahGrid) -Vue.use(pluginExport) -Vue.use(pluginImport) -``` - -*(可选)* 在`package.json`里更改`element-ui`版本: - -```json -"element-ui": ">2.15.9 || 2.15.8", -``` - -`2.15.9`有一个小[bug](https://github.com/ElemeFE/element/issues/21941),会导致性能下降。 - -此外请参照目录结构中注释进行代码合并: - -```pre -📦src - ┣ 📂api - ┃ ┣ 📂modules - ┃ ┃ ┣ 📜scada.configure.api.js # 增添Axios请求 - ┃ ┃ ┗ 📜sys.user.api.js - ┃ ┣ 📜index.js - ┃ ┣ 📜service.js - ┃ ┗ 📜tools.js - ┣ 📂assets - ┣ 📂components - ┃ ┣ 📂d2-markdown # 渲染markdown所需组件(在D2Admin的基础上精简了功能) - ┃ ┃ ┗ 📜index.vue - ┃ ┗ 📜index.js # 在此处注册d2-markdown - ┣ 📂libs - ┣ 📂locales - ┣ 📂menu - ┃ ┗ 📜index.js # 增添菜单 - ┣ 📂plugin - ┣ 📂router - ┃ ┣ 📜index.js - ┃ ┗ 📜routes.js # 增添路由 - ┣ 📂store - ┣ 📂views - ┃ ┣ 📂scada # 增添页面 - ┃ ┃ ┣ 📂scadaConfigure - ┃ ┃ ┃ ┗ 📜index.vue - ┃ ┃ ┗ 📂scadaQuery - ┃ ┃ ┃ ┗ 📜index.vue - ┃ ┗ 📂system - ┣ 📜App.vue - ┣ 📜i18n.js - ┣ 📜main.js - ┗ 📜setting.js -``` - -## 开发环境 - -拉取代码: - -```bash -# 建议先配置SSH key pair -git clone ssh://git@118.195.187.246:10022/ysun/EdgeManager.git -cd EdgeManager -``` - -一键部署PHP workerman和TimescaleDB环境: - -```bash -# docker build --network host -t edge_manager . -docker compose up -d -``` - -进入交互式Prompt: - -```bash -docker exec -it edge_manager bash -``` - -后端调试: - -```bash -# In container -# --no_dup_code:禁止code在不同的working subclass间复用 -# --relay_device_status:不判断是否是设备状态并转发到MES接口 -php EdgeManager.php --no_dup_code --relay_device_status --server_name=GPU-server-01 --user=postgres --password=big_dick start -``` - -前端调试: - -```bash -# In host -# yarn -# yarn watch -yarn serve -``` - -客户端连接: - -```bash -# sudo apt install postgresql-client -# 登入 -psql -h localhost -U postgres -# 显示数据库列表 -\l -``` - -## 调试环境 - -不使用`docker compose`创建两个container分别运行EdgeManager和pg: - -```bash -# cd EdgeManager # 先定位到项目目录,方便创建image和挂载 -# 创建EdgeManager的image -docker build --network host -t edge_manager . -# 创建container运行EdgeManager -docker run -d --name edge_manager_test -v $PWD:/EdgeManager --network host --ipc host -it edge_manager -# 创建container运行pg,将端口映射到host的55432 -docker run -d --name pg_test -v $PWD/config/postgresql.conf:/etc/postgresql/postgresql.conf -p 55432:5432 -e POSTGRES_PASSWORD=big_dick -it timescale/timescaledb-ha:pg14-latest postgres -c 'config_file=/etc/postgresql/postgresql.conf' -# 进入交互式Prompt -docker exec -it edge_manager_test bash -# 启动EdgeManager(workerman)命令省略... - -# 常用命令 -# 查看全部container -docker ps -a -# 启动已停止的container -docker start [container] -``` - -## 技术细节 - -### 0. `EdgeManager\EDataCapture\EDataCapture -> set_data()`为什么是以`6710885`为大小chunked的? - -首先明确一点,根据PostgreSQL的技术架构,条件允许的情况下,**一次性插入多条记录是效率最高的**,比如: - -```sql -INSERT INTO films (code, title, did, date_prod, kind) VALUES - ('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'), - ('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy'); -``` - -这其中,仅插入部分字段的值,比如: - -```sql -INSERT INTO films (code, title, did, date_prod, kind) - VALUES ('T_601', 'Yojimbo', 106, '1961-06-16', 'Drama'); -``` - -的效率又远远不如插入全部列,但使用特殊变量DEFAULT替代缺少的字段,比如: - -```sql -INSERT INTO films VALUES - ('UA502', 'Bananas', DEFAULT, '1971-07-13', 'Comedy', '82 minutes'); -``` - -PostgreSQL的文档里没有提及这个话题,因为源码里为SQL语句的内存分配[指定了一个最大长度常量`MaxAllocSize`](https://github.com/postgres/postgres/blob/2373fe78dfc9d4aa2348a86fffdf8eb9d757e9d5/src/common/stringinfo.c#L28),其大小为比1GB小1字节。这个值可以在编译前手动修改,所以理论上每个PG运行的实例都可以不一样,如果我们使用的PG并非修改过源码手动编译的,那么SQL语句的最大长度为`1024 ** 3 - 1 = 1073741823` bytes。 - -> 安全起见,下述计算假定working_subclass等字段的最大长度均为我们以往约定的45 bytes。 - -而此处我源码中的SQL语句头长度为138 bytes(注意末尾空格): - -![](imgs/sql_head.png) - -单个语句体的长度为160 bytes: - -![](imgs/sql_body.png) - -所以其最多可以一次性插入`(1073741823 - 138) / 160`条记录,向下取整后即为`6710885`。 diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index ba304b9..78c046b 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -4,6 +4,17 @@ [系统配置](system.md) + +# SCADA系统 + +- [环境搭建](scada/environment.md) +- [作为MES插件使用](scada/plug_in.md) + - [前端配置](scada/front.md) + - [后端配置](scada/service.md) +- [API](scada/API.md) +- [技术细节](scada/details.md) + + # 测试 - [集群搭建](tests/cluster.md) diff --git a/docs/src/scada/api.md b/docs/src/scada/api.md new file mode 100644 index 0000000..b23858b --- /dev/null +++ b/docs/src/scada/api.md @@ -0,0 +1,91 @@ +# API +> API设计最大程度遵循了已有的MES规范。 + +## 请求说明 +>上位机程序中请单次传入尽量多的数据使得性能最大化。 +> +>但仍需兼顾传输速率和超时时间。 +> +>无需指定数值类型,服务端会自动根据已添加的节点信息检查,若不符则会报错。 + +### set_node_data,上传设备实时数据 +#### 输入接口参数说明: + +| 参数名称 | 参数说明 | 类型 | 是否必传 |备注 | +| ---- | ---- | ---- | ---- |---- | +| action | 接口名称 |String | 是 | +| param | 接口参数 |Object | 是| +| working_subclass | 工序单元属性 |String | 可选 | 通过数据系统对各工序动态定义| +| data | 上传数值 |Array | 是| +| code | 值 | String | 是 | +| value | 值 |string/int/float/bool | 是 | value的类型需与数据库节点配置表的type对应| +| device_code | 设备编码 |string | 是 | +| parent_device_code | 值 | String | 可选 | MES系统中的设备编号,上传设备状态时为必传| +| batch | 批次号 | String | 可选 | MES系统中的生产批次号| + +```json +{ + "action": "set_node_data", + "param": { + "working_subclass": , // 可选 + "data": [ + { + "code": , + "value": ,// value的类型需与type对应 + "device_code": [string], // 必需字段 + "parent_device_code": [string], // 可选字段,对应MES中的device_code + "batch": [string] // 可选字段 + }, + { + //code的值在后台的数据类别为设备状态类型时 + "code": , + // value的类型需与type对应 + "value": , + "device_code": [string], // 必需字段 + "parent_device_code": [string], // 必需字段,对应MES中的code + "batch": [string] // 可选字段 + }, + { + "code": , + "value": , + "device_code": [string], + "parent_device_code": [string], + "batch": [string] + }, + ... + ] + } +} +``` + +#### 1.2 输出接口参数说明: + +| 参数名称 | 参数说明 | 类型 | 长度 |备注 | +| ----- | ----- | ----- | ----- |----- | +| action | 接口名称 |String | 45 | +| errcode | 返回码 |Int | 11 |0:请求成功
4002:未登记过的工序单元!
4002:节点编码和数值类型不匹配!
4002:ROLLBACKed: Bad data received (structure and/or values) | +| errmsg | 错误信息 |String | 255 |errcode 不为0,errmsg才有错误信息 | + +``` +{ + action: "result_set_node_data", + errcode: 0, + errmsg: "" +} +``` + +## 上传设备状态 +>数据节点的数据类型在后台设定为设备状态时,该节点上传的值只能上传int或string两种类型。 +> +>并且int或string这两种类型,只能上传下方表格中int或string指定的值,若不符则会报错。 +> +>上传对应的设备状态parent_device_code为必传字段 + +| int类型 | string类型 | 描述 | +| ----- | ----- | ----- | +| 1 | IDLE | 设备已经初始化,再等待工作 | +| 2 | RUN | 设备在工作 | +| 3 | FINISH | 工序结束后,托盘还没取出时,设备为FINISH,取出后变为IDLE状态 | +| 4 | TROUBLE | 设备有问题,需要维修,这时msg有相应的关键字 | +| 5 | PAUSE | 设备通过手工操作变成暂停状态 | +| 6 | OFFLINE | 设备处于离线状态 | \ No newline at end of file diff --git a/docs/src/scada/details.md b/docs/src/scada/details.md new file mode 100644 index 0000000..a18bc57 --- /dev/null +++ b/docs/src/scada/details.md @@ -0,0 +1,39 @@ +## 技术细节 + +### 0. `EdgeManager\EDataCapture\EDataCapture -> set_data()`为什么是以`6710885`为大小chunked的? + +首先明确一点,根据PostgreSQL的技术架构,条件允许的情况下,**一次性插入多条记录是效率最高的**,比如: + +```sql +INSERT INTO films (code, title, did, date_prod, kind) VALUES + ('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'), + ('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy'); +``` + +这其中,仅插入部分字段的值,比如: + +```sql +INSERT INTO films (code, title, did, date_prod, kind) + VALUES ('T_601', 'Yojimbo', 106, '1961-06-16', 'Drama'); +``` + +的效率又远远不如插入全部列,但使用特殊变量DEFAULT替代缺少的字段,比如: + +```sql +INSERT INTO films VALUES + ('UA502', 'Bananas', DEFAULT, '1971-07-13', 'Comedy', '82 minutes'); +``` + +PostgreSQL的文档里没有提及这个话题,因为源码里为SQL语句的内存分配[指定了一个最大长度常量`MaxAllocSize`](https://github.com/postgres/postgres/blob/2373fe78dfc9d4aa2348a86fffdf8eb9d757e9d5/src/common/stringinfo.c#L28),其大小为比1GB小1字节。这个值可以在编译前手动修改,所以理论上每个PG运行的实例都可以不一样,如果我们使用的PG并非修改过源码手动编译的,那么SQL语句的最大长度为`1024 ** 3 - 1 = 1073741823` bytes。 + +> 安全起见,下述计算假定working_subclass等字段的最大长度均为我们以往约定的45 bytes。 + +而此处我源码中的SQL语句头长度为138 bytes(注意末尾空格): + +![](sql_head.png) + +单个语句体的长度为160 bytes: + +![](sql_body.png) + +所以其最多可以一次性插入`(1073741823 - 138) / 160`条记录,向下取整后即为`6710885`。 \ No newline at end of file diff --git a/docs/src/scada/environment.md b/docs/src/scada/environment.md new file mode 100644 index 0000000..0b23772 --- /dev/null +++ b/docs/src/scada/environment.md @@ -0,0 +1,77 @@ +# 环境搭建 +## 开发环境 + +拉取代码: + +```bash +# 建议先配置SSH key pair +git clone ssh://git@118.195.187.246:10022/ysun/EdgeManager.git +cd EdgeManager +``` + +一键部署PHP workerman和TimescaleDB环境: + +```bash +# docker build --network host -t edge_manager . +docker compose up -d +``` + +进入交互式Prompt: + +```bash +docker exec -it edge_manager sh +``` + +后端调试: + +>--no_dup_code:禁止code在不同的working subclass间复用 +> +>--relay_device_status:不判断是否是设备状态并转发到MES接口 +> +>--port:连接PG数据库端口,默认5432端口 + +```bash +# In container +php EdgeManager.php --no_dup_code --relay_device_status --server_name=GPU-server-01 --user=postgres --password=big_dick start +``` + +前端调试: + +```bash +# In host +# yarn +# yarn watch +yarn serve +``` + +客户端PG连接: + +```bash +# sudo apt install postgresql-client +# 登入 +psql -h localhost -U postgres +# 显示数据库列表 +\l +``` + +## 调试环境 + +不使用`docker compose`创建两个container分别运行EdgeManager和pg: + +```bash +# cd EdgeManager # 先定位到项目目录,方便创建image和挂载 +# 创建EdgeManager的image +docker build --network host -t edge_manager . +# 创建container运行EdgeManager +docker run -d --name edge_manager_test -v $PWD:/EdgeManager --network host --ipc host -it edge_manager +# 创建container运行pg,将端口映射到host的55432 +docker run -d --name pg_test -v $PWD/config/postgresql.conf:/etc/postgresql/postgresql.conf -p 55432:5432 -e POSTGRES_PASSWORD=big_dick -it timescale/timescaledb-ha:pg14-latest postgres -c 'config_file=/etc/postgresql/postgresql.conf' +# 进入交互式Prompt +docker exec -it edge_manager_test sh +# 启动EdgeManager(workerman)命令省略... + +# 常用命令 +# 查看全部container +docker ps -a +# 启动已停止的container +docker start [container] diff --git a/docs/src/scada/front.md b/docs/src/scada/front.md new file mode 100644 index 0000000..47a6639 --- /dev/null +++ b/docs/src/scada/front.md @@ -0,0 +1,91 @@ +# 前端配置 +添加依赖(**使用npm**): + +```bash +npm i element-ui \ + @d2-projects/d2-crud \ + vue-cheetah-grid \ + @d2-projects/vue-table-export \ + @d2-projects/vue-table-import \ + github-markdown-css \ + marked@^2.0.0 \ + jschardet -S +``` + +添加依赖(**使用yarn**): +```bash +yarn add element-ui \ + @d2-projects/d2-crud \ + vue-cheetah-grid \ + @d2-projects/vue-table-export \ + @d2-projects/vue-table-import \ + github-markdown-css \ + marked@^2.0.0 \ + jschardet +``` + +需要在`main.js`中增加(全局引入组件): + +```js +// D2-Crud +import ElementUI from 'element-ui' +import 'element-ui/lib/theme-chalk/index.css' +import D2Crud from '@d2-projects/d2-crud' +// Cheetah-Grid +import vueCheetahGrid from 'vue-cheetah-grid' +// 表格导出插件 +import pluginExport from '@d2-projects/vue-table-export' +import pluginImport from '@d2-projects/vue-table-import' + +Vue.use(ElementUI) +Vue.use(D2Crud) +Vue.use(vueCheetahGrid) +Vue.use(pluginExport) +Vue.use(pluginImport) +``` + +*(可选)* 在`package.json`里更改`element-ui`版本: + +```json +"element-ui": ">2.15.9 || 2.15.8", +``` + +`2.15.9`有一个小[bug](https://github.com/ElemeFE/element/issues/21941),会导致性能下降。 + +此外请参照目录结构中注释进行代码合并: + +```pre +📦src + ┣ 📂api + ┃ ┣ 📂modules + ┃ ┃ ┣ 📜scada.configure.api.js # 增添Axios请求 + ┃ ┃ ┗ 📜sys.user.api.js + ┃ ┣ 📜index.js + ┃ ┣ 📜service.js + ┃ ┗ 📜tools.js + ┣ 📂assets + ┣ 📂components + ┃ ┣ 📂d2-markdown # 渲染markdown所需组件(在D2Admin的基础上精简了功能) + ┃ ┃ ┗ 📜index.vue + ┃ ┗ 📜index.js # 在此处注册d2-markdown + ┣ 📂libs + ┣ 📂locales + ┣ 📂menu + ┃ ┗ 📜index.js # 增添菜单 + ┣ 📂plugin + ┣ 📂router + ┃ ┣ 📜index.js + ┃ ┗ 📜routes.js # 增添路由 + ┣ 📂store + ┣ 📂views + ┃ ┣ 📂scada # 增添页面 + ┃ ┃ ┣ 📂scadaConfigure + ┃ ┃ ┃ ┗ 📜index.vue + ┃ ┃ ┗ 📂scadaQuery + ┃ ┃ ┃ ┗ 📜index.vue + ┃ ┗ 📂system + ┣ 📜App.vue + ┣ 📜i18n.js + ┣ 📜main.js + ┗ 📜setting.js +``` \ No newline at end of file diff --git a/docs/src/scada/plug_in.md b/docs/src/scada/plug_in.md new file mode 100644 index 0000000..7dfda28 --- /dev/null +++ b/docs/src/scada/plug_in.md @@ -0,0 +1,6 @@ +# SCADA系统 +> 本项目可独立运行,也可作为MES的插件使用。 +> +> 下面章节简要介绍如何将代码合并入MES中。 +> +> 需要保证timescaledb的版本在2.2.0或以上。 \ No newline at end of file diff --git a/docs/src/scada/service.md b/docs/src/scada/service.md new file mode 100644 index 0000000..a4c8a23 --- /dev/null +++ b/docs/src/scada/service.md @@ -0,0 +1,48 @@ +# 后端配置 + +添加pg扩展(Ubuntu): + +```bash +sudo apt install php-pgsql +``` + +添加pg扩展(CentOS 7): + +```bash +sudo yum install php-pgsql +``` + +添加pg扩展(CentOS8): + +```bash +sudo dnf install php-pgsql +``` + +目录结构: + +```pre +📦EdgeManager + ┣ 📂EDataCapture + ┃ ┣ 📜EDataCapture.php + ┃ ┗ 📜ENodeConfigure.php + ┣ 📜Init.php + ┗ 📜Utils.php +``` + +建议使用composer autoload引入。 + +将目录`EdgeManager`复制至项目后在composer.json内写入: + +```json +"autoload": { + "psr-4": { + "EdgeManager\\": "EdgeManager/" + }, + "files": [ + "EdgeManager/Utils.php", + "EdgeManager/Init.php" + ] +}, +``` + +运行`composer dump-autoload`即可正常调用EdgeManager的代码。 \ No newline at end of file diff --git a/docs/src/scada/sql_body.png b/docs/src/scada/sql_body.png new file mode 100644 index 0000000..c96d713 Binary files /dev/null and b/docs/src/scada/sql_body.png differ diff --git a/docs/src/scada/sql_head.png b/docs/src/scada/sql_head.png new file mode 100644 index 0000000..314cf66 Binary files /dev/null and b/docs/src/scada/sql_head.png differ