mirror of
https://github.com/emqx/neuron.git
synced 2026-10-30 07:45:59 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad5c49440e | ||
|
|
92a854531a | ||
|
|
1323df30af | ||
|
|
c231e3d928 | ||
|
|
11f721dc30 | ||
|
|
8fc11f5ded | ||
|
|
ba418c75f7 | ||
|
|
e1fc4a93f8 | ||
|
|
f762721af9 | ||
|
|
7fb5e65879 | ||
|
|
395985c7fb | ||
|
|
e05b18a275 | ||
|
|
82beb88535 | ||
|
|
ef25effa10 | ||
|
|
805935f04a | ||
|
|
6d92e250ed | ||
|
|
f1ef251b33 | ||
|
|
097bf9fdc7 | ||
|
|
d3528ceca0 | ||
|
|
1bda4e0d69 | ||
|
|
b8a9eb33fa | ||
|
|
9346403bb0 | ||
|
|
7570e01f00 | ||
|
|
1219ef55b2 | ||
|
|
f938736979 | ||
|
|
622d2ee4e3 | ||
|
|
3c35e5822c | ||
|
|
a10d50199f | ||
|
|
733684b6a2 | ||
|
|
e41328495b | ||
|
|
643d3d6ce0 | ||
|
|
5c5049793a | ||
|
|
392a2134a6 | ||
|
|
1c165c6be0 | ||
|
|
9f5c027bde | ||
|
|
7e67d7828e | ||
|
|
15d329c473 | ||
|
|
af1cb1feb5 | ||
|
|
d58b6cb871 | ||
|
|
97084c8255 | ||
|
|
a6fbfc0330 | ||
|
|
96ccd6cb72 | ||
|
|
ca2d8af6e8 | ||
|
|
3bb42f900f | ||
|
|
bbda93038c | ||
|
|
9a5c1862b8 | ||
|
|
0652bbe00b | ||
|
|
6c088356f6 | ||
|
|
7441908f3a | ||
|
|
0a32d72fcd | ||
|
|
bda5ef1b2e | ||
|
|
6b47dc6a4a | ||
|
|
16ad3f6873 | ||
|
|
63f9a0eb9c | ||
|
|
942fcf8663 | ||
|
|
05b6d6be5a | ||
|
|
6c34076976 | ||
|
|
96ebfa4034 | ||
|
|
2ca52aecec | ||
|
|
989421f000 | ||
|
|
88e5fc85b7 | ||
|
|
14b55309e4 | ||
|
|
f761bb9c85 | ||
|
|
c38ae03b2b | ||
|
|
b522583f51 | ||
|
|
9f2b04300f | ||
|
|
d94afeedf6 | ||
|
|
ffe0316e5f | ||
|
|
7457543471 | ||
|
|
82615c0908 | ||
|
|
584732fe8c | ||
|
|
8c11f83540 | ||
|
|
c9bf291fa2 | ||
|
|
ae959acc52 | ||
|
|
d1b3838695 | ||
|
|
159be9f511 | ||
|
|
e07e5ad2e8 | ||
|
|
68c9dc0b0e | ||
|
|
ef859090bb | ||
|
|
04defee4da | ||
|
|
811ba04408 | ||
|
|
1dc53e3ff1 | ||
|
|
14326c8da1 | ||
|
|
d0f7335c2b | ||
|
|
157618269f | ||
|
|
c8f5feb28e | ||
|
|
acd9f87ee8 | ||
|
|
763783b4d9 | ||
|
|
4d42b2ab16 | ||
|
|
472044a2a3 | ||
|
|
87817e080d | ||
|
|
65b87e37ff | ||
|
|
6c8b684f80 | ||
|
|
161172632c | ||
|
|
84ecdde3d4 |
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
cppcheck:
|
||||
runs-on: ubuntu-24.04
|
||||
container: ghcr.io/neugates/build:x86_64-main
|
||||
container: ghcr.io/neugates/build:x86_64-v2.14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
container: ghcr.io/neugates/build:x86_64-main
|
||||
container: ghcr.io/neugates/build:x86_64-v2.14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
|
||||
ut:
|
||||
runs-on: ubuntu-24.04
|
||||
container: ghcr.io/neugates/build:x86_64-main
|
||||
container: ghcr.io/neugates/build:x86_64-v2.14
|
||||
needs: [build]
|
||||
|
||||
steps:
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
|
||||
pft:
|
||||
runs-on: ubuntu-24.04
|
||||
container: ghcr.io/neugates/build:x86_64-main
|
||||
container: ghcr.io/neugates/build:x86_64-v2.14
|
||||
needs: [build]
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
@@ -8,9 +8,9 @@ on:
|
||||
- published
|
||||
|
||||
env:
|
||||
VERSION: 2.12.0
|
||||
VERSION: 2.14.0
|
||||
UI_VERSION: 2.6.1
|
||||
BRANCH: main
|
||||
BRANCH: v2.14
|
||||
|
||||
jobs:
|
||||
cross-build:
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
matrix:
|
||||
arch:
|
||||
- [x86_64-linux-gnu, x86_64, amd64]
|
||||
container: ghcr.io/neugates/build:${{ matrix.arch[1] }}-main
|
||||
container: ghcr.io/neugates/build:${{ matrix.arch[1] }}-v2.14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
matrix:
|
||||
arch:
|
||||
- [x86_64-linux-gnu, x86_64, amd64]
|
||||
container: ghcr.io/neugates/build:${{ matrix.arch[1] }}-main
|
||||
container: ghcr.io/neugates/build:${{ matrix.arch[1] }}-v2.14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
+3
-1
@@ -169,7 +169,9 @@ set(NEURON_SOURCES
|
||||
plugins/restful/system_handle.c
|
||||
plugins/restful/datalayers_handle.c
|
||||
plugins/restful/rest.c
|
||||
plugins/restful/user.c)
|
||||
plugins/restful/user.c
|
||||
plugins/restful/simulator_handle.c
|
||||
simulator/modbus_tcp_simulator.c)
|
||||
|
||||
set(CMAKE_BUILD_RPATH ./)
|
||||
add_executable(neuron)
|
||||
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
# Neuron
|
||||
|
||||
[](https://github.com/neugates/neuron/releases)
|
||||
[](https://hub.docker.com/r/emqx/neuron)
|
||||
[](https://slack-invite.emqx.io/)
|
||||
[](https://discord.gg/xYGf3fQnES)
|
||||
[](https://twitter.com/EMQTech)
|
||||
[](https://www.youtube.com/channel/UC5FjR77ErAxvZENEWzQaO5Q)
|
||||
|
||||
|
||||
|
||||
[English](https://github.com/emqx/neuron/blob/main/README.md) | 简体中文
|
||||
|
||||
Neuron 是一款开源的、轻量级工业协议网关软件,支持数十种工业协议的一站式设备连接、数据接入、MQTT 协议转换,为工业设备赋予工业 4.0 时代关键的物联网连接能力。
|
||||
|
||||

|
||||
|
||||
以下是 Neuron 的一些重要特性:
|
||||
|
||||
- 具有实时能力的边缘原生应用程序可以利用边缘端的低延迟网络。
|
||||
- 松耦合模块化架构设计,通过可插拔模块扩展更多功能服务。
|
||||
- 支持可以在运行时更新设备和应用程序模块的热插件。
|
||||
- 支持多种工业设备协议,包括 Modbus、OPCUA、Ethernet/IP、IEC104、BACnet 等。
|
||||
- 支持同时连接大量不同协议的工业设备。
|
||||
- 结合[eKuiper](https://www.lfedge.org/projects/ekuiper)提供的规则引擎功能,快速实现基于规则的设备控制或 AI/ML 分析。
|
||||
- 通过 SparkplugB 解决方案支持对工业应用程序的数据访问,例如 MES 或 ERP、SCADA、historian 和数据分析软件。
|
||||
- 具有非常低的内存占用,小于 10M 的内存占用和 CPU 使用率,可以在 ARM、x86 和 RISC-V 等资源有限的硬件上运行。
|
||||
- 支持在本地安装可执行文件或部署在容器化环境中。
|
||||
- 控制工业设备,通过 [HTTP API](docs/api/cn/http.md) 和 [MQTT API](docs/api/cn/mqtt.md) 服务更改参数和数据标签。
|
||||
- 与其他 EMQ 产品高度集成,包括 [EMQX](https://www.emqx.com/zh/products/emqx)、[NanoMQ](https://nanomq.io/zh)、[eKuiper](https://ekuiper.org/zh)。
|
||||
- 核心框架和 Modbus、MQTT 和 eKuiper 的代码在 LGPLv3 的许可下开源。
|
||||
|
||||
开源版本只提供了部分插件,要使用更多的插件以及更完善的 Dashboard,请使用 [NeuronEX](https://www.emqx.com/zh/products/neuronex)。
|
||||
|
||||
## 快速开始
|
||||
|
||||
Neuron 管理面板的默认登陆账号为 `admin`,密码为 `0000`。
|
||||
|
||||
### 下载 tar 包运行
|
||||
|
||||
选择 [Relase](https://github.com/emqx/neuron/releases) 版本下载
|
||||
|
||||
```bash
|
||||
$ tar xvf neuron-{version}-linux-amd64.tar.gz
|
||||
$ cd neuron
|
||||
$ ./neuron --log
|
||||
```
|
||||
|
||||
浏览器中打开 `http://localhost:7000` 访问 Neuron。
|
||||
|
||||
|
||||
### 源码编译运行
|
||||
|
||||
1. [安装依赖](https://github.com/emqx/neuron/install-dependencies.md)
|
||||
|
||||
2. 编译 Neuron
|
||||
```bash
|
||||
$ git clone https://github.com/emqx/neuron
|
||||
$ cd neuron && mkdir build && cd build
|
||||
$ cmake .. && make
|
||||
```
|
||||
|
||||
3. 下载并解压 Dashboard
|
||||
```bash
|
||||
$ wget https://github.com/emqx/neuron-dashboard/releases/download/2.6.3/neuron-dashboard.zip
|
||||
|
||||
# 解压 neuron-dashboard.zip 到 build 目录
|
||||
$ unzip neuron-dashboard.zip
|
||||
```
|
||||
|
||||
3. 运行 Neuron
|
||||
```bash
|
||||
$ ./neuron --log
|
||||
```
|
||||
|
||||
4. 浏览器中打开 `http://localhost:7000` 访问 Neuron。
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
$ docker run -d --name neuron -p 7000:7000 emqx/neuron:2.6.9
|
||||
```
|
||||
|
||||
目前 Neuron 维护的最新镜像是 emqx/neuron:2.6.9,2.7.x 及后续的版本不再提供镜像。
|
||||
|
||||
### [采集 Modbus TCP 数据并通过 MQTT 发送](./docs/quick_start/quick_start_cn.md)
|
||||
|
||||
## Dashboard
|
||||
|
||||
开源版 [Dashboard](https://github.com/emqx/neuron-dashboard) 最新 Release 的版本为 2.6.3,目前已暂停开发与维护,此版本也是 Neuron 默认集成的版本。要用更完善与更专业的 Dashboard,请使用 [NeuronEX](https://www.emqx.com/zh/products/neuronex)。
|
||||
|
||||
|
||||
## 社区
|
||||
|
||||
- 访问 [Neuron 论坛](https://askemq.com/c/neuron/8) 以获取帮助,也可以分享您的想法或项目。
|
||||
- 添加小助手微信号 `emqmkt`,加入 Neuron 微信技术交流群。
|
||||
- 加入我们的 [Discord](https://discord.gg/xYGf3fQnES),参于实时讨论。
|
||||
- 关注我们的 [bilibili](https://space.bilibili.com/522222081),获取最新物联网技术分享。
|
||||
- 关注我们的 [微博](https://weibo.com/emqtt) 或 [Twitter](https://twitter.com/EMQTech),获取 Neuron 最新资讯。
|
||||
|
||||
|
||||
## 开源许可
|
||||
|
||||
详见 [LICENSE](./LICENSE)。
|
||||
@@ -8,33 +8,54 @@
|
||||
[](https://twitter.com/EMQTech)
|
||||
[](https://www.youtube.com/channel/UC5FjR77ErAxvZENEWzQaO5Q)
|
||||
|
||||
English | [简体中文](https://github.com/emqx/neuron/blob/main/README-CN.md)
|
||||
|
||||
|
||||
Neuron is an Industrial IoT (IIoT) connectivity server for modern big data and AI/ML technology to leverage the power of Industrial 4.0. It supports one-stop access to dozens of industrial protocols and converts them into MQTT protocol and realize the interconnection between IIoT platforms and various industrial devices.
|
||||
Neuron is an Industrial IoT (IIoT) connectivity server that bridges industrial devices and modern data platforms. It supports dozens of industrial protocols and converts them into MQTT for seamless integration between IIoT platforms and shop-floor devices.
|
||||
|
||||

|
||||
|
||||
The following are some important features of Neuron:
|
||||
Key features:
|
||||
|
||||
- Edge native application with real-time capability to leverage the low latency network of edge side.
|
||||
- Loosely-coupled modularity architecture design for extending more functional services by pluggable modules.
|
||||
- Support hot plugins that can update device and application modules during runtime.
|
||||
- Support numerous protocols for industrial devices, including Modbus, OPCUA, Ethernet/IP, IEC104, BACnet and more.
|
||||
- Support simultaneous connection of a large number of industrial devices with different protocols.
|
||||
- Combine with the rule engine function provided by [eKuiper](https://www.lfedge.org/projects/ekuiper) to quickly implement rule-based device control or AI/ML analytics.
|
||||
- Support data access to industrial applications, such as MES or ERP, SCADA, historian and data analytics software via SparkplugB solution.
|
||||
- Has very low memory footprint, less than 10M, and CPU usage, can run on limited resource hardware like ARM, x86 and RISC-V.
|
||||
- Support installation of native executable or deployed in containerized enviornment.
|
||||
- Control industrial devices, and make changes to the parameters and data tags through [HTTP API](docs/api/english/http.md) and [MQTT API](docs/api/english/mqtt.md) services.
|
||||
- Highly integrated with other EMQ products, including [EMQX](https://www.emqx.com/en/products/emqx), [NanoMQ](https://nanomq.io/), [eKuiper](https://ekuiper.org/).
|
||||
- The code of the core framework and Modbus, MQTT and eKuiper are licensed under open source LGPLv3.
|
||||
- Edge-native application with real-time capability and low latency at the edge.
|
||||
- Loosely coupled modular architecture with pluggable modules for easy extension.
|
||||
- Hot-pluggable plugins: update device and application modules at runtime.
|
||||
- Broad protocol support: Modbus, OPC UA, Ethernet/IP, IEC 60870-5-104, BACnet, and more.
|
||||
- High concurrency: connect many devices with heterogeneous protocols simultaneously.
|
||||
- Built-in stream processing via [eKuiper](https://www.lfedge.org/projects/ekuiper) for rules and AI/ML analytics.
|
||||
- Northbound access for MES/ERP, SCADA, historians, and analytics via Sparkplug B.
|
||||
- Tiny footprint (<10 MB) and low CPU usage; runs on ARM, x86, and RISC-V.
|
||||
- Flexible deployments: native binaries or containers.
|
||||
- Manage devices and tags via [HTTP API](docs/api/english/http.md) and [MQTT API](docs/api/english/mqtt.md).
|
||||
- Works well with [EMQX](https://www.emqx.com/en/products/emqx), [NanoMQ](https://nanomq.io/), and [eKuiper](https://ekuiper.org/).
|
||||
- Core framework and Modbus/MQTT/eKuiper plugins are available under LGPLv3.
|
||||
|
||||
Neuron only provides limited plugins in the open source version. To use more plugins and a more complete Dashboard, please use [NeuronEX](https://www.emqx.com/en/products/neuronex).
|
||||
## About NeuronEX
|
||||
|
||||
[NeuronEX](https://www.emqx.com/en/products/emqx-neuron) is the commercial distribution of Neuron, offering extended capabilities and professional support. Compared to the open-source Neuron:
|
||||
|
||||
- **Extended protocol support**: Additional industrial protocols (e.g., OPC UA, Siemens S7, Ethernet/IP, etc.)
|
||||
- **Advanced dashboard**: Full-featured web UI with enhanced visualization and management capabilities
|
||||
- **Enterprise plugins**: More southbound device adapters, northbound application connectors, and rule engines
|
||||
- **Cloud integration**: Direct connectors for major cloud platforms (AWS, Azure, Google Cloud, etc.)
|
||||
- **Data security**: Enhanced encryption, auditing, and compliance features
|
||||
|
||||
For more details, visit the [NeuronEX documentation](https://docs.emqx.com/en/neuronex/latest/).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Quick Start](#quick-start)
|
||||
- [Installation](#installation)
|
||||
- [Binaries](#binaries)
|
||||
- [Build from Source](#build-from-source)
|
||||
- [Configuration](#configuration)
|
||||
- [Documentation](#documentation)
|
||||
- [Dashboard](#dashboard)
|
||||
- [Community](#community)
|
||||
- [Contributing](#contributing)
|
||||
- [Security](#security)
|
||||
- [License](#license)
|
||||
|
||||
## Quick Start
|
||||
|
||||
Default username is `admin` and password is `0000`.
|
||||
Default credentials: username `admin`, password `0000`.
|
||||
|
||||
### Download
|
||||
|
||||
@@ -46,11 +67,17 @@ $ cd neuron
|
||||
$ ./neuron --log
|
||||
```
|
||||
|
||||
Open a web browser and navigate to `http://localhost:7000` to access the Neuron web interface.
|
||||
Open a browser and navigate to `http://localhost:7000` to access the Neuron web interface.
|
||||
|
||||
### Build and Run
|
||||
## Installation
|
||||
|
||||
1. [Install Dependencies](https://github.com/emqx/neuron/install-dependencies.md)
|
||||
### Binaries
|
||||
|
||||
Download the latest release from [Releases](https://github.com/emqx/neuron/releases), then extract and run as shown above.
|
||||
|
||||
### Build from Source
|
||||
|
||||
1. [Install Dependencies](./Install-dependencies.md)
|
||||
|
||||
2. Build Neuron
|
||||
```bash
|
||||
@@ -72,29 +99,41 @@ $ unzip neuron-dashboard.zip
|
||||
$ ./neuron --log
|
||||
```
|
||||
|
||||
5. Open a web browser and navigate to `http://localhost:7000` to access the Neuron web interface.
|
||||
5. Open a browser and navigate to `http://localhost:7000` to access the Neuron web interface.
|
||||
|
||||
### Docker
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
$ docker run -d --name neuron -p 7000:7000 emqx/neuron:2.6.9
|
||||
```
|
||||
- Web UI: `http://localhost:7000` (default credentials above)
|
||||
- Main config: `neuron.conf`
|
||||
- Default plugins: `default_plugins.json`
|
||||
- Logging: `zlog.conf` / `sdk-zlog.conf`
|
||||
|
||||
Currently, the latest image maintained for Neuron is `emqx/neuron:2.6.9`, and images for versions `2.7.x` and later are no longer provided.
|
||||
See the quick start for a hands-on walkthrough.
|
||||
|
||||
### [Modbus TCP Data Collection and MQTT Transmission](./docs/quick_start/quick_start.md)
|
||||
## Documentation
|
||||
|
||||
- Quick start: [Modbus TCP collection and MQTT publishing](./docs/quick_start/quick_start.md)
|
||||
- APIs: [HTTP](docs/api/english/http.md), [MQTT](docs/api/english/mqtt.md)
|
||||
|
||||
## Dashboard
|
||||
|
||||
The open-source version of the [Dashboard](https://github.com/emqx/neuron-dashboard) is currently at version 2.6.3, which has been suspended for development and maintenance. This version is also the one integrated by default with Neuron. For a more complete and professional Dashboard, please use [NeuronEX](https://www.emqx.com/en/products/neuronex).
|
||||
|
||||
## Get Involved
|
||||
## Community
|
||||
|
||||
- Follow [@EMQTech on Twitter](https://twitter.com/EMQTech).
|
||||
- If you have a specific question, check out our [discussion forums](https://github.com/emqx/neuron/discussions).
|
||||
- For general discussions, join us on the [official Discord](https://discord.gg/xYGf3fQnES) team.
|
||||
- Keep updated on [EMQ YouTube](https://www.youtube.com/channel/UC5FjR77ErAxvZENEWzQaO5Q) by subscribing.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Feel free to open issues and pull requests to improve Neuron. If you plan a larger change, please start a discussion first to align on direction.
|
||||
|
||||
## Security
|
||||
|
||||
If you believe you have found a security vulnerability, please avoid creating a public issue. Instead, contact the maintainers privately (e.g., via GitHub Security Advisories) so we can investigate and fix it responsibly.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(COMPILER_PREFIX aarch64-linux-gnu)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
set(LIBRARY_DIR /home/neuron/main/libs)
|
||||
set(LIBRARY_DIR /home/neuron/v2.14/libs)
|
||||
|
||||
set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(COMPILER_PREFIX arm-linux-gnueabi)
|
||||
set(CMAKE_SYSTEM_PROCESSOR armv5tejl)
|
||||
set(LIBRARY_DIR /home/neuron/main/libs)
|
||||
set(LIBRARY_DIR /home/neuron/v2.14/libs)
|
||||
|
||||
set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(COMPILER_PREFIX arm-linux-gnueabihf)
|
||||
set(CMAKE_SYSTEM_PROCESSOR armv7l)
|
||||
set(LIBRARY_DIR /home/neuron/main/libs)
|
||||
set(LIBRARY_DIR /home/neuron/v2.14/libs)
|
||||
|
||||
set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(COMPILER_PREFIX x86_64-linux-gnu)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
set(LIBRARY_DIR /home/neuron/main/libs)
|
||||
set(LIBRARY_DIR /home/neuron/v2.14/libs)
|
||||
|
||||
set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc)
|
||||
set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++)
|
||||
|
||||
@@ -10,6 +10,5 @@ syslog = "<P>%d(%Y-%m-%dT%T%z) %H neuron[%p]: level=%v msg="[%c] %m" file="%f:%L
|
||||
|
||||
[rules]
|
||||
|
||||
*.* "./logs/%c.log", 25MB * 1 ~ "./logs/%c.#2r.log"; file_format
|
||||
*.* "./logs/%c.log", 50MB * 2 ~ "./logs/%c.#2r.log"; file_format
|
||||
*.* >stdout; stdout_format
|
||||
*.* $remote_syslog,"%V"; syslog
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
# 快速开始
|
||||
|
||||
本文档需要先根据 [安装运行](https://github.com/emqx/neuron/README-CN.md) 成功运行 Neuron,并且能通过浏览器打开 Neuron 的管理面板。
|
||||
|
||||
## 启动 Modbus TCP 模拟器
|
||||
|
||||
提供两种 Modbus TCP 模拟器,根据情况运行一种模拟器即可。
|
||||
|
||||
### Neuron 自带模拟器
|
||||
|
||||
Neuron 源码或 Release 中自带了在 Linux 上运行的测试模拟器,可以读写一些固定点位。Slave ID 为 1。
|
||||
|
||||
```bash
|
||||
# 通过源码编译的方式,build/simulator 目录下
|
||||
# Release 包中根目录下
|
||||
|
||||
# 运行 Modbus 模拟器,使用 TCP 模式,绑定 0.0.0.0:1502
|
||||
$ ./modbus_simulator tcp 1502 ip_v4
|
||||
```
|
||||
|
||||
### PeakHMI Slave Simulators 模拟器
|
||||
|
||||
PeakHMI 模拟器需要在 Windows 系统中运行,[PeakHMI 官网](https://hmisys.com)
|
||||
|
||||
下载并安装后,运行 Modbus TCP slave EX 模拟器即可,该模拟器默认绑定的端口为 502,会被 Windows 默认的防火墙拦截,确保防火墙能开放此接口或关闭防火墙。
|
||||
|
||||

|
||||
|
||||
## Neuron 中创建南向设备
|
||||
|
||||
### 创建设备
|
||||
|
||||
**南向设备管理** -> **添加设备** 在弹出的对话框中填写设备信息后创建设备,**插件** 选择 `Modbus TCP`,**名称** 填写 `modbus-tcp-1` 即可。
|
||||
|
||||
### 配置设备
|
||||
创建设备后,进入设备详情页面配置设备参数。
|
||||
|
||||
**IP 地址**: 填写 Modbus TCP 模拟器运行机器的 IP 地址。如果 Neuron 与模拟器运行在同一台机器上,可以填写 `127.0.0.1`。
|
||||
**端口**: 默认502,根据模拟器的监听端口进行修改。
|
||||
|
||||
其他参数默认即可。
|
||||
|
||||
### 配置数据点
|
||||
|
||||
在 **南向设备管理** 页面,点击 **设备名称** 进入点位组页面,添加一个点位组,名称填写 `group1` 即可。
|
||||
|
||||
进入点位组后,点击 **组名称** 进入点位配置页面,创建点位。
|
||||
|
||||

|
||||
|
||||
**名称**: `tag-1`
|
||||
**地址**: `1!40001` ( Slave ID 1, 保持寄存器40001 )
|
||||
**属性**: `Read` 与 `Write`
|
||||
**数据类型**: `INT16`
|
||||
|
||||
### 查看点位值
|
||||
|
||||
配置完点位后,进入 **数据监控** 页面,选择刚才创建的设备以及点位组,即可看到点位的值。
|
||||
|
||||

|
||||
|
||||
## 创建北向应用
|
||||
|
||||
使用 MQTT,把刚才 modbus 设备中采集到的数据发送到 MQTT Broker。
|
||||
|
||||
**北向应用管理** -> **添加应用**,在弹出的对话框中填写应用信息创建应用。
|
||||
**插件** 选择 `MQTT`,**名称** 填写 `mqtt` 即可。
|
||||
|
||||
### 配置北向应用
|
||||
|
||||
创建北向应用后,进入北向配置页面,默认服务器地址为 `broker.emqx.io`,端口为 `1883`,如果需要连接其他 MQTT Broker,请根据实际情况修改。
|
||||
|
||||
### 订阅南向数据组
|
||||
|
||||
在**北向应用管理**页面中,点击北向应用,进入**订阅管理**页面,点击**添加订阅**,在弹出的对话框中选择刚才创建的 Modbus 设备以及点位组。
|
||||
|
||||

|
||||
|
||||
记住订阅时的 topic,后续订阅该 topic 即可看到数据。topic 默认格式为`/neuron/{应用名称}`
|
||||
|
||||
至此,Neuron 的 Modbus TCP 采集数据并通过 MQTT 发送数据的配置完成。
|
||||
|
||||
## 使用 MQTTX 订阅 topic 查看数据
|
||||
|
||||
订阅完成后,打开 [MQTTX](https://mqttx.app/zh),创建一个新的连接,连接到 `broker.emqx.io:1883`(根据北向应用配置的 MQTT broker 修改)。
|
||||
|
||||
连接成功后,订阅 topic `/neuron/mqtt` (根据北向订阅时的 topic 修改),即可看到点位数据。
|
||||
|
||||

|
||||
@@ -86,6 +86,9 @@ typedef struct adapter_callbacks {
|
||||
void (*update_im)(neu_adapter_t *adapter, const char *group,
|
||||
const char *tag, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta);
|
||||
void (*update_im_f_m)(neu_adapter_t *adapter, const char *group,
|
||||
const char *tag, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta);
|
||||
void (*scan_tags_response)(neu_adapter_t *adapter, void *r,
|
||||
neu_resp_scan_tags_t *resp_scan);
|
||||
void (*test_read_tag_response)(neu_adapter_t *adapter, void *r,
|
||||
|
||||
@@ -305,5 +305,6 @@ int is_ipv6(const char *ip);
|
||||
|
||||
bool neu_conn_is_connected(neu_conn_t *conn);
|
||||
uint16_t neu_conn_udp_act_port(neu_conn_t *conn);
|
||||
bool conn_tcp_server_is_listen(neu_conn_t *conn);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#define NEU_VERSION_MAJOR 2
|
||||
#define NEU_VERSION_MINOR 12
|
||||
#define NEU_VERSION_MINOR 14
|
||||
#define NEU_VERSION_FIX 0
|
||||
|
||||
#define NEU_GET_VERSION_MAJOR(V) ((0xffff0000 & V) >> 16)
|
||||
@@ -40,6 +40,7 @@
|
||||
#define NEU_DEFAULT_GROUP_INTERVAL 100
|
||||
#define NEU_NODE_NAME_LEN 128
|
||||
#define NEU_PLUGIN_NAME_LEN 32
|
||||
#define NEU_NODE_TAGS_LEN 256
|
||||
#define NEU_PLUGIN_LIBRARY_LEN 64
|
||||
#define NEU_PLUGIN_DESCRIPTION_LEN 512
|
||||
#define NEU_TEMPLATE_NAME_LEN 128
|
||||
@@ -67,7 +68,7 @@
|
||||
#define NEU_LOG_LEVEL_FATAL "fatal"
|
||||
|
||||
#define NEU_CID_LNCLASS_LEN 12
|
||||
#define NEU_CID_LNTYPE_LEN 32
|
||||
#define NEU_CID_LNTYPE_LEN 40
|
||||
#define NEU_CID_LNPREFIX_LEN 10
|
||||
#define NEU_CID_LNINST_LEN 4
|
||||
#define NEU_CID_LDEVICE_LEN 32
|
||||
@@ -85,7 +86,7 @@
|
||||
#define NEU_CID_DATASET_NAME_LEN 32
|
||||
#define NEU_CID_REPORT_NAME_LEN 32
|
||||
#define NEU_CID_REPORT_ID_LEN 64
|
||||
#define NEU_CID_ID_LEN 32
|
||||
#define NEU_CID_ID_LEN 40
|
||||
#define NEU_CID_REF_TYPE_LEN 32
|
||||
#define NEU_CID_IED_NAME_LEN 32
|
||||
#define NEU_CID_ACCESS_POINT_NAME_LEN 16
|
||||
|
||||
@@ -53,6 +53,7 @@ typedef enum {
|
||||
NEU_ERR_USER_NOT_EXISTS = 1020,
|
||||
NEU_ERR_INVALID_USER_LEN = 1021,
|
||||
NEU_ERR_USER_NO_PERMISSION = 1022,
|
||||
NEU_ERR_PORT_IN_USE = 1023,
|
||||
|
||||
NEU_ERR_NODE_EXIST = 2002,
|
||||
NEU_ERR_NODE_NOT_EXIST = 2003,
|
||||
@@ -68,6 +69,9 @@ typedef enum {
|
||||
NEU_ERR_NODE_NOT_ALLOW_UPDATE = 2013,
|
||||
NEU_ERR_NODE_NOT_ALLOW_MAP = 2014,
|
||||
NEU_ERR_NODE_NAME_EMPTY = 2015,
|
||||
NEU_ERR_NODE_TAGS_TOO_LONG = 2016,
|
||||
NEU_ERR_NODE_TAGS_TOO_MANY = 2017,
|
||||
NEU_ERR_NODE_TAGS_INVALID = 2018,
|
||||
|
||||
NEU_ERR_GROUP_ALREADY_SUBSCRIBED = 2101,
|
||||
NEU_ERR_GROUP_NOT_SUBSCRIBE = 2102,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* NEURON IIoT System for Industry 4.0
|
||||
* Copyright (C) 2020-2025 EMQ Technologies Co., Ltd All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**/
|
||||
#ifndef NEU_MODBUS_TCP_SIMULATOR_H
|
||||
#define NEU_MODBUS_TCP_SIMULATOR_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
int neu_modbus_simulator_init(const char *config_dir);
|
||||
void neu_modbus_simulator_fini(void);
|
||||
|
||||
int neu_modbus_simulator_start(const char *ip, uint16_t port);
|
||||
void neu_modbus_simulator_stop(void);
|
||||
void neu_modbus_simulator_apply_persist(void);
|
||||
|
||||
typedef enum {
|
||||
NEU_SIM_TAG_NONE = 0,
|
||||
NEU_SIM_TAG_SINE = 1, // float, [-100,100], period 60s
|
||||
NEU_SIM_TAG_SAW = 2, // int16, [0,100], period 100s
|
||||
NEU_SIM_TAG_SQUARE = 3, // int16, [-10,10], period 10s
|
||||
NEU_SIM_TAG_RANDOM = 4 // int16, [0,100]
|
||||
} neu_sim_tag_type_e;
|
||||
|
||||
int neu_modbus_simulator_config_tags(const char **names, const char **addr_strs,
|
||||
const uint16_t * addresses,
|
||||
const neu_sim_tag_type_e *types,
|
||||
int tag_count);
|
||||
|
||||
char *neu_modbus_simulator_export_drivers_json(void);
|
||||
char *neu_modbus_simulator_list_tags_json(void);
|
||||
|
||||
typedef struct {
|
||||
bool running;
|
||||
char ip[64];
|
||||
uint16_t port;
|
||||
int tag_count;
|
||||
int error;
|
||||
} neu_modbus_simulator_status_t;
|
||||
|
||||
void neu_modbus_simulator_get_status(neu_modbus_simulator_status_t *out);
|
||||
|
||||
#endif
|
||||
+18
-2
@@ -78,6 +78,7 @@ typedef enum neu_reqresp_type {
|
||||
NEU_RESP_NODE_UNINIT,
|
||||
NEU_REQ_ADD_NODE,
|
||||
NEU_REQ_UPDATE_NODE,
|
||||
NEU_REQ_UPDATE_NODE_TAG,
|
||||
NEU_REQ_DEL_NODE,
|
||||
NEU_REQ_GET_NODE,
|
||||
NEU_RESP_GET_NODE,
|
||||
@@ -242,6 +243,7 @@ static const char *neu_reqresp_type_string_t[] = {
|
||||
[NEU_RESP_NODE_UNINIT] = "NEU_RESP_NODE_UNINIT",
|
||||
[NEU_REQ_ADD_NODE] = "NEU_REQ_ADD_NODE",
|
||||
[NEU_REQ_UPDATE_NODE] = "NEU_REQ_UPDATE_NODE",
|
||||
[NEU_REQ_UPDATE_NODE_TAG] = "NEU_REQ_UPDATE_NODE_TAG",
|
||||
[NEU_REQ_DEL_NODE] = "NEU_REQ_DEL_NODE",
|
||||
[NEU_REQ_GET_NODE] = "NEU_REQ_GET_NODE",
|
||||
[NEU_RESP_GET_NODE] = "NEU_RESP_GET_NODE",
|
||||
@@ -522,11 +524,14 @@ typedef struct neu_req_add_node {
|
||||
char node[NEU_NODE_NAME_LEN];
|
||||
char plugin[NEU_PLUGIN_NAME_LEN];
|
||||
char *setting;
|
||||
char *tags;
|
||||
} neu_req_add_node_t;
|
||||
|
||||
static inline void neu_req_add_node_fini(neu_req_add_node_t *req)
|
||||
{
|
||||
free(req->setting);
|
||||
if (req->tags)
|
||||
free(req->tags);
|
||||
}
|
||||
|
||||
typedef struct neu_req_update_node {
|
||||
@@ -534,6 +539,11 @@ typedef struct neu_req_update_node {
|
||||
char new_name[NEU_NODE_NAME_LEN];
|
||||
} neu_req_update_node_t;
|
||||
|
||||
typedef struct neu_req_update_node_tag {
|
||||
char node[NEU_NODE_NAME_LEN];
|
||||
char tags[NEU_NODE_TAGS_LEN];
|
||||
} neu_req_update_node_tag_t;
|
||||
|
||||
typedef struct neu_req_del_node {
|
||||
char node[NEU_NODE_NAME_LEN];
|
||||
} neu_req_del_node_t;
|
||||
@@ -557,6 +567,7 @@ typedef struct neu_resp_node_info {
|
||||
int64_t delay;
|
||||
char node[NEU_NODE_NAME_LEN];
|
||||
char plugin[NEU_PLUGIN_NAME_LEN];
|
||||
char tags[NEU_NODE_TAGS_LEN];
|
||||
} neu_resp_node_info_t;
|
||||
|
||||
typedef struct neu_resp_get_node {
|
||||
@@ -1148,6 +1159,7 @@ typedef struct {
|
||||
char * node;
|
||||
char * plugin;
|
||||
char * setting;
|
||||
char * tags;
|
||||
uint16_t n_group;
|
||||
neu_gdatatag_t *groups;
|
||||
} neu_req_driver_t;
|
||||
@@ -1157,6 +1169,9 @@ static inline void neu_req_driver_fini(neu_req_driver_t *req)
|
||||
free(req->node);
|
||||
free(req->plugin);
|
||||
free(req->setting);
|
||||
if (req->tags)
|
||||
free(req->tags);
|
||||
|
||||
for (uint16_t i = 0; i < req->n_group; i++) {
|
||||
for (int j = 0; j < req->groups[i].n_tag; j++) {
|
||||
neu_tag_fini(&req->groups[i].tags[j]);
|
||||
@@ -1625,8 +1640,9 @@ neu_tag_value_to_json_paginate(neu_resp_tag_value_meta_paginate_t *tag_value,
|
||||
tag_json->value.val_array_str.p_strs = tag_value->value.value.strs.strs;
|
||||
break;
|
||||
case NEU_TYPE_CUSTOM:
|
||||
tag_json->t = NEU_JSON_OBJECT;
|
||||
tag_json->value.val_object = tag_value->value.value.json;
|
||||
tag_json->t = NEU_JSON_OBJECT;
|
||||
tag_json->value.val_object =
|
||||
json_deep_copy(tag_value->value.value.json);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -36,6 +36,7 @@ typedef struct {
|
||||
int type;
|
||||
char *plugin_name;
|
||||
int state;
|
||||
char *tags;
|
||||
} neu_persist_node_info_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -125,6 +126,9 @@ static inline void neu_persist_node_info_fini(neu_persist_node_info_t *info)
|
||||
{
|
||||
free(info->name);
|
||||
free(info->plugin_name);
|
||||
if (info->tags) {
|
||||
free(info->tags);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void neu_persist_group_info_fini(neu_persist_group_info_t *info)
|
||||
@@ -250,6 +254,7 @@ int neu_persister_delete_node(const char *node_name);
|
||||
* @return 0 on success, none-zero on failure
|
||||
*/
|
||||
int neu_persister_update_node(const char *node_name, const char *new_name);
|
||||
int neu_persister_update_node_tags(const char *node_name, const char *tags);
|
||||
|
||||
/**
|
||||
* Update node state.
|
||||
|
||||
@@ -114,6 +114,8 @@ inline static cid_basictype_e decode_basictype(const char *btype)
|
||||
ret = VisString64;
|
||||
} else if (strcmp(btype, "VisString255") == 0) {
|
||||
ret = VisString255;
|
||||
} else if (strcmp(btype, "VisString65") == 0) {
|
||||
ret = Unicode255;
|
||||
} else if (strcmp(btype, "Octet64") == 0) {
|
||||
ret = Octet64;
|
||||
} else if (strcmp(btype, "Struct") == 0) {
|
||||
@@ -200,6 +202,10 @@ typedef struct {
|
||||
|
||||
cid_basictype_e btypes[NEU_TAG_FORMAT_LENGTH]; // for do-[da]
|
||||
int n_btypes;
|
||||
cid_basictype_e btype;
|
||||
|
||||
bool is_array;
|
||||
uint8_t array_size;
|
||||
} cid_fcda_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -298,6 +304,9 @@ typedef struct {
|
||||
|
||||
cid_basictype_e btype;
|
||||
cid_fc_e fc;
|
||||
|
||||
bool is_array;
|
||||
uint8_t array_size;
|
||||
} cid_tm_do_da_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
+3
-2
@@ -4,5 +4,6 @@
|
||||
"disable_auth": 0,
|
||||
"syslog_host": "",
|
||||
"syslog_port": 541,
|
||||
"sub_filter_error": 0
|
||||
}
|
||||
"sub_filter_error": 0,
|
||||
"modbus_simulator": { "port":502 }
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* NEURON IIoT System for Industry 4.0
|
||||
* Copyright (C) 2020-2023 EMQ Technologies Co., Ltd All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**/
|
||||
|
||||
--- fix primary key ---
|
||||
BEGIN;
|
||||
|
||||
DROP TABLE IF EXISTS old_node_cache;
|
||||
ALTER TABLE node_cache RENAME TO old_node_cache;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS node_cache (
|
||||
node_name TEXT PRIMARY KEY,
|
||||
CACHE TEXT NOT NULL,
|
||||
FOREIGN KEY (node_name) REFERENCES nodes (name) ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
||||
INSERT OR REPLACE INTO node_cache SELECT * FROM old_node_cache;
|
||||
|
||||
DROP TABLE old_node_cache;
|
||||
|
||||
COMMIT;
|
||||
@@ -62,7 +62,7 @@ CREATE TABLE IF NOT EXISTS client_certificates (
|
||||
valid_from DATETIME,
|
||||
valid_to DATETIME,
|
||||
serial_number TEXT,
|
||||
fingerprint TEXT UNIQUE,
|
||||
fingerprint TEXT,
|
||||
certificate_data BLOB NOT NULL,
|
||||
is_ca INTEGER DEFAULT 0,
|
||||
trust_status INTEGER DEFAULT 0,
|
||||
@@ -75,6 +75,7 @@ CREATE TABLE IF NOT EXISTS client_certificates (
|
||||
CREATE INDEX IF NOT EXISTS idx_client_certificates_app_name ON client_certificates(app_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_client_certificates_common_name ON client_certificates(common_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_client_certificates_fingerprint ON client_certificates(fingerprint);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_client_certificates_app_name_fingerprint ON client_certificates(app_name, fingerprint);
|
||||
|
||||
-- Create security policies table for northbound app security management
|
||||
CREATE TABLE IF NOT EXISTS security_policies (
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS modbus_tcp_simulator (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
enabled INTEGER NOT NULL DEFAULT 0,
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
alter TABLE nodes add column tags TEXT NULL;
|
||||
|
||||
COMMIT;
|
||||
@@ -234,9 +234,22 @@ void recv_data_callback(void *arg)
|
||||
if (req->singular) {
|
||||
rv = send_write_tag_req(plugin, &req->single, json_str, trace_flag,
|
||||
trace_id, span_id);
|
||||
if (rv == 0) {
|
||||
plog_notice(plugin, "write tag %s.%s on node %s", req->single.group,
|
||||
req->single.tag, req->single.node);
|
||||
}
|
||||
} else {
|
||||
rv = send_write_tags_req(plugin, &req->plural, json_str, trace_flag,
|
||||
trace_id, span_id);
|
||||
if (rv == 0) {
|
||||
if (req->plural.n_tag == 0) {
|
||||
plog_warn(plugin, "no tags to write on node %s",
|
||||
req->plural.node);
|
||||
} else {
|
||||
plog_notice(plugin, "write %d tags on node %s",
|
||||
req->plural.n_tag, req->plural.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != rv) {
|
||||
|
||||
@@ -52,6 +52,10 @@ int modbus_tag_to_point(const neu_datatag_t *tag, modbus_point_t *point,
|
||||
return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID;
|
||||
}
|
||||
|
||||
if (start_address > 65536) {
|
||||
return NEU_ERR_TAG_ADDRESS_FORMAT_INVALID;
|
||||
}
|
||||
|
||||
if (start_address == 65536 && address_base == 0) {
|
||||
point->start_address = 65535;
|
||||
} else if (start_address == 0 && address_base == 1) {
|
||||
|
||||
@@ -99,6 +99,8 @@ int modbus_stack_recv(modbus_stack_t *stack, uint8_t slave_id,
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *trace_ctx = (void *) ((intptr_t) stack + (intptr_t) stack->read_seq);
|
||||
|
||||
switch (code.function) {
|
||||
case MODBUS_READ_COIL:
|
||||
case MODBUS_READ_INPUT:
|
||||
@@ -107,7 +109,7 @@ int modbus_stack_recv(modbus_stack_t *stack, uint8_t slave_id,
|
||||
neu_otel_trace_ctx trace = NULL;
|
||||
neu_otel_scope_ctx scope = NULL;
|
||||
if (neu_otel_data_is_started()) {
|
||||
trace = neu_otel_find_trace((void *) (intptr_t) stack->read_seq);
|
||||
trace = neu_otel_find_trace(trace_ctx);
|
||||
if (trace) {
|
||||
char new_span_id[36] = { 0 };
|
||||
neu_otel_new_span_id(new_span_id);
|
||||
@@ -140,14 +142,14 @@ int modbus_stack_recv(modbus_stack_t *stack, uint8_t slave_id,
|
||||
stack->value_fn(stack->ctx, code.slave_id,
|
||||
header.len - sizeof(struct modbus_code) -
|
||||
sizeof(struct modbus_data),
|
||||
bytes, 0, (void *) (intptr_t) stack->read_seq);
|
||||
bytes, 0, trace_ctx);
|
||||
} else {
|
||||
bytes = neu_protocol_unpack_buf(buf, data.n_byte);
|
||||
if (bytes == NULL) {
|
||||
return -1;
|
||||
}
|
||||
stack->value_fn(stack->ctx, code.slave_id, data.n_byte, bytes,
|
||||
0, (void *) (intptr_t) stack->read_seq);
|
||||
0, trace_ctx);
|
||||
}
|
||||
break;
|
||||
case MODBUS_PROTOCOL_RTU:
|
||||
@@ -156,7 +158,7 @@ int modbus_stack_recv(modbus_stack_t *stack, uint8_t slave_id,
|
||||
return -1;
|
||||
}
|
||||
stack->value_fn(stack->ctx, code.slave_id, data.n_byte, bytes, 0,
|
||||
(void *) (intptr_t) stack->read_seq);
|
||||
trace_ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -374,8 +376,10 @@ int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id,
|
||||
neu_otel_trace_ctx trace = NULL;
|
||||
neu_otel_scope_ctx scope = NULL;
|
||||
trace = neu_otel_create_trace(
|
||||
new_trace_id, (void *) (intptr_t) stack->read_seq, 0,
|
||||
trace_state);
|
||||
new_trace_id,
|
||||
(void *) ((intptr_t) stack +
|
||||
(intptr_t) stack->read_seq),
|
||||
0, trace_state);
|
||||
if (trace) {
|
||||
scope = neu_otel_add_span(trace);
|
||||
neu_otel_scope_set_span_name(scope, "driver cmd send");
|
||||
|
||||
@@ -167,6 +167,7 @@ int mqtt_plugin_uninit(neu_plugin_t *plugin)
|
||||
|
||||
mqtt_config_fini(&plugin->config);
|
||||
if (plugin->client) {
|
||||
neu_mqtt_client_remove_cache_db(plugin->client);
|
||||
neu_mqtt_client_close(plugin->client);
|
||||
neu_mqtt_client_free(plugin->client);
|
||||
plugin->client = NULL;
|
||||
@@ -395,6 +396,7 @@ int mqtt_plugin_config(neu_plugin_t *plugin, const char *setting)
|
||||
}
|
||||
|
||||
if (plugin->client != NULL) {
|
||||
neu_mqtt_client_remove_cache_db(plugin->client);
|
||||
neu_mqtt_client_close(plugin->client);
|
||||
neu_mqtt_client_free(plugin->client);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,46 @@
|
||||
#include "utils/utextend.h"
|
||||
#include "utils/uthash.h"
|
||||
|
||||
static int tags_length(const char *tags)
|
||||
{
|
||||
int length = 0;
|
||||
char *tag = NULL;
|
||||
char *tmp = strdup(tags);
|
||||
char *save = NULL;
|
||||
|
||||
tag = strtok_r(tmp, ",", &save);
|
||||
while (tag != NULL) {
|
||||
length++;
|
||||
tag = strtok_r(NULL, ",", &save);
|
||||
}
|
||||
|
||||
free(tmp);
|
||||
return length;
|
||||
}
|
||||
|
||||
/*
|
||||
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '+', '=', '[', ']', '{',
|
||||
'}',
|
||||
';', ':', '\'', '"', ',', '<', '>', '?', '\\', '|', '~', '`', '\t',
|
||||
'\n',
|
||||
*/
|
||||
static bool tags_check_symbol(const char *tags)
|
||||
{
|
||||
for (size_t i = 0; i < strlen(tags); i++) {
|
||||
char c = tags[i];
|
||||
if (c == '!' || c == '@' || c == '#' || c == '$' || c == '%' ||
|
||||
c == '^' || c == '&' || c == '*' || c == '(' || c == ')' ||
|
||||
c == '-' || c == '+' || c == '=' || c == '[' || c == ']' ||
|
||||
c == '{' || c == '}' || c == ';' || c == ':' || c == '\'' ||
|
||||
c == '"' || c == '<' || c == '>' || c == '?' || c == '\\' ||
|
||||
c == '|' || c == '~' || c == '`' || c == '\t' || c == '\n') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void handle_add_adapter(nng_aio *aio)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
@@ -45,6 +85,14 @@ void handle_add_adapter(nng_aio *aio)
|
||||
aio, neu_json_add_node_req_t, neu_json_decode_add_node_req, {
|
||||
if (strlen(req->name) >= NEU_NODE_NAME_LEN) {
|
||||
CHECK_NODE_NAME_LENGTH_ERR;
|
||||
} else if (req->tags != NULL && !tags_check_symbol(req->tags)) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_INVALID, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
});
|
||||
} else if (req->tags != NULL && tags_length(req->tags) > 5) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_TOO_MANY, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
});
|
||||
} else if (strcmp(req->name, "monitor") == 0 ||
|
||||
strcmp(req->name, "DataStorage") == 0) {
|
||||
CHECK_NODE_SINGLE_ERR;
|
||||
@@ -59,7 +107,9 @@ void handle_add_adapter(nng_aio *aio)
|
||||
strcpy(cmd.node, req->name);
|
||||
strcpy(cmd.plugin, req->plugin);
|
||||
cmd.setting = req->setting;
|
||||
cmd.tags = req->tags;
|
||||
req->setting = NULL; // moved
|
||||
req->tags = NULL; // moved
|
||||
ret = neu_plugin_op(plugin, header, &cmd);
|
||||
if (ret != 0) {
|
||||
neu_req_add_node_fini(&cmd);
|
||||
@@ -236,6 +286,7 @@ void handle_get_adapter_resp(nng_aio *aio, neu_resp_get_node_t *nodes)
|
||||
|
||||
nodes_res.nodes[index].name = info->node;
|
||||
nodes_res.nodes[index].plugin = info->plugin;
|
||||
nodes_res.nodes[index].tags = info->tags;
|
||||
nodes_res.nodes[index].support_import_tags =
|
||||
neu_plugin_support_import_tags_simple(info->plugin);
|
||||
}
|
||||
@@ -501,6 +552,12 @@ static int send_drivers(nng_aio *aio, neu_json_drivers_req_t *req)
|
||||
ret = NEU_ERR_NODE_NAME_TOO_LONG;
|
||||
goto check_end;
|
||||
}
|
||||
if (driver->node.tags != NULL &&
|
||||
strlen(driver->node.tags) >= NEU_NODE_TAGS_LEN) {
|
||||
ret = NEU_ERR_NODE_TAGS_TOO_LONG;
|
||||
goto check_end;
|
||||
}
|
||||
|
||||
if (strlen(driver->node.plugin) >= NEU_PLUGIN_NAME_LEN) {
|
||||
ret = NEU_ERR_PLUGIN_NAME_TOO_LONG;
|
||||
goto check_end;
|
||||
@@ -592,9 +649,11 @@ check_end:
|
||||
cmd.drivers[i].plugin = driver->node.plugin;
|
||||
cmd.drivers[i].setting = driver->node.setting;
|
||||
cmd.drivers[i].n_group = driver->gtags.len;
|
||||
cmd.drivers[i].tags = driver->node.tags;
|
||||
driver->node.name = NULL; // moved
|
||||
driver->node.plugin = NULL; // moved
|
||||
driver->node.setting = NULL; // moved
|
||||
driver->node.tags = NULL; // moved
|
||||
cmd.n_driver += 1;
|
||||
|
||||
ret = json_to_gdatatags(&driver->gtags, &cmd.drivers[i].groups);
|
||||
@@ -623,3 +682,36 @@ void handle_put_drivers(nng_aio *aio)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
void handle_put_node_tag(nng_aio *aio)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
|
||||
NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT(
|
||||
aio, neu_json_update_node_tag_req_t,
|
||||
neu_json_decode_update_node_tag_req, {
|
||||
if (req->tags != NULL && !tags_check_symbol(req->tags)) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_INVALID, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
});
|
||||
} else if (req->tags != NULL && tags_length(req->tags) > 5) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_TAGS_TOO_MANY, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
});
|
||||
neu_json_decode_update_node_tag_req_free(req);
|
||||
} else {
|
||||
neu_reqresp_head_t header = { 0 };
|
||||
neu_req_update_node_tag_t cmd = { 0 };
|
||||
header.ctx = aio;
|
||||
header.type = NEU_REQ_UPDATE_NODE_TAG;
|
||||
strcpy(cmd.node, req->name);
|
||||
strcpy(cmd.tags, req->tags);
|
||||
int ret = neu_plugin_op(plugin, header, &cmd);
|
||||
if (ret != 0) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, {
|
||||
neu_http_response(aio, NEU_ERR_IS_BUSY, result_error);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -41,5 +41,6 @@ void handle_get_nodes_state_resp(nng_aio * aio,
|
||||
neu_resp_get_nodes_state_t *states);
|
||||
|
||||
void handle_put_drivers(nng_aio *aio);
|
||||
void handle_put_node_tag(nng_aio *aio);
|
||||
|
||||
#endif
|
||||
@@ -44,6 +44,8 @@ typedef enum {
|
||||
GET_GLOBAL,
|
||||
PUT_GLOBAL,
|
||||
GET_DRIVERS,
|
||||
GET_APPS,
|
||||
PUT_APPS,
|
||||
} context_type_e;
|
||||
|
||||
typedef enum {
|
||||
@@ -58,8 +60,11 @@ typedef enum {
|
||||
STATE_DEL_APP,
|
||||
STATE_DEL_DRIVER,
|
||||
STATE_ADD_NODE,
|
||||
STATE_UPDATE_DEFAULT_APP_TAGS,
|
||||
STATE_ADD_GROUP,
|
||||
STATE_ADD_TAG,
|
||||
STATE_GET_APP_SUBS,
|
||||
STATE_UNSUB_APP,
|
||||
STATE_ADD_SUBSCRIPTION,
|
||||
STATE_ADD_SETTING,
|
||||
STATE_END,
|
||||
@@ -79,14 +84,19 @@ typedef struct {
|
||||
UT_array * groups;
|
||||
void * iter;
|
||||
neu_json_driver_array_t jdrivers; // get drivers
|
||||
char * names; //
|
||||
neu_strset_t filter; //
|
||||
neu_json_app_array_t japps; // get apps
|
||||
char * names;
|
||||
neu_strset_t filter;
|
||||
};
|
||||
// put
|
||||
struct {
|
||||
neu_json_global_config_req_t *req;
|
||||
neu_json_apps_req_t * apps_req;
|
||||
UT_array * nodes;
|
||||
size_t idx;
|
||||
size_t sub_idx;
|
||||
size_t unsub_idx;
|
||||
UT_array * default_app_subs;
|
||||
};
|
||||
};
|
||||
} context_t;
|
||||
@@ -117,6 +127,7 @@ static int add_group(context_t * ctx,
|
||||
static int add_tag(context_t *ctx, neu_json_add_tags_req_t *data);
|
||||
static int add_subscription(context_t *ctx, neu_json_subscribe_req_t *data);
|
||||
static int add_setting(context_t *ctx, neu_json_node_setting_req_t *data);
|
||||
static int add_app_tags(context_t *ctx, neu_json_app_t *app);
|
||||
|
||||
static inline bool is_static_node(const char *name, const char *plugin)
|
||||
{
|
||||
@@ -134,7 +145,8 @@ static context_t *context_new(nng_aio *aio, context_type_e t)
|
||||
ctx->type = t;
|
||||
ctx->state = STATE_START;
|
||||
|
||||
if (PUT_GLOBAL != ctx->type && !(ctx->json = neu_json_encode_new())) {
|
||||
if (PUT_GLOBAL != ctx->type && PUT_APPS != ctx->type &&
|
||||
!(ctx->json = neu_json_encode_new())) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
@@ -164,6 +176,32 @@ static void context_free(context_t *ctx)
|
||||
utarray_free(ctx->nodes);
|
||||
}
|
||||
neu_json_decode_global_config_req_free(ctx->req);
|
||||
} else if (GET_APPS == ctx->type) {
|
||||
neu_json_encode_free(ctx->json);
|
||||
if (ctx->apps) {
|
||||
utarray_free(ctx->apps);
|
||||
}
|
||||
free(ctx->names);
|
||||
neu_strset_free(&ctx->filter);
|
||||
for (int i = 0; i < ctx->japps.n_app; ++i) {
|
||||
neu_json_app_t *app = &ctx->japps.apps[i];
|
||||
free(app->node.setting);
|
||||
for (int j = 0; j < app->subscriptions.n_group; ++j) {
|
||||
free(app->subscriptions.groups[j].driver);
|
||||
free(app->subscriptions.groups[j].group);
|
||||
free(app->subscriptions.groups[j].params);
|
||||
}
|
||||
free(app->subscriptions.groups);
|
||||
}
|
||||
free(ctx->japps.apps);
|
||||
} else if (PUT_APPS == ctx->type) {
|
||||
if (ctx->nodes) {
|
||||
utarray_free(ctx->nodes);
|
||||
}
|
||||
if (ctx->default_app_subs) {
|
||||
utarray_free(ctx->default_app_subs);
|
||||
}
|
||||
neu_json_decode_apps_req_free(ctx->apps_req);
|
||||
} else {
|
||||
neu_json_encode_free(ctx->json);
|
||||
if (ctx->drivers) {
|
||||
@@ -558,12 +596,295 @@ static void get_drivers_context_next(context_t *ctx, neu_reqresp_type_e type,
|
||||
}
|
||||
}
|
||||
|
||||
static int get_apps_resp(context_t *ctx, neu_resp_get_node_t *resp);
|
||||
static int get_app_setting_resp(context_t * ctx,
|
||||
neu_resp_get_node_setting_t *setting);
|
||||
static int get_app_subscriptions_resp(context_t * ctx,
|
||||
neu_resp_get_subscribe_group_t *groups);
|
||||
static int del_app_node(context_t *ctx, neu_json_app_t *app);
|
||||
static int add_app_node(context_t *ctx, neu_json_app_t *app);
|
||||
static int add_app_setting(context_t *ctx, neu_json_app_t *app);
|
||||
static int add_app_subscription(context_t *ctx, neu_json_app_t *app,
|
||||
neu_json_app_group_t *grp);
|
||||
static int get_default_app_subs(context_t *ctx, neu_json_app_t *app);
|
||||
static int get_default_app_subs_resp(context_t * ctx,
|
||||
neu_resp_get_subscribe_group_t *groups);
|
||||
static int unsub_app(context_t *ctx, neu_resp_subscribe_info_t *info);
|
||||
|
||||
static inline bool is_default_app_node(const char *name, const char *plugin)
|
||||
{
|
||||
return (0 == strcmp("DataStorage", name) &&
|
||||
0 == strcmp("Datalayers", plugin)) ||
|
||||
(0 == strcmp("DataProcessing", name) &&
|
||||
0 == strcmp("eKuiper", plugin)) ||
|
||||
(0 == strcmp("monitor", name) && 0 == strcmp("Monitor", plugin));
|
||||
}
|
||||
|
||||
static void get_apps_context_next(context_t *ctx, neu_reqresp_type_e type,
|
||||
void *data)
|
||||
{
|
||||
char *result = NULL;
|
||||
|
||||
switch (ctx->state) {
|
||||
case STATE_START:
|
||||
NEXT(ctx, get_nodes, NEU_NA_TYPE_APP);
|
||||
ctx->state = STATE_GET_APP;
|
||||
break;
|
||||
case STATE_GET_APP:
|
||||
NEXT(ctx, get_apps_resp, data);
|
||||
ctx->iter = NULL;
|
||||
ctx->state = STATE_GET_APP_SETTING;
|
||||
// fall through
|
||||
case STATE_GET_APP_SETTING:
|
||||
if (NULL != ctx->iter && NEU_RESP_ERROR != type) {
|
||||
NEXT(ctx, get_app_setting_resp, data);
|
||||
}
|
||||
ctx->iter = utarray_next(ctx->apps, ctx->iter);
|
||||
if (ctx->iter) {
|
||||
NEXT(ctx, get_setting, ctx->iter);
|
||||
break;
|
||||
}
|
||||
ctx->iter = NULL;
|
||||
ctx->state = STATE_GET_SUBSCRIPTION;
|
||||
// fall through
|
||||
case STATE_GET_SUBSCRIPTION:
|
||||
if (NULL != ctx->iter && NEU_RESP_ERROR != type) {
|
||||
NEXT(ctx, get_app_subscriptions_resp, data);
|
||||
}
|
||||
ctx->iter = utarray_next(ctx->apps, ctx->iter);
|
||||
if (ctx->iter) {
|
||||
NEXT(ctx, get_subscriptions, ctx->iter);
|
||||
break;
|
||||
}
|
||||
if (0 != neu_json_encode_apps_req(ctx->json, &ctx->japps)) {
|
||||
ctx->error = NEU_ERR_EINTERNAL;
|
||||
}
|
||||
ctx->state = STATE_END;
|
||||
break;
|
||||
default:
|
||||
ctx->error = NEU_ERR_EINTERNAL;
|
||||
ctx->state = STATE_END;
|
||||
break;
|
||||
}
|
||||
|
||||
if (STATE_END == ctx->state) {
|
||||
if (ctx->error) {
|
||||
NEU_JSON_RESPONSE_ERROR(ctx->error, {
|
||||
neu_http_response(ctx->aio, ctx->error, result_error);
|
||||
});
|
||||
} else {
|
||||
neu_json_encode(ctx->json, &result);
|
||||
neu_http_response_file(ctx->aio, result, strlen(result),
|
||||
"attachment; filename=\"apps.json\"");
|
||||
free(result);
|
||||
}
|
||||
context_free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void put_apps_context_next(context_t *ctx, neu_reqresp_type_e type,
|
||||
void *data)
|
||||
{
|
||||
(void) type;
|
||||
|
||||
switch (ctx->state) {
|
||||
case STATE_START:
|
||||
ctx->idx = 0;
|
||||
ctx->sub_idx = 0;
|
||||
ctx->unsub_idx = 0;
|
||||
ctx->state = STATE_DEL_APP;
|
||||
// fall through
|
||||
|
||||
case STATE_DEL_APP: {
|
||||
neu_resp_error_t * err = data;
|
||||
neu_json_apps_req_t *apps = ctx->apps_req;
|
||||
|
||||
if (ctx->idx > 0 && 0 != err->error &&
|
||||
NEU_ERR_NODE_NOT_EXIST != err->error) {
|
||||
ctx->error = err->error;
|
||||
ctx->state = STATE_END;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; ctx->idx < (size_t) apps->n_app; ++ctx->idx) {
|
||||
neu_json_app_t *app = &apps->apps[ctx->idx];
|
||||
if (!is_default_app_node(app->node.name, app->node.plugin)) {
|
||||
NEXT(ctx, del_app_node, app);
|
||||
++ctx->idx;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->idx = 0;
|
||||
ctx->state = STATE_ADD_NODE;
|
||||
}
|
||||
// fall through
|
||||
|
||||
case STATE_ADD_NODE: {
|
||||
neu_resp_error_t * err = data;
|
||||
neu_json_apps_req_t *apps = ctx->apps_req;
|
||||
|
||||
if (ctx->idx > 0 && 0 != err->error) {
|
||||
ctx->error = err->error;
|
||||
ctx->state = STATE_END;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; ctx->idx < (size_t) apps->n_app; ++ctx->idx) {
|
||||
neu_json_app_t *app = &apps->apps[ctx->idx];
|
||||
if (is_default_app_node(app->node.name, app->node.plugin)) {
|
||||
if (NULL == app->node.setting) {
|
||||
continue;
|
||||
}
|
||||
NEXT(ctx, add_app_setting, app);
|
||||
} else {
|
||||
NEXT(ctx, add_app_node, app);
|
||||
}
|
||||
++ctx->idx;
|
||||
goto done;
|
||||
}
|
||||
|
||||
// After adding nodes, get subscriptions for default apps to unsub
|
||||
ctx->idx = 0;
|
||||
ctx->unsub_idx = 0;
|
||||
ctx->state = STATE_UPDATE_DEFAULT_APP_TAGS;
|
||||
}
|
||||
// fall through
|
||||
case STATE_UPDATE_DEFAULT_APP_TAGS: {
|
||||
neu_resp_error_t * err = data;
|
||||
neu_json_apps_req_t *apps = ctx->apps_req;
|
||||
|
||||
if (ctx->idx > 0 && 0 != err->error) {
|
||||
ctx->error = err->error;
|
||||
ctx->state = STATE_END;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; ctx->idx < (size_t) apps->n_app; ++ctx->idx) {
|
||||
neu_json_app_t *app = &apps->apps[ctx->idx];
|
||||
NEXT(ctx, add_app_tags, app);
|
||||
|
||||
++ctx->idx;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ctx->idx = 0;
|
||||
ctx->unsub_idx = 0;
|
||||
ctx->state = STATE_GET_APP_SUBS;
|
||||
}
|
||||
// fall through
|
||||
|
||||
case STATE_GET_APP_SUBS: {
|
||||
neu_json_apps_req_t *apps = ctx->apps_req;
|
||||
|
||||
if (NEU_RESP_GET_SUBSCRIBE_GROUP == type && data) {
|
||||
NEXT(ctx, get_default_app_subs_resp, data);
|
||||
if (ctx->default_app_subs &&
|
||||
utarray_len(ctx->default_app_subs) > 0) {
|
||||
ctx->unsub_idx = 0;
|
||||
ctx->state = STATE_UNSUB_APP;
|
||||
// fall through to STATE_UNSUB_APP
|
||||
}
|
||||
}
|
||||
|
||||
if (STATE_UNSUB_APP != ctx->state) {
|
||||
// Find next default app to get subscriptions
|
||||
for (; ctx->idx < (size_t) apps->n_app; ++ctx->idx) {
|
||||
neu_json_app_t *app = &apps->apps[ctx->idx];
|
||||
if (is_default_app_node(app->node.name, app->node.plugin) &&
|
||||
app->subscriptions.n_group > 0) {
|
||||
NEXT(ctx, get_default_app_subs, app);
|
||||
++ctx->idx;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
// No more default apps, proceed to add subscriptions
|
||||
ctx->idx = 0;
|
||||
ctx->sub_idx = 0;
|
||||
ctx->state = STATE_ADD_SUBSCRIPTION;
|
||||
}
|
||||
}
|
||||
if (STATE_UNSUB_APP != ctx->state &&
|
||||
STATE_ADD_SUBSCRIPTION != ctx->state) {
|
||||
break;
|
||||
}
|
||||
if (STATE_UNSUB_APP == ctx->state) {
|
||||
// fall through
|
||||
case STATE_UNSUB_APP: {
|
||||
if (ctx->default_app_subs) {
|
||||
size_t len = utarray_len(ctx->default_app_subs);
|
||||
if (ctx->unsub_idx < len) {
|
||||
neu_resp_subscribe_info_t *info =
|
||||
(neu_resp_subscribe_info_t *) utarray_eltptr(
|
||||
ctx->default_app_subs, ctx->unsub_idx);
|
||||
NEXT(ctx, unsub_app, info);
|
||||
++ctx->unsub_idx;
|
||||
goto done;
|
||||
}
|
||||
|
||||
utarray_free(ctx->default_app_subs);
|
||||
ctx->default_app_subs = NULL;
|
||||
}
|
||||
|
||||
ctx->state = STATE_GET_APP_SUBS;
|
||||
put_apps_context_next(ctx, type, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// fall through
|
||||
|
||||
case STATE_ADD_SUBSCRIPTION: {
|
||||
neu_resp_error_t * err = data;
|
||||
neu_json_apps_req_t *apps = ctx->apps_req;
|
||||
|
||||
if (ctx->sub_idx > 0 && 0 != err->error) {
|
||||
ctx->error = err->error;
|
||||
ctx->state = STATE_END;
|
||||
break;
|
||||
}
|
||||
|
||||
while (ctx->idx < (size_t) apps->n_app) {
|
||||
neu_json_app_t *app = &apps->apps[ctx->idx];
|
||||
if (ctx->sub_idx < (size_t) app->subscriptions.n_group) {
|
||||
NEXT(ctx, add_app_subscription, app,
|
||||
&app->subscriptions.groups[ctx->sub_idx]);
|
||||
++ctx->sub_idx;
|
||||
goto done;
|
||||
}
|
||||
++ctx->idx;
|
||||
ctx->sub_idx = 0;
|
||||
}
|
||||
|
||||
ctx->state = STATE_END;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ctx->error = NEU_ERR_EINTERNAL;
|
||||
ctx->state = STATE_END;
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
if (STATE_END == ctx->state) {
|
||||
NEU_JSON_RESPONSE_ERROR(ctx->error, {
|
||||
neu_http_response(ctx->aio, ctx->error, result_error);
|
||||
});
|
||||
context_free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void context_next(context_t *ctx, neu_reqresp_type_e type, void *data)
|
||||
{
|
||||
if (GET_GLOBAL == ctx->type) {
|
||||
get_global_context_next(ctx, type, data);
|
||||
} else if (PUT_GLOBAL == ctx->type) {
|
||||
put_global_context_next(ctx, type, data);
|
||||
} else if (GET_APPS == ctx->type) {
|
||||
get_apps_context_next(ctx, type, data);
|
||||
} else if (PUT_APPS == ctx->type) {
|
||||
put_apps_context_next(ctx, type, data);
|
||||
} else {
|
||||
get_drivers_context_next(ctx, type, data);
|
||||
}
|
||||
@@ -654,6 +975,7 @@ static int get_drivers_resp(context_t *ctx, neu_resp_get_node_t *resp)
|
||||
neu_resp_node_info_t *info = utarray_eltptr(nodes, i);
|
||||
drivers.drivers[i].node.name = info->node;
|
||||
drivers.drivers[i].node.plugin = info->plugin;
|
||||
drivers.drivers[i].node.tags = info->tags;
|
||||
}
|
||||
|
||||
end:
|
||||
@@ -1328,6 +1650,414 @@ error:
|
||||
context_free(ctx);
|
||||
}
|
||||
|
||||
static int get_apps_resp(context_t *ctx, neu_resp_get_node_t *resp)
|
||||
{
|
||||
int rv = 0;
|
||||
neu_json_app_array_t apps = { 0 };
|
||||
UT_array * nodes = resp->nodes;
|
||||
|
||||
if (ctx->filter) {
|
||||
utarray_new(nodes, &resp->nodes->icd);
|
||||
utarray_foreach(resp->nodes, neu_resp_node_info_t *, info)
|
||||
{
|
||||
if (neu_strset_test(&ctx->filter, info->node)) {
|
||||
utarray_push_back(nodes, info);
|
||||
}
|
||||
}
|
||||
utarray_free(resp->nodes);
|
||||
}
|
||||
|
||||
apps.n_app = utarray_len(nodes);
|
||||
if (apps.n_app > 0) {
|
||||
apps.apps = calloc(apps.n_app, sizeof(apps.apps[0]));
|
||||
if (NULL == apps.apps) {
|
||||
rv = NEU_ERR_EINTERNAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < utarray_len(nodes); ++i) {
|
||||
neu_resp_node_info_t *info = utarray_eltptr(nodes, i);
|
||||
apps.apps[i].node.name = info->node;
|
||||
apps.apps[i].node.plugin = info->plugin;
|
||||
apps.apps[i].node.tags = info->tags;
|
||||
}
|
||||
|
||||
end:
|
||||
ctx->apps = nodes;
|
||||
ctx->japps = apps;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int get_app_setting_resp(context_t * ctx,
|
||||
neu_resp_get_node_setting_t *setting)
|
||||
{
|
||||
int i = utarray_eltidx(ctx->apps, ctx->iter);
|
||||
if (i >= ctx->japps.n_app ||
|
||||
0 != strcmp(ctx->japps.apps[i].node.name, setting->node)) {
|
||||
if (setting) {
|
||||
free(setting->setting);
|
||||
}
|
||||
return NEU_ERR_NODE_NOT_EXIST;
|
||||
}
|
||||
|
||||
ctx->japps.apps[i].node.setting = setting->setting;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_app_subscriptions_resp(context_t * ctx,
|
||||
neu_resp_get_subscribe_group_t *groups)
|
||||
{
|
||||
int rv = 0;
|
||||
int i = utarray_eltidx(ctx->apps, ctx->iter);
|
||||
|
||||
if (i >= ctx->japps.n_app) {
|
||||
rv = NEU_ERR_NODE_NOT_EXIST;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (NULL == groups || NULL == groups->groups ||
|
||||
0 == utarray_len(groups->groups)) {
|
||||
ctx->japps.apps[i].subscriptions.n_group = 0;
|
||||
ctx->japps.apps[i].subscriptions.groups = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
int n_grp = utarray_len(groups->groups);
|
||||
neu_json_app_group_t *grps = calloc(n_grp, sizeof(*grps));
|
||||
if (NULL == grps) {
|
||||
rv = NEU_ERR_EINTERNAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
utarray_foreach(groups->groups, neu_resp_subscribe_info_t *, group)
|
||||
{
|
||||
int index = utarray_eltidx(groups->groups, group);
|
||||
grps[index].driver = strdup(group->driver);
|
||||
grps[index].group = strdup(group->group);
|
||||
grps[index].params = group->params ? strdup(group->params) : NULL;
|
||||
grps[index].static_tags =
|
||||
group->static_tags ? strdup(group->static_tags) : NULL;
|
||||
|
||||
if (NULL == grps[index].driver || NULL == grps[index].group) {
|
||||
for (int j = 0; j <= index; ++j) {
|
||||
free(grps[j].driver);
|
||||
free(grps[j].group);
|
||||
free(grps[j].params);
|
||||
if (grps[j].static_tags) {
|
||||
free(grps[j].static_tags);
|
||||
}
|
||||
}
|
||||
free(grps);
|
||||
rv = NEU_ERR_EINTERNAL;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->japps.apps[i].subscriptions.n_group = n_grp;
|
||||
ctx->japps.apps[i].subscriptions.groups = grps;
|
||||
|
||||
end:
|
||||
if (groups && groups->groups) {
|
||||
utarray_free(groups->groups);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int del_app_node(context_t *ctx, neu_json_app_t *app)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
|
||||
neu_reqresp_head_t header = {
|
||||
.type = NEU_REQ_DEL_NODE,
|
||||
.ctx = ctx->aio,
|
||||
.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM,
|
||||
};
|
||||
|
||||
neu_req_del_node_t cmd = { 0 };
|
||||
strcpy(cmd.node, app->node.name);
|
||||
|
||||
if (0 != neu_plugin_op(plugin, header, &cmd)) {
|
||||
return NEU_ERR_IS_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_app_node(context_t *ctx, neu_json_app_t *app)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
|
||||
neu_reqresp_head_t header = {
|
||||
.type = NEU_REQ_ADD_NODE,
|
||||
.ctx = ctx->aio,
|
||||
.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM,
|
||||
};
|
||||
|
||||
neu_req_add_node_t cmd = { 0 };
|
||||
strcpy(cmd.node, app->node.name);
|
||||
strcpy(cmd.plugin, app->node.plugin);
|
||||
cmd.setting = app->node.setting;
|
||||
if (app->node.tags != NULL) {
|
||||
cmd.tags = strdup(app->node.tags);
|
||||
}
|
||||
app->node.setting = NULL;
|
||||
|
||||
if (0 != neu_plugin_op(plugin, header, &cmd)) {
|
||||
return NEU_ERR_IS_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_app_tags(context_t *ctx, neu_json_app_t *app)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
|
||||
neu_reqresp_head_t header = {
|
||||
.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM,
|
||||
.type = NEU_REQ_UPDATE_NODE_TAG,
|
||||
.ctx = ctx->aio,
|
||||
};
|
||||
|
||||
neu_req_update_node_tag_t cmd = { 0 };
|
||||
strcpy(cmd.node, app->node.name);
|
||||
if (app->node.tags != NULL) {
|
||||
strcpy(cmd.tags, app->node.tags);
|
||||
}
|
||||
|
||||
if (0 != neu_plugin_op(plugin, header, &cmd)) {
|
||||
return NEU_ERR_IS_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_app_setting(context_t *ctx, neu_json_app_t *app)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
|
||||
neu_reqresp_head_t header = {
|
||||
.ctx = ctx->aio,
|
||||
.type = NEU_REQ_NODE_SETTING,
|
||||
.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM,
|
||||
};
|
||||
|
||||
neu_req_node_setting_t cmd = { 0 };
|
||||
strcpy(cmd.node, app->node.name);
|
||||
cmd.setting = app->node.setting;
|
||||
app->node.setting = NULL;
|
||||
|
||||
if (0 != neu_plugin_op(plugin, header, &cmd)) {
|
||||
return NEU_ERR_IS_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_app_subscription(context_t *ctx, neu_json_app_t *app,
|
||||
neu_json_app_group_t *grp)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
|
||||
neu_reqresp_head_t header = {
|
||||
.ctx = ctx->aio,
|
||||
.type = NEU_REQ_SUBSCRIBE_GROUP,
|
||||
.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM,
|
||||
};
|
||||
|
||||
neu_req_subscribe_t cmd = { 0 };
|
||||
strcpy(cmd.app, app->node.name);
|
||||
strcpy(cmd.driver, grp->driver);
|
||||
strcpy(cmd.group, grp->group);
|
||||
cmd.params = grp->params;
|
||||
cmd.static_tags = grp->static_tags;
|
||||
grp->params = NULL;
|
||||
grp->static_tags = NULL;
|
||||
|
||||
if (0 != neu_plugin_op(plugin, header, &cmd)) {
|
||||
return NEU_ERR_IS_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_default_app_subs(context_t *ctx, neu_json_app_t *app)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
|
||||
neu_reqresp_head_t header = {
|
||||
.ctx = ctx->aio,
|
||||
.type = NEU_REQ_GET_SUBSCRIBE_GROUP,
|
||||
.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM,
|
||||
};
|
||||
|
||||
neu_req_get_subscribe_group_t cmd = { 0 };
|
||||
strcpy(cmd.app, app->node.name);
|
||||
|
||||
if (0 != neu_plugin_op(plugin, header, &cmd)) {
|
||||
return NEU_ERR_IS_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_default_app_subs_resp(context_t * ctx,
|
||||
neu_resp_get_subscribe_group_t *resp)
|
||||
{
|
||||
if (NULL == resp || NULL == resp->groups) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ctx->default_app_subs) {
|
||||
utarray_free(ctx->default_app_subs);
|
||||
ctx->default_app_subs = NULL;
|
||||
}
|
||||
|
||||
size_t len = utarray_len(resp->groups);
|
||||
if (len > 0) {
|
||||
UT_icd icd = { sizeof(neu_resp_subscribe_info_t), NULL, NULL, NULL };
|
||||
utarray_new(ctx->default_app_subs, &icd);
|
||||
|
||||
utarray_foreach(resp->groups, neu_resp_subscribe_info_t *, info)
|
||||
{
|
||||
neu_resp_subscribe_info_t copy = { 0 };
|
||||
strcpy(copy.app, info->app);
|
||||
strcpy(copy.driver, info->driver);
|
||||
strcpy(copy.group, info->group);
|
||||
utarray_push_back(ctx->default_app_subs, ©);
|
||||
}
|
||||
}
|
||||
|
||||
utarray_foreach(resp->groups, neu_resp_subscribe_info_t *, info)
|
||||
{
|
||||
neu_resp_subscribe_info_fini(info);
|
||||
}
|
||||
utarray_free(resp->groups);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unsub_app(context_t *ctx, neu_resp_subscribe_info_t *info)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
|
||||
neu_reqresp_head_t header = {
|
||||
.ctx = ctx->aio,
|
||||
.type = NEU_REQ_UNSUBSCRIBE_GROUP,
|
||||
.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM,
|
||||
};
|
||||
|
||||
neu_req_unsubscribe_t cmd = { 0 };
|
||||
strcpy(cmd.app, info->app);
|
||||
strcpy(cmd.driver, info->driver);
|
||||
strcpy(cmd.group, info->group);
|
||||
|
||||
if (0 != neu_plugin_op(plugin, header, &cmd)) {
|
||||
return NEU_ERR_IS_BUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handle_get_apps(nng_aio *aio)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
NEU_VALIDATE_JWT(aio);
|
||||
|
||||
context_t *ctx = context_new(aio, GET_APPS);
|
||||
if (NULL == ctx) {
|
||||
rv = NEU_ERR_EINTERNAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
size_t len = 0;
|
||||
const char *param = neu_http_get_param(aio, "name", &len);
|
||||
if (NULL != param) {
|
||||
char *names = NULL;
|
||||
|
||||
len += 1;
|
||||
if (NULL == (names = calloc(len, 1))) {
|
||||
rv = NEU_ERR_EINTERNAL;
|
||||
goto error;
|
||||
}
|
||||
ctx->names = names;
|
||||
|
||||
if (neu_url_decode(param, len, names, len) < 0) {
|
||||
rv = NEU_ERR_PARAM_IS_WRONG;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (char *next = NULL; names; names = next) {
|
||||
if (NULL != (next = strchr(names, ','))) {
|
||||
*next++ = '\0';
|
||||
}
|
||||
if (neu_strset_add(&ctx->filter, names) < 0) {
|
||||
rv = NEU_ERR_EINTERNAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(void) rv;
|
||||
nng_aio_set_input(aio, 3, ctx);
|
||||
context_next(ctx, NEU_REQ_GET_NODE, NULL);
|
||||
return;
|
||||
|
||||
error:
|
||||
NEU_JSON_RESPONSE_ERROR(rv, { neu_http_response(aio, rv, result_error); });
|
||||
context_free(ctx);
|
||||
}
|
||||
|
||||
void handle_put_apps(nng_aio *aio)
|
||||
{
|
||||
NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT(
|
||||
aio, neu_json_apps_req_t, neu_json_decode_apps_req, {
|
||||
int ret = 0;
|
||||
for (int i = 0; i < req->n_app && 0 == ret; ++i) {
|
||||
if (strlen(req->apps[i].node.name) >= NEU_NODE_NAME_LEN) {
|
||||
ret = NEU_ERR_NODE_NAME_TOO_LONG;
|
||||
break;
|
||||
}
|
||||
if (strlen(req->apps[i].node.plugin) >= NEU_PLUGIN_NAME_LEN) {
|
||||
ret = NEU_ERR_PLUGIN_NAME_TOO_LONG;
|
||||
break;
|
||||
}
|
||||
for (int j = 0; j < req->apps[i].subscriptions.n_group; ++j) {
|
||||
neu_json_app_group_t *grp =
|
||||
&req->apps[i].subscriptions.groups[j];
|
||||
if (strlen(grp->driver) >= NEU_NODE_NAME_LEN) {
|
||||
ret = NEU_ERR_NODE_NAME_TOO_LONG;
|
||||
break;
|
||||
}
|
||||
if (strlen(grp->group) >= NEU_GROUP_NAME_LEN) {
|
||||
ret = NEU_ERR_GROUP_NAME_TOO_LONG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
NEU_JSON_RESPONSE_ERROR(
|
||||
ret, { neu_http_response(aio, ret, result_error); });
|
||||
} else {
|
||||
context_t *ctx = context_new(aio, PUT_APPS);
|
||||
if (NULL == ctx) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, {
|
||||
neu_http_response(aio, NEU_ERR_EINTERNAL, result_error);
|
||||
});
|
||||
} else {
|
||||
nng_aio_set_input(aio, 3, ctx);
|
||||
ctx->apps_req = req;
|
||||
req = NULL;
|
||||
context_next(ctx, NEU_REQ_ADD_NODE, NULL);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handle_global_config_resp(nng_aio *aio, neu_reqresp_type_e type,
|
||||
void *data)
|
||||
{
|
||||
|
||||
@@ -28,6 +28,9 @@ void handle_put_global_config(nng_aio *aio);
|
||||
|
||||
void handle_get_drivers(nng_aio *aio);
|
||||
|
||||
void handle_get_apps(nng_aio *aio);
|
||||
void handle_put_apps(nng_aio *aio);
|
||||
|
||||
void handle_global_config_resp(nng_aio *aio, neu_reqresp_type_e type,
|
||||
void *data);
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "version_handle.h"
|
||||
|
||||
#include "handle.h"
|
||||
#include "simulator_handle.h"
|
||||
|
||||
struct neu_rest_handle_ctx {
|
||||
void *plugin;
|
||||
@@ -133,6 +134,9 @@ static struct neu_http_handler cors_handler[] = {
|
||||
{
|
||||
.url = "/api/v2/global/drivers",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/global/apps",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/metrics",
|
||||
},
|
||||
@@ -190,6 +194,24 @@ static struct neu_http_handler cors_handler[] = {
|
||||
{
|
||||
.url = "/api/v2/auth/basic/security_policies",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/simulator/status",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/simulator/start",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/simulator/stop",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/simulator/config",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/simulator/tags",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/simulator/export",
|
||||
},
|
||||
};
|
||||
|
||||
static struct neu_http_handler rest_handlers[] = {
|
||||
@@ -506,6 +528,18 @@ static struct neu_http_handler rest_handlers[] = {
|
||||
.url = "/api/v2/global/drivers",
|
||||
.value.handler = handle_get_drivers,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_GET,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/global/apps",
|
||||
.value.handler = handle_get_apps,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_PUT,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/global/apps",
|
||||
.value.handler = handle_put_apps,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_GET,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
@@ -686,6 +720,48 @@ static struct neu_http_handler rest_handlers[] = {
|
||||
.url = "/api/v2/auth/basic/security_policies",
|
||||
.value.handler = handle_server_security_policy,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_GET,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/simulator/status",
|
||||
.value.handler = handle_simulator_status,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_GET,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/simulator/tags",
|
||||
.value.handler = handle_simulator_list_tags,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_POST,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/simulator/start",
|
||||
.value.handler = handle_simulator_start,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_POST,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/simulator/stop",
|
||||
.value.handler = handle_simulator_stop,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_POST,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/simulator/config",
|
||||
.value.handler = handle_simulator_set_config,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_GET,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/simulator/export",
|
||||
.value.handler = handle_simulator_export,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_PUT,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/node/tag",
|
||||
.value.handler = handle_put_node_tag,
|
||||
},
|
||||
};
|
||||
|
||||
void neu_rest_handler(const struct neu_http_handler **handlers, uint32_t *size)
|
||||
|
||||
@@ -835,11 +835,7 @@ void handle_export_db(nng_aio *aio)
|
||||
rv = neu_persister_load_node_setting(node->name,
|
||||
(const char **) &settings);
|
||||
if (rv != 0) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_FILE_WRITE_FAILURE, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
});
|
||||
utarray_free(nodes);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
rv = settings_write_to_csv("settings.csv", node->name, settings);
|
||||
free(settings);
|
||||
@@ -858,11 +854,7 @@ void handle_export_db(nng_aio *aio)
|
||||
UT_array *subscribers = NULL;
|
||||
rv = neu_persister_load_subscriptions(node->name, &subscribers);
|
||||
if (rv != 0) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_FILE_WRITE_FAILURE, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
});
|
||||
utarray_free(nodes);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
rv = subscribes_write_to_csv("subscribes.csv", node->name,
|
||||
subscribers);
|
||||
@@ -882,11 +874,7 @@ void handle_export_db(nng_aio *aio)
|
||||
UT_array *groups = NULL;
|
||||
rv = neu_persister_load_groups(node->name, &groups);
|
||||
if (rv != 0) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_FILE_WRITE_FAILURE, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
});
|
||||
utarray_free(nodes);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
rv = groups_write_to_csv("groups.csv", node->name, groups);
|
||||
if (rv != 0) {
|
||||
@@ -902,12 +890,7 @@ void handle_export_db(nng_aio *aio)
|
||||
UT_array *tags = NULL;
|
||||
rv = neu_persister_load_tags(node->name, group->name, &tags);
|
||||
if (rv != 0) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_FILE_WRITE_FAILURE, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
});
|
||||
utarray_free(nodes);
|
||||
utarray_free(groups);
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
rv = tags_write_to_csv("tags.csv", node->name, group->name, tags);
|
||||
if (rv != 0) {
|
||||
@@ -925,14 +908,14 @@ void handle_export_db(nng_aio *aio)
|
||||
|
||||
utarray_free(nodes);
|
||||
|
||||
system("rm -rf neuron.tar.gz");
|
||||
system("tar -zcf neuron.tar.gz nodes.csv settings.csv groups.csv tags.csv "
|
||||
system("rm -rf /tmp/neuron.zip");
|
||||
system("zip /tmp/neuron.zip nodes.csv settings.csv groups.csv tags.csv "
|
||||
"subscribes.csv");
|
||||
|
||||
void * data = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
rv = read_file("neuron.tar.gz", &data, &len);
|
||||
rv = read_file("/tmp/neuron.zip", &data, &len);
|
||||
if (rv != 0) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_FILE_READ_FAILURE, {
|
||||
neu_http_response(aio, error_code.error, result_error);
|
||||
@@ -940,6 +923,6 @@ void handle_export_db(nng_aio *aio)
|
||||
return;
|
||||
}
|
||||
neu_http_response_file(aio, data, len,
|
||||
"attachment; filename=\"neuron.tar.gz\"");
|
||||
"attachment; filename=\"neuron.zip\"");
|
||||
free(data);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
#include <ctype.h>
|
||||
#include <jansson.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "handle.h"
|
||||
#include "modbus_tcp_simulator.h"
|
||||
#include "utils/http.h"
|
||||
#include "utils/log.h"
|
||||
#include "json/neu_json_fn.h"
|
||||
|
||||
static int extract_hold_start_from_addr(const char *addr_str,
|
||||
uint16_t * out_idx0)
|
||||
{
|
||||
if (!addr_str || !out_idx0)
|
||||
return -1;
|
||||
const char *p = strstr(addr_str, "!4");
|
||||
if (!p)
|
||||
return -1;
|
||||
p += 2;
|
||||
if (!isdigit((unsigned char) *p))
|
||||
return -1;
|
||||
long v = strtol(p, NULL, 10);
|
||||
if (v <= 0 || v > 65535)
|
||||
return -1;
|
||||
*out_idx0 = (uint16_t)(v - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handle_simulator_status(nng_aio *aio)
|
||||
{
|
||||
NEU_VALIDATE_JWT(aio);
|
||||
neu_modbus_simulator_status_t st = { 0 };
|
||||
neu_modbus_simulator_get_status(&st);
|
||||
char buf[256] = { 0 };
|
||||
snprintf(buf, sizeof(buf),
|
||||
"{\"running\":%d,\"port\":%u,\"tag_count\":%d,\"error\":%d}",
|
||||
st.running ? 1 : 0, st.port, st.tag_count, st.error);
|
||||
neu_http_ok(aio, buf);
|
||||
}
|
||||
|
||||
void handle_simulator_start(nng_aio *aio)
|
||||
{
|
||||
NEU_VALIDATE_JWT(aio);
|
||||
int rc = neu_modbus_simulator_start(NULL, 0);
|
||||
|
||||
neu_modbus_simulator_status_t st = { 0 };
|
||||
neu_modbus_simulator_get_status(&st);
|
||||
if (rc != 0 || (!st.running && st.error != 0)) {
|
||||
char buf[64] = { 0 };
|
||||
snprintf(buf, sizeof(buf), "{\"error\":%d}", st.error);
|
||||
neu_http_ok(aio, buf);
|
||||
return;
|
||||
}
|
||||
neu_http_ok(aio, "{\"error\":0}");
|
||||
}
|
||||
|
||||
void handle_simulator_list_tags(nng_aio *aio)
|
||||
{
|
||||
NEU_VALIDATE_JWT(aio);
|
||||
char *out = neu_modbus_simulator_list_tags_json();
|
||||
if (!out) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, {
|
||||
neu_http_response(aio, NEU_ERR_EINTERNAL, result_error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
neu_http_ok(aio, out);
|
||||
free(out);
|
||||
}
|
||||
|
||||
void handle_simulator_stop(nng_aio *aio)
|
||||
{
|
||||
NEU_VALIDATE_JWT(aio);
|
||||
neu_modbus_simulator_stop();
|
||||
neu_http_ok(aio, "{\"error\":0}");
|
||||
}
|
||||
|
||||
void handle_simulator_set_config(nng_aio *aio)
|
||||
{
|
||||
NEU_VALIDATE_JWT(aio);
|
||||
char * body = NULL;
|
||||
size_t sz = 0;
|
||||
if (neu_http_get_body(aio, (void **) &body, &sz) != 0 || !body) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, {
|
||||
neu_http_response(aio, NEU_ERR_PARAM_IS_WRONG, result_error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
json_error_t jerr;
|
||||
json_t * root = json_loads(body, 0, &jerr);
|
||||
free(body);
|
||||
if (!root) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, {
|
||||
neu_http_response(aio, NEU_ERR_PARAM_IS_WRONG, result_error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
json_t *tags = json_object_get(root, "tags");
|
||||
if (!tags || !json_is_array(tags)) {
|
||||
json_decref(root);
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, {
|
||||
neu_http_response(aio, NEU_ERR_PARAM_IS_WRONG, result_error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < json_array_size(tags); ++i) {
|
||||
json_t *obj = json_array_get(tags, i);
|
||||
if (!obj || !json_is_object(obj))
|
||||
continue;
|
||||
json_t *jaddr = json_object_get(obj, "address");
|
||||
json_t *jtype = json_object_get(obj, "type");
|
||||
if (!jaddr || !json_is_string(jaddr) || !jtype)
|
||||
continue;
|
||||
const char *addr_str = json_string_value(jaddr);
|
||||
uint16_t idx0 = 0;
|
||||
if (extract_hold_start_from_addr(addr_str, &idx0) != 0)
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
|
||||
char ** names = NULL;
|
||||
char ** addresses = NULL;
|
||||
uint16_t * indices = NULL;
|
||||
neu_sim_tag_type_e *types = NULL;
|
||||
if (count > 0) {
|
||||
names = calloc((size_t) count, sizeof(char *));
|
||||
addresses = calloc((size_t) count, sizeof(char *));
|
||||
indices = calloc((size_t) count, sizeof(uint16_t));
|
||||
types = calloc((size_t) count, sizeof(neu_sim_tag_type_e));
|
||||
if (!names || !addresses || !indices || !types) {
|
||||
free(names);
|
||||
free(addresses);
|
||||
free(indices);
|
||||
free(types);
|
||||
json_decref(root);
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, {
|
||||
neu_http_response(aio, NEU_ERR_EINTERNAL, result_error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int m = 0;
|
||||
for (size_t i = 0; i < json_array_size(tags); ++i) {
|
||||
json_t *obj = json_array_get(tags, i);
|
||||
if (!obj || !json_is_object(obj))
|
||||
continue;
|
||||
json_t *jname = json_object_get(obj, "name");
|
||||
json_t *jaddr = json_object_get(obj, "address");
|
||||
json_t *jtype = json_object_get(obj, "type");
|
||||
if (!jaddr || !json_is_string(jaddr) || !jtype)
|
||||
continue;
|
||||
const char *addr_str = json_string_value(jaddr);
|
||||
uint16_t idx0 = 0;
|
||||
if (extract_hold_start_from_addr(addr_str, &idx0) != 0)
|
||||
continue;
|
||||
neu_sim_tag_type_e tp = NEU_SIM_TAG_NONE;
|
||||
if (json_is_string(jtype)) {
|
||||
const char *ts = json_string_value(jtype);
|
||||
if (ts) {
|
||||
if (strcmp(ts, "sine") == 0)
|
||||
tp = NEU_SIM_TAG_SINE;
|
||||
else if (strcmp(ts, "saw") == 0)
|
||||
tp = NEU_SIM_TAG_SAW;
|
||||
else if (strcmp(ts, "square") == 0)
|
||||
tp = NEU_SIM_TAG_SQUARE;
|
||||
else if (strcmp(ts, "random") == 0)
|
||||
tp = NEU_SIM_TAG_RANDOM;
|
||||
}
|
||||
} else if (json_is_integer(jtype)) {
|
||||
tp = (neu_sim_tag_type_e) json_integer_value(jtype);
|
||||
}
|
||||
if (tp == NEU_SIM_TAG_NONE || idx0 >= 1000)
|
||||
continue;
|
||||
indices[m] = idx0;
|
||||
types[m] = tp;
|
||||
names[m] = jname && json_is_string(jname)
|
||||
? strdup(json_string_value(jname))
|
||||
: strdup("");
|
||||
addresses[m] = strdup(addr_str);
|
||||
m++;
|
||||
}
|
||||
json_decref(root);
|
||||
|
||||
neu_modbus_simulator_config_tags(
|
||||
(const char **) names, (const char **) addresses, indices, types, m);
|
||||
|
||||
for (int i = 0; i < m; ++i) {
|
||||
free(names[i]);
|
||||
free(addresses[i]);
|
||||
}
|
||||
free(names);
|
||||
free(addresses);
|
||||
free(indices);
|
||||
free(types);
|
||||
neu_http_ok(aio, "{\"error\":0}");
|
||||
}
|
||||
|
||||
void handle_simulator_export(nng_aio *aio)
|
||||
{
|
||||
NEU_VALIDATE_JWT(aio);
|
||||
char *drivers = neu_modbus_simulator_export_drivers_json();
|
||||
if (!drivers) {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_EINTERNAL, {
|
||||
neu_http_response(aio, NEU_ERR_EINTERNAL, result_error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
neu_http_response_file(
|
||||
aio, drivers, strlen(drivers),
|
||||
"attachment; filename=\"simulator_modbus_south.json\"");
|
||||
free(drivers);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef NEU_PLUGIN_REST_SIMULATOR_HANDLE_H
|
||||
#define NEU_PLUGIN_REST_SIMULATOR_HANDLE_H
|
||||
|
||||
#include <nng/nng.h>
|
||||
|
||||
void handle_simulator_status(nng_aio *aio);
|
||||
void handle_simulator_start(nng_aio *aio);
|
||||
void handle_simulator_stop(nng_aio *aio);
|
||||
void handle_simulator_set_config(nng_aio *aio);
|
||||
void handle_simulator_export(nng_aio *aio);
|
||||
void handle_simulator_list_tags(nng_aio *aio);
|
||||
|
||||
#endif
|
||||
@@ -90,6 +90,7 @@ static void sig_handler(int sig)
|
||||
for (uint32_t i = 0; i < sizeof(c_events) / sizeof(struct client_event);
|
||||
i++) {
|
||||
if (c_events[i].fd > 0) {
|
||||
close(c_events[i].fd);
|
||||
neu_event_del_io(events, c_events[i].client);
|
||||
}
|
||||
}
|
||||
@@ -149,6 +150,11 @@ int main(int argc, char *argv[])
|
||||
|
||||
events = neu_event_new("modbus_simulator");
|
||||
conn = neu_conn_new(¶m, NULL, connected, disconnected);
|
||||
while (conn_tcp_server_is_listen(conn) == false) {
|
||||
neu_msleep(1000);
|
||||
neu_conn_destory(conn);
|
||||
conn = neu_conn_new(¶m, NULL, connected, disconnected);
|
||||
}
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -219,14 +219,15 @@ void *neu_driver_cache_get_trace(neu_driver_cache_t *cache, const char *group)
|
||||
return trace;
|
||||
}
|
||||
|
||||
void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
bool neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
const char *group, const char *tag,
|
||||
int64_t timestamp, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta,
|
||||
bool change)
|
||||
{
|
||||
struct elem *elem = NULL;
|
||||
tkey_t key = to_key(group, tag);
|
||||
struct elem *elem = NULL;
|
||||
tkey_t key = to_key(group, tag);
|
||||
bool tag_changed = false;
|
||||
|
||||
pthread_mutex_lock(&cache->mtx);
|
||||
HASH_FIND(hh, cache->table, &key, sizeof(tkey_t), elem);
|
||||
@@ -241,6 +242,7 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
(sub_filter_err && elem->value.type != value.type &&
|
||||
elem->value.type != NEU_TYPE_ERROR)) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else if (sub_filter_err && elem->value.type != value.type &&
|
||||
elem->value.type == NEU_TYPE_ERROR) {
|
||||
switch (value.type) {
|
||||
@@ -264,17 +266,20 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (memcmp(&elem->value_old.value, &value.value,
|
||||
sizeof(value.value)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_BYTES:
|
||||
if (elem->value_old.value.bytes.length !=
|
||||
value.value.bytes.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcpy(elem->value_old.value.bytes.bytes,
|
||||
value.value.bytes.bytes,
|
||||
value.value.bytes.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -282,11 +287,13 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.bools.length !=
|
||||
value.value.bools.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.bools.bools,
|
||||
value.value.bools.bools,
|
||||
value.value.bools.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -294,11 +301,13 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.i8s.length !=
|
||||
value.value.i8s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.i8s.i8s,
|
||||
value.value.i8s.i8s,
|
||||
value.value.i8s.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -306,11 +315,13 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.u8s.length !=
|
||||
value.value.u8s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.u8s.u8s,
|
||||
value.value.u8s.u8s,
|
||||
value.value.u8s.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -318,12 +329,14 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.i16s.length !=
|
||||
value.value.i16s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.i16s.i16s,
|
||||
value.value.i16s.i16s,
|
||||
value.value.i16s.length * sizeof(int16_t)) !=
|
||||
0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -331,12 +344,14 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.u16s.length !=
|
||||
value.value.u16s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.u16s.u16s,
|
||||
value.value.u16s.u16s,
|
||||
value.value.u16s.length * sizeof(uint16_t)) !=
|
||||
0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -344,12 +359,14 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.i32s.length !=
|
||||
value.value.i32s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.i32s.i32s,
|
||||
value.value.i32s.i32s,
|
||||
value.value.i32s.length * sizeof(int32_t)) !=
|
||||
0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -357,12 +374,14 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.u32s.length !=
|
||||
value.value.u32s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.u32s.u32s,
|
||||
value.value.u32s.u32s,
|
||||
value.value.u32s.length * sizeof(uint32_t)) !=
|
||||
0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -370,12 +389,14 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.i64s.length !=
|
||||
value.value.i64s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.i64s.i64s,
|
||||
value.value.i64s.i64s,
|
||||
value.value.i64s.length * sizeof(int64_t)) !=
|
||||
0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -383,12 +404,14 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.u64s.length !=
|
||||
value.value.u64s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.u64s.u64s,
|
||||
value.value.u64s.u64s,
|
||||
value.value.u64s.length * sizeof(uint64_t)) !=
|
||||
0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -396,11 +419,13 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.f32s.length !=
|
||||
value.value.f32s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.f32s.f32s,
|
||||
value.value.f32s.f32s,
|
||||
value.value.f32s.length * sizeof(float)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -408,11 +433,13 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.f64s.length !=
|
||||
value.value.f64s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.f64s.f64s,
|
||||
value.value.f64s.f64s,
|
||||
value.value.f64s.length * sizeof(double)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -420,11 +447,13 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.strs.length !=
|
||||
value.value.strs.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.strs.strs,
|
||||
value.value.strs.strs,
|
||||
value.value.strs.length * sizeof(char *)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -432,6 +461,7 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (json_equal(elem->value_old.value.json, value.value.json) !=
|
||||
0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -439,11 +469,13 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.value.ptr.length !=
|
||||
value.value.ptr.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value_old.value.ptr.ptr,
|
||||
value.value.ptr.ptr,
|
||||
value.value.ptr.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,10 +485,12 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.precision == 0) {
|
||||
elem->changed =
|
||||
elem->value_old.value.f32 != value.value.f32;
|
||||
tag_changed = elem->changed;
|
||||
} else {
|
||||
if (fabs(elem->value_old.value.f32 - value.value.f32) >
|
||||
pow(0.1, elem->value_old.precision)) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -464,10 +498,12 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value_old.precision == 0) {
|
||||
elem->changed =
|
||||
elem->value_old.value.d64 != value.value.d64;
|
||||
tag_changed = elem->changed;
|
||||
} else {
|
||||
if (fabs(elem->value_old.value.d64 - value.value.d64) >
|
||||
pow(0.1, elem->value_old.precision)) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,17 +533,20 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (memcmp(&elem->value.value, &value.value,
|
||||
sizeof(value.value)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_BYTES:
|
||||
if (elem->value.value.bytes.length !=
|
||||
value.value.bytes.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value.value.bytes.bytes,
|
||||
value.value.bytes.bytes,
|
||||
value.value.bytes.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -515,140 +554,166 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
if (elem->value.value.bools.length !=
|
||||
value.value.bools.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value.value.bools.bools,
|
||||
value.value.bools.bools,
|
||||
value.value.bools.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_INT8:
|
||||
if (elem->value.value.i8s.length != value.value.i8s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value.value.i8s.i8s, value.value.i8s.i8s,
|
||||
value.value.i8s.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_UINT8:
|
||||
if (elem->value.value.u8s.length != value.value.u8s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value.value.u8s.u8s, value.value.u8s.u8s,
|
||||
value.value.u8s.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_INT16:
|
||||
if (elem->value.value.i16s.length != value.value.i16s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(
|
||||
elem->value.value.i16s.i16s, value.value.i16s.i16s,
|
||||
value.value.i16s.length * sizeof(int16_t)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_UINT16:
|
||||
if (elem->value.value.u16s.length != value.value.u16s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(
|
||||
elem->value.value.u16s.u16s, value.value.u16s.u16s,
|
||||
value.value.u16s.length * sizeof(uint16_t)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_INT32:
|
||||
if (elem->value.value.i32s.length != value.value.i32s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(
|
||||
elem->value.value.i32s.i32s, value.value.i32s.i32s,
|
||||
value.value.i32s.length * sizeof(int32_t)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_UINT32:
|
||||
if (elem->value.value.u32s.length != value.value.u32s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(
|
||||
elem->value.value.u32s.u32s, value.value.u32s.u32s,
|
||||
value.value.u32s.length * sizeof(uint32_t)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_INT64:
|
||||
if (elem->value.value.i64s.length != value.value.i64s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(
|
||||
elem->value.value.i64s.i64s, value.value.i64s.i64s,
|
||||
value.value.i64s.length * sizeof(int64_t)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_UINT64:
|
||||
if (elem->value.value.u64s.length != value.value.u64s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(
|
||||
elem->value.value.u64s.u64s, value.value.u64s.u64s,
|
||||
value.value.u64s.length * sizeof(uint64_t)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_FLOAT:
|
||||
if (elem->value.value.f32s.length != value.value.f32s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value.value.f32s.f32s,
|
||||
value.value.f32s.f32s,
|
||||
value.value.f32s.length * sizeof(float)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_DOUBLE:
|
||||
if (elem->value.value.f64s.length != value.value.f64s.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value.value.f64s.f64s,
|
||||
value.value.f64s.f64s,
|
||||
value.value.f64s.length * sizeof(double)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_ARRAY_STRING:
|
||||
if (elem->value.value.strs.length != value.value.strs.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value.value.strs.strs,
|
||||
value.value.strs.strs,
|
||||
value.value.strs.length * sizeof(char *)) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_PTR: {
|
||||
if (elem->value.value.ptr.length != value.value.ptr.length) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
} else {
|
||||
if (memcmp(elem->value.value.ptr.ptr, value.value.ptr.ptr,
|
||||
value.value.ptr.length) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,32 +722,38 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
case NEU_TYPE_CUSTOM: {
|
||||
if (json_equal(elem->value.value.json, value.value.json) != 0) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NEU_TYPE_FLOAT:
|
||||
if (elem->value.precision == 0) {
|
||||
elem->changed = elem->value.value.f32 != value.value.f32;
|
||||
tag_changed = elem->changed;
|
||||
} else {
|
||||
if (fabs(elem->value.value.f32 - value.value.f32) >
|
||||
pow(0.1, elem->value.precision)) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEU_TYPE_DOUBLE:
|
||||
if (elem->value.precision == 0) {
|
||||
elem->changed = elem->value.value.d64 != value.value.d64;
|
||||
tag_changed = elem->changed;
|
||||
} else {
|
||||
if (fabs(elem->value.value.d64 - value.value.d64) >
|
||||
pow(0.1, elem->value.precision)) {
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case NEU_TYPE_ERROR:
|
||||
elem->changed = true;
|
||||
tag_changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -786,6 +857,7 @@ void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&cache->mtx);
|
||||
return tag_changed;
|
||||
}
|
||||
|
||||
void neu_driver_cache_update(neu_driver_cache_t *cache, const char *group,
|
||||
|
||||
@@ -35,7 +35,7 @@ void neu_driver_cache_update(neu_driver_cache_t *cache, const char *group,
|
||||
const char *tag, int64_t timestamp,
|
||||
neu_dvalue_t value, neu_tag_meta_t *metas,
|
||||
int n_meta);
|
||||
void neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
bool neu_driver_cache_update_change(neu_driver_cache_t *cache,
|
||||
const char *group, const char *tag,
|
||||
int64_t timestamp, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta,
|
||||
|
||||
+268
-11
@@ -113,6 +113,9 @@ static void update(neu_adapter_t *adapter, const char *group, const char *tag,
|
||||
static void update_im(neu_adapter_t *adapter, const char *group,
|
||||
const char *tag, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta);
|
||||
static void update_im_f_m(neu_adapter_t *adapter, const char *group,
|
||||
const char *tag, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta);
|
||||
static void update_with_meta(neu_adapter_t *adapter, const char *group,
|
||||
const char *tag, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta);
|
||||
@@ -175,7 +178,8 @@ static void write_responses(neu_adapter_t *adapter, void *r,
|
||||
neu_driver_write_responses_t *response,
|
||||
int n_response)
|
||||
{
|
||||
neu_reqresp_head_t * req = (neu_reqresp_head_t *) r;
|
||||
neu_reqresp_head_t *req = (neu_reqresp_head_t *) r;
|
||||
const char *iface = (req->sender[0] != '\0') ? req->sender : "unknown";
|
||||
neu_resp_write_tags_t nresp = { 0 };
|
||||
req->type = NEU_RESP_WRITE_TAGS;
|
||||
UT_icd icd = { sizeof(neu_resp_write_tags_ele_t), NULL, NULL, NULL };
|
||||
@@ -190,8 +194,18 @@ static void write_responses(neu_adapter_t *adapter, void *r,
|
||||
strcpy(ele.group, resp->group);
|
||||
strcpy(ele.tag, resp->name);
|
||||
ele.error = resp->error;
|
||||
|
||||
nlog_notice(
|
||||
"driver multi write response detail, driver: %s, interface: "
|
||||
"%s, group: %s, tag: %s, error: %d",
|
||||
adapter->name, iface, resp->group, resp->name, resp->error);
|
||||
|
||||
utarray_push_back(array, &ele);
|
||||
}
|
||||
nlog_notice(
|
||||
"driver multi write response summary, driver: %s, interface: %s, "
|
||||
"result_count: %d",
|
||||
adapter->name, iface, n_response);
|
||||
|
||||
nresp.tags = array;
|
||||
adapter->cb_funs.response(adapter, req, &nresp);
|
||||
@@ -199,8 +213,10 @@ static void write_responses(neu_adapter_t *adapter, void *r,
|
||||
|
||||
static void write_response(neu_adapter_t *adapter, void *r, neu_error error)
|
||||
{
|
||||
neu_reqresp_head_t *req = (neu_reqresp_head_t *) r;
|
||||
neu_resp_error_t nerror = { .error = error };
|
||||
neu_reqresp_head_t *req = (neu_reqresp_head_t *) r;
|
||||
neu_reqresp_type_e req_type = req->type;
|
||||
const char * iface = (req->sender[0] != '\0') ? req->sender : "unknown";
|
||||
neu_resp_error_t nerror = { .error = error };
|
||||
|
||||
neu_otel_trace_ctx trace = NULL;
|
||||
neu_otel_scope_ctx scope = NULL;
|
||||
@@ -221,18 +237,55 @@ static void write_response(neu_adapter_t *adapter, void *r, neu_error error)
|
||||
}
|
||||
}
|
||||
|
||||
if (NEU_REQ_WRITE_TAG == req->type) {
|
||||
neu_req_write_tag_fini((neu_req_write_tag_t *) &req[1]);
|
||||
if (NEU_REQ_WRITE_TAG == req_type) {
|
||||
neu_req_write_tag_t *cmd = (neu_req_write_tag_t *) &req[1];
|
||||
const char * group = cmd->group ? cmd->group : "";
|
||||
const char * tag = cmd->tag ? cmd->tag : "";
|
||||
nlog_notice("driver single write response, driver: %s, interface: %s, "
|
||||
"group: %s, tag: %s, %s, error: %d",
|
||||
adapter->name, iface, group, tag,
|
||||
neu_value_str(cmd->value.type, cmd->value.value), error);
|
||||
neu_req_write_tag_fini(cmd);
|
||||
if (neu_otel_control_is_started() && trace) {
|
||||
neu_otel_scope_set_span_name(scope, "driver write tag response");
|
||||
}
|
||||
} else if (NEU_REQ_WRITE_TAGS == req->type) {
|
||||
neu_req_write_tags_fini((neu_req_write_tags_t *) &req[1]);
|
||||
} else if (NEU_REQ_WRITE_TAGS == req_type) {
|
||||
neu_req_write_tags_t *cmd = (neu_req_write_tags_t *) &req[1];
|
||||
const char * group = cmd->group ? cmd->group : "";
|
||||
nlog_notice("driver multi write response, driver: %s, interface: %s, "
|
||||
"group: %s, tag_count: %d, error: %d",
|
||||
adapter->name, iface, group, cmd->n_tag, error);
|
||||
for (int i = 0; i < cmd->n_tag; i++) {
|
||||
const char *tag_name = cmd->tags[i].tag;
|
||||
nlog_notice("driver multi write response tag detail, driver: %s, "
|
||||
"group: %s, tag: %s, %s",
|
||||
adapter->name, group, tag_name ? tag_name : "",
|
||||
neu_value_str(cmd->tags[i].value.type,
|
||||
cmd->tags[i].value.value));
|
||||
}
|
||||
neu_req_write_tags_fini(cmd);
|
||||
if (neu_otel_control_is_started() && trace) {
|
||||
neu_otel_scope_set_span_name(scope, "driver write tags response");
|
||||
}
|
||||
} else if (NEU_REQ_WRITE_GTAGS == req->type) {
|
||||
neu_req_write_gtags_fini((neu_req_write_gtags_t *) &req[1]);
|
||||
} else if (NEU_REQ_WRITE_GTAGS == req_type) {
|
||||
neu_req_write_gtags_t *cmd = (neu_req_write_gtags_t *) &req[1];
|
||||
nlog_notice("driver multi write response, driver: %s, interface: %s, "
|
||||
"group_count: %d, error: %d",
|
||||
adapter->name, iface, cmd->n_group, error);
|
||||
for (int i = 0; i < cmd->n_group; i++) {
|
||||
const char *group_name =
|
||||
cmd->groups[i].group ? cmd->groups[i].group : "";
|
||||
for (int k = 0; k < cmd->groups[i].n_tag; k++) {
|
||||
const char *tag_name = cmd->groups[i].tags[k].tag;
|
||||
nlog_notice(
|
||||
"driver multi write response tag detail, driver: %s, "
|
||||
"group: %s, tag: %s, %s",
|
||||
adapter->name, group_name, tag_name ? tag_name : "",
|
||||
neu_value_str(cmd->groups[i].tags[k].value.type,
|
||||
cmd->groups[i].tags[k].value.value));
|
||||
}
|
||||
}
|
||||
neu_req_write_gtags_fini(cmd);
|
||||
if (neu_otel_control_is_started() && trace) {
|
||||
neu_otel_scope_set_span_name(scope, "driver write gtags response");
|
||||
}
|
||||
@@ -389,6 +442,107 @@ static void update_with_trace(neu_adapter_t *adapter, const char *group,
|
||||
}
|
||||
}
|
||||
|
||||
static void update_im_f_m(neu_adapter_t *adapter, const char *group,
|
||||
const char *tag, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta)
|
||||
{
|
||||
neu_adapter_driver_t *driver = (neu_adapter_driver_t *) adapter;
|
||||
|
||||
if (tag == NULL) {
|
||||
nlog_warn("update_im_f_m tag is null");
|
||||
return;
|
||||
}
|
||||
|
||||
group_t *find = NULL;
|
||||
|
||||
HASH_FIND_STR(driver->groups, group, find);
|
||||
if (find == NULL) {
|
||||
nlog_error(
|
||||
"update_im_f_m group not found, driver: %s, group: %s, tag: %s",
|
||||
driver->adapter.name, group, tag);
|
||||
return;
|
||||
}
|
||||
|
||||
bool changed = neu_driver_cache_update_change(driver->cache, group, tag,
|
||||
global_timestamp, value,
|
||||
metas, n_meta, false);
|
||||
driver->adapter.cb_funs.update_metric(&driver->adapter,
|
||||
NEU_METRIC_TAG_READS_TOTAL, 1, NULL);
|
||||
if (value.type == NEU_TYPE_ERROR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
UT_array *tags = neu_adapter_driver_get_ptag(driver, group, tag);
|
||||
if (tags == NULL) {
|
||||
return;
|
||||
}
|
||||
neu_datatag_t *first = utarray_front(tags);
|
||||
|
||||
if (first == NULL) {
|
||||
utarray_free(tags);
|
||||
nlog_debug("update immediately, driver: %s, "
|
||||
"group: %s, tag: %s, type: %s, "
|
||||
"timestamp: %" PRId64,
|
||||
driver->adapter.name, group, tag,
|
||||
neu_type_string(value.type), global_timestamp);
|
||||
return;
|
||||
}
|
||||
|
||||
nlog_debug("update and report immediately, driver: %s, "
|
||||
"group: %s, tag: %s, type: %s, "
|
||||
"timestamp: %" PRId64,
|
||||
driver->adapter.name, group, tag, neu_type_string(value.type),
|
||||
global_timestamp);
|
||||
|
||||
neu_reqresp_head_t header = {
|
||||
.type = NEU_REQRESP_TRANS_DATA,
|
||||
};
|
||||
neu_reqresp_trans_data_t *data =
|
||||
calloc(1, sizeof(neu_reqresp_trans_data_t));
|
||||
|
||||
data->driver = strdup(driver->adapter.name);
|
||||
data->group = strdup(group);
|
||||
utarray_new(data->tags, neu_resp_tag_value_meta_icd());
|
||||
|
||||
read_report_group(global_timestamp, 0,
|
||||
neu_adapter_get_tag_cache_type(&driver->adapter),
|
||||
driver->cache, group, tags, data->tags);
|
||||
|
||||
if (utarray_len(data->tags) > 0) {
|
||||
|
||||
data->ctx = calloc(1, sizeof(neu_reqresp_trans_data_ctx_t));
|
||||
data->ctx->index = utarray_len(find->apps);
|
||||
pthread_mutex_init(&data->ctx->mtx, NULL);
|
||||
|
||||
if (utarray_len(find->apps) > 0) {
|
||||
pthread_mutex_lock(&find->apps_mtx);
|
||||
|
||||
utarray_foreach(find->apps, sub_app_t *, app)
|
||||
{
|
||||
if (driver->adapter.cb_funs.responseto(
|
||||
&driver->adapter, &header, data, app->addr) != 0) {
|
||||
neu_trans_data_free(data);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&find->apps_mtx);
|
||||
} else {
|
||||
neu_trans_data_free(data);
|
||||
}
|
||||
} else {
|
||||
utarray_free(data->tags);
|
||||
free(data->group);
|
||||
free(data->driver);
|
||||
}
|
||||
|
||||
utarray_free(tags);
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void update_im(neu_adapter_t *adapter, const char *group,
|
||||
const char *tag, neu_dvalue_t value,
|
||||
neu_tag_meta_t *metas, int n_meta)
|
||||
@@ -530,6 +684,7 @@ neu_adapter_driver_t *neu_adapter_driver_create()
|
||||
driver->adapter.cb_funs.driver.fdown_open_response = fdown_open_response;
|
||||
driver->adapter.cb_funs.driver.fup_data_response = fup_data_response;
|
||||
driver->adapter.cb_funs.driver.update_im = update_im;
|
||||
driver->adapter.cb_funs.driver.update_im_f_m = update_im_f_m;
|
||||
driver->adapter.cb_funs.driver.update_with_trace = update_with_trace;
|
||||
driver->adapter.cb_funs.driver.update_with_meta = update_with_meta;
|
||||
driver->adapter.cb_funs.driver.scan_tags_response = scan_tags_response;
|
||||
@@ -1347,6 +1502,8 @@ int neu_adapter_driver_write_tags(neu_adapter_driver_t *driver,
|
||||
group_t *g = find_group(driver, cmd->group);
|
||||
|
||||
if (g == NULL) {
|
||||
nlog_warn("driver write tags group not exist, driver: %s, group: %s",
|
||||
driver->adapter.name, cmd->group);
|
||||
driver->adapter.cb_funs.driver.write_response(&driver->adapter, req,
|
||||
NEU_ERR_GROUP_NOT_EXIST);
|
||||
return NEU_ERR_GROUP_NOT_EXIST;
|
||||
@@ -1367,6 +1524,9 @@ int neu_adapter_driver_write_tags(neu_adapter_driver_t *driver,
|
||||
cmd->tags[i].value.value.d64,
|
||||
cmd->tags[i].value.type, tag->decimal);
|
||||
} else {
|
||||
nlog_warn("driver write tags not exist, driver: %s, group: %s, "
|
||||
"tag: %s",
|
||||
driver->adapter.name, cmd->group, cmd->tags[i].tag);
|
||||
value_check = NEU_ERR_TAG_NOT_EXIST;
|
||||
}
|
||||
|
||||
@@ -1400,6 +1560,8 @@ int neu_adapter_driver_write_tags(neu_adapter_driver_t *driver,
|
||||
}
|
||||
|
||||
if (value_err != NEU_ERR_SUCCESS) {
|
||||
nlog_warn("driver write tags value error(%d), driver: %s, group: %s",
|
||||
value_err, driver->adapter.name, cmd->group);
|
||||
driver->adapter.cb_funs.driver.write_response(&driver->adapter, req,
|
||||
value_err);
|
||||
utarray_foreach(tags, neu_plugin_tag_value_t *, tv)
|
||||
@@ -1413,6 +1575,9 @@ int neu_adapter_driver_write_tags(neu_adapter_driver_t *driver,
|
||||
}
|
||||
|
||||
if (utarray_len(tags) != (unsigned int) cmd->n_tag) {
|
||||
nlog_warn("driver write tags some tag not exist, driver: %s, group: "
|
||||
"%s",
|
||||
driver->adapter.name, cmd->group);
|
||||
driver->adapter.cb_funs.driver.write_response(&driver->adapter, req,
|
||||
NEU_ERR_TAG_NOT_EXIST);
|
||||
utarray_foreach(tags, neu_plugin_tag_value_t *, tv)
|
||||
@@ -1425,6 +1590,23 @@ int neu_adapter_driver_write_tags(neu_adapter_driver_t *driver,
|
||||
return NEU_ERR_TAG_NOT_EXIST;
|
||||
}
|
||||
|
||||
const neu_reqresp_head_t *head = (const neu_reqresp_head_t *) req;
|
||||
const char * iface =
|
||||
(head && head->sender[0] != '\0') ? head->sender : "unknown";
|
||||
|
||||
nlog_notice("driver multi write request received, driver: %s, group: %s, "
|
||||
"interface: %s, tag_count: %d",
|
||||
driver->adapter.name, cmd->group, iface, cmd->n_tag);
|
||||
|
||||
for (int i = 0; i < cmd->n_tag; i++) {
|
||||
const char *tag_name = cmd->tags[i].tag;
|
||||
nlog_notice(
|
||||
"driver multi write request tag detail, driver: %s, group: %s, "
|
||||
"tag: %s, %s",
|
||||
driver->adapter.name, cmd->group, tag_name ? tag_name : "",
|
||||
neu_value_str(cmd->tags[i].value.type, cmd->tags[i].value.value));
|
||||
}
|
||||
|
||||
to_be_write_tag_t wtag = { 0 };
|
||||
wtag.single = false;
|
||||
wtag.req = (void *) req;
|
||||
@@ -1549,6 +1731,29 @@ int neu_adapter_driver_write_gtags(neu_adapter_driver_t *driver,
|
||||
return NEU_ERR_TAG_NOT_EXIST;
|
||||
}
|
||||
|
||||
const neu_reqresp_head_t *head = (const neu_reqresp_head_t *) req;
|
||||
const char * iface =
|
||||
(head && head->sender[0] != '\0') ? head->sender : "unknown";
|
||||
|
||||
nlog_notice(
|
||||
"driver multi write request received, driver: %s, interface: %s, "
|
||||
"group_count: %d, tag_count: %u",
|
||||
driver->adapter.name, iface, cmd->n_group, n_tag);
|
||||
|
||||
for (int i = 0; i < cmd->n_group; i++) {
|
||||
const char *group_name =
|
||||
cmd->groups[i].group ? cmd->groups[i].group : "";
|
||||
for (int k = 0; k < cmd->groups[i].n_tag; k++) {
|
||||
const char *tag_name = cmd->groups[i].tags[k].tag;
|
||||
nlog_notice("driver multi write request tag detail, driver: %s, "
|
||||
"group: %s, tag: %s, %s",
|
||||
driver->adapter.name, group_name,
|
||||
tag_name ? tag_name : "",
|
||||
neu_value_str(cmd->groups[i].tags[k].value.type,
|
||||
cmd->groups[i].tags[k].value.value));
|
||||
}
|
||||
}
|
||||
|
||||
to_be_write_tag_t wtag = { 0 };
|
||||
wtag.single = false;
|
||||
wtag.req = (void *) req;
|
||||
@@ -1572,6 +1777,8 @@ int neu_adapter_driver_write_tag(neu_adapter_driver_t *driver,
|
||||
group_t * g = find_group(driver, cmd->group);
|
||||
|
||||
if (g == NULL) {
|
||||
nlog_warn("driver write tag group not exist, driver: %s, group: %s",
|
||||
driver->adapter.name, cmd->group);
|
||||
driver->adapter.cb_funs.driver.write_response(&driver->adapter, req,
|
||||
NEU_ERR_GROUP_NOT_EXIST);
|
||||
return NEU_ERR_GROUP_NOT_EXIST;
|
||||
@@ -1579,11 +1786,16 @@ int neu_adapter_driver_write_tag(neu_adapter_driver_t *driver,
|
||||
neu_datatag_t *tag = neu_group_find_tag(g->group, cmd->tag);
|
||||
|
||||
if (tag == NULL) {
|
||||
nlog_warn("driver write tag not exist, driver: %s, group: %s, tag: %s",
|
||||
driver->adapter.name, cmd->group, cmd->tag);
|
||||
driver->adapter.cb_funs.driver.write_response(&driver->adapter, req,
|
||||
NEU_ERR_TAG_NOT_EXIST);
|
||||
return NEU_ERR_TAG_NOT_EXIST;
|
||||
} else {
|
||||
if ((tag->attribute & NEU_ATTRIBUTE_WRITE) != NEU_ATTRIBUTE_WRITE) {
|
||||
nlog_warn("driver write tag not allow write, driver: %s, group: "
|
||||
"%s, tag: %s",
|
||||
driver->adapter.name, cmd->group, cmd->tag);
|
||||
driver->adapter.cb_funs.driver.write_response(
|
||||
&driver->adapter, req, NEU_ERR_PLUGIN_TAG_NOT_ALLOW_WRITE);
|
||||
neu_tag_free(tag);
|
||||
@@ -1595,6 +1807,9 @@ int neu_adapter_driver_write_tag(neu_adapter_driver_t *driver,
|
||||
cmd->value.type, tag->decimal);
|
||||
|
||||
if (value_check != NEU_ERR_SUCCESS) {
|
||||
nlog_warn("driver write tag value error(%d), driver: %s, group: "
|
||||
"%s, tag: %s",
|
||||
value_check, driver->adapter.name, cmd->group, cmd->tag);
|
||||
driver->adapter.cb_funs.driver.write_response(&driver->adapter, req,
|
||||
value_check);
|
||||
neu_tag_free(tag);
|
||||
@@ -1614,6 +1829,17 @@ int neu_adapter_driver_write_tag(neu_adapter_driver_t *driver,
|
||||
|
||||
fix_value(tag, cmd->value.type, &cmd->value);
|
||||
|
||||
const neu_reqresp_head_t *head = (const neu_reqresp_head_t *) req;
|
||||
const char * iface =
|
||||
(head && head->sender[0] != '\0') ? head->sender : "unknown";
|
||||
const char *group_name = cmd->group ? cmd->group : "";
|
||||
const char *tag_name = cmd->tag ? cmd->tag : "";
|
||||
|
||||
nlog_notice("driver single write request received, driver: %s, group: "
|
||||
"%s, tag: %s, interface: %s, %s",
|
||||
driver->adapter.name, group_name, tag_name, iface,
|
||||
neu_value_str(cmd->value.type, cmd->value.value));
|
||||
|
||||
to_be_write_tag_t wtag = { 0 };
|
||||
wtag.single = true;
|
||||
wtag.req = (void *) req;
|
||||
@@ -1721,6 +1947,10 @@ int neu_adapter_driver_update_group(neu_adapter_driver_t *driver,
|
||||
if (NULL != new_name && 0 != strcmp(name, new_name)) {
|
||||
char *new_name_cp1 = strdup(new_name);
|
||||
char *new_name_cp2 = strdup(new_name);
|
||||
if (find->grp.group_free != NULL) {
|
||||
find->grp.group_free(&find->grp);
|
||||
find->grp.group_free = NULL;
|
||||
}
|
||||
if (new_name_cp1 && new_name_cp2 &&
|
||||
0 == neu_group_set_name(find->group, new_name)) {
|
||||
HASH_DEL(driver->groups, find);
|
||||
@@ -2436,9 +2666,35 @@ static int write_callback(void *usr_data)
|
||||
e_time = neu_time_ns();
|
||||
neu_tag_free(wtag->tag);
|
||||
} else {
|
||||
group->driver->adapter.module->intf_funs->driver.write_tags(
|
||||
group->driver->adapter.plugin, (void *) wtag->req, wtag->tvs);
|
||||
const neu_reqresp_head_t *head =
|
||||
(const neu_reqresp_head_t *) wtag->req;
|
||||
const char *iface =
|
||||
(head && head->sender[0] != '\0') ? head->sender : "unknown";
|
||||
|
||||
size_t tag_count = utarray_len(wtag->tvs);
|
||||
nlog_info("driver multi write request start, driver: %s, group: "
|
||||
"%s, interface: %s, tag_count: %zu",
|
||||
group->driver->adapter.name, group->name, iface,
|
||||
tag_count);
|
||||
utarray_foreach(wtag->tvs, neu_plugin_tag_value_t *, tv)
|
||||
{
|
||||
const char *tag_name =
|
||||
(tv && tv->tag && tv->tag->name) ? tv->tag->name : "";
|
||||
nlog_info("driver multi write tag detail, driver: %s, group: "
|
||||
"%s, tag: %s, %s",
|
||||
group->driver->adapter.name, group->name, tag_name,
|
||||
neu_value_str(tv->type, tv->value));
|
||||
}
|
||||
|
||||
int ret =
|
||||
group->driver->adapter.module->intf_funs->driver.write_tags(
|
||||
group->driver->adapter.plugin, (void *) wtag->req,
|
||||
wtag->tvs);
|
||||
e_time = neu_time_ms();
|
||||
|
||||
nlog_info("driver multi write request done, driver: %s, group: %s, "
|
||||
"interface: %s, ret: %d",
|
||||
group->driver->adapter.name, group->name, iface, ret);
|
||||
utarray_foreach(wtag->tvs, neu_plugin_tag_value_t *, tv)
|
||||
{
|
||||
neu_tag_free(tv->tag);
|
||||
@@ -2690,6 +2946,7 @@ static void read_group(int64_t timestamp, int64_t timeout,
|
||||
neu_driver_cache_value_t value = { 0 };
|
||||
|
||||
strcpy(tag_value.tag, tag->name);
|
||||
tag_value.datatag = *tag;
|
||||
|
||||
tag_value.datatag.bias = tag->bias;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ extern "C" {
|
||||
XX(NEU_RESP_NODE_UNINIT, neu_resp_node_uninit_t) \
|
||||
XX(NEU_REQ_ADD_NODE, neu_req_add_node_t) \
|
||||
XX(NEU_REQ_UPDATE_NODE, neu_req_update_node_t) \
|
||||
XX(NEU_REQ_UPDATE_NODE_TAG, neu_req_update_node_tag_t) \
|
||||
XX(NEU_REQ_DEL_NODE, neu_req_del_node_t) \
|
||||
XX(NEU_REQ_GET_NODE, neu_req_get_node_t) \
|
||||
XX(NEU_RESP_GET_NODE, neu_resp_get_node_t) \
|
||||
@@ -83,6 +84,8 @@ extern "C" {
|
||||
XX(NEU_RESP_ADD_TAG, neu_resp_add_tag_t) \
|
||||
XX(NEU_REQ_ADD_GTAG, neu_req_add_gtag_t) \
|
||||
XX(NEU_RESP_ADD_GTAG, neu_resp_add_tag_t) \
|
||||
XX(NEU_REQ_IMPORT_TAGS, neu_req_import_tags_t) \
|
||||
XX(NEU_RESP_IMPORT_TAGS, neu_resp_add_tag_t) \
|
||||
XX(NEU_REQ_DEL_TAG, neu_req_del_tag_t) \
|
||||
XX(NEU_REQ_UPDATE_TAG, neu_req_update_tag_t) \
|
||||
XX(NEU_RESP_UPDATE_TAG, neu_resp_update_tag_t) \
|
||||
@@ -98,6 +101,8 @@ extern "C" {
|
||||
XX(NEU_REQRESP_NODE_DELETED, neu_reqresp_node_deleted_t) \
|
||||
XX(NEU_REQ_ADD_DRIVERS, neu_req_driver_array_t) \
|
||||
XX(NEU_REQ_UPDATE_LOG_LEVEL, neu_req_update_log_level_t) \
|
||||
XX(NEU_REQ_UPDATE_GLOBAL_LOG_LEVEL, neu_req_update_global_log_level_t) \
|
||||
XX(NEU_REQ_UPDATE_NODE_LOG_LEVEL, neu_req_update_node_log_level_t) \
|
||||
XX(NEU_REQ_PRGFILE_UPLOAD, neu_req_prgfile_upload_t) \
|
||||
XX(NEU_REQ_PRGFILE_PROCESS, neu_req_prgfile_process_t) \
|
||||
XX(NEU_RESP_PRGFILE_PROCESS, neu_resp_prgfile_process_t) \
|
||||
@@ -128,6 +133,7 @@ extern "C" {
|
||||
XX(NEU_REQ_DEL_TAG_EVENT, neu_req_del_tag_t) \
|
||||
XX(NEU_REQ_UPDATE_TAG_EVENT, neu_req_update_tag_t) \
|
||||
XX(NEU_REQ_ADD_GTAG_EVENT, neu_req_add_gtag_t) \
|
||||
XX(NEU_REQ_IMPORT_TAGS_EVENT, neu_req_import_tags_t) \
|
||||
XX(NEU_REQ_ADD_PLUGIN_EVENT, neu_req_add_plugin_t) \
|
||||
XX(NEU_REQ_DEL_PLUGIN_EVENT, neu_req_del_plugin_t) \
|
||||
XX(NEU_REQ_SUBSCRIBE_GROUP_EVENT, neu_req_subscribe_t) \
|
||||
|
||||
@@ -732,6 +732,11 @@ static void conn_init_param(neu_conn_t *conn, neu_conn_param_t *param)
|
||||
}
|
||||
}
|
||||
|
||||
bool conn_tcp_server_is_listen(neu_conn_t *conn)
|
||||
{
|
||||
return conn->tcp_server.is_listen;
|
||||
}
|
||||
|
||||
static void conn_tcp_server_listen(neu_conn_t *conn)
|
||||
{
|
||||
if (conn->param.type == NEU_CONN_TCP_SERVER &&
|
||||
@@ -746,6 +751,7 @@ static void conn_tcp_server_listen(neu_conn_t *conn)
|
||||
};
|
||||
|
||||
fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int) { 1 }, sizeof(int));
|
||||
|
||||
ret = bind(fd, (struct sockaddr *) &local, sizeof(local));
|
||||
} else if (is_ipv6(conn->param.params.tcp_server.ip)) {
|
||||
@@ -756,7 +762,7 @@ static void conn_tcp_server_listen(neu_conn_t *conn)
|
||||
&local.sin6_addr);
|
||||
|
||||
fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
|
||||
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int) { 1 }, sizeof(int));
|
||||
ret = bind(fd, (struct sockaddr *) &local, sizeof(local));
|
||||
} else {
|
||||
zlog_error(conn->param.log, "invalid ip: %s",
|
||||
@@ -871,6 +877,7 @@ static void conn_connect(neu_conn_t *conn)
|
||||
} else {
|
||||
zlog_error(conn->param.log, "invalid ip: %s",
|
||||
conn->param.params.tcp_server.ip);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -941,6 +948,7 @@ static void conn_connect(neu_conn_t *conn)
|
||||
} else {
|
||||
zlog_error(conn->param.log, "invalid ip: %s",
|
||||
conn->param.params.tcp_server.ip);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+24
-4
@@ -276,6 +276,26 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
|
||||
neu_msg_free(msg);
|
||||
break;
|
||||
}
|
||||
case NEU_REQ_UPDATE_NODE_TAG: {
|
||||
neu_req_update_node_tag_t *cmd =
|
||||
(neu_req_update_node_tag_t *) &header[1];
|
||||
int ret = neu_node_manager_update_tags(manager->node_manager, cmd->node,
|
||||
cmd->tags);
|
||||
if (ret == 0) {
|
||||
nlog_info("update node %s tags to %s", cmd->node, cmd->tags);
|
||||
manager_storage_update_node_tags(manager, cmd->node, cmd->tags);
|
||||
neu_resp_error_t e = { .error = NEU_ERR_SUCCESS };
|
||||
header->type = NEU_RESP_ERROR;
|
||||
neu_msg_exchange(header);
|
||||
reply(manager, header, &e);
|
||||
} else {
|
||||
neu_resp_error_t e = { .error = NEU_ERR_NODE_NOT_EXIST };
|
||||
header->type = NEU_RESP_ERROR;
|
||||
neu_msg_exchange(header);
|
||||
reply(manager, header, &e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NEU_REQ_ADD_PLUGIN: {
|
||||
neu_req_add_plugin_t *cmd = (neu_req_add_plugin_t *) &header[1];
|
||||
neu_resp_error_t e = { 0 };
|
||||
@@ -742,12 +762,12 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
|
||||
nlog_notice("add node name:%s plugin:%s", cmd->node, cmd->plugin);
|
||||
int error =
|
||||
neu_manager_add_node(manager, cmd->node, cmd->plugin, cmd->setting,
|
||||
NEU_NODE_RUNNING_STATE_INIT, false);
|
||||
cmd->tags, NEU_NODE_RUNNING_STATE_INIT, false);
|
||||
|
||||
neu_resp_error_t e = { .error = error };
|
||||
|
||||
if (error == NEU_ERR_SUCCESS) {
|
||||
manager_storage_add_node(manager, cmd->node);
|
||||
manager_storage_add_node(manager, cmd->node, cmd->tags);
|
||||
if (cmd->setting) {
|
||||
adapter_storage_setting(cmd->node, cmd->setting);
|
||||
}
|
||||
@@ -1600,7 +1620,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
|
||||
if (NEU_ERR_SUCCESS == e.error) {
|
||||
for (uint16_t i = 0; i < cmd->n_driver; ++i) {
|
||||
neu_req_driver_t *driver = &cmd->drivers[i];
|
||||
manager_storage_add_node(manager, driver->node);
|
||||
manager_storage_add_node(manager, driver->node, driver->tags);
|
||||
adapter_storage_setting(driver->node, driver->setting);
|
||||
for (uint16_t j = 0; j < driver->n_group; j++) {
|
||||
adapter_storage_add_group(driver->node,
|
||||
@@ -1787,7 +1807,7 @@ static void start_single_adapter(neu_manager_t *manager, const char *name,
|
||||
|
||||
neu_node_manager_add_single(manager->node_manager, adapter, display);
|
||||
if (display) {
|
||||
manager_storage_add_node(manager, name);
|
||||
manager_storage_add_node(manager, name, "");
|
||||
}
|
||||
|
||||
neu_adapter_init(adapter, false);
|
||||
|
||||
@@ -48,7 +48,8 @@ UT_array *neu_manager_get_plugins(neu_manager_t *manager)
|
||||
|
||||
int neu_manager_add_node(neu_manager_t *manager, const char *node_name,
|
||||
const char *plugin_name, const char *setting,
|
||||
neu_node_running_state_e state, bool load)
|
||||
const char *tags, neu_node_running_state_e state,
|
||||
bool load)
|
||||
{
|
||||
neu_adapter_t * adapter = NULL;
|
||||
neu_plugin_instance_t instance = { 0 };
|
||||
@@ -84,7 +85,7 @@ int neu_manager_add_node(neu_manager_t *manager, const char *node_name,
|
||||
if (adapter == NULL) {
|
||||
return neu_adapter_error();
|
||||
}
|
||||
neu_node_manager_add(manager->node_manager, adapter);
|
||||
neu_node_manager_add(manager->node_manager, adapter, tags);
|
||||
neu_adapter_init(adapter, state);
|
||||
|
||||
if (NULL != setting &&
|
||||
@@ -511,7 +512,7 @@ static inline int add_driver(neu_manager_t *manager, neu_req_driver_t *driver)
|
||||
}
|
||||
|
||||
ret = neu_manager_add_node(manager, driver->node, driver->plugin,
|
||||
driver->setting, false, false);
|
||||
driver->setting, driver->tags, false, false);
|
||||
if (0 != ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ UT_array *neu_manager_get_plugins(neu_manager_t *manager);
|
||||
|
||||
int neu_manager_add_node(neu_manager_t *manager, const char *node_name,
|
||||
const char *plugin_name, const char *setting,
|
||||
neu_node_running_state_e state, bool load);
|
||||
const char *tags, neu_node_running_state_e state,
|
||||
bool load);
|
||||
int neu_manager_del_node(neu_manager_t *manager, const char *node_name);
|
||||
UT_array *neu_manager_get_nodes(neu_manager_t *manager, int type,
|
||||
const char *plugin, const char *node,
|
||||
|
||||
+79
-4
@@ -35,6 +35,8 @@ typedef struct node_entity {
|
||||
bool is_monitor;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
char *tags;
|
||||
|
||||
UT_hash_handle hh;
|
||||
} node_entity_t;
|
||||
|
||||
@@ -43,6 +45,24 @@ struct neu_node_manager {
|
||||
UT_array * monitors;
|
||||
};
|
||||
|
||||
int neu_node_manager_update_tags(neu_node_manager_t *mgr, const char *name,
|
||||
const char *tags)
|
||||
{
|
||||
node_entity_t *node = NULL;
|
||||
|
||||
HASH_FIND_STR(mgr->nodes, name, node);
|
||||
if (NULL == node) {
|
||||
return NEU_ERR_NODE_NOT_EXIST;
|
||||
}
|
||||
|
||||
if (node->tags) {
|
||||
free(node->tags);
|
||||
}
|
||||
node->tags = strdup(tags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
neu_node_manager_t *neu_node_manager_create()
|
||||
{
|
||||
neu_node_manager_t *node_manager = calloc(1, sizeof(neu_node_manager_t));
|
||||
@@ -69,7 +89,8 @@ void neu_node_manager_destroy(neu_node_manager_t *mgr)
|
||||
free(mgr);
|
||||
}
|
||||
|
||||
int neu_node_manager_add(neu_node_manager_t *mgr, neu_adapter_t *adapter)
|
||||
int neu_node_manager_add(neu_node_manager_t *mgr, neu_adapter_t *adapter,
|
||||
const char *tags)
|
||||
{
|
||||
node_entity_t *node = calloc(1, sizeof(node_entity_t));
|
||||
|
||||
@@ -78,6 +99,7 @@ int neu_node_manager_add(neu_node_manager_t *mgr, neu_adapter_t *adapter)
|
||||
node->display = true;
|
||||
node->is_monitor =
|
||||
(0 == strcmp(node->adapter->module->module_name, "Monitor"));
|
||||
node->tags = tags ? strdup(tags) : NULL;
|
||||
|
||||
HASH_ADD_STR(mgr->nodes, name, node);
|
||||
|
||||
@@ -166,6 +188,9 @@ void neu_node_manager_del(neu_node_manager_t *mgr, const char *name)
|
||||
1);
|
||||
}
|
||||
free(node->name);
|
||||
if (node->tags) {
|
||||
free(node->tags);
|
||||
}
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
@@ -225,6 +250,26 @@ static int neu_resp_node_info_sort(const void *a, const void *b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool find_tag_in_tags(const char *tags, const char *tag)
|
||||
{
|
||||
if (strlen(tags) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *tags_copy = strdup(tags);
|
||||
char *saveptr = NULL;
|
||||
char *token = strtok_r(tags_copy, ",", &saveptr);
|
||||
while (token != NULL) {
|
||||
if (strcmp(token, tag) == 0) {
|
||||
free(tags_copy);
|
||||
return true;
|
||||
}
|
||||
token = strtok_r(NULL, ",", &saveptr);
|
||||
}
|
||||
free(tags_copy);
|
||||
return false;
|
||||
}
|
||||
|
||||
UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
|
||||
const char *plugin, const char *node,
|
||||
bool sort_delay, bool q_state, int state,
|
||||
@@ -245,9 +290,38 @@ UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
|
||||
strcmp(el->adapter->module->module_name, plugin) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (strlen(node) > 0 &&
|
||||
strstr(el->adapter->name, node) == NULL) {
|
||||
continue;
|
||||
if (strlen(node) > 0) {
|
||||
char *name_find = strstr(el->adapter->name, node);
|
||||
if (name_find == NULL &&
|
||||
(el->tags == NULL || strlen(el->tags) == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (el->tags != NULL) {
|
||||
char node_tmp[NEU_NODE_NAME_LEN] = { 0 };
|
||||
strncpy(node_tmp, node, NEU_NODE_NAME_LEN - 1);
|
||||
size_t index = 0;
|
||||
char tag_split_array[10][NEU_NODE_TAGS_LEN] = { 0 };
|
||||
char * token = strtok(node_tmp, ",");
|
||||
while (token != NULL && index < 10) {
|
||||
strcpy(tag_split_array[index], token);
|
||||
index++;
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
bool tag_found = true;
|
||||
for (size_t i = 0; i < index; i++) {
|
||||
if (find_tag_in_tags(el->tags,
|
||||
tag_split_array[i]) == false) {
|
||||
tag_found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tag_found && name_find == NULL) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (q_state) {
|
||||
if (el->adapter->state !=
|
||||
@@ -280,6 +354,7 @@ UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
|
||||
|
||||
neu_resp_node_info_t info = { 0 };
|
||||
strcpy(info.node, el->adapter->name);
|
||||
strcpy(info.tags, el->tags ? el->tags : "");
|
||||
strcpy(info.plugin, el->adapter->module->module_name);
|
||||
if (sort_delay) {
|
||||
neu_metric_entry_t *e = NULL;
|
||||
|
||||
@@ -33,7 +33,10 @@ typedef struct neu_node_manager neu_node_manager_t;
|
||||
neu_node_manager_t *neu_node_manager_create();
|
||||
void neu_node_manager_destroy(neu_node_manager_t *mgr);
|
||||
|
||||
int neu_node_manager_add(neu_node_manager_t *mgr, neu_adapter_t *adapter);
|
||||
int neu_node_manager_update_tags(neu_node_manager_t *mgr, const char *name,
|
||||
const char *tags);
|
||||
int neu_node_manager_add(neu_node_manager_t *mgr, neu_adapter_t *adapter,
|
||||
const char *tags);
|
||||
int neu_node_manager_add_static(neu_node_manager_t *mgr,
|
||||
neu_adapter_t * adapter);
|
||||
int neu_node_manager_add_single(neu_node_manager_t *mgr, neu_adapter_t *adapter,
|
||||
|
||||
+11
-2
@@ -38,7 +38,8 @@ void manager_strorage_plugin(neu_manager_t *manager)
|
||||
return;
|
||||
}
|
||||
|
||||
void manager_storage_add_node(neu_manager_t *manager, const char *node)
|
||||
void manager_storage_add_node(neu_manager_t *manager, const char *node,
|
||||
const char *tags)
|
||||
{
|
||||
int rv = 0;
|
||||
neu_persist_node_info_t node_info = {};
|
||||
@@ -48,6 +49,7 @@ void manager_storage_add_node(neu_manager_t *manager, const char *node)
|
||||
nlog_error("unable to get adapter:%s info", node);
|
||||
return;
|
||||
}
|
||||
node_info.tags = tags ? strdup(tags) : NULL;
|
||||
|
||||
rv = neu_persister_store_node(&node_info);
|
||||
if (0 != rv) {
|
||||
@@ -64,6 +66,13 @@ void manager_storage_update_node(neu_manager_t *manager, const char *node,
|
||||
neu_persister_update_node(node, new_name);
|
||||
}
|
||||
|
||||
void manager_storage_update_node_tags(neu_manager_t *manager, const char *node,
|
||||
const char *new_tags)
|
||||
{
|
||||
(void) manager;
|
||||
neu_persister_update_node_tags(node, new_tags);
|
||||
}
|
||||
|
||||
void manager_storage_del_node(neu_manager_t *manager, const char *node)
|
||||
{
|
||||
(void) manager;
|
||||
@@ -146,7 +155,7 @@ int manager_load_node(neu_manager_t *manager)
|
||||
utarray_foreach(node_infos, neu_persist_node_info_t *, node_info)
|
||||
{
|
||||
rv = neu_manager_add_node(manager, node_info->name,
|
||||
node_info->plugin_name, NULL,
|
||||
node_info->plugin_name, NULL, node_info->tags,
|
||||
node_info->state, true);
|
||||
const char *ok_or_err = (0 == rv) ? "success" : "fail";
|
||||
nlog_notice("load adapter %s type:%d, name:%s plugin:%s state:%d",
|
||||
|
||||
+4
-1
@@ -26,7 +26,10 @@
|
||||
|
||||
void manager_strorage_plugin(neu_manager_t *manager);
|
||||
void manager_storage_del_node(neu_manager_t *manager, const char *node);
|
||||
void manager_storage_add_node(neu_manager_t *manager, const char *node);
|
||||
void manager_storage_add_node(neu_manager_t *manager, const char *node,
|
||||
const char *tags);
|
||||
void manager_storage_update_node_tags(neu_manager_t *manager, const char *node,
|
||||
const char *new_tags);
|
||||
void manager_storage_update_node(neu_manager_t *manager, const char *node,
|
||||
const char *new_name);
|
||||
void manager_storage_subscribe(neu_manager_t *manager, const char *app,
|
||||
|
||||
+19
-20
@@ -72,7 +72,8 @@ struct neu_events {
|
||||
int epoll_fd;
|
||||
pthread_t thread;
|
||||
bool stop;
|
||||
char * name;
|
||||
|
||||
zlog_category_t *log;
|
||||
|
||||
pthread_mutex_t mtx;
|
||||
int n_event;
|
||||
@@ -123,8 +124,8 @@ static void *event_loop(void *arg)
|
||||
}
|
||||
|
||||
if (ret == -1 || events->stop) {
|
||||
zlog_warn(neuron, "event loop(%s) exit, errno: %s(%d), stop: %d",
|
||||
events->name, strerror(errno), errno, events->stop);
|
||||
zlog_warn(events->log, "event loop exit, errno: %s(%d), stop: %d",
|
||||
strerror(errno), errno, events->stop);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -182,14 +183,14 @@ neu_events_t *neu_event_new(const char *name)
|
||||
{
|
||||
neu_events_t *events = calloc(1, sizeof(struct neu_events));
|
||||
|
||||
events->log = zlog_get_category(name);
|
||||
events->epoll_fd = epoll_create(1);
|
||||
|
||||
nlog_notice("create epoll(%s): %d(%d)", name, events->epoll_fd, errno);
|
||||
zlog_notice(events->log, "create epoll: %d(%d)", events->epoll_fd, errno);
|
||||
assert(events->epoll_fd > 0);
|
||||
|
||||
events->stop = false;
|
||||
events->n_event = 0;
|
||||
events->name = strdup(name);
|
||||
pthread_mutex_init(&events->mtx, NULL);
|
||||
|
||||
pthread_create(&events->thread, NULL, event_loop, events);
|
||||
@@ -201,7 +202,6 @@ int neu_event_close(neu_events_t *events)
|
||||
{
|
||||
events->stop = true;
|
||||
close(events->epoll_fd);
|
||||
free(events->name);
|
||||
|
||||
pthread_join(events->thread, NULL);
|
||||
pthread_mutex_destroy(&events->mtx);
|
||||
@@ -223,8 +223,7 @@ neu_event_timer_t *neu_event_add_timer(neu_events_t * events,
|
||||
};
|
||||
int index = get_free_event(events);
|
||||
if (index < 0) {
|
||||
zlog_fatal(neuron, "no free event(%s): %d", events->name,
|
||||
events->epoll_fd);
|
||||
zlog_fatal(events->log, "no free event: %d", events->epoll_fd);
|
||||
}
|
||||
assert(index >= 0);
|
||||
|
||||
@@ -253,21 +252,20 @@ neu_event_timer_t *neu_event_add_timer(neu_events_t * events,
|
||||
|
||||
ret = epoll_ctl(events->epoll_fd, EPOLL_CTL_ADD, timer_fd, &event);
|
||||
|
||||
zlog_notice(neuron,
|
||||
zlog_notice(events->log,
|
||||
"add timer, second: %" PRId64 ", millisecond: %" PRId64
|
||||
", timer: %d in epoll(%s) %d, "
|
||||
", timer: %d in epoll %d, "
|
||||
"ret: %d, index: %d",
|
||||
timer.second, timer.millisecond, timer_fd, events->name,
|
||||
events->epoll_fd, ret, index);
|
||||
timer.second, timer.millisecond, timer_fd, events->epoll_fd,
|
||||
ret, index);
|
||||
|
||||
return timer_ctx;
|
||||
}
|
||||
|
||||
int neu_event_del_timer(neu_events_t *events, neu_event_timer_t *timer)
|
||||
{
|
||||
zlog_notice(neuron, "del timer: %d from epoll(%s): %d, index: %d",
|
||||
timer->fd, events->name, events->epoll_fd,
|
||||
timer->event_data->index);
|
||||
zlog_notice(events->log, "del timer: %d from epoll: %d, index: %d",
|
||||
timer->fd, events->epoll_fd, timer->event_data->index);
|
||||
|
||||
timer->stop = true;
|
||||
epoll_ctl(events->epoll_fd, EPOLL_CTL_DEL, timer->fd, NULL);
|
||||
@@ -286,7 +284,7 @@ neu_event_io_t *neu_event_add_io(neu_events_t *events, neu_event_io_param_t io)
|
||||
int ret = 0;
|
||||
int index = get_free_event(events);
|
||||
|
||||
nlog_notice("add io, fd: %d, epoll(%s): %d, index: %d", io.fd, events->name,
|
||||
zlog_notice(events->log, "add io, fd: %d, epoll: %d, index: %d", io.fd,
|
||||
events->epoll_fd, index);
|
||||
assert(index >= 0);
|
||||
|
||||
@@ -308,8 +306,9 @@ neu_event_io_t *neu_event_add_io(neu_events_t *events, neu_event_io_param_t io)
|
||||
|
||||
ret = epoll_ctl(events->epoll_fd, EPOLL_CTL_ADD, io.fd, &event);
|
||||
|
||||
nlog_notice("add io, fd: %d, epoll(%s): %d, ret: %d(%d), index: %d", io.fd,
|
||||
events->name, events->epoll_fd, ret, errno, index);
|
||||
zlog_notice(events->log,
|
||||
"add io, fd: %d, epoll: %d, ret: %d(%d), index: %d", io.fd,
|
||||
events->epoll_fd, ret, errno, index);
|
||||
assert(ret == 0);
|
||||
|
||||
return io_ctx;
|
||||
@@ -321,8 +320,8 @@ int neu_event_del_io(neu_events_t *events, neu_event_io_t *io)
|
||||
return 0;
|
||||
}
|
||||
|
||||
zlog_notice(neuron, "del io: %d from epoll(%s): %d, index: %d", io->fd,
|
||||
events->name, events->epoll_fd, io->event_data->index);
|
||||
zlog_notice(events->log, "del io: %d from epoll: %d, index: %d", io->fd,
|
||||
events->epoll_fd, io->event_data->index);
|
||||
|
||||
epoll_ctl(events->epoll_fd, EPOLL_CTL_DEL, io->fd, NULL);
|
||||
free_event(events, io->event_data->index);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "core/manager.h"
|
||||
#include "modbus_tcp_simulator.h"
|
||||
#include "utils/log.h"
|
||||
#include "utils/time.h"
|
||||
|
||||
@@ -58,6 +59,7 @@ static void sig_handler(int sig)
|
||||
|
||||
if (!sig_trigger) {
|
||||
if (sig == SIGINT || sig == SIGTERM) {
|
||||
neu_modbus_simulator_fini();
|
||||
neu_manager_destroy(g_manager);
|
||||
neu_persister_destroy();
|
||||
zlog_fini();
|
||||
@@ -143,6 +145,8 @@ static int neuron_run(const neu_cli_args_t *args)
|
||||
rv = neu_persister_create(args->config_dir);
|
||||
assert(rv == 0);
|
||||
|
||||
neu_modbus_simulator_apply_persist();
|
||||
|
||||
zlog_notice(neuron, "neuron start, daemon: %d, version: %s (%s %s)",
|
||||
args->daemonized, NEURON_VERSION,
|
||||
NEURON_GIT_REV NEURON_GIT_DIFF, NEURON_BUILD_DATE);
|
||||
@@ -190,6 +194,10 @@ int main(int argc, char *argv[])
|
||||
neuron = zlog_get_category("neuron");
|
||||
zlog_level_switch(neuron, default_log_level);
|
||||
|
||||
if (neu_modbus_simulator_init(args.config_dir) != 0) {
|
||||
nlog_warn("modbus simulator init failed");
|
||||
}
|
||||
|
||||
if (neuron_already_running()) {
|
||||
if (args.stop) {
|
||||
rv = neuron_stop();
|
||||
@@ -242,6 +250,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
main_end:
|
||||
neu_cli_args_fini(&args);
|
||||
neu_modbus_simulator_fini();
|
||||
zlog_fini();
|
||||
return rv;
|
||||
}
|
||||
|
||||
+16
-8
@@ -37,7 +37,7 @@
|
||||
|
||||
#define SPAN_ID_LENGTH 16
|
||||
#define ID_CHARSET "0123456789abcdef"
|
||||
#define TRACE_TIME_OUT (3 * 60 * 1000)
|
||||
#define TRACE_TIME_OUT (60 * 1000)
|
||||
|
||||
bool otel_flag = false;
|
||||
char otel_collector_url[128] = { 0 };
|
||||
@@ -1072,25 +1072,33 @@ static int otel_timer_cb(void *data)
|
||||
|
||||
HASH_ITER(hh, traces_table, el, tmp)
|
||||
{
|
||||
bool should_send = false;
|
||||
bool is_timeout = false;
|
||||
|
||||
if (neu_time_ms() - el->ctx->ts >= TRACE_TIME_OUT) {
|
||||
nlog_debug("trace:%s time out", (char *) el->ctx->trace_id);
|
||||
HASH_DEL(traces_table, el);
|
||||
neu_otel_free_trace(el->ctx);
|
||||
free(el);
|
||||
should_send = true;
|
||||
is_timeout = true;
|
||||
nlog_debug("trace:%s time out, reporting incomplete trace",
|
||||
(char *) el->ctx->trace_id);
|
||||
} else if (el->ctx->final &&
|
||||
el->ctx->span_num ==
|
||||
el->ctx->trace_data.resource_spans[0]
|
||||
->scope_spans[0]
|
||||
->n_spans &&
|
||||
el->ctx->expected_span_num <= 0) {
|
||||
int data_size = neu_otel_trace_pack_size(el->ctx);
|
||||
should_send = true;
|
||||
}
|
||||
|
||||
uint8_t *data_buf = calloc(1, data_size);
|
||||
if (should_send) {
|
||||
int data_size = neu_otel_trace_pack_size(el->ctx);
|
||||
uint8_t *data_buf = calloc(1, data_size);
|
||||
neu_otel_trace_pack(el->ctx, data_buf);
|
||||
int status = neu_http_post_otel_trace(data_buf, data_size);
|
||||
free(data_buf);
|
||||
nlog_debug("send trace:%s status:%d", (char *) el->ctx->trace_id,
|
||||
nlog_debug("send %strace:%s status:%d",
|
||||
is_timeout ? "timeout " : "", (char *) el->ctx->trace_id,
|
||||
status);
|
||||
|
||||
if (status == 200 || status == 400) {
|
||||
HASH_DEL(traces_table, el);
|
||||
neu_otel_free_trace(el->ctx);
|
||||
|
||||
@@ -583,3 +583,281 @@ void neu_json_decode_drivers_req_free(neu_json_drivers_req_t *req)
|
||||
free(req);
|
||||
}
|
||||
}
|
||||
|
||||
static void fini_app_group(neu_json_app_group_t *grp)
|
||||
{
|
||||
free(grp->driver);
|
||||
free(grp->group);
|
||||
free(grp->params);
|
||||
if (grp->static_tags) {
|
||||
free(grp->static_tags);
|
||||
}
|
||||
}
|
||||
|
||||
int neu_json_encode_app(void *node_json, void *param)
|
||||
{
|
||||
neu_json_app_t *app = param;
|
||||
|
||||
if (!json_is_object((json_t *) node_json)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 != neu_json_encode_add_node_req(node_json, &app->node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_t *groups_json = json_array();
|
||||
if (NULL == groups_json ||
|
||||
0 != json_object_set_new(node_json, "groups", groups_json)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < app->subscriptions.n_group; ++i) {
|
||||
json_t *grp_json = json_object();
|
||||
if (NULL == grp_json) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
neu_json_app_group_t *grp = &app->subscriptions.groups[i];
|
||||
neu_json_elem_t grp_elems[] = {
|
||||
{
|
||||
.name = "driver",
|
||||
.t = NEU_JSON_STR,
|
||||
.v.val_str = grp->driver,
|
||||
},
|
||||
{
|
||||
.name = "group",
|
||||
.t = NEU_JSON_STR,
|
||||
.v.val_str = grp->group,
|
||||
},
|
||||
};
|
||||
|
||||
if (0 !=
|
||||
neu_json_encode_field(grp_json, grp_elems,
|
||||
NEU_JSON_ELEM_SIZE(grp_elems))) {
|
||||
json_decref(grp_json);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (grp->params && strlen(grp->params) > 0) {
|
||||
if (0 !=
|
||||
neu_json_load_key(grp_json, "params", grp->params, false)) {
|
||||
json_decref(grp_json);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (grp->static_tags && strlen(grp->static_tags) > 0) {
|
||||
if (0 !=
|
||||
neu_json_load_key(grp_json, "static_tags", grp->static_tags,
|
||||
false)) {
|
||||
json_decref(grp_json);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != json_array_append_new(groups_json, grp_json)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int neu_json_decode_app_json(void *node_json, neu_json_app_t *app_p)
|
||||
{
|
||||
if (!json_is_object((json_t *) node_json)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 != neu_json_decode_add_node_req_json(node_json, &app_p->node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_t *groups_json = json_object_get(node_json, "groups");
|
||||
if (NULL == groups_json) {
|
||||
app_p->subscriptions.n_group = 0;
|
||||
app_p->subscriptions.groups = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!json_is_array(groups_json)) {
|
||||
neu_json_decode_add_node_req_fini(&app_p->node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int n_group = json_array_size(groups_json);
|
||||
if (n_group <= 0) {
|
||||
app_p->subscriptions.n_group = 0;
|
||||
app_p->subscriptions.groups = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
neu_json_app_group_t *groups = calloc(n_group, sizeof(*groups));
|
||||
if (NULL == groups) {
|
||||
neu_json_decode_add_node_req_fini(&app_p->node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_group; ++i) {
|
||||
json_t *grp_json = json_array_get(groups_json, i);
|
||||
|
||||
neu_json_elem_t grp_elems[] = {
|
||||
{
|
||||
.name = "driver",
|
||||
.t = NEU_JSON_STR,
|
||||
},
|
||||
{
|
||||
.name = "group",
|
||||
.t = NEU_JSON_STR,
|
||||
},
|
||||
};
|
||||
|
||||
if (0 !=
|
||||
neu_json_decode_by_json(grp_json, NEU_JSON_ELEM_SIZE(grp_elems),
|
||||
grp_elems)) {
|
||||
for (int j = 0; j < i; ++j) {
|
||||
fini_app_group(&groups[j]);
|
||||
}
|
||||
free(groups);
|
||||
neu_json_decode_add_node_req_fini(&app_p->node);
|
||||
return -1;
|
||||
}
|
||||
|
||||
groups[i].driver = grp_elems[0].v.val_str;
|
||||
groups[i].group = grp_elems[1].v.val_str;
|
||||
neu_json_dump_key(grp_json, "params", &groups[i].params, false);
|
||||
neu_json_dump_key(grp_json, "static_tags", &groups[i].static_tags,
|
||||
false);
|
||||
}
|
||||
|
||||
app_p->subscriptions.n_group = n_group;
|
||||
app_p->subscriptions.groups = groups;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void neu_json_app_fini(neu_json_app_t *app)
|
||||
{
|
||||
neu_json_decode_add_node_req_fini(&app->node);
|
||||
for (int i = 0; i < app->subscriptions.n_group; ++i) {
|
||||
fini_app_group(&app->subscriptions.groups[i]);
|
||||
}
|
||||
free(app->subscriptions.groups);
|
||||
}
|
||||
|
||||
int neu_json_encode_app_array(void *obj_json, void *param)
|
||||
{
|
||||
neu_json_app_array_t *arr = param;
|
||||
|
||||
json_t *nodes_json = obj_json;
|
||||
if (!json_is_array(nodes_json)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < arr->n_app; ++i) {
|
||||
json_t *node_json = json_object();
|
||||
if (NULL == node_json ||
|
||||
0 != json_array_append_new(nodes_json, node_json)) {
|
||||
return -1;
|
||||
}
|
||||
if (0 != neu_json_encode_app(node_json, &arr->apps[i])) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int neu_json_decode_app_array_json(void *obj_json, neu_json_app_array_t *arr)
|
||||
{
|
||||
json_t *nodes_json = obj_json;
|
||||
if (!json_is_array(nodes_json)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int n_app = json_array_size(nodes_json);
|
||||
neu_json_app_t *apps = calloc(n_app, sizeof(*apps));
|
||||
if (NULL == apps) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_app; ++i) {
|
||||
if (0 !=
|
||||
neu_json_decode_app_json(json_array_get(nodes_json, i), &apps[i])) {
|
||||
while (--i >= 0) {
|
||||
neu_json_app_fini(&apps[i]);
|
||||
}
|
||||
free(apps);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
arr->n_app = n_app;
|
||||
arr->apps = apps;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void neu_json_app_array_fini(neu_json_app_array_t *arr)
|
||||
{
|
||||
for (int i = 0; i < arr->n_app; ++i) {
|
||||
neu_json_app_fini(&arr->apps[i]);
|
||||
}
|
||||
free(arr->apps);
|
||||
}
|
||||
|
||||
int neu_json_encode_apps_req(void *obj_json, void *param)
|
||||
{
|
||||
json_t *root = obj_json;
|
||||
if (!json_is_object(root)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_t *apps_json = json_array();
|
||||
if (NULL == apps_json ||
|
||||
0 != json_object_set_new(root, "apps", apps_json)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return neu_json_encode_app_array(apps_json, param);
|
||||
}
|
||||
|
||||
int neu_json_decode_apps_req(char *buf, neu_json_apps_req_t **result)
|
||||
{
|
||||
int ret = 0;
|
||||
neu_json_apps_req_t *req = calloc(1, sizeof(*req));
|
||||
if (req == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *json_obj = neu_json_decode_new(buf);
|
||||
if (NULL == json_obj) {
|
||||
free(req);
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_t *apps_json = json_object_get(json_obj, "apps");
|
||||
if (!json_is_array(apps_json)) {
|
||||
neu_json_decode_free(json_obj);
|
||||
free(req);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = neu_json_decode_app_array_json(apps_json, req);
|
||||
if (0 == ret) {
|
||||
*result = req;
|
||||
} else {
|
||||
free(req);
|
||||
}
|
||||
|
||||
neu_json_decode_free(json_obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void neu_json_decode_apps_req_free(neu_json_apps_req_t *req)
|
||||
{
|
||||
if (req) {
|
||||
neu_json_app_array_fini(req);
|
||||
free(req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,42 @@ int neu_json_encode_drivers_req(void *obj_json, void *param);
|
||||
int neu_json_decode_drivers_req(char *buf, neu_json_drivers_req_t **result);
|
||||
void neu_json_decode_drivers_req_free(neu_json_drivers_req_t *req);
|
||||
|
||||
typedef struct {
|
||||
char *driver;
|
||||
char *group;
|
||||
char *params;
|
||||
char *static_tags;
|
||||
} neu_json_app_group_t;
|
||||
|
||||
typedef struct {
|
||||
int n_group;
|
||||
neu_json_app_group_t *groups;
|
||||
} neu_json_app_group_array_t;
|
||||
|
||||
typedef struct {
|
||||
neu_json_add_node_req_t node;
|
||||
neu_json_app_group_array_t subscriptions;
|
||||
} neu_json_app_t;
|
||||
|
||||
int neu_json_encode_app(void *node_json, void *param);
|
||||
int neu_json_decode_app_json(void *node_json, neu_json_app_t *app_p);
|
||||
void neu_json_app_fini(neu_json_app_t *app);
|
||||
|
||||
typedef struct {
|
||||
int n_app;
|
||||
neu_json_app_t *apps;
|
||||
} neu_json_app_array_t;
|
||||
|
||||
int neu_json_encode_app_array(void *obj_json, void *param);
|
||||
int neu_json_decode_app_array_json(void *obj_json, neu_json_app_array_t *arr);
|
||||
void neu_json_app_array_fini(neu_json_app_array_t *arr);
|
||||
|
||||
typedef neu_json_app_array_t neu_json_apps_req_t;
|
||||
|
||||
int neu_json_encode_apps_req(void *obj_json, void *param);
|
||||
int neu_json_decode_apps_req(char *buf, neu_json_apps_req_t **result);
|
||||
void neu_json_decode_apps_req_free(neu_json_apps_req_t *req);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+104
-18
@@ -36,16 +36,24 @@ int neu_json_encode_add_node_req(void *json_object, void *param)
|
||||
int ret = 0;
|
||||
neu_json_add_node_req_t *req = param;
|
||||
|
||||
neu_json_elem_t req_elems[] = { {
|
||||
.name = "plugin",
|
||||
.t = NEU_JSON_STR,
|
||||
.v.val_str = req->plugin,
|
||||
},
|
||||
{
|
||||
.name = "name",
|
||||
.t = NEU_JSON_STR,
|
||||
.v.val_str = req->name,
|
||||
} };
|
||||
neu_json_elem_t req_elems[] = {
|
||||
{
|
||||
.name = "plugin",
|
||||
.t = NEU_JSON_STR,
|
||||
.v.val_str = req->plugin,
|
||||
},
|
||||
{
|
||||
.name = "name",
|
||||
.t = NEU_JSON_STR,
|
||||
.v.val_str = req->name,
|
||||
},
|
||||
{
|
||||
.name = "tags",
|
||||
.t = NEU_JSON_STR,
|
||||
.v.val_str = req->tags,
|
||||
.attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
|
||||
},
|
||||
};
|
||||
|
||||
ret = neu_json_encode_field(json_object, req_elems,
|
||||
NEU_JSON_ELEM_SIZE(req_elems));
|
||||
@@ -66,14 +74,22 @@ int neu_json_decode_add_node_req_json(void * json_obj,
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
neu_json_elem_t req_elems[] = { {
|
||||
.name = "plugin",
|
||||
.t = NEU_JSON_STR,
|
||||
},
|
||||
{
|
||||
.name = "name",
|
||||
.t = NEU_JSON_STR,
|
||||
} };
|
||||
neu_json_elem_t req_elems[] = {
|
||||
{
|
||||
.name = "plugin",
|
||||
.t = NEU_JSON_STR,
|
||||
},
|
||||
{
|
||||
.name = "name",
|
||||
.t = NEU_JSON_STR,
|
||||
},
|
||||
{
|
||||
.name = "tags",
|
||||
.t = NEU_JSON_STR,
|
||||
.attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
|
||||
.v.val_str = NULL,
|
||||
},
|
||||
};
|
||||
ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
|
||||
req_elems);
|
||||
if (ret != 0) {
|
||||
@@ -82,6 +98,9 @@ int neu_json_decode_add_node_req_json(void * json_obj,
|
||||
|
||||
req->plugin = req_elems[0].v.val_str;
|
||||
req->name = req_elems[1].v.val_str;
|
||||
if (req_elems[2].v.val_str != NULL) {
|
||||
req->tags = req_elems[2].v.val_str;
|
||||
}
|
||||
|
||||
ret = neu_json_dump_key(json_obj, "params", &req->setting, false);
|
||||
if (0 != ret) {
|
||||
@@ -126,6 +145,8 @@ void neu_json_decode_add_node_req_fini(neu_json_add_node_req_t *req)
|
||||
{
|
||||
free(req->plugin);
|
||||
free(req->name);
|
||||
if (req->tags)
|
||||
free(req->tags);
|
||||
free(req->setting);
|
||||
}
|
||||
|
||||
@@ -330,6 +351,11 @@ int neu_json_encode_get_nodes_resp(void *json_object, void *param)
|
||||
.t = NEU_JSON_BOOL,
|
||||
.v.val_bool = p_node->support_import_tags,
|
||||
},
|
||||
{
|
||||
.name = "tags",
|
||||
.t = NEU_JSON_STR,
|
||||
.v.val_str = p_node->tags,
|
||||
},
|
||||
};
|
||||
node_array = neu_json_encode_array(node_array, node_elems,
|
||||
NEU_JSON_ELEM_SIZE(node_elems));
|
||||
@@ -663,3 +689,63 @@ int neu_json_encode_get_node_setting_resp(void *json_object, void *param)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int neu_json_decode_update_node_tag_req(char * buf,
|
||||
neu_json_update_node_tag_req_t **result)
|
||||
{
|
||||
int ret = 0;
|
||||
void * json_obj = NULL;
|
||||
neu_json_update_node_tag_req_t *req =
|
||||
calloc(1, sizeof(neu_json_update_node_tag_req_t));
|
||||
if (req == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_obj = neu_json_decode_new(buf);
|
||||
if (NULL == json_obj) {
|
||||
free(req);
|
||||
return -1;
|
||||
}
|
||||
|
||||
neu_json_elem_t req_elems[] = {
|
||||
{
|
||||
.name = "name",
|
||||
.t = NEU_JSON_STR,
|
||||
},
|
||||
{
|
||||
.name = "tags",
|
||||
.t = NEU_JSON_STR,
|
||||
.attribute = NEU_JSON_ATTRIBUTE_OPTIONAL,
|
||||
.v.val_str = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
|
||||
req_elems);
|
||||
if (ret != 0) {
|
||||
free(req);
|
||||
neu_json_decode_free(json_obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
req->name = req_elems[0].v.val_str;
|
||||
if (req_elems[1].v.val_str != NULL) {
|
||||
req->tags = req_elems[1].v.val_str;
|
||||
}
|
||||
|
||||
*result = req;
|
||||
|
||||
neu_json_decode_free(json_obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void neu_json_decode_update_node_tag_req_free(
|
||||
neu_json_update_node_tag_req_t *req)
|
||||
{
|
||||
if (req) {
|
||||
free(req->name);
|
||||
if (req->tags)
|
||||
free(req->tags);
|
||||
free(req);
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ typedef struct {
|
||||
char *name;
|
||||
char *plugin;
|
||||
char *setting;
|
||||
char *tags;
|
||||
} neu_json_add_node_req_t;
|
||||
|
||||
int neu_json_encode_add_node_req(void *json_object, void *param);
|
||||
@@ -45,6 +46,16 @@ int neu_json_decode_add_node_req(char *buf, neu_json_add_node_req_t **result);
|
||||
void neu_json_decode_add_node_req_fini(neu_json_add_node_req_t *req);
|
||||
void neu_json_decode_add_node_req_free(neu_json_add_node_req_t *req);
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *tags;
|
||||
} neu_json_update_node_tag_req_t;
|
||||
|
||||
int neu_json_decode_update_node_tag_req(
|
||||
char *buf, neu_json_update_node_tag_req_t **result);
|
||||
void neu_json_decode_update_node_tag_req_free(
|
||||
neu_json_update_node_tag_req_t *req);
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
} neu_json_del_node_req_t;
|
||||
@@ -87,6 +98,7 @@ typedef struct {
|
||||
char *name;
|
||||
char *plugin;
|
||||
bool support_import_tags;
|
||||
char *tags;
|
||||
} neu_json_get_nodes_resp_node_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -495,7 +495,11 @@ void neu_json_decode_write_req_free(neu_json_write_req_t *req)
|
||||
if (req->t == NEU_JSON_STR) {
|
||||
free(req->value.val_str);
|
||||
}
|
||||
if (req->t == NEU_JSON_ARRAY_STR && req->value.val_array_str.length > 0) {
|
||||
if (req->t == NEU_JSON_ARRAY_STR &&
|
||||
req->value.val_array_str.p_strs != NULL) {
|
||||
for (size_t i = 0; i < req->value.val_array_str.length; i++) {
|
||||
free(req->value.val_array_str.p_strs[i]);
|
||||
}
|
||||
free(req->value.val_array_str.p_strs);
|
||||
}
|
||||
if (req->t == NEU_JSON_ARRAY_INT8 && req->value.val_array_int8.length > 0) {
|
||||
@@ -705,6 +709,13 @@ void neu_json_decode_write_tags_req_free(neu_json_write_tags_req_t *req)
|
||||
req->tags[i].value.val_array_bool.length > 0) {
|
||||
free(req->tags[i].value.val_array_bool.bools);
|
||||
}
|
||||
if (req->tags[i].t == NEU_JSON_ARRAY_STR &&
|
||||
req->tags[i].value.val_array_str.p_strs != NULL) {
|
||||
for (int j = 0; j < req->tags[i].value.val_array_str.length; j++) {
|
||||
free(req->tags[i].value.val_array_str.p_strs[j]);
|
||||
}
|
||||
free(req->tags[i].value.val_array_str.p_strs);
|
||||
}
|
||||
}
|
||||
free(req->tags);
|
||||
free(req);
|
||||
@@ -1430,6 +1441,15 @@ void neu_json_decode_write_gtags_req_free(neu_json_write_gtags_req_t *req)
|
||||
req->groups[i].tags[k].value.val_array_bool.length > 0) {
|
||||
free(req->groups[i].tags[k].value.val_array_bool.bools);
|
||||
}
|
||||
if (req->groups[i].tags[k].t == NEU_JSON_ARRAY_STR &&
|
||||
req->groups[i].tags[k].value.val_array_str.p_strs != NULL) {
|
||||
for (int j = 0;
|
||||
j < req->groups[i].tags[k].value.val_array_str.length;
|
||||
j++) {
|
||||
free(req->groups[i].tags[k].value.val_array_str.p_strs[j]);
|
||||
}
|
||||
free(req->groups[i].tags[k].value.val_array_str.p_strs);
|
||||
}
|
||||
}
|
||||
if (req->groups[i].n_tag > 0) {
|
||||
free(req->groups[i].tags);
|
||||
|
||||
@@ -346,6 +346,11 @@ int neu_persister_update_node(const char *node_name, const char *new_name)
|
||||
return g_impl->vtbl->update_node(g_impl, node_name, new_name);
|
||||
}
|
||||
|
||||
int neu_persister_update_node_tags(const char *node_name, const char *tags)
|
||||
{
|
||||
return g_impl->vtbl->update_node_tags(g_impl, node_name, tags);
|
||||
}
|
||||
|
||||
int neu_persister_update_node_state(const char *node_name, int state)
|
||||
{
|
||||
return g_impl->vtbl->update_node_state(g_impl, node_name, state);
|
||||
|
||||
@@ -73,6 +73,8 @@ struct neu_persister_vtbl_s {
|
||||
*/
|
||||
int (*update_node)(neu_persister_t *self, const char *node_name,
|
||||
const char *new_name);
|
||||
int (*update_node_tags)(neu_persister_t *self, const char *node_name,
|
||||
const char *tags);
|
||||
|
||||
/**
|
||||
* Update node state.
|
||||
|
||||
+26
-6
@@ -458,6 +458,7 @@ static struct neu_persister_vtbl_s g_sqlite_persister_vtbl = {
|
||||
.load_auth_user = neu_sqlite_persister_load_auth_user,
|
||||
.load_auth_users_by_app = neu_sqlite_persister_load_auth_users_by_app,
|
||||
.delete_auth_user = neu_sqlite_persister_delete_auth_user,
|
||||
.update_node_tags = neu_sqlite_persister_update_node_tags,
|
||||
};
|
||||
|
||||
neu_persister_t *neu_sqlite_persister_create(const char *schema_dir)
|
||||
@@ -496,9 +497,10 @@ int neu_sqlite_persister_store_node(neu_persister_t * self,
|
||||
{
|
||||
int rv = 0;
|
||||
rv = execute_sql(((neu_sqlite_persister_t *) self)->db,
|
||||
"INSERT INTO nodes (name, type, state, plugin_name) "
|
||||
"VALUES (%Q, %i, %i, %Q)",
|
||||
info->name, info->type, info->state, info->plugin_name);
|
||||
"INSERT INTO nodes (name, type, state, plugin_name, tags) "
|
||||
"VALUES (%Q, %i, %i, %Q, %Q)",
|
||||
info->name, info->type, info->state, info->plugin_name,
|
||||
info->tags);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -514,9 +516,10 @@ int neu_sqlite_persister_load_nodes(neu_persister_t *self,
|
||||
{
|
||||
neu_sqlite_persister_t *persister = (neu_sqlite_persister_t *) self;
|
||||
|
||||
int rv = 0;
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
const char * query = "SELECT name, type, state, plugin_name FROM nodes;";
|
||||
int rv = 0;
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
const char * query =
|
||||
"SELECT name, type, state, plugin_name, tags FROM nodes;";
|
||||
|
||||
utarray_new(*node_infos, &node_info_icd);
|
||||
|
||||
@@ -543,10 +546,18 @@ int neu_sqlite_persister_load_nodes(neu_persister_t *self,
|
||||
break;
|
||||
}
|
||||
|
||||
char *tags = (char *) sqlite3_column_text(stmt, 4);
|
||||
|
||||
info.name = name;
|
||||
info.type = sqlite3_column_int(stmt, 1);
|
||||
info.state = sqlite3_column_int(stmt, 2);
|
||||
info.plugin_name = plugin_name;
|
||||
if (tags) {
|
||||
info.tags = strdup(tags);
|
||||
} else {
|
||||
info.tags = NULL;
|
||||
}
|
||||
|
||||
utarray_push_back(*node_infos, &info);
|
||||
|
||||
step = sqlite3_step(stmt);
|
||||
@@ -580,6 +591,15 @@ int neu_sqlite_persister_update_node(neu_persister_t *self,
|
||||
node_name);
|
||||
}
|
||||
|
||||
int neu_sqlite_persister_update_node_tags(neu_persister_t *self,
|
||||
const char * node_name,
|
||||
const char * tags)
|
||||
{
|
||||
return execute_sql(((neu_sqlite_persister_t *) self)->db,
|
||||
"UPDATE nodes SET tags=%Q WHERE name=%Q;", tags,
|
||||
node_name);
|
||||
}
|
||||
|
||||
int neu_sqlite_persister_update_node_state(neu_persister_t *self,
|
||||
const char *node_name, int state)
|
||||
{
|
||||
|
||||
@@ -45,6 +45,9 @@ int neu_sqlite_persister_delete_node(neu_persister_t *self,
|
||||
int neu_sqlite_persister_update_node(neu_persister_t *self,
|
||||
const char * node_name,
|
||||
const char * new_name);
|
||||
int neu_sqlite_persister_update_node_tags(neu_persister_t *self,
|
||||
const char * node_name,
|
||||
const char * tags);
|
||||
int neu_sqlite_persister_update_node_state(neu_persister_t *self,
|
||||
const char *node_name, int state);
|
||||
int neu_sqlite_persister_store_tag(neu_persister_t * self,
|
||||
|
||||
+45
-10
@@ -307,7 +307,8 @@ static int find_da_basic_type(cid_template_t *template, const char *datype_id,
|
||||
|
||||
static int find_basic_type(cid_template_t *template, const char *type_id,
|
||||
const char *do_name, const char *da_name,
|
||||
cid_fc_e fc, cid_basictype_e **btypes)
|
||||
cid_fc_e fc, cid_basictype_e **btypes,
|
||||
bool *is_array, uint8_t *array_size)
|
||||
{
|
||||
*btypes = NULL;
|
||||
cid_tm_do_type_t *dotype = NULL;
|
||||
@@ -330,6 +331,8 @@ static int find_basic_type(cid_template_t *template, const char *type_id,
|
||||
(*btypes)[index - 1] = dotype->das[i].btype;
|
||||
continue;
|
||||
}
|
||||
*is_array = dotype->das[i].is_array;
|
||||
*array_size = dotype->das[i].array_size;
|
||||
|
||||
da_basic_type_t da_types[32] = { 0 };
|
||||
int n_da_types = find_da_basic_type(template, dotype->das[i].ref_type,
|
||||
@@ -362,15 +365,28 @@ static void update_dataset(cid_dataset_t *dataset, cid_ldevice_t *ldev,
|
||||
find_type_id(ldev, dataset->fcdas[i].prefix,
|
||||
dataset->fcdas[i].lnclass, dataset->fcdas[i].lninst);
|
||||
if (type_id != NULL) {
|
||||
cid_basictype_e *bs = NULL;
|
||||
int ret = find_basic_type(
|
||||
template, type_id, dataset->fcdas[i].do_name,
|
||||
dataset->fcdas[i].da_name, dataset->fcdas[i].fc, &bs);
|
||||
cid_basictype_e *bs = NULL;
|
||||
bool is_array = false;
|
||||
uint8_t array_size = 0;
|
||||
|
||||
int ret =
|
||||
find_basic_type(template, type_id, dataset->fcdas[i].do_name,
|
||||
dataset->fcdas[i].da_name, dataset->fcdas[i].fc,
|
||||
&bs, &is_array, &array_size);
|
||||
if (ret > 0) {
|
||||
if (ret <= 16) {
|
||||
memcpy(dataset->fcdas[i].btypes, bs,
|
||||
ret * sizeof(cid_basictype_e));
|
||||
dataset->fcdas[i].n_btypes = ret;
|
||||
dataset->fcdas[i].btype = bs[0];
|
||||
for (int j = 1; j < ret; j++) {
|
||||
if ((bs[j] & 0x7f) != 15 && (bs[j] & 0x7f) != 16) {
|
||||
dataset->fcdas[i].btype = bs[j];
|
||||
}
|
||||
}
|
||||
|
||||
dataset->fcdas[i].n_btypes = ret;
|
||||
dataset->fcdas[i].is_array = is_array;
|
||||
dataset->fcdas[i].array_size = array_size;
|
||||
} else {
|
||||
nlog_warn(
|
||||
"Too many basic types for %s %s %s %s %s %d %s",
|
||||
@@ -987,6 +1003,8 @@ static int parse_template(xmlNode *xml_template, cid_template_t *template)
|
||||
(char *) xmlGetProp(da, (const xmlChar *) "name");
|
||||
char *ref_type =
|
||||
(char *) xmlGetProp(da, (const xmlChar *) "type");
|
||||
char *count =
|
||||
(char *) xmlGetProp(da, (const xmlChar *) "count");
|
||||
|
||||
if (btype != NULL && fc != NULL && name != NULL) {
|
||||
tm_do->n_das += 1;
|
||||
@@ -1014,6 +1032,14 @@ static int parse_template(xmlNode *xml_template, cid_template_t *template)
|
||||
nlog_warn("Unknown fc %s, %d", fc,
|
||||
(int) da->line);
|
||||
}
|
||||
|
||||
if (count != NULL) {
|
||||
tm_da->is_array = true;
|
||||
tm_da->array_size = atoi(count);
|
||||
} else {
|
||||
tm_da->is_array = false;
|
||||
tm_da->array_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (btype != NULL) {
|
||||
@@ -1028,6 +1054,9 @@ static int parse_template(xmlNode *xml_template, cid_template_t *template)
|
||||
if (ref_type != NULL) {
|
||||
xmlFree(ref_type);
|
||||
}
|
||||
if (count != NULL) {
|
||||
xmlFree(count);
|
||||
}
|
||||
}
|
||||
|
||||
da = da->next;
|
||||
@@ -1475,7 +1504,7 @@ static void ln_dataset_to_group(const char *ied_name, const char *ap_name,
|
||||
fc_to_str(ln->datasets[j].fcdas[k].fc),
|
||||
ln->datasets[j].fcdas[k].do_name);
|
||||
}
|
||||
switch (ln->datasets[j].fcdas[k].btypes[0] & 0x7f) {
|
||||
switch (ln->datasets[j].fcdas[k].btype & 0x7f) {
|
||||
case BOOLEAN:
|
||||
tag->type = NEU_TYPE_BOOL;
|
||||
break;
|
||||
@@ -1506,9 +1535,15 @@ static void ln_dataset_to_group(const char *ied_name, const char *ap_name,
|
||||
case INT32U:
|
||||
tag->type = NEU_TYPE_UINT32;
|
||||
break;
|
||||
case FLOAT32:
|
||||
tag->type = NEU_TYPE_FLOAT;
|
||||
break;
|
||||
case FLOAT32: {
|
||||
if (ln->datasets[j].fcdas[k].is_array) {
|
||||
tag->type = NEU_TYPE_ARRAY_FLOAT;
|
||||
break;
|
||||
} else {
|
||||
tag->type = NEU_TYPE_FLOAT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case FLOAT64:
|
||||
tag->type = NEU_TYPE_DOUBLE;
|
||||
break;
|
||||
|
||||
@@ -339,6 +339,10 @@ int neu_http_response(nng_aio *aio, neu_err_code_e code, char *content)
|
||||
case NEU_ERR_USER_NOT_EXISTS:
|
||||
case NEU_ERR_USER_NO_PERMISSION:
|
||||
case NEU_ERR_INVALID_USER_LEN:
|
||||
case NEU_ERR_PORT_IN_USE:
|
||||
case NEU_ERR_NODE_TAGS_TOO_MANY:
|
||||
case NEU_ERR_NODE_TAGS_INVALID:
|
||||
case NEU_ERR_NODE_TAGS_TOO_LONG:
|
||||
status = NNG_HTTP_STATUS_BAD_REQUEST;
|
||||
break;
|
||||
case NEU_ERR_FILE_NOT_EXIST:
|
||||
|
||||
+5
-1
@@ -187,7 +187,11 @@ static json_t *encode_object(json_t *object, neu_json_elem_t ele)
|
||||
json_object_set_new(ob, ele.name, json_integer(ele.v.val_int));
|
||||
break;
|
||||
case NEU_JSON_STR:
|
||||
json_object_set_new(ob, ele.name, json_string(ele.v.val_str));
|
||||
if (ele.v.val_str != NULL) {
|
||||
json_object_set_new(ob, ele.name, json_string(ele.v.val_str));
|
||||
} else {
|
||||
json_object_set_new(ob, ele.name, json_string(""));
|
||||
}
|
||||
break;
|
||||
case NEU_JSON_FLOAT: {
|
||||
double t = ele.v.val_float;
|
||||
|
||||
+1
-1
@@ -211,7 +211,7 @@ void neu_tpy_to_msg(char *driver, tpy_t *tpy, neu_req_add_gtag_t *cmd)
|
||||
to_neu_type(tpy->statics[i].vars[k].type);
|
||||
cmd->groups[i].tags[k].attribute =
|
||||
NEU_ATTRIBUTE_READ | NEU_ATTRIBUTE_WRITE;
|
||||
snprintf(address, sizeof(address) - 1, "%ld,%ld",
|
||||
snprintf(address, sizeof(address) - 1, "%" PRIi64 ",%" PRIi64,
|
||||
tpy->statics[i].vars[k].igroup,
|
||||
tpy->statics[i].vars[k].ioffset);
|
||||
cmd->groups[i].tags[k].address = strdup(address);
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
import neuron.api as api
|
||||
import neuron.error as error
|
||||
import neuron.config as config
|
||||
from neuron.common import *
|
||||
|
||||
hold_int16 = [{"name": "hold_int16", "address": "1!400001",
|
||||
"attribute": config.NEU_TAG_ATTRIBUTE_RW, "type": config.NEU_TYPE_INT16}]
|
||||
|
||||
|
||||
class TestAppsExportImport:
|
||||
|
||||
@description(given="mqtt node subscribed to modbus group",
|
||||
when="export apps",
|
||||
then="returns node config with subscriptions")
|
||||
def test_export_app_with_subscriptions(self):
|
||||
driver = 'modbus-export-test'
|
||||
app = 'mqtt-export-test'
|
||||
group = 'group1'
|
||||
|
||||
try:
|
||||
api.add_node_check(node=driver, plugin=config.PLUGIN_MODBUS_TCP)
|
||||
api.modbus_tcp_node_setting(node=driver, port=502)
|
||||
api.add_group_check(node=driver, group=group)
|
||||
api.add_tags_check(node=driver, group=group, tags=hold_int16)
|
||||
|
||||
api.add_node_check(node=app, plugin=config.PLUGIN_MQTT)
|
||||
api.subscribe_group_check(app=app, driver=driver, group=group)
|
||||
|
||||
response = api.get_apps()
|
||||
assert 200 == response.status_code
|
||||
assert 'attachment' in response.headers.get('Content-Disposition', '')
|
||||
|
||||
data = response.json()
|
||||
assert 'apps' in data
|
||||
|
||||
app_data = None
|
||||
for node in data['apps']:
|
||||
if node['name'] == app:
|
||||
app_data = node
|
||||
break
|
||||
|
||||
assert app_data is not None
|
||||
assert app_data['plugin'] == config.PLUGIN_MQTT
|
||||
assert 'groups' in app_data
|
||||
assert len(app_data['groups']) >= 1
|
||||
|
||||
sub = app_data['groups'][0]
|
||||
assert sub['driver'] == driver
|
||||
assert sub['group'] == group
|
||||
|
||||
finally:
|
||||
api.del_node(app)
|
||||
api.del_node(driver)
|
||||
|
||||
@description(given="app node does not exist",
|
||||
when="import new app node",
|
||||
then="node created successfully")
|
||||
def test_import_new_app_node(self):
|
||||
app = 'mqtt-import-new'
|
||||
|
||||
try:
|
||||
api.del_node(app)
|
||||
|
||||
apps = [{
|
||||
"name": app,
|
||||
"plugin": config.PLUGIN_MQTT,
|
||||
"params": {
|
||||
"client-id": "neuron_test_import",
|
||||
"qos": 0,
|
||||
"format": 0,
|
||||
"write-req-topic": f"/neuron/{app}/write/req",
|
||||
"write-resp-topic": f"/neuron/{app}/write/resp",
|
||||
"offline-cache": False,
|
||||
"host": "broker.emqx.io",
|
||||
"port": 1883,
|
||||
"ssl": False
|
||||
},
|
||||
"groups": []
|
||||
}]
|
||||
|
||||
response = api.put_apps(apps)
|
||||
assert 200 == response.status_code
|
||||
assert error.NEU_ERR_SUCCESS == response.json()['error']
|
||||
|
||||
response = api.get_nodes_state(app)
|
||||
assert 200 == response.status_code
|
||||
|
||||
finally:
|
||||
api.del_node(app)
|
||||
|
||||
@description(given="app node already exists",
|
||||
when="import same app node",
|
||||
then="old node deleted and new node created")
|
||||
def test_import_existing_app_node_overwrite(self):
|
||||
app = 'mqtt-import-overwrite'
|
||||
|
||||
try:
|
||||
api.add_node_check(node=app, plugin=config.PLUGIN_MQTT)
|
||||
|
||||
response = api.get_nodes_state(app)
|
||||
assert 200 == response.status_code
|
||||
|
||||
apps = [{
|
||||
"name": app,
|
||||
"plugin": config.PLUGIN_MQTT,
|
||||
"params": {
|
||||
"client-id": "neuron_overwrite_test",
|
||||
"qos": 1,
|
||||
"format": 0,
|
||||
"write-req-topic": f"/neuron/{app}/write/req",
|
||||
"write-resp-topic": f"/neuron/{app}/write/resp",
|
||||
"offline-cache": False,
|
||||
"host": "broker.emqx.io",
|
||||
"port": 1883,
|
||||
"ssl": False
|
||||
},
|
||||
"groups": []
|
||||
}]
|
||||
|
||||
response = api.put_apps(apps)
|
||||
assert 200 == response.status_code
|
||||
assert error.NEU_ERR_SUCCESS == response.json()['error']
|
||||
|
||||
response = api.get_nodes_state(app)
|
||||
assert 200 == response.status_code
|
||||
|
||||
response = api.get_apps(names=[app])
|
||||
assert 200 == response.status_code
|
||||
data = response.json()
|
||||
assert len(data['apps']) == 1
|
||||
assert 'neuron_overwrite_test' in str(data['apps'][0].get('params', ''))
|
||||
|
||||
finally:
|
||||
api.del_node(app)
|
||||
|
||||
@description(given="subscription driver does not exist",
|
||||
when="import app with subscription",
|
||||
then="returns subscription error")
|
||||
def test_import_app_subscription_driver_not_exist(self):
|
||||
app = 'mqtt-import-sub-error'
|
||||
non_exist_driver = 'non-exist-driver-12345'
|
||||
|
||||
try:
|
||||
api.del_node(app)
|
||||
|
||||
apps = [{
|
||||
"name": app,
|
||||
"plugin": config.PLUGIN_MQTT,
|
||||
"params": {
|
||||
"client-id": "neuron_sub_test",
|
||||
"qos": 0,
|
||||
"format": 0,
|
||||
"write-req-topic": f"/neuron/{app}/write/req",
|
||||
"write-resp-topic": f"/neuron/{app}/write/resp",
|
||||
"offline-cache": False,
|
||||
"host": "broker.emqx.io",
|
||||
"port": 1883,
|
||||
"ssl": False
|
||||
},
|
||||
"groups": [{
|
||||
"driver": non_exist_driver,
|
||||
"group": "group1"
|
||||
}]
|
||||
}]
|
||||
|
||||
response = api.put_apps(apps)
|
||||
assert response.json()['error'] != error.NEU_ERR_SUCCESS
|
||||
|
||||
finally:
|
||||
api.del_node(app)
|
||||
|
||||
@description(given="configured app node",
|
||||
when="export then delete then import",
|
||||
then="node restored with same config")
|
||||
def test_export_import_roundtrip(self):
|
||||
driver = 'modbus-roundtrip'
|
||||
app = 'mqtt-roundtrip'
|
||||
group = 'group1'
|
||||
|
||||
try:
|
||||
api.add_node_check(node=driver, plugin=config.PLUGIN_MODBUS_TCP)
|
||||
api.modbus_tcp_node_setting(node=driver, port=502)
|
||||
api.add_group_check(node=driver, group=group)
|
||||
api.add_tags_check(node=driver, group=group, tags=hold_int16)
|
||||
|
||||
api.add_node_check(node=app, plugin=config.PLUGIN_MQTT)
|
||||
api.subscribe_group_check(app=app, driver=driver, group=group)
|
||||
|
||||
response = api.get_apps(names=[app])
|
||||
assert 200 == response.status_code
|
||||
exported_data = response.json()
|
||||
|
||||
assert 'apps' in exported_data
|
||||
assert len(exported_data['apps']) == 1
|
||||
app_config = exported_data['apps'][0]
|
||||
assert app_config['name'] == app
|
||||
|
||||
api.del_node(app)
|
||||
response = api.get_nodes_state(app)
|
||||
assert response.json()['error'] == error.NEU_ERR_NODE_NOT_EXIST
|
||||
|
||||
response = api.put_apps(exported_data['apps'])
|
||||
assert 200 == response.status_code
|
||||
assert error.NEU_ERR_SUCCESS == response.json()['error']
|
||||
|
||||
response = api.get_nodes_state(app)
|
||||
assert 200 == response.status_code
|
||||
|
||||
response = api.get_subscribe_group(app)
|
||||
assert 200 == response.status_code
|
||||
groups = response.json().get('groups', [])
|
||||
assert len(groups) >= 1
|
||||
|
||||
found = False
|
||||
for g in groups:
|
||||
if g['driver'] == driver and g['group'] == group:
|
||||
found = True
|
||||
break
|
||||
assert found, "Subscription not restored"
|
||||
|
||||
finally:
|
||||
api.del_node(app)
|
||||
api.del_node(driver)
|
||||
|
||||
@@ -133,6 +133,7 @@ global_config_del_modbus = {
|
||||
driver_1 = {
|
||||
"name": "modbus-driver-1",
|
||||
"plugin": "Modbus TCP",
|
||||
"tags": "",
|
||||
"params": {
|
||||
"connection_mode": 0,
|
||||
"transport_mode": 0,
|
||||
@@ -180,6 +181,7 @@ driver_1 = {
|
||||
driver_2 = {
|
||||
"name": "modbus-driver-2",
|
||||
"plugin": "Modbus TCP",
|
||||
"tags": "",
|
||||
"params": {
|
||||
"connection_mode": 0,
|
||||
"transport_mode": 0,
|
||||
|
||||
@@ -308,6 +308,16 @@ def put_drivers(drivers, jwt=config.default_jwt):
|
||||
return requests.put(url=config.BASE_URL + '/api/v2/global/drivers', headers={"Authorization": jwt}, json={"nodes": drivers})
|
||||
|
||||
|
||||
def get_apps(names=None, jwt=config.default_jwt):
|
||||
if names and not isinstance(names, str):
|
||||
names = ','.join(names)
|
||||
return requests.get(url=config.BASE_URL + '/api/v2/global/apps', headers={"Authorization": jwt}, params={"name": names})
|
||||
|
||||
|
||||
def put_apps(apps, jwt=config.default_jwt):
|
||||
return requests.put(url=config.BASE_URL + '/api/v2/global/apps', headers={"Authorization": jwt}, json={"apps": apps})
|
||||
|
||||
|
||||
def get_status():
|
||||
return requests.get(url=config.BASE_URL + "/api/v2/status", headers={"Authorization": config.default_jwt})
|
||||
|
||||
@@ -544,3 +554,37 @@ def server_security_policy(app_name, policy_name, jwt=config.default_jwt):
|
||||
return requests.post(url=config.BASE_URL + "/api/v2/auth/basic/security_policies",
|
||||
headers={"Authorization": jwt},
|
||||
json={"node": app_name, "policyName": policy_name})
|
||||
|
||||
|
||||
def simulator_status(jwt=config.default_jwt):
|
||||
return requests.get(url=config.BASE_URL + "/api/v2/simulator/status",
|
||||
headers={"Authorization": jwt})
|
||||
|
||||
|
||||
@gen_check
|
||||
def simulator_start(jwt=config.default_jwt):
|
||||
return requests.post(url=config.BASE_URL + "/api/v2/simulator/start",
|
||||
headers={"Authorization": jwt})
|
||||
|
||||
|
||||
@gen_check
|
||||
def simulator_stop(jwt=config.default_jwt):
|
||||
return requests.post(url=config.BASE_URL + "/api/v2/simulator/stop",
|
||||
headers={"Authorization": jwt})
|
||||
|
||||
|
||||
@gen_check
|
||||
def simulator_set_config(tags, jwt=config.default_jwt):
|
||||
return requests.post(url=config.BASE_URL + "/api/v2/simulator/config",
|
||||
headers={"Authorization": jwt},
|
||||
json={"tags": tags})
|
||||
|
||||
|
||||
def simulator_export(jwt=config.default_jwt):
|
||||
return requests.get(url=config.BASE_URL + "/api/v2/simulator/export",
|
||||
headers={"Authorization": jwt})
|
||||
|
||||
|
||||
def simulator_list_tags(jwt=config.default_jwt):
|
||||
return requests.get(url=config.BASE_URL + "/api/v2/simulator/tags",
|
||||
headers={"Authorization": jwt})
|
||||
@@ -38,7 +38,9 @@ def start_neuron_disable_auth(dir='build/'):
|
||||
process = subprocess.Popen(
|
||||
command, stderr=subprocess.PIPE, cwd=dir)
|
||||
time.sleep(1)
|
||||
assert process.poll() is None
|
||||
if process.poll() is not None:
|
||||
stderr = process.stderr.read().decode()
|
||||
assert False, "Neuron failed to start. Stderr: " + stderr
|
||||
return process
|
||||
|
||||
def start_neuron_debug(dir='build/'):
|
||||
@@ -46,7 +48,9 @@ def start_neuron_debug(dir='build/'):
|
||||
process = subprocess.Popen(
|
||||
command, stderr=subprocess.PIPE, cwd=dir)
|
||||
time.sleep(1)
|
||||
assert process.poll() is None
|
||||
if process.poll() is not None:
|
||||
stderr = process.stderr.read().decode()
|
||||
assert False, "Neuron failed to start. Stderr: " + stderr
|
||||
return process
|
||||
|
||||
def start_neuron_sub_filter_err(dir='build/'):
|
||||
@@ -54,14 +58,18 @@ def start_neuron_sub_filter_err(dir='build/'):
|
||||
process = subprocess.Popen(
|
||||
command, stderr=subprocess.PIPE, cwd=dir)
|
||||
time.sleep(1)
|
||||
assert process.poll() is None
|
||||
if process.poll() is not None:
|
||||
stderr = process.stderr.read().decode()
|
||||
assert False, "Neuron failed to start. Stderr: " + stderr
|
||||
return process
|
||||
|
||||
def start_neuron(dir='build/'):
|
||||
process = subprocess.Popen(
|
||||
['./neuron'], stderr=subprocess.PIPE, cwd=dir)
|
||||
time.sleep(1)
|
||||
assert process.poll() is None
|
||||
if process.poll() is not None:
|
||||
stderr = process.stderr.read().decode()
|
||||
assert False, "Neuron failed to start. Stderr: " + stderr
|
||||
return process
|
||||
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ class TestPlugin:
|
||||
assert 200 == response.status_code
|
||||
for plugin in response.json().get("plugins"):
|
||||
if plugin['name'] == 'c1':
|
||||
assert plugin['version'] == '2.12.99'
|
||||
assert plugin['version'] == '2.14.99'
|
||||
|
||||
@description(given="incorrect configuration", when="update plugin", then="fail")
|
||||
def test_10_update_plugin_fail3(self):
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
import socket
|
||||
import time
|
||||
|
||||
import neuron.api as api
|
||||
import neuron.error as error
|
||||
import neuron.config as config
|
||||
from neuron.common import *
|
||||
|
||||
|
||||
def _update_build_neuron_json(port):
|
||||
roots = [Path("build"), Path(__file__).parents[3] / "build"]
|
||||
for path in (r / s / "neuron.json" for r in roots for s in ["config", ""]):
|
||||
if path.exists():
|
||||
with path.open("r+", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
sim = data.get("modbus_simulator") or {}
|
||||
sim["port"] = int(port)
|
||||
if not sim.get("ip") or sim["ip"] == "0.0.0.0":
|
||||
sim["ip"] = "127.0.0.1"
|
||||
data["modbus_simulator"] = sim
|
||||
f.seek(0)
|
||||
json.dump(data, f, ensure_ascii=False, indent=1)
|
||||
f.truncate()
|
||||
return
|
||||
assert False, "neuron.json not found"
|
||||
|
||||
class TestSimulator:
|
||||
|
||||
def _check_status(self, running=True, port=None, tag_count=None):
|
||||
st = api.simulator_status().json()
|
||||
assert set(st.keys()) >= {"running", "port", "tag_count", "error"}
|
||||
assert st["error"] == 0
|
||||
assert st["running"] == (1 if running else 0)
|
||||
if port is not None:
|
||||
assert st["port"] == port
|
||||
if tag_count is not None:
|
||||
assert st["tag_count"] == tag_count
|
||||
return st
|
||||
|
||||
@description(given="load neuron.json",
|
||||
when="start and stop simulator",
|
||||
then="status reflects running and correct port")
|
||||
def test_start_stop_status(self, class_setup_and_teardown):
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(("127.0.0.1", 0))
|
||||
sim_port = s.getsockname()[1]
|
||||
_update_build_neuron_json(sim_port)
|
||||
class_setup_and_teardown.restart()
|
||||
|
||||
api.simulator_start()
|
||||
self._check_status(running=True, port=sim_port, tag_count=0)
|
||||
|
||||
api.simulator_stop()
|
||||
self._check_status(running=False, port=sim_port)
|
||||
|
||||
@description(given="set three tags using full name/address strings",
|
||||
when="list tags",
|
||||
then="returned tags")
|
||||
def test_set_config_and_list_tags(self, class_setup_and_teardown):
|
||||
api.simulator_start()
|
||||
|
||||
tags = [
|
||||
{"type": "sine", "name": "t1", "address": "1!400001"},
|
||||
{"type": "square", "name": "t3", "address": "1!400005"},
|
||||
{"type": "saw", "name": "t2", "address": "1!400010"},
|
||||
]
|
||||
api.simulator_set_config(tags)
|
||||
|
||||
lt = api.simulator_list_tags().json()
|
||||
assert "tags" in lt
|
||||
assert isinstance(lt["tags"], list) and len(lt["tags"]) == 3
|
||||
names = [t["name"] for t in lt["tags"]]
|
||||
addrs = [t["address"] for t in lt["tags"]]
|
||||
types = [t["type"] for t in lt["tags"]]
|
||||
assert addrs == ["1!400001", "1!400005", "1!400010"]
|
||||
assert names == ["t1", "t3", "t2"]
|
||||
assert types == ["sine", "square", "saw"]
|
||||
|
||||
@description(given="simulator configured and running",
|
||||
when="export configuration",
|
||||
then="exports as attachment with expected structure and correct port")
|
||||
def test_export_attachment(self):
|
||||
st = api.simulator_status().json()
|
||||
port = st.get("port", 0)
|
||||
resp = api.simulator_export()
|
||||
assert 200 == resp.status_code
|
||||
dispo = resp.headers.get("Content-Disposition", "")
|
||||
assert "attachment;" in dispo
|
||||
assert "simulator_modbus_south.json" in dispo
|
||||
|
||||
data = json.loads(resp.text)
|
||||
assert "nodes" in data and isinstance(data["nodes"], list)
|
||||
assert len(data["nodes"]) >= 1
|
||||
node0 = data["nodes"][0]
|
||||
assert "plugin" in node0 and node0["plugin"] == "Modbus TCP"
|
||||
assert "params" in node0 and isinstance(node0["params"], dict)
|
||||
assert node0["params"].get("port") == port
|
||||
assert "groups" in node0 and isinstance(node0["groups"], list) and len(node0["groups"]) >= 1
|
||||
assert node0["groups"][0].get("group") == "group1"
|
||||
|
||||
@description(given="simulator started and tags configured",
|
||||
when="restart neuron with new port in config",
|
||||
then="enabled state and tags restored from DB, port updated from neuron.json")
|
||||
def test_persistence_after_restart(self, class_setup_and_teardown):
|
||||
api.simulator_start()
|
||||
tags = [{"type": "sine", "name": "p1", "address": "1!400001"},
|
||||
{"type": "sine", "name": "p2", "address": "1!400011"}]
|
||||
api.simulator_set_config(tags)
|
||||
|
||||
st_before = self._check_status(running=True, tag_count=2)
|
||||
port_before = st_before["port"]
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(("127.0.0.1", 0))
|
||||
new_port = s.getsockname()[1]
|
||||
if new_port == port_before:
|
||||
new_port += 1
|
||||
|
||||
_update_build_neuron_json(new_port)
|
||||
|
||||
class_setup_and_teardown.restart()
|
||||
|
||||
self._check_status(running=True, port=new_port, tag_count=2)
|
||||
|
||||
lt = api.simulator_list_tags().json()
|
||||
assert len(lt["tags"]) == 2
|
||||
assert lt["tags"][0]["name"] == "p1"
|
||||
assert lt["tags"][1]["name"] == "p2"
|
||||
@@ -63,7 +63,7 @@ static const neu_plugin_intf_funs_t plugin_intf_funs = {
|
||||
};
|
||||
|
||||
const neu_plugin_module_t neu_plugin_module = {
|
||||
.version = NEU_VERSION(2, 12, 99),
|
||||
.version = NEU_VERSION(2, 14, 99),
|
||||
.schema = "c1",
|
||||
.module_name = "c1",
|
||||
.module_descr = "c1 v2",
|
||||
|
||||
Reference in New Issue
Block a user