mirror of
https://github.com/emqx/neuron.git
synced 2026-10-21 00:36:23 +08:00
+3
-1
@@ -95,7 +95,8 @@ set(NEURON_BASE_SOURCES
|
||||
src/utils/base64.c
|
||||
src/utils/async_queue.c
|
||||
src/utils/log.c
|
||||
src/utils/cid.c
|
||||
src/utils/cid.c
|
||||
src/utils/tpy.c
|
||||
${PERSIST_SOURCES})
|
||||
|
||||
if (SMART_LINK)
|
||||
@@ -163,6 +164,7 @@ set(NEURON_SOURCES
|
||||
plugins/restful/scan_handle.c
|
||||
plugins/restful/otel_handle.c
|
||||
plugins/restful/cid_handle.c
|
||||
plugins/restful/tpy_handle.c
|
||||
plugins/restful/system_handle.c
|
||||
plugins/restful/datalayers_handle.c
|
||||
plugins/restful/rest.c
|
||||
|
||||
@@ -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_UTILS_TPY_H_
|
||||
#define _NEU_UTILS_TPY_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "define.h"
|
||||
#include "msg.h"
|
||||
|
||||
typedef struct {
|
||||
int64_t igroup;
|
||||
int64_t ioffset;
|
||||
char name[NEU_TAG_NAME_LEN];
|
||||
char type[NEU_TAG_NAME_LEN];
|
||||
} tpy_var_t;
|
||||
|
||||
typedef struct {
|
||||
char name[NEU_CID_IED_NAME_LEN];
|
||||
|
||||
int n_vars;
|
||||
tpy_var_t *vars;
|
||||
} tpy_static_t;
|
||||
|
||||
typedef struct {
|
||||
int n_statics;
|
||||
tpy_static_t *statics;
|
||||
} tpy_t;
|
||||
|
||||
int neu_tpy_parse(const char *path, tpy_t *tpy);
|
||||
void neu_tpy_free(tpy_t *tpy);
|
||||
|
||||
void neu_tpy_to_msg(char *driver, tpy_t *tpy, neu_req_add_gtag_t *cmd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "rw_handle.h"
|
||||
#include "scan_handle.h"
|
||||
#include "system_handle.h"
|
||||
#include "tpy_handle.h"
|
||||
#include "utils/http.h"
|
||||
#include "version_handle.h"
|
||||
|
||||
@@ -143,6 +144,9 @@ static struct neu_http_handler cors_handler[] = {
|
||||
{
|
||||
.url = "/api/v2/cid",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/tpy",
|
||||
},
|
||||
{
|
||||
.url = "/api/v2/system/ctl",
|
||||
},
|
||||
@@ -179,6 +183,12 @@ static struct neu_http_handler rest_handlers[] = {
|
||||
.url = "/api/v2/cid",
|
||||
.value.handler = handle_cid,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_POST,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
.url = "/api/v2/tpy",
|
||||
.value.handler = handle_tpy,
|
||||
},
|
||||
{
|
||||
.method = NEU_HTTP_METHOD_POST,
|
||||
.type = NEU_HTTP_HANDLER_FUNCTION,
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <nng/nng.h>
|
||||
#include <nng/supplemental/http/http.h>
|
||||
|
||||
#include "define.h"
|
||||
#include "errcodes.h"
|
||||
#include "handle.h"
|
||||
#include "utils/http.h"
|
||||
#include "utils/log.h"
|
||||
#include "utils/tpy.h"
|
||||
#include "json/neu_json_error.h"
|
||||
#include "json/neu_json_fn.h"
|
||||
|
||||
#include "parser/neu_json_cid.h"
|
||||
|
||||
void handle_tpy(nng_aio *aio)
|
||||
{
|
||||
neu_plugin_t *plugin = neu_rest_get_plugin();
|
||||
(void) plugin;
|
||||
|
||||
NEU_PROCESS_HTTP_REQUEST(
|
||||
aio, neu_json_upload_cid_t, neu_json_decode_upload_cid_req, {
|
||||
tpy_t tpy = { 0 };
|
||||
int parse_re = neu_tpy_parse(req->path, &tpy);
|
||||
if (parse_re == 0) {
|
||||
neu_reqresp_head_t header = { 0 };
|
||||
neu_req_add_gtag_t cmd = { 0 };
|
||||
header.ctx = aio;
|
||||
header.type = NEU_REQ_ADD_GTAG;
|
||||
header.otel_trace_type = NEU_OTEL_TRACE_TYPE_REST_COMM;
|
||||
|
||||
neu_tpy_to_msg(req->driver, &tpy, &cmd);
|
||||
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);
|
||||
});
|
||||
}
|
||||
neu_tpy_free(&tpy);
|
||||
} else {
|
||||
NEU_JSON_RESPONSE_ERROR(NEU_ERR_INVALID_CID, {
|
||||
neu_http_response(aio, NEU_ERR_INVALID_CID, result_error);
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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_PLUGIN_REST_TPY_HANDLE_H_
|
||||
#define _NEU_PLUGIN_REST_TPY_HANDLE_H_
|
||||
|
||||
#include <nng/nng.h>
|
||||
|
||||
void handle_tpy(nng_aio *aio);
|
||||
|
||||
#endif
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* NEURON IIoT System for Industry 4.0
|
||||
* Copyright (C) 2020-2022 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.
|
||||
**/
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
#include "utils/log.h"
|
||||
#include "utils/tpy.h"
|
||||
|
||||
static int parse_vars(xmlNode *xml_ied, tpy_t *tpy);
|
||||
|
||||
int neu_tpy_parse(const char *path, tpy_t *tpy)
|
||||
{
|
||||
memset(tpy, 0, sizeof(tpy_t));
|
||||
xmlDoc *doc = xmlReadFile(path, NULL, 0);
|
||||
if (doc == NULL) {
|
||||
nlog_warn("Failed to read tpy file %s", path);
|
||||
return -1;
|
||||
}
|
||||
xmlNode *root = xmlDocGetRootElement(doc);
|
||||
if (root == NULL ||
|
||||
strcmp((const char *) root->name, "PlcProjectInfo") != 0) {
|
||||
nlog_warn("Failed to get root element(PlcProjectInfo)");
|
||||
xmlFreeDoc(doc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xmlNode *find = root->children;
|
||||
bool vars_parsed = false;
|
||||
|
||||
while (find != NULL) {
|
||||
if (find->type == XML_ELEMENT_NODE &&
|
||||
strcmp((const char *) find->name, "VarConfigs") == 0) {
|
||||
int ret = parse_vars(find, tpy);
|
||||
if (ret == 0) {
|
||||
vars_parsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
find = find->next;
|
||||
}
|
||||
|
||||
if (vars_parsed) {
|
||||
xmlFreeDoc(doc);
|
||||
return 0;
|
||||
} else {
|
||||
neu_tpy_free(tpy);
|
||||
nlog_warn("Failed to parse VarConfigs");
|
||||
xmlFreeDoc(doc);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void neu_tpy_free(tpy_t *tpy)
|
||||
{
|
||||
for (int i = 0; i < tpy->n_statics; i++) {
|
||||
if (tpy->statics[i].n_vars > 0) {
|
||||
free(tpy->statics[i].vars);
|
||||
}
|
||||
}
|
||||
|
||||
if (tpy->n_statics > 0) {
|
||||
free(tpy->statics);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_vars(xmlNode *xml_ied, tpy_t *tpy)
|
||||
{
|
||||
xmlNode *var = xml_ied->children;
|
||||
while (var != NULL) {
|
||||
if (var->type == XML_ELEMENT_NODE &&
|
||||
strcmp((const char *) var->name, "VarConfig") == 0) {
|
||||
xmlNode * attr = var->children;
|
||||
tpy_var_t tpy_var = { 0 };
|
||||
char static_name[NEU_GROUP_NAME_LEN] = { 0 };
|
||||
char igroup_str[32] = { 0 };
|
||||
char ioffset_str[32] = { 0 };
|
||||
|
||||
while (attr != NULL) {
|
||||
if (attr->type == XML_ELEMENT_NODE) {
|
||||
char *content = (char *) xmlNodeGetContent(attr);
|
||||
if (strcmp((const char *) attr->name, "Name") == 0) {
|
||||
char *name_static = (char *) xmlGetProp(
|
||||
attr, (const xmlChar *) "Static");
|
||||
char *name_task = (char *) xmlGetProp(
|
||||
attr, (const xmlChar *) "TaskPrio");
|
||||
|
||||
snprintf(static_name, sizeof(static_name), "%s_%s",
|
||||
name_static, name_task);
|
||||
strncpy(tpy_var.name, content,
|
||||
sizeof(tpy_var.name) - 1);
|
||||
xmlFree(name_static);
|
||||
xmlFree(name_task);
|
||||
} else if (strcmp((const char *) attr->name, "Type") == 0) {
|
||||
strncpy(tpy_var.type, content,
|
||||
sizeof(tpy_var.type) - 1);
|
||||
} else if (strcmp((const char *) attr->name, "IGroup") ==
|
||||
0) {
|
||||
strncpy(igroup_str, content, sizeof(igroup_str) - 1);
|
||||
tpy_var.igroup = atoll(igroup_str);
|
||||
} else if (strcmp((const char *) attr->name, "IOffset") ==
|
||||
0) {
|
||||
strncpy(ioffset_str, content, sizeof(ioffset_str) - 1);
|
||||
tpy_var.ioffset = atoll(ioffset_str);
|
||||
}
|
||||
|
||||
if (content != NULL) {
|
||||
xmlFree(content);
|
||||
}
|
||||
}
|
||||
attr = attr->next;
|
||||
}
|
||||
|
||||
if (strlen(tpy_var.name) > 0 && strlen(tpy_var.type) > 0) {
|
||||
int found = 0;
|
||||
for (int i = 0; i < tpy->n_statics; i++) {
|
||||
if (strcmp(tpy->statics[i].name, static_name) == 0) {
|
||||
found = 1;
|
||||
tpy->statics[i].n_vars += 1;
|
||||
tpy->statics[i].vars =
|
||||
realloc(tpy->statics[i].vars,
|
||||
tpy->statics[i].n_vars * sizeof(tpy_var_t));
|
||||
tpy->statics[i].vars[tpy->statics[i].n_vars - 1] =
|
||||
tpy_var;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
tpy->n_statics += 1;
|
||||
tpy->statics = realloc(
|
||||
tpy->statics, tpy->n_statics * sizeof(tpy_static_t));
|
||||
tpy->statics[tpy->n_statics - 1].n_vars = 1;
|
||||
strncpy(tpy->statics[tpy->n_statics - 1].name, static_name,
|
||||
sizeof(tpy->statics[tpy->n_statics - 1].name) - 1);
|
||||
tpy->statics[tpy->n_statics - 1].vars =
|
||||
malloc(sizeof(tpy_var_t));
|
||||
tpy->statics[tpy->n_statics - 1].vars[0] = tpy_var;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var = var->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static neu_type_e to_neu_type(char *type)
|
||||
{
|
||||
if (strcmp(type, "BOOL") == 0) {
|
||||
return NEU_TYPE_BOOL;
|
||||
} else if (strcmp(type, "WORD") == 0) {
|
||||
return NEU_TYPE_UINT16;
|
||||
} else if (strcmp(type, "INT") == 0) {
|
||||
return NEU_TYPE_INT16;
|
||||
} else if (strcmp(type, "USINT") == 0) {
|
||||
return NEU_TYPE_UINT8;
|
||||
} else if (strcmp(type, "BYTE") == 0) {
|
||||
return NEU_TYPE_UINT8;
|
||||
} else if (strcmp(type, "SINT") == 0) {
|
||||
return NEU_TYPE_INT8;
|
||||
} else if (strcmp(type, "UINT") == 0) {
|
||||
return NEU_TYPE_UINT16;
|
||||
} else if (strcmp(type, "UDINT") == 0) {
|
||||
return NEU_TYPE_UINT32;
|
||||
} else if (strcmp(type, "DINT") == 0) {
|
||||
return NEU_TYPE_INT32;
|
||||
} else if (strcmp(type, "DWORD") == 0) {
|
||||
return NEU_TYPE_UINT32;
|
||||
} else if (strcmp(type, "REAL") == 0) {
|
||||
return NEU_TYPE_FLOAT;
|
||||
} else if (strcmp(type, "LREAL") == 0) {
|
||||
return NEU_TYPE_DOUBLE;
|
||||
} else {
|
||||
return NEU_TYPE_UINT8;
|
||||
}
|
||||
}
|
||||
|
||||
void neu_tpy_to_msg(char *driver, tpy_t *tpy, neu_req_add_gtag_t *cmd)
|
||||
{
|
||||
strcpy(cmd->driver, driver);
|
||||
cmd->n_group = tpy->n_statics;
|
||||
cmd->groups = calloc(cmd->n_group, sizeof(neu_gdatatag_t));
|
||||
|
||||
for (int i = 0; i < cmd->n_group; i++) {
|
||||
strcpy(cmd->groups[i].group, tpy->statics[i].name);
|
||||
cmd->groups[i].interval = 1000;
|
||||
cmd->groups[i].n_tag = tpy->statics[i].n_vars;
|
||||
cmd->groups[i].tags =
|
||||
calloc(cmd->groups[i].n_tag, sizeof(neu_datatag_t));
|
||||
for (int k = 0; k < cmd->groups[i].n_tag; k++) {
|
||||
char address[128] = { 0 };
|
||||
cmd->groups[i].tags[k].name = strdup(tpy->statics[i].vars[k].name);
|
||||
cmd->groups[i].tags[k].type =
|
||||
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",
|
||||
tpy->statics[i].vars[k].igroup,
|
||||
tpy->statics[i].vars[k].ioffset);
|
||||
cmd->groups[i].tags[k].address = strdup(address);
|
||||
cmd->groups[i].tags[k].description = strdup("");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user