From ae30128dd306813c70bc5e9528a04dbca5a7f49e Mon Sep 17 00:00:00 2001 From: fengzero Date: Tue, 22 Oct 2024 08:53:22 +0000 Subject: [PATCH] cid: import and parse --- CMakeLists.txt | 9 +- include/neuron/define.h | 15 + include/neuron/errcodes.h | 1 + include/neuron/msg.h | 3 +- include/neuron/persist/persist.h | 7 +- include/neuron/plugin.h | 1 + include/neuron/utils/cid.h | 144 +++++ persistence/0090_2.11.0_tag_type.sql | 16 + persistence/0091_2.11.0_group.sql | 36 ++ persistence/0092_2.11.0_tag.sql | 60 ++ plugins/restful/cid_handle.c | 46 ++ plugins/restful/cid_handle.h | 26 + plugins/restful/handle.c | 10 + src/adapter/adapter.c | 13 +- src/adapter/driver/driver.c | 17 +- src/adapter/driver/driver_internal.h | 2 +- src/adapter/storage.c | 19 +- src/adapter/storage.h | 2 +- src/core/manager.c | 2 +- src/parser/neu_json_cid.c | 98 +++ src/parser/neu_json_cid.h | 48 ++ src/persist/persist.c | 5 +- src/persist/persist_impl.h | 3 +- src/persist/sqlite.c | 21 +- src/persist/sqlite.h | 3 +- src/utils/cid.c | 863 +++++++++++++++++++++++++++ src/utils/http.c | 1 + 27 files changed, 1441 insertions(+), 30 deletions(-) create mode 100644 include/neuron/utils/cid.h create mode 100644 persistence/0091_2.11.0_group.sql create mode 100644 persistence/0092_2.11.0_tag.sql create mode 100644 plugins/restful/cid_handle.c create mode 100644 plugins/restful/cid_handle.h create mode 100644 src/parser/neu_json_cid.c create mode 100644 src/parser/neu_json_cid.h create mode 100644 src/utils/cid.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c810463..65f2064f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,14 +54,17 @@ add_custom_target(neuron-version ${CMAKE_CURRENT_SOURCE_DIR}/cmake/neuron-version.cmake) find_package(nng CONFIG REQUIRED) +find_package(LibXml2 REQUIRED) find_package(Threads) if (CMAKE_STAGING_PREFIX) include_directories(${CMAKE_STAGING_PREFIX}/include) link_directories(${CMAKE_STAGING_PREFIX}/lib) + include_directories(${CMAKE_STAGING_PREFIX}/include/libxml2) else() include_directories(/usr/local/include) link_directories(/usr/local/lib) + include_directories(/usr/local/include/libxml2) endif() set(PERSIST_SOURCES @@ -90,6 +93,7 @@ set(NEURON_BASE_SOURCES src/utils/base64.c src/utils/async_queue.c src/utils/log.c + src/utils/cid.c ${PERSIST_SOURCES}) if (SMART_LINK) @@ -114,7 +118,7 @@ target_sources(neuron-base PRIVATE ${NEURON_BASE_SOURCES} ${NEURON_SRC_PARSE} ${ target_include_directories(neuron-base PRIVATE include/neuron src) target_link_libraries(neuron-base libssl.a libcrypto.a) -target_link_libraries(neuron-base nng libzlog.so jansson jwt +target_link_libraries(neuron-base nng libzlog.so jansson jwt xml2 ${CMAKE_THREAD_LIBS_INIT} -lm protobuf-c) add_dependencies(neuron-base neuron-version) @@ -150,6 +154,7 @@ set(NEURON_SOURCES plugins/restful/version_handle.c plugins/restful/scan_handle.c plugins/restful/otel_handle.c + plugins/restful/cid_handle.c plugins/restful/rest.c plugins/restful/user.c) @@ -157,7 +162,7 @@ set(CMAKE_BUILD_RPATH ./) add_executable(neuron) target_sources(neuron PRIVATE ${NEURON_SOURCES}) target_include_directories(neuron PRIVATE include/neuron src plugins) -target_link_libraries(neuron dl neuron-base sqlite3 -lm) +target_link_libraries(neuron dl neuron-base sqlite3 -lm xml2) target_link_options(neuron PRIVATE "LINKER:--dynamic-list-data") #copy file for run diff --git a/include/neuron/define.h b/include/neuron/define.h index a1240eda..7f43f355 100644 --- a/include/neuron/define.h +++ b/include/neuron/define.h @@ -62,6 +62,21 @@ #define NEU_LOG_LEVEL_ERROR "error" #define NEU_LOG_LEVEL_FATAL "fatal" +#define NEU_CID_LNCLASS_LEN 8 +#define NEU_CID_LNTYPE_LEN 32 +#define NEU_CID_IED_NAME_LEN 32 +#define NEU_CID_LDEVICE_LEN 32 +#define NEU_CID_LNO_NAME_LEN 32 +#define NEU_CID_LNO_TYPE_LEN 32 +#define NEU_CID_INST_LEN 32 +#define NEU_CID_SDI_NAME_LEN 16 +#define NEU_CID_DO_NAME_LEN 32 +#define NEU_CID_DO_ID_LEN 32 +#define NEU_CID_DA_NAME_LEN 32 +#define NEU_CID_DATASET_NAME_LEN 32 +#define NEU_CID_REPORT_NAME_LEN 32 +#define NEU_CID_REPORT_ID_LEN 64 + #define CHECK_NODE_NAME_LENGTH_ERR \ do { \ NEU_JSON_RESPONSE_ERROR(NEU_ERR_NODE_NAME_TOO_LONG, { \ diff --git a/include/neuron/errcodes.h b/include/neuron/errcodes.h index 2c351f0e..901303e6 100644 --- a/include/neuron/errcodes.h +++ b/include/neuron/errcodes.h @@ -48,6 +48,7 @@ typedef enum { NEU_ERR_IP_ADDRESS_INVALID = 1015, NEU_ERR_IP_ADDRESS_IN_USE = 1016, NEU_ERR_BODY_TOO_BIG = 1017, + NEU_ERR_INVALID_CID = 1018, NEU_ERR_NODE_EXIST = 2002, NEU_ERR_NODE_NOT_EXIST = 2003, diff --git a/include/neuron/msg.h b/include/neuron/msg.h index 1ebb772d..4b490f8a 100644 --- a/include/neuron/msg.h +++ b/include/neuron/msg.h @@ -352,9 +352,10 @@ typedef struct neu_resp_get_group { typedef struct { char group[NEU_GROUP_NAME_LEN]; - int n_tag; int interval; + int n_tag; neu_datatag_t *tags; + void * context; } neu_gdatatag_t; typedef struct { diff --git a/include/neuron/persist/persist.h b/include/neuron/persist/persist.h index 890c1562..831032f0 100644 --- a/include/neuron/persist/persist.h +++ b/include/neuron/persist/persist.h @@ -41,6 +41,7 @@ typedef struct { typedef struct { uint32_t interval; char * name; + char * context; } neu_persist_group_info_t; typedef struct { @@ -68,6 +69,9 @@ static inline void neu_persist_node_info_fini(neu_persist_node_info_t *info) static inline void neu_persist_group_info_fini(neu_persist_group_info_t *info) { free(info->name); + if (info->context) { + free(info->context); + } } static inline void @@ -255,7 +259,8 @@ int neu_persister_delete_subscription(const char *app_name, * @return 0 on success, non-zero otherwise */ int neu_persister_store_group(const char * driver_name, - neu_persist_group_info_t *group_info); + neu_persist_group_info_t *group_info, + const char * context); /** * Update group config. diff --git a/include/neuron/plugin.h b/include/neuron/plugin.h index 188cce56..85ff7ebf 100644 --- a/include/neuron/plugin.h +++ b/include/neuron/plugin.h @@ -67,6 +67,7 @@ struct neu_plugin_group { char * group_name; UT_array *tags; + void * context; void * user_data; neu_plugin_group_free group_free; uint32_t interval; diff --git a/include/neuron/utils/cid.h b/include/neuron/utils/cid.h new file mode 100644 index 00000000..b861c294 --- /dev/null +++ b/include/neuron/utils/cid.h @@ -0,0 +1,144 @@ +/** + * 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. + **/ + +#ifndef _NEU_UTILS_CID_H_ +#define _NEU_UTILS_CID_H_ + +#include "define.h" +#include "msg.h" + +typedef enum { + CO = 0, + ST = 1, // boolean + MX = 2, // float + UNKNOWN = 100, +} cid_fc_e; + +typedef struct { + char name[NEU_CID_DO_NAME_LEN]; + char lnclass[NEU_CID_LNCLASS_LEN]; + cid_fc_e fc; +} cid_do_t; + +typedef struct { + char name[NEU_CID_DATASET_NAME_LEN]; + cid_do_t *dos; + int n_dos; +} cid_dataset_t; + +typedef struct { + char name[NEU_CID_REPORT_NAME_LEN]; + char id[NEU_CID_REPORT_ID_LEN]; + char dataset[NEU_CID_DATASET_NAME_LEN]; + bool buffered; + int intg_pd; +} cid_report_t; + +typedef struct { + char do_name[NEU_CID_DO_NAME_LEN]; + char sdi_name[NEU_CID_SDI_NAME_LEN]; +} cid_ctrl_t; + +typedef struct { + char lnclass[NEU_CID_LNCLASS_LEN]; + char lntype[NEU_CID_LNTYPE_LEN]; + + cid_dataset_t *datasets; + int n_datasets; + + cid_report_t *reports; + int n_reports; + + cid_ctrl_t *ctrls; + int n_ctrls; +} cid_ln_t; + +typedef struct { + char inst[NEU_CID_INST_LEN]; + cid_ln_t *lns; + int n_lns; +} cid_ldevice_t; + +typedef struct { + char name[NEU_CID_IED_NAME_LEN]; + cid_ldevice_t *ldevices; + int n_ldevices; +} cid_ied_t; + +typedef enum { + SPC = 0, + DPC = 1, +} cid_tm_cdc_e; + +typedef struct { + char name[NEU_CID_DA_NAME_LEN]; + cid_fc_e fc; +} cid_tm_da_t; + +typedef struct { + char id[NEU_CID_DO_ID_LEN]; + cid_tm_cdc_e cdc; + cid_tm_da_t *das; + int n_das; +} cid_tm_do_t; + +typedef struct { + char name[NEU_CID_LNO_NAME_LEN]; + char type[NEU_CID_LNO_TYPE_LEN]; +} cid_tm_lno_do_t; + +typedef struct { + char id[NEU_CID_DO_ID_LEN]; + cid_tm_lno_do_t *dos; + int n_dos; +} cid_tm_lno_t; + +typedef struct { + cid_tm_do_t *dos; + int n_dos; + + cid_tm_lno_t *lnos; + int n_lnos; +} cid_template_t; + +typedef struct { + cid_ied_t ied; + cid_template_t template; +} cid_t; + +typedef struct { + bool control; + char ied_name[NEU_CID_IED_NAME_LEN]; + char ldevice_inst[NEU_CID_LDEVICE_LEN]; + char ln_class[NEU_CID_LNCLASS_LEN]; + bool buffered; + char report_name[NEU_CID_REPORT_NAME_LEN]; + char report_id[NEU_CID_REPORT_ID_LEN]; + char dataset_name[NEU_CID_DATASET_NAME_LEN]; +} cid_dataset_info_t; + +int neu_cid_parse(const char *path, cid_t *cid); +void neu_cid_free(cid_t *cid); + +void neu_cid_to_msg(char *driver, cid_t *cid, neu_req_add_gtag_t *cmd); + +char * neu_cid_info_to_string(cid_dataset_info_t *info); +cid_dataset_info_t *neu_cid_info_from_string(const char *str); + +#endif \ No newline at end of file diff --git a/persistence/0090_2.11.0_tag_type.sql b/persistence/0090_2.11.0_tag_type.sql index 7e8c853f..63ec2e05 100644 --- a/persistence/0090_2.11.0_tag_type.sql +++ b/persistence/0090_2.11.0_tag_type.sql @@ -41,4 +41,20 @@ INSERT INTO tags SELECT * FROM temp_tags; DROP TABLE temp_tags; +ALTER TABLE subscriptions RENAME TO temp_subscriptions; +CREATE TABLE + IF NOT EXISTS subscriptions ( + app_name TEXT NOT NULL, + driver_name TEXT NOT NULL, + group_name TEXT NOT NULL, + params TEXT DEFAULT NULL, + CHECK (app_name != driver_name), + UNIQUE (app_name, driver_name, group_name), + FOREIGN KEY (app_name) REFERENCES nodes (name) ON UPDATE CASCADE ON DELETE CASCADE, + FOREIGN KEY (driver_name, group_name) REFERENCES groups (driver_name, name) ON UPDATE CASCADE ON DELETE CASCADE + ); +INSERT INTO subscriptions SELECT * FROM temp_subscriptions; + +DROP TABLE temp_subscriptions; + COMMIT; diff --git a/persistence/0091_2.11.0_group.sql b/persistence/0091_2.11.0_group.sql new file mode 100644 index 00000000..4abe0064 --- /dev/null +++ b/persistence/0091_2.11.0_group.sql @@ -0,0 +1,36 @@ +/** +* NEURON IIoT System for Industry 4.0 +* Copyright (C) 2020-2024 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. +**/ + +BEGIN TRANSACTION; + +ALTER TABLE groups RENAME TO temp_groups; + +CREATE TABLE IF NOT EXISTS + groups ( + driver_name TEXT NOT NULL, + name TEXT NULL check(length(name) <= 32), + interval INTEGER NOT NULL check(interval >= 100), + context TEXT NULL check(length(context) <= 512), + UNIQUE (driver_name, name), + FOREIGN KEY (driver_name) REFERENCES nodes (name) ON UPDATE CASCADE ON DELETE CASCADE + ); +INSERT INTO groups SELECT driver_name, name, interval, NULL FROM temp_groups; + +DROP TABLE temp_groups; +COMMIT; diff --git a/persistence/0092_2.11.0_tag.sql b/persistence/0092_2.11.0_tag.sql new file mode 100644 index 00000000..937995d6 --- /dev/null +++ b/persistence/0092_2.11.0_tag.sql @@ -0,0 +1,60 @@ +/** +* NEURON IIoT System for Industry 4.0 +* Copyright (C) 2020-2024 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. +**/ +BEGIN TRANSACTION; + +ALTER TABLE tags RENAME TO temp_tags; + +CREATE TABLE + IF NOT EXISTS tags ( + driver_name TEXT NOT NULL, + group_name TEXT NOT NULL, + name TEXT NULL check (length (name) <= 128), + address TEXT NULL check (length (address) <= 128), + attribute INTEGER NOT NULL check (attribute BETWEEN 0 AND 15), + precision INTEGER NOT NULL check (precision BETWEEN 0 AND 17), + decimal REAL NOT NULL, + bias REAL NOT NULL check (bias BETWEEN -1000 AND 1000), + type INTEGER NOT NULL check (type BETWEEN 0 AND 22), + description TEXT NULL check (length (description) <= 512), + value TEXT, + UNIQUE (driver_name, group_name, name), + FOREIGN KEY (driver_name, group_name) REFERENCES groups (driver_name, name) ON UPDATE CASCADE ON DELETE CASCADE + ); + +INSERT INTO tags SELECT * FROM temp_tags; + +DROP TABLE temp_tags; + +ALTER TABLE subscriptions RENAME TO temp_subscriptions; +CREATE TABLE + IF NOT EXISTS subscriptions ( + app_name TEXT NOT NULL, + driver_name TEXT NOT NULL, + group_name TEXT NOT NULL, + params TEXT DEFAULT NULL, + CHECK (app_name != driver_name), + UNIQUE (app_name, driver_name, group_name), + FOREIGN KEY (app_name) REFERENCES nodes (name) ON UPDATE CASCADE ON DELETE CASCADE, + FOREIGN KEY (driver_name, group_name) REFERENCES groups (driver_name, name) ON UPDATE CASCADE ON DELETE CASCADE + ); +INSERT INTO subscriptions SELECT * FROM temp_subscriptions; + +DROP TABLE temp_subscriptions; + +COMMIT; \ No newline at end of file diff --git a/plugins/restful/cid_handle.c b/plugins/restful/cid_handle.c new file mode 100644 index 00000000..eca8d5cb --- /dev/null +++ b/plugins/restful/cid_handle.c @@ -0,0 +1,46 @@ +#include +#include + +#include "define.h" +#include "errcodes.h" +#include "handle.h" +#include "utils/cid.h" +#include "utils/http.h" +#include "utils/log.h" +#include "json/neu_json_error.h" +#include "json/neu_json_fn.h" + +#include "parser/neu_json_cid.h" + +void handle_cid(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, { + cid_t cid = { 0 }; + int parse_re = neu_cid_parse(req->path, &cid); + 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; + + nlog_notice("cid parse success: %s", req->path); + neu_cid_to_msg(req->driver, &cid, &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); + }); + } + } else { + NEU_JSON_RESPONSE_ERROR(NEU_ERR_INVALID_CID, { + neu_http_response(aio, NEU_ERR_INVALID_CID, result_error); + }); + } + neu_cid_free(&cid); + }) +} \ No newline at end of file diff --git a/plugins/restful/cid_handle.h b/plugins/restful/cid_handle.h new file mode 100644 index 00000000..5ee030d9 --- /dev/null +++ b/plugins/restful/cid_handle.h @@ -0,0 +1,26 @@ +/** + * 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. + **/ +#ifndef _NEU_PLUGIN_REST_CID_HANDLE_H_ +#define _NEU_PLUGIN_REST_CID_HANDLE_H_ + +#include + +void handle_cid(nng_aio *aio); + +#endif \ No newline at end of file diff --git a/plugins/restful/handle.c b/plugins/restful/handle.c index 3672f570..69cea3e1 100644 --- a/plugins/restful/handle.c +++ b/plugins/restful/handle.c @@ -25,6 +25,7 @@ #include "plugin.h" #include "adapter_handle.h" +#include "cid_handle.h" #include "datatag_handle.h" #include "global_config_handle.h" #include "group_config_handle.h" @@ -131,6 +132,9 @@ static struct neu_http_handler cors_handler[] = { { .url = "/api/v2/otel", }, + { + .url = "/api/v2/cid", + }, }; static struct neu_http_handler rest_handlers[] = { @@ -146,6 +150,12 @@ static struct neu_http_handler rest_handlers[] = { .url = "/", .value.path = "/web", }, + { + .method = NEU_HTTP_METHOD_POST, + .type = NEU_HTTP_HANDLER_FUNCTION, + .url = "/api/v2/cid", + .value.handler = handle_cid, + }, { .method = NEU_HTTP_METHOD_POST, .type = NEU_HTTP_HANDLER_FUNCTION, diff --git a/src/adapter/adapter.c b/src/adapter/adapter.c index 8865872c..c06a6470 100644 --- a/src/adapter/adapter.c +++ b/src/adapter/adapter.c @@ -1066,15 +1066,16 @@ static int adapter_loop(enum neu_event_io_type type, int fd, void *usr_data) } else { if (adapter->module->type == NEU_NA_TYPE_DRIVER) { error.error = neu_adapter_driver_add_group( - (neu_adapter_driver_t *) adapter, cmd->group, - cmd->interval); + (neu_adapter_driver_t *) adapter, cmd->group, cmd->interval, + NULL); } else { error.error = NEU_ERR_GROUP_NOT_ALLOW; } } if (error.error == NEU_ERR_SUCCESS) { - adapter_storage_add_group(adapter->name, cmd->group, cmd->interval); + adapter_storage_add_group(adapter->name, cmd->group, cmd->interval, + NULL); } neu_msg_exchange(header); @@ -1465,9 +1466,11 @@ int neu_adapter_add_gtags(neu_adapter_t *adapter, neu_req_add_gtag_t *cmd, // ensure group created` neu_adapter_driver_add_group((neu_adapter_driver_t *) adapter, cmd->groups[group_index].group, - cmd->groups[group_index].interval); + cmd->groups[group_index].interval, + cmd->groups[group_index].context); adapter_storage_add_group(adapter->name, cmd->groups[group_index].group, - cmd->groups[group_index].interval); + cmd->groups[group_index].interval, + cmd->groups[group_index].context); for (int tag_index = 0; tag_index < cmd->groups[group_index].n_tag; tag_index++) { int add_tag_result = neu_adapter_driver_add_tag( diff --git a/src/adapter/driver/driver.c b/src/adapter/driver/driver.c index cf601e58..626de5a0 100644 --- a/src/adapter/driver/driver.c +++ b/src/adapter/driver/driver.c @@ -467,6 +467,9 @@ int neu_adapter_driver_uninit(neu_adapter_driver_t *driver) el->grp.group_free(&el->grp); } free(el->grp.group_name); + if (el->grp.context) { + free(el->grp.context); + } free(el->name); utarray_free(el->grp.tags); @@ -1377,7 +1380,7 @@ int neu_adapter_driver_write_tag(neu_adapter_driver_t *driver, name##_TYPE, (init)) int neu_adapter_driver_add_group(neu_adapter_driver_t *driver, const char *name, - uint32_t interval) + uint32_t interval, void *context) { UT_icd icd = { sizeof(to_be_write_tag_t), NULL, NULL, NULL }; UT_icd sub_icd = { sizeof(sub_app_t), NULL, NULL, NULL }; @@ -1399,6 +1402,7 @@ int neu_adapter_driver_add_group(neu_adapter_driver_t *driver, const char *name, find->group = neu_group_new(name, interval); find->grp.group_name = strdup(name); find->grp.interval = interval; + find->grp.context = context; neu_group_split_static_tags(find->group, &find->static_tags, &find->grp.tags); @@ -1515,6 +1519,9 @@ int neu_adapter_driver_del_group(neu_adapter_driver_t *driver, const char *name) } free(find->grp.group_name); free(find->name); + if (find->grp.context) { + free(find->grp.context); + } utarray_foreach(find->static_tags, neu_datatag_t *, tag) { @@ -1732,8 +1739,8 @@ int neu_adapter_driver_add_tag(neu_adapter_driver_t *driver, const char *group, HASH_FIND_STR(driver->groups, group, find); if (find == NULL) { - neu_adapter_driver_add_group(driver, group, interval); - adapter_storage_add_group(driver->adapter.name, group, interval); + neu_adapter_driver_add_group(driver, group, interval, NULL); + adapter_storage_add_group(driver->adapter.name, group, interval, NULL); } HASH_FIND_STR(driver->groups, group, find); assert(find != NULL); @@ -2135,9 +2142,11 @@ static void group_change(void *arg, int64_t timestamp, UT_array *static_tags, .tags = NULL, .group_free = NULL, .user_data = NULL, + .context = NULL, }; - grp.tags = other_tags; + grp.context = group->grp.context; + grp.tags = other_tags; free(group->grp.group_name); if (group->static_tags != NULL) { utarray_free(group->static_tags); diff --git a/src/adapter/driver/driver_internal.h b/src/adapter/driver/driver_internal.h index 7eef3337..352852df 100644 --- a/src/adapter/driver/driver_internal.h +++ b/src/adapter/driver/driver_internal.h @@ -46,7 +46,7 @@ int neu_adapter_driver_write_gtags(neu_adapter_driver_t *driver, neu_reqresp_head_t * req); int neu_adapter_driver_add_group(neu_adapter_driver_t *driver, const char *name, - uint32_t interval); + uint32_t interval, void *context); int neu_adapter_driver_update_group(neu_adapter_driver_t *driver, const char *name, const char *new_name, uint32_t interval); diff --git a/src/adapter/storage.c b/src/adapter/storage.c index 57163020..f658a558 100644 --- a/src/adapter/storage.c +++ b/src/adapter/storage.c @@ -17,6 +17,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ +#include "utils/cid.h" #include "utils/log.h" #include "adapter_internal.h" @@ -34,14 +35,19 @@ void adapter_storage_setting(const char *node, const char *setting) } void adapter_storage_add_group(const char *node, const char *group, - uint32_t interval) + uint32_t interval, void *context) { neu_persist_group_info_t info = { .name = (char *) group, .interval = interval, }; - - neu_persister_store_group(node, &info); + if (context != NULL) { + char *ctx = neu_cid_info_to_string((cid_dataset_info_t *) context); + neu_persister_store_group(node, &info, ctx); + free(ctx); + } else { + neu_persister_store_group(node, &info, NULL); + } } void adapter_storage_update_group(const char *node, const char *group, @@ -146,7 +152,12 @@ int adapter_load_group_and_tag(neu_adapter_driver_t *driver) utarray_foreach(group_infos, neu_persist_group_info_t *, p) { UT_array *tags = NULL; - neu_adapter_driver_add_group(driver, p->name, p->interval); + if (p->context == NULL) { + neu_adapter_driver_add_group(driver, p->name, p->interval, NULL); + } else { + cid_dataset_info_t *info = neu_cid_info_from_string(p->context); + neu_adapter_driver_add_group(driver, p->name, p->interval, info); + } rv = neu_persister_load_tags(adapter->name, p->name, &tags); if (0 != rv) { diff --git a/src/adapter/storage.h b/src/adapter/storage.h index 7c9c8fab..03a3ad5a 100644 --- a/src/adapter/storage.h +++ b/src/adapter/storage.h @@ -27,7 +27,7 @@ void adapter_storage_state(const char *node, neu_node_running_state_e state); void adapter_storage_setting(const char *node, const char *setting); void adapter_storage_add_group(const char *node, const char *group, - uint32_t interval); + uint32_t interval, void *context); void adapter_storage_update_group(const char *node, const char *group, const char *new_name, uint32_t interval); void adapter_storage_del_group(const char *node, const char *group); diff --git a/src/core/manager.c b/src/core/manager.c index 4890db6e..2c973412 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1367,7 +1367,7 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data) for (uint16_t j = 0; j < driver->n_group; j++) { adapter_storage_add_group(driver->node, driver->groups[j].group, - driver->groups[j].interval); + driver->groups[j].interval, NULL); adapter_storage_add_tags( driver->node, driver->groups[j].group, driver->groups[j].tags, driver->groups[j].n_tag); diff --git a/src/parser/neu_json_cid.c b/src/parser/neu_json_cid.c new file mode 100644 index 00000000..20abe8a0 --- /dev/null +++ b/src/parser/neu_json_cid.c @@ -0,0 +1,98 @@ +/** + * 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. + **/ + +/* + * DO NOT EDIT THIS FILE MANUALLY! + * It was automatically generated by `json-autotype`. + */ + +#include +#include + +#include + +#include "json/json.h" + +#include "neu_json_cid.h" + +int neu_json_decode_upload_cid_req_json(void * json_obj, + neu_json_upload_cid_t *req) +{ + int ret = 0; + + neu_json_elem_t req_elems[] = { + { + .name = "driver", + .t = NEU_JSON_STR, + }, + { + .name = "path", + .t = NEU_JSON_STR, + }, + }; + ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems), + req_elems); + if (ret != 0) { + return -1; + } + + req->driver = req_elems[0].v.val_str; + req->path = req_elems[1].v.val_str; + + return 0; +} + +int neu_json_decode_upload_cid_req(char *buf, neu_json_upload_cid_t **result) +{ + int ret = 0; + void * json_obj = NULL; + neu_json_upload_cid_t *req = calloc(1, sizeof(neu_json_upload_cid_t)); + if (req == NULL) { + return -1; + } + + json_obj = neu_json_decode_new(buf); + if (NULL == json_obj) { + free(req); + return -1; + } + + ret = neu_json_decode_upload_cid_req_json(json_obj, req); + if (0 == ret) { + *result = req; + } else { + free(req); + } + + neu_json_decode_free(json_obj); + return ret; +} + +void neu_json_decode_upload_cid_req_fini(neu_json_upload_cid_t *req) +{ + free(req->driver); + free(req->path); +} +void neu_json_decode_upload_cid_req_free(neu_json_upload_cid_t *req) +{ + if (req) { + neu_json_decode_upload_cid_req_fini(req); + free(req); + } +} \ No newline at end of file diff --git a/src/parser/neu_json_cid.h b/src/parser/neu_json_cid.h new file mode 100644 index 00000000..0e915802 --- /dev/null +++ b/src/parser/neu_json_cid.h @@ -0,0 +1,48 @@ +/** + * 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. + **/ + +/* + * DO NOT EDIT THIS FILE MANUALLY! + * It was automatically generated by `json-autotype`. + */ + +#ifndef _NEU_JSON_API_NEU_JSON_CID_H_ +#define _NEU_JSON_API_NEU_JSON_CID_H_ + +#include "define.h" +#include "json/json.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char *driver; + char *path; +} neu_json_upload_cid_t; + +int neu_json_decode_upload_cid_req(char *buf, neu_json_upload_cid_t **result); +void neu_json_decode_upload_cid_req_fini(neu_json_upload_cid_t *req); +void neu_json_decode_upload_cid_req_free(neu_json_upload_cid_t *req); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/persist/persist.c b/src/persist/persist.c index 791b44b2..2d333d0e 100644 --- a/src/persist/persist.c +++ b/src/persist/persist.c @@ -421,9 +421,10 @@ int neu_persister_delete_subscription(const char *app_name, } int neu_persister_store_group(const char * driver_name, - neu_persist_group_info_t *group_info) + neu_persist_group_info_t *group_info, + const char * context) { - return g_impl->vtbl->store_group(g_impl, driver_name, group_info); + return g_impl->vtbl->store_group(g_impl, driver_name, group_info, context); } int neu_persister_update_group(const char *driver_name, const char *group_name, diff --git a/src/persist/persist_impl.h b/src/persist/persist_impl.h index 30d64586..37f302bb 100644 --- a/src/persist/persist_impl.h +++ b/src/persist/persist_impl.h @@ -224,7 +224,8 @@ struct neu_persister_vtbl_s { * @return 0 on success, non-zero otherwise */ int (*store_group)(neu_persister_t *self, const char *driver_name, - neu_persist_group_info_t *group_info); + neu_persist_group_info_t *group_info, + const char * context); /** * Update group config. diff --git a/src/persist/sqlite.c b/src/persist/sqlite.c index 9a108afb..75756a5b 100644 --- a/src/persist/sqlite.c +++ b/src/persist/sqlite.c @@ -958,12 +958,14 @@ int neu_sqlite_persister_delete_subscription(neu_persister_t *self, int neu_sqlite_persister_store_group(neu_persister_t * self, const char * driver_name, - neu_persist_group_info_t *group_info) + neu_persist_group_info_t *group_info, + const char * context) { - return execute_sql( - ((neu_sqlite_persister_t *) self)->db, - "INSERT INTO groups (driver_name, name, interval) VALUES (%Q, %Q, %u)", - driver_name, group_info->name, (unsigned) group_info->interval); + return execute_sql(((neu_sqlite_persister_t *) self)->db, + "INSERT INTO groups (driver_name, name, interval, " + "context) VALUES (%Q, %Q, %u, %Q)", + driver_name, group_info->name, + (unsigned) group_info->interval, context); } int neu_sqlite_persister_update_group(neu_persister_t * self, @@ -1017,6 +1019,12 @@ static int collect_group_info(sqlite3_stmt *stmt, UT_array **group_infos) info.name = name; info.interval = sqlite3_column_int(stmt, 1); + char *context = (char *) sqlite3_column_text(stmt, 2); + if (NULL != context) { + info.context = strdup(context); + } else { + info.context = NULL; + } utarray_push_back(*group_infos, &info); step = sqlite3_step(stmt); @@ -1036,7 +1044,8 @@ int neu_sqlite_persister_load_groups(neu_persister_t *self, neu_sqlite_persister_t *persister = (neu_sqlite_persister_t *) self; sqlite3_stmt *stmt = NULL; - const char *query = "SELECT name, interval FROM groups WHERE driver_name=?"; + const char * query = + "SELECT name, interval, context FROM groups WHERE driver_name=?"; utarray_new(*group_infos, &group_info_icd); diff --git a/src/persist/sqlite.h b/src/persist/sqlite.h index 16f37b9c..b24180ae 100644 --- a/src/persist/sqlite.h +++ b/src/persist/sqlite.h @@ -90,7 +90,8 @@ int neu_sqlite_persister_delete_subscription(neu_persister_t *self, const char * group_name); int neu_sqlite_persister_store_group(neu_persister_t * self, const char * driver_name, - neu_persist_group_info_t *group_info); + neu_persist_group_info_t *group_info, + const char * context); int neu_sqlite_persister_update_group(neu_persister_t * self, const char * driver_name, const char * group_name, diff --git a/src/utils/cid.c b/src/utils/cid.c new file mode 100644 index 00000000..62b8c512 --- /dev/null +++ b/src/utils/cid.c @@ -0,0 +1,863 @@ +/** + * 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 + +#include "define.h" +#include "utils/cid.h" +#include "utils/log.h" +#include "json/json.h" + +static int parse_ied(xmlNode *xml_ied, cid_ied_t *ied, + cid_template_t *template); +static int parse_template(xmlNode *xml_template, cid_template_t *template); +static int parse_ldevice(xmlNode *xml_server, cid_ied_t *ied, + cid_template_t *template); +static int parse_lnode(xmlNode *xml_ldevice, cid_ldevice_t *ldev, + cid_template_t *template); +static int parse_dataset(xmlNode *xml_dataset, cid_ln_t *ln); +static int parse_report(xmlNode *xml_report, cid_ln_t *ln); +static int parse_doi(xmlNode *xml_doi, cid_ln_t *ln, cid_template_t *template, + int index); + +int neu_cid_parse(const char *path, cid_t *cid) +{ + memset(cid, 0, sizeof(cid_t)); + xmlDoc *doc = xmlReadFile(path, NULL, 0); + if (doc == NULL) { + nlog_warn("Failed to read icd file %s", path); + return -1; + } + xmlNode *root = xmlDocGetRootElement(doc); + if (root == NULL || strcmp((const char *) root->name, "SCL") != 0) { + nlog_warn("Failed to get root element(SCL)"); + xmlFreeDoc(doc); + return -1; + } + + xmlNode *find = root->children; + bool ied_parsed = false; + bool templates_parsed = false; + while (find != NULL) { + if (find->type == XML_ELEMENT_NODE && + strcmp((const char *) find->name, "DataTypeTemplates") == 0) { + int ret = parse_template(find, &cid->template); + if (ret == 0) { + templates_parsed = true; + } + } + + find = find->next; + } + xmlNode *find_ied = root->children; + while (find_ied != NULL) { + if (find_ied->type == XML_ELEMENT_NODE && + strcmp((const char *) find_ied->name, "IED") == 0) { + int ret = parse_ied(find_ied, &cid->ied, &cid->template); + if (ret == 0) { + ied_parsed = true; + } + } + + find_ied = find_ied->next; + } + + if (ied_parsed && templates_parsed) { + xmlFreeDoc(doc); + return 0; + } else { + neu_cid_free(cid); + nlog_warn("Failed to parse IED or DataTypeTemplates, %d, %d", + ied_parsed, templates_parsed); + xmlFreeDoc(doc); + return -1; + } +} + +void neu_cid_free(cid_t *cid) +{ + for (int i = 0; i < cid->template.n_lnos; i++) { + if (cid->template.lnos[i].n_dos > 0) { + free(cid->template.lnos[i].dos); + } + } + if (cid->template.n_lnos > 0) { + free(cid->template.lnos); + } + + for (int i = 0; i < cid->template.n_dos; i++) { + if (cid->template.dos[i].n_das > 0) { + free(cid->template.dos[i].das); + } + } + if (cid->template.n_dos > 0) { + free(cid->template.dos); + } + + for (int i = 0; i < cid->ied.n_ldevices; i++) { + for (int j = 0; j < cid->ied.ldevices[i].n_lns; j++) { + if (cid->ied.ldevices[i].lns[j].n_reports > 0) { + free(cid->ied.ldevices[i].lns[j].reports); + } + if (cid->ied.ldevices[i].lns[j].n_ctrls > 0) { + free(cid->ied.ldevices[i].lns[j].ctrls); + } + + for (int k = 0; k < cid->ied.ldevices[i].lns[j].n_datasets; k++) { + if (cid->ied.ldevices[i].lns[j].datasets[k].n_dos > 0) { + free(cid->ied.ldevices[i].lns[j].datasets[k].dos); + } + } + if (cid->ied.ldevices[i].lns[j].n_datasets > 0) { + free(cid->ied.ldevices[i].lns[j].datasets); + } + } + + if (cid->ied.ldevices[i].n_lns > 0) { + free(cid->ied.ldevices[i].lns); + } + } + if (cid->ied.n_ldevices > 0) { + free(cid->ied.ldevices); + } + + return; +} + +void neu_cid_to_msg(char *driver, cid_t *cid, neu_req_add_gtag_t *cmd) +{ + strcpy(cmd->driver, driver); + cmd->n_group = 0; + cmd->groups = NULL; + + cmd->n_group += 1; + cmd->groups = realloc(cmd->groups, cmd->n_group * sizeof(neu_gdatatag_t)); + neu_gdatatag_t *ctl_group = &cmd->groups[cmd->n_group - 1]; + memset(ctl_group, 0, sizeof(neu_gdatatag_t)); + + strcpy(ctl_group->group, "Control"); + ctl_group->interval = 5000; + + cid_dataset_info_t *ctl_info = calloc(1, sizeof(cid_dataset_info_t)); + ctl_info->control = true; + strcpy(ctl_info->ied_name, cid->ied.name); + ctl_group->context = ctl_info; + + for (int i = 0; i < cid->ied.n_ldevices; i++) { + for (int j = 0; j < cid->ied.ldevices[i].n_lns; j++) { + for (int k = 0; k < cid->ied.ldevices[i].lns[j].n_ctrls; k++) { + ctl_group->n_tag += 1; + ctl_group->tags = realloc( + ctl_group->tags, ctl_group->n_tag * sizeof(neu_datatag_t)); + neu_datatag_t *tag = &ctl_group->tags[ctl_group->n_tag - 1]; + memset(tag, 0, sizeof(neu_datatag_t)); + + tag->name = calloc(1, NEU_TAG_NAME_LEN); + snprintf(tag->name, NEU_TAG_NAME_LEN - 1, "%s/%s$%s", + cid->ied.ldevices[i].inst, + cid->ied.ldevices[i].lns[j].ctrls[k].do_name, + cid->ied.ldevices[i].lns[j].ctrls[k].sdi_name); + + tag->attribute = NEU_ATTRIBUTE_WRITE; + tag->type = NEU_TYPE_BOOL; + tag->address = calloc(1, NEU_TAG_ADDRESS_LEN); + tag->description = strdup(""); + snprintf(tag->address, NEU_TAG_ADDRESS_LEN - 1, + "%s%s/%s$%s$%s$%s", cid->ied.name, + cid->ied.ldevices[i].inst, + cid->ied.ldevices[i].lns[j].lnclass, "CO", + cid->ied.ldevices[i].lns[j].ctrls[k].do_name, + cid->ied.ldevices[i].lns[j].ctrls[k].sdi_name); + } + } + } + + for (int i = 0; i < cid->ied.n_ldevices; i++) { + for (int j = 0; j < cid->ied.ldevices[i].n_lns; j++) { + for (int k = 0; k < cid->ied.ldevices[i].lns[j].n_datasets; k++) { + for (int l = 0; l < cid->ied.ldevices[i].lns[j].n_reports; + l++) { + if (strcmp( + cid->ied.ldevices[i].lns[j].datasets[k].name, + cid->ied.ldevices[i].lns[j].reports[l].dataset) == + 0) { + if (cid->ied.ldevices[i].lns[j].datasets[k].n_dos == + 0) { + break; + } + cmd->n_group += 1; + cmd->groups = realloc( + cmd->groups, cmd->n_group * sizeof(neu_gdatatag_t)); + neu_gdatatag_t *group = &cmd->groups[cmd->n_group - 1]; + memset(group, 0, sizeof(neu_gdatatag_t)); + + cid_dataset_info_t *info = + calloc(1, sizeof(cid_dataset_info_t)); + + info->control = false; + info->buffered = + cid->ied.ldevices[i].lns[j].reports[l].buffered; + strcpy(info->ied_name, cid->ied.name); + strcpy(info->ldevice_inst, cid->ied.ldevices[i].inst); + strcpy(info->ln_class, + cid->ied.ldevices[i].lns[j].lnclass); + strcpy(info->report_name, + cid->ied.ldevices[i].lns[j].reports[l].name); + strcpy(info->report_id, + cid->ied.ldevices[i].lns[j].reports[l].id); + strcpy(info->dataset_name, + cid->ied.ldevices[i].lns[j].datasets[k].name); + group->context = info; + + snprintf(group->group, NEU_GROUP_NAME_LEN - 1, + "%s_%s_%s", cid->ied.ldevices[i].inst, + cid->ied.ldevices[i].lns[j].lnclass, + cid->ied.ldevices[i].lns[j].datasets[k].name); + group->interval = + cid->ied.ldevices[i].lns[j].reports[l].intg_pd; + group->n_tag = + cid->ied.ldevices[i].lns[j].datasets[k].n_dos; + group->tags = + calloc(group->n_tag, sizeof(neu_datatag_t)); + + for (int m = 0; + m < cid->ied.ldevices[i].lns[j].datasets[k].n_dos; + m++) { + group->tags[m].name = strdup(cid->ied.ldevices[i] + .lns[j] + .datasets[k] + .dos[m] + .name); + group->tags[m].attribute = + NEU_ATTRIBUTE_READ | NEU_ATTRIBUTE_SUBSCRIBE; + group->tags[m].address = + calloc(1, NEU_TAG_ADDRESS_LEN); + group->tags[m].description = strdup(""); + switch (cid->ied.ldevices[i] + .lns[j] + .datasets[k] + .dos[m] + .fc) { + case ST: + group->tags[m].type = NEU_TYPE_BOOL; + snprintf(group->tags[m].address, + NEU_TAG_ADDRESS_LEN - 1, + "%s%s/%s$%s$%s", cid->ied.name, + cid->ied.ldevices[i].inst, + cid->ied.ldevices[i] + .lns[j] + .datasets[k] + .dos[m] + .lnclass, + "ST", + cid->ied.ldevices[i] + .lns[j] + .datasets[k] + .dos[m] + .name); + break; + case MX: + group->tags[m].type = NEU_TYPE_FLOAT; + snprintf(group->tags[m].address, + NEU_TAG_ADDRESS_LEN - 1, + "%s%s/%s$%s$%s", cid->ied.name, + cid->ied.ldevices[i].inst, + cid->ied.ldevices[i] + .lns[j] + .datasets[k] + .dos[m] + .lnclass, + "MX", + cid->ied.ldevices[i] + .lns[j] + .datasets[k] + .dos[m] + .name); + break; + default: + group->tags[m].type = NEU_TYPE_INT16; + break; + } + } + + break; + } + } + } + } + } +} + +static int parse_ied(xmlNode *xml_ied, cid_ied_t *ied, cid_template_t *template) +{ + int ret = -1; + char *ied_name = (char *) xmlGetProp(xml_ied, (const xmlChar *) "name"); + if (strlen(ied_name) >= NEU_CID_IED_NAME_LEN) { + xmlFree(ied_name); + nlog_warn("IED name is too long"); + return -1; + } else { + strcpy(ied->name, ied_name); + xmlFree(ied_name); + } + xmlNode *access_point = xml_ied->children; + while (access_point != NULL) { + if (access_point->type == XML_ELEMENT_NODE && + strcmp((const char *) access_point->name, "AccessPoint") == 0) { + xmlNode *server = access_point->children; + while (server != NULL) { + if (server->type == XML_ELEMENT_NODE && + strcmp((const char *) server->name, "Server") == 0) { + ret = parse_ldevice(server, ied, template); + break; + } + + server = server->next; + } + break; + } + access_point = access_point->next; + } + + return ret; +} + +static int parse_ldevice(xmlNode *xml_server, cid_ied_t *ied, + cid_template_t *template) +{ + ied->ldevices = NULL; + ied->n_ldevices = 0; + xmlNode *ldevice = xml_server->children; + + while (ldevice != NULL) { + if (ldevice->type == XML_ELEMENT_NODE && + strcmp((const char *) ldevice->name, "LDevice") == 0) { + ied->n_ldevices++; + ied->ldevices = + realloc(ied->ldevices, ied->n_ldevices * sizeof(cid_ldevice_t)); + cid_ldevice_t *ldev = &ied->ldevices[ied->n_ldevices - 1]; + ldev->lns = NULL; + ldev->n_lns = 0; + memset(ldev->inst, 0, sizeof(ldev->inst)); + + char *inst = (char *) xmlGetProp(ldevice, (const xmlChar *) "inst"); + if (strlen(inst) >= NEU_CID_INST_LEN) { + xmlFree(inst); + nlog_warn("LDevice inst is too long"); + return -1; + } else { + strcpy(ldev->inst, inst); + xmlFree(inst); + } + + int ret = parse_lnode(ldevice, ldev, template); + if (ret != 0) { + return ret; + } + } + + ldevice = ldevice->next; + } + + return 0; +} + +static int parse_lnode(xmlNode *xml_ldevice, cid_ldevice_t *ldev, + cid_template_t *template) +{ + xmlNode *lnode = xml_ldevice->children; + while (lnode != NULL) { + if (lnode->type == XML_ELEMENT_NODE && + strncmp((const char *) lnode->name, "LN", 2) == 0) { + char *ln_class = + (char *) xmlGetProp(lnode, (const xmlChar *) "lnClass"); + char *ln_type = + (char *) xmlGetProp(lnode, (const xmlChar *) "lnType"); + if (strlen(ln_class) >= NEU_CID_LNCLASS_LEN || + strlen(ln_type) >= NEU_CID_LNTYPE_LEN) { + xmlFree(ln_class); + xmlFree(ln_type); + nlog_warn("LN class/type is too long %d", (int) lnode->line); + return -1; + } else { + ldev->n_lns += 1; + ldev->lns = realloc(ldev->lns, ldev->n_lns * sizeof(cid_ln_t)); + cid_ln_t *ln = &ldev->lns[ldev->n_lns - 1]; + memset(ln->lnclass, 0, sizeof(ln->lnclass)); + memset(ln->lntype, 0, sizeof(ln->lntype)); + ln->datasets = NULL; + ln->n_datasets = 0; + ln->reports = NULL; + ln->n_reports = 0; + ln->ctrls = NULL; + ln->n_ctrls = 0; + + strcpy(ln->lnclass, ln_class); + strcpy(ln->lntype, ln_type); + xmlFree(ln_class); + xmlFree(ln_type); + + xmlNode *child = lnode->children; + while (child != NULL) { + if (child->type == XML_ELEMENT_NODE && + strcmp((const char *) child->name, "DataSet") == 0) { + int ret = parse_dataset(child, ln); + if (ret != 0) { + return ret; + } + } + + if (child->type == XML_ELEMENT_NODE && + strcmp((const char *) child->name, "ReportControl") == + 0) { + int ret = parse_report(child, ln); + if (ret != 0) { + return ret; + } + } + + if (child->type == XML_ELEMENT_NODE && + strcmp((const char *) child->name, "DOI") == 0) { + for (int i = 0; i < template->n_lnos; i++) { + if (strcmp(template->lnos[i].id, ln->lntype) == 0 && + template->lnos[i].n_dos > 0) { + int ret = parse_doi(child, ln, template, i); + if (ret != 0) { + return ret; + } + break; + } + } + } + + child = child->next; + } + } + } + + lnode = lnode->next; + } + + return 0; +} + +static int parse_dataset(xmlNode *xml_dataset, cid_ln_t *ln) +{ + char *dataset_name = + (char *) xmlGetProp(xml_dataset, (const xmlChar *) "name"); + if (strlen(dataset_name) >= NEU_CID_DATASET_NAME_LEN) { + xmlFree(dataset_name); + nlog_warn("Dataset name is too long %d", (int) xml_dataset->line); + return -1; + } else { + ln->n_datasets += 1; + ln->datasets = + realloc(ln->datasets, ln->n_datasets * sizeof(cid_dataset_t)); + cid_dataset_t *dataset = &ln->datasets[ln->n_datasets - 1]; + dataset->n_dos = 0; + dataset->dos = NULL; + memset(dataset->name, 0, sizeof(dataset->name)); + + strcpy(dataset->name, dataset_name); + xmlFree(dataset_name); + + xmlNode *fcda = xml_dataset->children; + while (fcda != NULL) { + if (fcda->type == XML_ELEMENT_NODE && + strcmp((const char *) fcda->name, "FCDA") == 0) { + dataset->n_dos += 1; + dataset->dos = + realloc(dataset->dos, dataset->n_dos * sizeof(cid_do_t)); + cid_do_t *doi = &dataset->dos[dataset->n_dos - 1]; + memset(doi->name, 0, sizeof(doi->name)); + memset(doi->lnclass, 0, sizeof(doi->lnclass)); + + char *do_name = + (char *) xmlGetProp(fcda, (const xmlChar *) "doName"); + char *lnclass = + (char *) xmlGetProp(fcda, (const xmlChar *) "lnClass"); + if (strlen(do_name) >= NEU_CID_DO_NAME_LEN || + strlen(lnclass) >= NEU_CID_LNCLASS_LEN) { + xmlFree(do_name); + xmlFree(lnclass); + nlog_warn("DO name or lnclass is too long %d", + (int) fcda->line); + return -1; + } else { + strcpy(doi->name, do_name); + strcpy(doi->lnclass, lnclass); + xmlFree(do_name); + xmlFree(lnclass); + } + + char *fc = (char *) xmlGetProp(fcda, (const xmlChar *) "fc"); + if (fc != NULL && strcmp(fc, "ST") == 0) { + doi->fc = ST; + } else if (fc != NULL && strcmp(fc, "MX") == 0) { + doi->fc = MX; + } else { + doi->fc = UNKNOWN; + nlog_warn("FCDA unknown fc %s, %d", fc, (int) fcda->line); + } + + xmlFree(fc); + } + + fcda = fcda->next; + } + } + return 0; +} + +static int parse_report(xmlNode *xml_report, cid_ln_t *ln) +{ + char *report_name = + (char *) xmlGetProp(xml_report, (const xmlChar *) "name"); + char *report_id = + (char *) xmlGetProp(xml_report, (const xmlChar *) "rptID"); + char *dataset = (char *) xmlGetProp(xml_report, (const xmlChar *) "datSet"); + char *buffered = + (char *) xmlGetProp(xml_report, (const xmlChar *) "buffered"); + char *intg_pd = (char *) xmlGetProp(xml_report, (const xmlChar *) "intgPd"); + + if (strlen(report_name) >= NEU_CID_REPORT_NAME_LEN || + strlen(report_id) >= NEU_CID_REPORT_ID_LEN || + strlen(dataset) >= NEU_CID_DATASET_NAME_LEN) { + xmlFree(report_name); + xmlFree(report_id); + xmlFree(dataset); + xmlFree(buffered); + xmlFree(intg_pd); + + nlog_warn("report name/id/datset too long %d", (int) xml_report->line); + return -1; + } else { + ln->n_reports += 1; + ln->reports = + realloc(ln->reports, ln->n_reports * sizeof(cid_report_t)); + cid_report_t *report = &ln->reports[ln->n_reports - 1]; + memset(report->name, 0, sizeof(report->name)); + memset(report->id, 0, sizeof(report->id)); + memset(report->dataset, 0, sizeof(report->dataset)); + report->intg_pd = 15000; // default + report->buffered = false; + + strcpy(report->name, report_name); + strcpy(report->id, report_id); + strcpy(report->dataset, dataset); + if (buffered != NULL && strcmp(buffered, "true") == 0) { + report->buffered = true; + } + + int interval = atoi(intg_pd); + if (interval > 0) { + report->intg_pd = interval; + } + + xmlFree(report_name); + xmlFree(report_id); + xmlFree(dataset); + xmlFree(buffered); + xmlFree(intg_pd); + } + + return 0; +} + +static int parse_doi(xmlNode *xml_doi, cid_ln_t *ln, cid_template_t *template, + int index) +{ + char *doi_name = (char *) xmlGetProp(xml_doi, (const xmlChar *) "name"); + for (int i = 0; i < template->lnos[index].n_dos; i++) { + if (strcmp(template->lnos[index].dos[i].name, doi_name) == 0) { + for (int k = 0; k < template->n_dos; k++) { + if (strcmp(template->dos[k].id, + template->lnos[index].dos[i].type) == 0) { + for (int j = 0; j < template->dos[k].n_das; j++) { + if (strcmp(template->dos[k].id, + template->lnos[index].dos[i].type) == 0) { + ln->n_ctrls += 1; + ln->ctrls = realloc( + ln->ctrls, ln->n_ctrls * sizeof(cid_ctrl_t)); + cid_ctrl_t *ctrl = &ln->ctrls[ln->n_ctrls - 1]; + memset(ctrl->do_name, 0, sizeof(ctrl->do_name)); + memset(ctrl->sdi_name, 0, sizeof(ctrl->sdi_name)); + + strcpy(ctrl->do_name, doi_name); + strcpy(ctrl->sdi_name, + template->dos[k].das[j].name); + } + } + break; + } + } + break; + } + } + + xmlFree(doi_name); + + return 0; +} + +static int parse_template(xmlNode *xml_template, cid_template_t *template) +{ + template->dos = NULL; + template->lnos = NULL; + template->n_dos = 0; + template->n_lnos = 0; + xmlNode *child = xml_template->children; + + while (child != NULL) { + if (child->type == XML_ELEMENT_NODE && + strcmp((const char *) child->name, "DOType") == 0) { + char *id = (char *) xmlGetProp(child, (const xmlChar *) "id"); + char *cdc = (char *) xmlGetProp(child, (const xmlChar *) "cdc"); + if (strlen(id) >= NEU_CID_DO_ID_LEN) { + xmlFree(id); + xmlFree(cdc); + nlog_warn("DOType id is too long %d", (int) child->line); + return -1; + } else { + if (strcmp(cdc, "SPC") == 0 || strcmp(cdc, "DPC") == 0) { + template->n_dos += 1; + template->dos = realloc( + template->dos, template->n_dos * sizeof(cid_tm_do_t)); + cid_tm_do_t *tm_do = &template->dos[template->n_dos - 1]; + memset(tm_do->id, 0, sizeof(tm_do->id)); + tm_do->n_das = 0; + tm_do->das = NULL; + + strcpy(tm_do->id, id); + if (strcmp(cdc, "SPC") == 0) { + tm_do->cdc = SPC; + } else { + tm_do->cdc = DPC; + } + + xmlNode *da = child->children; + while (da != NULL) { + if (da->type != XML_ELEMENT_NODE || + strcmp((const char *) da->name, "DA") != 0) { + da = da->next; + continue; + } + char *fc = + (char *) xmlGetProp(da, (const xmlChar *) "fc"); + if (fc != NULL && strcmp(fc, "CO") == 0) { + char *da_name = (char *) xmlGetProp( + da, (const xmlChar *) "name"); + tm_do->n_das += 1; + tm_do->das = realloc( + tm_do->das, tm_do->n_das * sizeof(cid_tm_da_t)); + cid_tm_da_t *tm_da = &tm_do->das[tm_do->n_das - 1]; + memset(tm_da->name, 0, sizeof(tm_da->name)); + + tm_da->fc = CO; + strcpy(tm_da->name, da_name); + + xmlFree(da_name); + } + + xmlFree(fc); + da = da->next; + } + } + xmlFree(id); + xmlFree(cdc); + } + } + child = child->next; + } + + xmlNode *child_node = xml_template->children; + while (child_node != NULL) { + if (child_node->type == XML_ELEMENT_NODE && + strcmp((const char *) child_node->name, "LNodeType") == 0) { + char *id = (char *) xmlGetProp(child_node, (const xmlChar *) "id"); + + template->n_lnos += 1; + template->lnos = realloc(template->lnos, + template->n_lnos * sizeof(cid_tm_lno_t)); + cid_tm_lno_t *tm_lno = &template->lnos[template->n_lnos - 1]; + memset(tm_lno->id, 0, sizeof(tm_lno->id)); + tm_lno->n_dos = 0; + tm_lno->dos = NULL; + + strncpy(tm_lno->id, id, NEU_CID_DO_ID_LEN); + xmlFree(id); + xmlNode *dot = child_node->children; + + while (dot != NULL) { + if (dot->type == XML_ELEMENT_NODE && + strcmp((const char *) dot->name, "DO") == 0) { + char *do_type = + (char *) xmlGetProp(dot, (const xmlChar *) "type"); + for (int i = 0; i < template->n_dos; i++) { + if (strcmp(template->dos[i].id, do_type) == 0) { + char *do_name = (char *) xmlGetProp( + dot, (const xmlChar *) "name"); + tm_lno->n_dos += 1; + tm_lno->dos = realloc(tm_lno->dos, + tm_lno->n_dos * + sizeof(cid_tm_lno_do_t)); + cid_tm_lno_do_t *tm_lno_do = + &tm_lno->dos[tm_lno->n_dos - 1]; + memset(tm_lno_do->name, 0, sizeof(tm_lno_do->name)); + memset(tm_lno_do->type, 0, sizeof(tm_lno_do->type)); + + strcpy(tm_lno_do->name, do_name); + strcpy(tm_lno_do->type, do_type); + xmlFree(do_name); + break; + } + } + + xmlFree(do_type); + } + + dot = dot->next; + } + } + + child_node = child_node->next; + } + + return 0; +} + +char *neu_cid_info_to_string(cid_dataset_info_t *info) +{ + neu_json_elem_t elems[] = { + { + .name = "control", + .t = NEU_JSON_BOOL, + .v.val_bool = info->control, + }, + { + .name = "buffered", + .t = NEU_JSON_BOOL, + .v.val_bool = info->buffered, + }, + { + .name = "ied_name", + .t = NEU_JSON_STR, + .v.val_str = info->ied_name, + }, + { + .name = "ldevice_inst", + .t = NEU_JSON_STR, + .v.val_str = info->ldevice_inst, + }, + { + .name = "ln_class", + .t = NEU_JSON_STR, + .v.val_str = info->ln_class, + }, + { + .name = "report_name", + .t = NEU_JSON_STR, + .v.val_str = info->report_name, + }, + { + .name = "report_id", + .t = NEU_JSON_STR, + .v.val_str = info->report_id, + }, + { + .name = "dataset_name", + .t = NEU_JSON_STR, + .v.val_str = info->dataset_name, + }, + }; + + char *result = NULL; + void *json = neu_json_encode_new(); + neu_json_encode_field(json, elems, NEU_JSON_ELEM_SIZE(elems)); + neu_json_encode(json, &result); + neu_json_encode_free(json); + + return result; +} + +cid_dataset_info_t *neu_cid_info_from_string(const char *str) +{ + neu_json_elem_t elems[] = { + { + .name = "control", + .t = NEU_JSON_BOOL, + }, + { + .name = "buffered", + .t = NEU_JSON_BOOL, + }, + { + .name = "ied_name", + .t = NEU_JSON_STR, + }, + { + .name = "ldevice_inst", + .t = NEU_JSON_STR, + }, + { + .name = "ln_class", + .t = NEU_JSON_STR, + }, + { + .name = "report_name", + .t = NEU_JSON_STR, + }, + { + .name = "report_id", + .t = NEU_JSON_STR, + }, + { + .name = "dataset_name", + .t = NEU_JSON_STR, + }, + }; + void *json = neu_json_decode_new(str); + int ret = neu_json_decode_by_json(json, NEU_JSON_ELEM_SIZE(elems), elems); + if (ret != 0) { + neu_json_decode_free(json); + return NULL; + } else { + cid_dataset_info_t *info = calloc(1, sizeof(cid_dataset_info_t)); + info->control = elems[0].v.val_bool; + info->buffered = elems[1].v.val_bool; + strcpy(info->ied_name, elems[2].v.val_str); + strcpy(info->ldevice_inst, elems[3].v.val_str); + strcpy(info->ln_class, elems[4].v.val_str); + strcpy(info->report_name, elems[5].v.val_str); + strcpy(info->report_id, elems[6].v.val_str); + strcpy(info->dataset_name, elems[7].v.val_str); + + free(elems[2].v.val_str); + free(elems[3].v.val_str); + free(elems[4].v.val_str); + free(elems[5].v.val_str); + free(elems[6].v.val_str); + free(elems[7].v.val_str); + + neu_json_decode_free(json); + return info; + } +} \ No newline at end of file diff --git a/src/utils/http.c b/src/utils/http.c index 279911b7..05caae37 100644 --- a/src/utils/http.c +++ b/src/utils/http.c @@ -286,6 +286,7 @@ int neu_http_response(nng_aio *aio, neu_err_code_e code, char *content) break; case NEU_ERR_BODY_IS_WRONG: case NEU_ERR_PARAM_IS_WRONG: + case NEU_ERR_INVALID_CID: case NEU_ERR_IP_ADDRESS_INVALID: case NEU_ERR_IP_ADDRESS_IN_USE: case NEU_ERR_NODE_SETTING_INVALID: