From 93bd66323b20a437e8f742aa747c432292ea7b1d Mon Sep 17 00:00:00 2001 From: fengzero Date: Mon, 22 Sep 2025 03:21:55 +0000 Subject: [PATCH] api: import tpy file --- CMakeLists.txt | 4 +- include/neuron/utils/tpy.h | 58 +++++++++ plugins/restful/handle.c | 10 ++ plugins/restful/tpy_handle.c | 45 +++++++ plugins/restful/tpy_handle.h | 26 +++++ src/utils/tpy.c | 221 +++++++++++++++++++++++++++++++++++ 6 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 include/neuron/utils/tpy.h create mode 100644 plugins/restful/tpy_handle.c create mode 100644 plugins/restful/tpy_handle.h create mode 100644 src/utils/tpy.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 400e823b..e57f3d43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/include/neuron/utils/tpy.h b/include/neuron/utils/tpy.h new file mode 100644 index 00000000..3115f84f --- /dev/null +++ b/include/neuron/utils/tpy.h @@ -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 \ No newline at end of file diff --git a/plugins/restful/handle.c b/plugins/restful/handle.c index f124fdb9..53139908 100644 --- a/plugins/restful/handle.c +++ b/plugins/restful/handle.c @@ -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, diff --git a/plugins/restful/tpy_handle.c b/plugins/restful/tpy_handle.c new file mode 100644 index 00000000..ebf1683e --- /dev/null +++ b/plugins/restful/tpy_handle.c @@ -0,0 +1,45 @@ +#include +#include + +#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); + }); + } + }) +} \ No newline at end of file diff --git a/plugins/restful/tpy_handle.h b/plugins/restful/tpy_handle.h new file mode 100644 index 00000000..ce28a07d --- /dev/null +++ b/plugins/restful/tpy_handle.h @@ -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 + +void handle_tpy(nng_aio *aio); + +#endif \ No newline at end of file diff --git a/src/utils/tpy.c b/src/utils/tpy.c new file mode 100644 index 00000000..e6b35fca --- /dev/null +++ b/src/utils/tpy.c @@ -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 +#include + +#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(""); + } + } +} \ No newline at end of file