Compare commits

...
95 Commits
Author SHA1 Message Date
fengzeroz ad5c49440e Merge pull request #2691 from fengzeroz/v2.14
version: 2.14.0
2026-02-06 09:58:08 +08:00
fengzero 92a854531a version: 2.14.0 2026-02-06 01:56:08 +00:00
fengzeroz 1323df30af Merge pull request #2690 from fengzeroz/v2.14
V2.14
2026-02-03 13:43:25 +08:00
fengzero c231e3d928 rm mqtt db cache 2026-02-03 05:43:09 +00:00
fengzero 11f721dc30 reuse tcp server port 2026-02-03 03:22:43 +00:00
fengzero 8fc11f5ded fix: import apps crash && import empty tags 2026-02-03 03:02:04 +00:00
fengzeroz ba418c75f7 Merge pull request #2688 from fengzeroz/v2.14
fix: import apps crash
2026-01-29 16:03:12 +08:00
fengzero e1fc4a93f8 fix: import apps crash 2026-01-29 08:02:51 +00:00
fengzeroz f762721af9 Merge pull request #2686 from fengzeroz/v2.14
version: 2.14.0-beta2
2026-01-28 09:53:34 +08:00
fengzero 7fb5e65879 version: 2.14.0-beta2 2026-01-28 01:52:47 +00:00
fengzeroz 395985c7fb Merge pull request #2684 from fengzeroz/v2.14
modify default log config
2026-01-27 18:06:38 +08:00
fengzero e05b18a275 modify default log config 2026-01-27 09:59:12 +00:00
fengzeroz 82beb88535 Merge pull request #2680 from fengzeroz/v2.14
core(cache): update_im_f_m, filter dup value
2026-01-27 17:32:02 +08:00
fengzero ef25effa10 core(cache): update_im_f_m, filter dup value 2026-01-27 09:31:39 +00:00
fengzeroz 805935f04a Merge pull request #2679 from fengzeroz/v2.14
V2.14
2026-01-27 10:26:29 +08:00
fengzero 6d92e250ed simulator: close fd & exit when recv signal 2026-01-27 02:16:36 +00:00
fengzero f1ef251b33 fix import tags in defalut node 2026-01-26 03:31:13 +00:00
fengzeroz 097bf9fdc7 Merge pull request #2677 from fengzeroz/v2.14
version: 2.14.0-beta1
2026-01-23 13:56:27 +08:00
fengzero d3528ceca0 version: 2.14.0-beta1 2026-01-23 05:55:43 +00:00
fengzeroz 1bda4e0d69 Merge pull request #2676 from fengzeroz/v2.14
simulator: close fd
2026-01-23 13:54:48 +08:00
fengzero b8a9eb33fa export app: include static tags 2026-01-23 02:42:17 +00:00
fengzero 9346403bb0 modbus tag address range 2026-01-22 06:03:57 +00:00
fengzero 7570e01f00 simulator: close fd 2026-01-22 03:39:41 +00:00
fengzeroz 1219ef55b2 Merge pull request #2673 from fengzeroz/v2.14
node tags: filter spec symbol
2026-01-21 11:07:49 +08:00
fengzero f938736979 node tags: filter spec symbol 2026-01-21 02:54:06 +00:00
fengzeroz 622d2ee4e3 Merge pull request #2674 from ZhouBox/v2.14
[v2.14]memery leak
2026-01-21 10:52:14 +08:00
xiang.zhou 3c35e5822c memery leak 2026-01-21 10:38:56 +08:00
fengzeroz a10d50199f Merge pull request #2670 from fengzeroz/v2.14
fix query by tags
2026-01-20 11:38:07 +08:00
fengzero 733684b6a2 fix query by tags 2026-01-20 03:26:25 +00:00
fengzeroz e41328495b Merge pull request #2669 from fengzeroz/v2.14
fix query by tags
2026-01-20 10:44:58 +08:00
fengzero 643d3d6ce0 fix query by tags 2026-01-20 02:33:37 +00:00
fengzeroz 5c5049793a Merge pull request #2668 from fengzeroz/v2.14
get nodes: rm query tags
2026-01-19 17:00:15 +08:00
fengzero 392a2134a6 get nodes: rm query tags 2026-01-19 08:02:16 +00:00
fengzeroz 1c165c6be0 Merge pull request #2662 from fengzeroz/v2.14
write tag log
2026-01-19 14:51:01 +08:00
fengzero 9f5c027bde write tag log 2026-01-19 06:41:35 +00:00
fengzeroz 7e67d7828e Merge pull request #2661 from fengzeroz/v2.14
get nodes by tags
2026-01-19 14:17:43 +08:00
fengzero 15d329c473 get nodes by tags 2026-01-19 06:07:50 +00:00
fengzeroz af1cb1feb5 Merge pull request #2660 from fengzeroz/v2.14
nodes: support tags
2026-01-19 13:58:36 +08:00
fengzero d58b6cb871 nodes: support tags 2026-01-19 03:37:10 +00:00
fengzeroz 97084c8255 Merge pull request #2659 from fengzeroz/v2.14
branch v2.14
2026-01-13 14:20:28 +08:00
fengzero a6fbfc0330 branch v2.14 2026-01-13 03:42:33 +00:00
fengzeroz 96ccd6cb72 Merge pull request #2658 from fengzeroz/v2.14
branch v2.14
2026-01-13 11:38:48 +08:00
fengzero ca2d8af6e8 branch v2.14 2026-01-13 03:38:08 +00:00
fengzeroz 3bb42f900f Merge pull request #2655 from fengzeroz/main
cid name len
2026-01-06 14:31:07 +08:00
fengzero bbda93038c cid name len 2026-01-06 06:25:20 +00:00
fengzeroz 9a5c1862b8 Merge pull request #2652 from hxy7yx/main-2
chore:update key from nodes to apps
2025-12-26 10:15:32 +08:00
hxy7yx 0652bbe00b chore:update key from nodes to apps 2025-12-25 18:04:31 -08:00
fengzeroz 6c088356f6 Merge pull request #2648 from hxy7yx/main-2
feat:export&import apps
2025-12-25 10:09:46 +08:00
fengzeroz 7441908f3a Merge pull request #2651 from fengzeroz/main
fix(group): free group context with old group
2025-12-25 10:08:54 +08:00
hxy7yx 0a32d72fcd feat:export&import apps 2025-12-24 18:06:39 -08:00
fengzero bda5ef1b2e fix(group): free group context with old group 2025-12-25 02:03:55 +00:00
fengzeroz 6b47dc6a4a Merge pull request #2645 from fengzeroz/main
update readme
2025-12-24 15:17:39 +08:00
fengzero 16ad3f6873 update readme 2025-12-24 06:28:16 +00:00
fengzeroz 63f9a0eb9c Merge pull request #2644 from fengzeroz/main
readme: remove cn
2025-12-24 14:18:44 +08:00
fengzeroz 942fcf8663 Merge pull request #2643 from hxy7yx/main-2
feat:export&import apps
2025-12-24 14:18:12 +08:00
fengzero 05b6d6be5a readme: about NeuronEX 2025-12-24 06:16:31 +00:00
fengzero 6c34076976 readme: remove cn 2025-12-24 05:50:43 +00:00
hxy7yx 96ebfa4034 feat:export&import apps 2025-12-21 18:57:19 -08:00
fengzeroz 2ca52aecec Merge pull request #2642 from fengzeroz/main
fix(connection): close fd if ip invalid
2025-12-19 15:08:56 +08:00
fengzero 989421f000 fix(connection): close fd if ip invalid 2025-12-19 07:00:57 +00:00
fengzeroz 88e5fc85b7 Merge pull request #2632 from fengzeroz/main
cid: parse float array
2025-12-12 17:31:09 +08:00
fengzeroz 14b55309e4 Merge pull request #2631 from hxy7yx/main-1
fix(simulator):increase update frequency
2025-12-12 17:30:57 +08:00
fengzero f761bb9c85 cid: parse float array 2025-12-12 03:37:46 +00:00
hxy7yx c38ae03b2b fix(simulator):increase update frequency 2025-12-11 17:57:47 -08:00
fengzeroz b522583f51 Merge pull request #2630 from fengzeroz/main
cid: parse VisString65
2025-12-09 16:06:19 +08:00
fengzero 9f2b04300f cid: parse VisString65 2025-12-09 07:00:25 +00:00
fengzeroz d94afeedf6 Merge pull request #2623 from ZhouBox/main
fixbug:double free about json obj
2025-12-04 17:22:12 +08:00
xiang.zhou ffe0316e5f fixbug:double free about json obj 2025-12-04 15:20:34 +08:00
fengzeroz 7457543471 Merge pull request #2622 from fengzeroz/main
event log
2025-11-28 13:47:29 +08:00
fengzero 82615c0908 event: log category 2025-11-28 03:00:12 +00:00
fengzero 584732fe8c rm unused node cache sql 2025-11-26 02:55:41 +00:00
fengzeroz 8c11f83540 Merge pull request #2621 from hxy7yx/main-1
feat:add modbus-tcp simulator
2025-11-26 10:55:28 +08:00
hxy7yx c9bf291fa2 feat:add modbus-tcp simulator 2025-11-24 02:09:43 -08:00
fengzeroz ae959acc52 Merge pull request #2617 from ZhouBox/main
ekuiper:add warn log
2025-11-20 10:01:53 +08:00
xiang.zhou d1b3838695 ekuiper:add warn log 2025-11-19 16:37:11 +08:00
fengzeroz 159be9f511 Merge pull request #2614 from hxy7yx/main-2
add notice log for write tag/tags/gtags
2025-11-19 14:39:06 +08:00
hxy7yx e07e5ad2e8 add notice log for write tag/tags/tags 2025-11-18 22:24:31 -08:00
fengzeroz 68c9dc0b0e Merge pull request #2610 from ZhouBox/main
ekuiper:add log
2025-11-19 11:04:15 +08:00
xiang.zhou ef859090bb ekuiper:add log 2025-11-19 10:28:25 +08:00
fengzeroz 04defee4da Merge pull request #2605 from hxy7yx/main-1
add info logging for driver write tags
2025-11-12 10:17:44 +08:00
hxy7yx 811ba04408 add info logging for driver write tags 2025-11-10 18:12:12 -08:00
fengzeroz 1dc53e3ff1 Merge pull request #2603 from ZhouBox/main
reporting incomplete trace when timeout
2025-11-06 10:03:12 +08:00
xiang.zhou 14326c8da1 reporting incomplete trace when timeout 2025-11-04 15:04:13 +08:00
fengzeroz d0f7335c2b Merge pull request #2596 from hxy7yx/main-1
fix msg_internal.h
2025-10-22 10:01:53 +08:00
hxy7yx 157618269f fix msg_internal.h 2025-10-21 18:39:50 -07:00
fengzeroz c8f5feb28e Merge pull request #2591 from ZhouBox/main
1.update cert schema 2.add datatag when sub app frist sending
2025-10-22 09:31:46 +08:00
fengzeroz acd9f87ee8 Merge pull request #2595 from fengzeroz/main
export db: skip load failed data
2025-10-21 16:47:10 +08:00
xiang.zhou 763783b4d9 add index 2025-10-21 16:18:36 +08:00
fengzero 4d42b2ab16 export db: skip load failed data 2025-10-21 07:31:49 +00:00
xiang.zhou 472044a2a3 1.update cert schema 2.add datatag when sub app frist sending 2025-10-21 14:48:50 +08:00
fengzeroz 87817e080d Merge pull request #2589 from hxy7yx/main-1
fix(log):global config
2025-10-21 13:34:58 +08:00
hxy7yx 65b87e37ff fix(log):global config 2025-10-20 19:38:19 -07:00
fengzeroz 6c8b684f80 Merge pull request #2581 from fengzeroz/main
fix: arm32 compile error
2025-10-20 14:53:57 +08:00
fengzero 161172632c replace tar with zip db data 2025-10-20 03:29:29 +00:00
fengzero 84ecdde3d4 fix: arm32 compile error 2025-10-17 06:38:42 +00:00
75 changed files with 4076 additions and 422 deletions
+4 -4
View File
@@ -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:
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -1,104 +0,0 @@
# Neuron
[![GitHub Release](https://img.shields.io/github/release/neugates/neuron?color=brightgreen&label=Release)](https://github.com/neugates/neuron/releases)
[![Docker Pulls](https://img.shields.io/docker/pulls/emqx/neuron?label=Docker%20Pulls)](https://hub.docker.com/r/emqx/neuron)
[![Slack](https://img.shields.io/badge/Slack-Neuron-39AE85?logo=slack)](https://slack-invite.emqx.io/)
[![Discord](https://img.shields.io/discord/931086341838622751?label=Discord&logo=discord)](https://discord.gg/xYGf3fQnES)
[![Twitter](https://img.shields.io/badge/Follow-EMQ-1DA1F2?logo=twitter)](https://twitter.com/EMQTech)
[![YouTube](https://img.shields.io/badge/Subscribe-EMQ-FF0000?logo=youtube)](https://www.youtube.com/channel/UC5FjR77ErAxvZENEWzQaO5Q)
[English](https://github.com/emqx/neuron/blob/main/README.md) | 简体中文
Neuron 是一款开源的、轻量级工业协议网关软件,支持数十种工业协议的一站式设备连接、数据接入、MQTT 协议转换,为工业设备赋予工业 4.0 时代关键的物联网连接能力。
![neuron-overview](docs/pictures/neuron-final.png)
以下是 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)。
+69 -30
View File
@@ -8,33 +8,54 @@
[![Twitter](https://img.shields.io/badge/Follow-EMQ-1DA1F2?logo=twitter)](https://twitter.com/EMQTech)
[![YouTube](https://img.shields.io/badge/Subscribe-EMQ-FF0000?logo=youtube)](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.
![neuron-overview](docs/pictures/neuron-final.png)
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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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++)
+1 -2
View File
@@ -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
-89
View File
@@ -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 默认的防火墙拦截,确保防火墙能开放此接口或关闭防火墙。
![modbus-simulator](./assets/modbus-simulator.png)
## Neuron 中创建南向设备
### 创建设备
**南向设备管理** -> **添加设备** 在弹出的对话框中填写设备信息后创建设备,**插件** 选择 `Modbus TCP`,**名称** 填写 `modbus-tcp-1` 即可。
### 配置设备
创建设备后,进入设备详情页面配置设备参数。
**IP 地址**: 填写 Modbus TCP 模拟器运行机器的 IP 地址。如果 Neuron 与模拟器运行在同一台机器上,可以填写 `127.0.0.1`。
**端口**: 默认502,根据模拟器的监听端口进行修改。
其他参数默认即可。
### 配置数据点
在 **南向设备管理** 页面,点击 **设备名称** 进入点位组页面,添加一个点位组,名称填写 `group1` 即可。
进入点位组后,点击 **组名称** 进入点位配置页面,创建点位。
![add-tag](./assets/tag-list-null.png)
**名称**: `tag-1`
**地址**: `1!40001` ( Slave ID 1, 保持寄存器40001 )
**属性**: `Read` 与 `Write`
**数据类型**: `INT16`
### 查看点位值
配置完点位后,进入 **数据监控** 页面,选择刚才创建的设备以及点位组,即可看到点位的值。
![data-monitor](./assets/data-monitoring.png)
## 创建北向应用
使用 MQTT,把刚才 modbus 设备中采集到的数据发送到 MQTT Broker。
**北向应用管理** -> **添加应用**,在弹出的对话框中填写应用信息创建应用。
**插件** 选择 `MQTT`,**名称** 填写 `mqtt` 即可。
### 配置北向应用
创建北向应用后,进入北向配置页面,默认服务器地址为 `broker.emqx.io`,端口为 `1883`,如果需要连接其他 MQTT Broker,请根据实际情况修改。
### 订阅南向数据组
在**北向应用管理**页面中,点击北向应用,进入**订阅管理**页面,点击**添加订阅**,在弹出的对话框中选择刚才创建的 Modbus 设备以及点位组。
![subscribe](./assets/subscription-add.png)
记住订阅时的 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 修改),即可看到点位数据。
![mqttx](./assets/mqttx.png)
+3
View File
@@ -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
+4 -3
View File
@@ -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
+4
View File
@@ -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,
+58
View File
@@ -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
View File
@@ -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;
+5
View File
@@ -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.
+9
View File
@@ -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
View File
@@ -4,5 +4,6 @@
"disable_auth": 0,
"syslog_host": "",
"syslog_port": 541,
"sub_filter_error": 0
}
"sub_filter_error": 0,
"modbus_simulator": { "port":502 }
}
-36
View File
@@ -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;
+2 -1
View File
@@ -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;
+13
View File
@@ -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) {
+4
View File
@@ -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) {
+10 -6
View File
@@ -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");
+2
View File
@@ -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);
}
+92
View File
@@ -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);
});
}
}
})
}
+1
View File
@@ -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
+733 -3
View File
@@ -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, &copy);
}
}
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)
{
+3
View File
@@ -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);
+76
View File
@@ -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)
+8 -25
View File
@@ -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);
}
+217
View File
@@ -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);
}
+13
View File
@@ -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
+6
View File
@@ -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(&param, NULL, connected, disconnected);
while (conn_tcp_server_is_listen(conn) == false) {
neu_msleep(1000);
neu_conn_destory(conn);
conn = neu_conn_new(&param, NULL, connected, disconnected);
}
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
File diff suppressed because it is too large Load Diff
+75 -3
View File
@@ -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,
+1 -1
View File
@@ -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
View File
@@ -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;
+6
View File
@@ -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) \
+9 -1
View File
@@ -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
View File
@@ -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);
+4 -3
View File
@@ -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;
}
+2 -1
View File
@@ -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
View File
@@ -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;
+4 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+9
View File
@@ -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
View File
@@ -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);
+278
View File
@@ -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);
}
}
+36
View File
@@ -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
View File
@@ -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);
}
}
+12
View File
@@ -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 {
+21 -1
View File
@@ -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);
+5
View File
@@ -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);
+2
View File
@@ -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
View File
@@ -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)
{
+3
View File
@@ -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
View File
@@ -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;
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+224
View File
@@ -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)
+2
View File
@@ -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,
+44
View File
@@ -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})
+12 -4
View File
@@ -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
+1 -1
View File
@@ -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):
+1
View File
@@ -0,0 +1 @@
+130
View File
@@ -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"
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -1 +1 @@
2.12.0-alpha
2.14.0
+1 -2
View File
@@ -9,5 +9,4 @@ 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"; simple
*.* $remote_syslog,"%V"; syslog
*.* "./logs/%c.log", 50MB * 2 ~ "./logs/%c.#2r.log"; simple