diff --git a/include/neuron/json/neu_json_rw.h b/include/neuron/json/neu_json_rw.h index bd84c2c1..e45747fa 100644 --- a/include/neuron/json/neu_json_rw.h +++ b/include/neuron/json/neu_json_rw.h @@ -126,6 +126,8 @@ void neu_json_decode_write_free(neu_json_write_t *req); typedef struct { char *group; char *node; + char *name; + char *desc; bool sync; } neu_json_read_req_t; diff --git a/include/neuron/msg.h b/include/neuron/msg.h index 3663269c..62cfc9b5 100644 --- a/include/neuron/msg.h +++ b/include/neuron/msg.h @@ -576,6 +576,8 @@ typedef struct { typedef struct neu_req_read_group { char *driver; char *group; + char *name; + char *desc; bool sync; } neu_req_read_group_t; @@ -583,6 +585,8 @@ static inline void neu_req_read_group_fini(neu_req_read_group_t *req) { free(req->driver); free(req->group); + free(req->name); + free(req->desc); } typedef struct neu_req_write_tag { diff --git a/plugins/restful/rw_handle.c b/plugins/restful/rw_handle.c index 22bc6beb..0c743ae7 100644 --- a/plugins/restful/rw_handle.c +++ b/plugins/restful/rw_handle.c @@ -43,6 +43,8 @@ void handle_read(nng_aio *aio) cmd.driver = req->node; cmd.group = req->group; + cmd.name = req->name; + cmd.desc = req->desc; cmd.sync = req->sync; req->node = NULL; req->group = NULL; diff --git a/src/adapter/driver/driver.c b/src/adapter/driver/driver.c index dfe7bca9..46c415ee 100644 --- a/src/adapter/driver/driver.c +++ b/src/adapter/driver/driver.c @@ -406,7 +406,7 @@ void neu_adapter_driver_read_group(neu_adapter_driver_t *driver, neu_resp_read_group_t resp = { 0 }; neu_group_t * group = g->group; - UT_array * tags = neu_group_get_read_tag(group); + UT_array *tags = neu_group_query_read_tag(group, cmd->name, cmd->desc); utarray_new(resp.tags, neu_resp_tag_value_meta_icd()); diff --git a/src/base/group.c b/src/base/group.c index 99112755..02e1ad6c 100644 --- a/src/base/group.c +++ b/src/base/group.c @@ -44,7 +44,6 @@ struct neu_group { }; static UT_array *to_array(tag_elem_t *tags); -static UT_array *to_read_array(tag_elem_t *tags); static void split_static_array(tag_elem_t *tags, UT_array **static_tags, UT_array **other_tags); static void update_timestamp(neu_group_t *group); @@ -192,19 +191,84 @@ UT_array *neu_group_get_tag(neu_group_t *group) return array; } -UT_array *neu_group_query_tag(neu_group_t *group, const char *name) +static inline UT_array * +filter_tags(tag_elem_t *tags, + bool (*predicate)(const neu_datatag_t *, void *data), void *data) { tag_elem_t *el = NULL, *tmp = NULL; UT_array * array = NULL; - pthread_mutex_lock(&group->mtx); utarray_new(array, neu_tag_get_icd()); - HASH_ITER(hh, group->tags, el, tmp) + HASH_ITER(hh, tags, el, tmp) { - if (strstr(el->tag->name, name) != NULL) { + if (predicate(el->tag, data)) { utarray_push_back(array, el->tag); } } + + return array; +} + +static inline bool is_readable(const neu_datatag_t *tag, void *data) +{ + (void) data; + return neu_tag_attribute_test(tag, NEU_ATTRIBUTE_READ) || + neu_tag_attribute_test(tag, NEU_ATTRIBUTE_SUBSCRIBE) || + neu_tag_attribute_test(tag, NEU_ATTRIBUTE_STATIC); +} + +static inline bool name_contains(const neu_datatag_t *tag, void *data) +{ + const char *name = data; + return strstr(tag->name, name) != NULL; +} + +static inline bool description_contains(const neu_datatag_t *tag, void *data) +{ + const char *str = data; + return tag->description && strstr(tag->description, str) != NULL; +} + +struct query { + char *name; + char *desc; +}; + +static inline bool match_query(const neu_datatag_t *tag, void *data) +{ + struct query *q = data; + return (!q->name || name_contains(tag, q->name)) && + (!q->desc || description_contains(tag, q->desc)); +} + +static inline bool is_readable_and_match_query(const neu_datatag_t *tag, + void * data) +{ + return is_readable(tag, NULL) && match_query(tag, data); +} + +UT_array *neu_group_query_tag(neu_group_t *group, const char *name) +{ + UT_array *array = NULL; + + pthread_mutex_lock(&group->mtx); + array = filter_tags(group->tags, name_contains, (void *) name); + pthread_mutex_unlock(&group->mtx); + + return array; +} + +UT_array *neu_group_query_read_tag(neu_group_t *group, const char *name, + const char *desc) +{ + UT_array * array = NULL; + struct query q = { + .name = (char *) name, + .desc = (char *) desc, + }; + + pthread_mutex_lock(&group->mtx); + array = filter_tags(group->tags, is_readable_and_match_query, &q); pthread_mutex_unlock(&group->mtx); return array; @@ -215,7 +279,7 @@ UT_array *neu_group_get_read_tag(neu_group_t *group) UT_array *array = NULL; pthread_mutex_lock(&group->mtx); - array = to_read_array(group->tags); + array = filter_tags(group->tags, is_readable, NULL); pthread_mutex_unlock(&group->mtx); return array; @@ -290,24 +354,6 @@ static UT_array *to_array(tag_elem_t *tags) return array; } -static UT_array *to_read_array(tag_elem_t *tags) -{ - tag_elem_t *el = NULL, *tmp = NULL; - UT_array * array = NULL; - - utarray_new(array, neu_tag_get_icd()); - HASH_ITER(hh, tags, el, tmp) - { - if (neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_READ) || - neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_SUBSCRIBE) || - neu_tag_attribute_test(el->tag, NEU_ATTRIBUTE_STATIC)) { - utarray_push_back(array, el->tag); - } - } - - return array; -} - static void split_static_array(tag_elem_t *tags, UT_array **static_tags, UT_array **other_tags) { diff --git a/src/base/group.h b/src/base/group.h index e109fdd3..a5ed41c4 100644 --- a/src/base/group.h +++ b/src/base/group.h @@ -41,6 +41,8 @@ int neu_group_del_tag(neu_group_t *group, const char *tag_name); UT_array * neu_group_get_tag(neu_group_t *group); UT_array * neu_group_query_tag(neu_group_t *group, const char *name); UT_array * neu_group_get_read_tag(neu_group_t *group); +UT_array * neu_group_query_read_tag(neu_group_t *group, const char *name, + const char *desc); uint16_t neu_group_tag_size(const neu_group_t *group); neu_datatag_t *neu_group_find_tag(neu_group_t *group, const char *tag); void neu_group_split_static_tags(neu_group_t *group, UT_array **static_tags, diff --git a/src/base/msg_internal.h b/src/base/msg_internal.h index b0648652..49223f74 100644 --- a/src/base/msg_internal.h +++ b/src/base/msg_internal.h @@ -149,9 +149,6 @@ static inline neu_msg_t *neu_msg_new(neu_reqresp_type_e t, void *ctx, case NEU_REQ_GET_NODES_STATE: body_size = neu_reqresp_size(NEU_RESP_GET_NODES_STATE); break; - case NEU_REQ_READ_GROUP: - body_size = neu_reqresp_size(NEU_RESP_READ_GROUP); - break; default: body_size = data_size; } diff --git a/src/parser/neu_json_rw.c b/src/parser/neu_json_rw.c index 719afac4..344ea14a 100644 --- a/src/parser/neu_json_rw.c +++ b/src/parser/neu_json_rw.c @@ -430,6 +430,11 @@ int neu_json_decode_read_req(char *buf, neu_json_read_req_t **result) .t = NEU_JSON_BOOL, .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, }, + { + .name = "query", + .t = NEU_JSON_OBJECT, + .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, + }, }; ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems), req_elems); @@ -441,11 +446,36 @@ int neu_json_decode_read_req(char *buf, neu_json_read_req_t **result) req->group = req_elems[1].v.val_str; req->sync = req_elems[2].v.val_bool; + neu_json_elem_t query_elems[] = { + { + .name = "name", + .t = NEU_JSON_STR, + .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, + }, + { + .name = "description", + .t = NEU_JSON_STR, + .attribute = NEU_JSON_ATTRIBUTE_OPTIONAL, + }, + }; + if (req_elems[3].v.val_object) { + ret = neu_json_decode_by_json(req_elems[3].v.val_object, + NEU_JSON_ELEM_SIZE(query_elems), + query_elems); + if (ret != 0) { + goto error; + } + req->name = query_elems[0].v.val_str; + req->desc = query_elems[1].v.val_str; + } + *result = req; neu_json_decode_free(json_obj); return ret; error: + free(query_elems[0].v.val_str); + free(query_elems[1].v.val_str); free(req_elems[0].v.val_str); free(req_elems[1].v.val_str); free(req); diff --git a/tests/ft/driver/test_modbus.py b/tests/ft/driver/test_modbus.py index fd597fb8..5f32623b 100644 --- a/tests/ft/driver/test_modbus.py +++ b/tests/ft/driver/test_modbus.py @@ -68,15 +68,15 @@ coil_bit_5 = [{"name": "coil_bit_5", "address": "1!000010", "attribute": config.NEU_TAG_ATTRIBUTE_RW, "type": config.NEU_TYPE_BIT}] input_bit_1 = [{"name": "input_bit_1", "address": "1!100001", - "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT}] + "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT, "description": "bit_1"}] input_bit_2 = [{"name": "input_bit_2", "address": "1!100002", - "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT}] + "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT, "description": "bit_2"}] input_bit_3 = [{"name": "input_bit_3", "address": "1!100003", - "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT}] + "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT, "description": "bit_3"}] input_bit_4 = [{"name": "input_bit_4", "address": "1!100005", - "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT}] + "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT, "description": "bit_4"}] input_bit_5 = [{"name": "input_bit_5", "address": "1!100010", - "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT}] + "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT, "description": "bit_5"}] input_register_bit = [{"name": "input_register_bit", "address": "1!30101.15", "attribute": config.NEU_TAG_ATTRIBUTE_READ, "type": config.NEU_TYPE_BIT}] @@ -568,6 +568,40 @@ class TestModbus: assert 0 == api.read_tag( node=param[0], group='group', tag=input_bit_5[0]['name']) + @description(given="created modbus node and tags", when="read tags fuzz", then="should return correct result") + def test_read_tags_fuzz(self, param): + response = api.read_tags( + node=param[0], group='group', query={"name": "input_bit"}) + assert 200 == response.status_code + assert 5 == len(response.json()["tags"]) + + response = api.read_tags( + node=param[0], group='group', query={"name": "input_bit_1"}) + assert 200 == response.status_code + assert 1 == len(response.json()["tags"]) + + response = api.read_tags( + node=param[0], group='group', query={"description": "bit"}) + assert 200 == response.status_code + assert 5 == len(response.json()["tags"]) + + response = api.read_tags( + node=param[0], group='group', query={"description": "bit_1"}) + assert 200 == response.status_code + assert 1 == len(response.json()["tags"]) + + response = api.read_tags( + node=param[0], group='group', query={"name": "input_bit", "description": "bit"}) + assert 200 == response.status_code + assert 5 == len(response.json()["tags"]) + + response = api.read_tags( + node=param[0], group='group', query={"name": "input_bit_1", "description": "bit_1"}) + assert 200 == response.status_code + assert 1 == len(response.json()["tags"]) + + + @description(given="created modbus node and tags", when="read tags synchronously", then="read fail") def test_read_tags_sync(self, param): assert error.NEU_ERR_PLUGIN_NOT_SUPPORT_READ_SYNC == api.read_tag_error( diff --git a/tests/ft/neuron/api.py b/tests/ft/neuron/api.py index c376426a..18620cb3 100644 --- a/tests/ft/neuron/api.py +++ b/tests/ft/neuron/api.py @@ -145,12 +145,15 @@ def get_tags(node, group): return requests.get(url=config.BASE_URL + "/api/v2/tags", headers={"Authorization": config.default_jwt}, params={"node": node, "group": group}) -def read_tags(node, group, sync=False): - return requests.post(url=config.BASE_URL + "/api/v2/read", headers={"Authorization": config.default_jwt}, json={"node": node, "group": group, "sync": sync}) +def read_tags(node, group, sync=False, query=None): + body = {"node": node, "group": group, "sync": sync} + if query: + body["query"] = query + return requests.post(url=config.BASE_URL + "/api/v2/read", headers={"Authorization": config.default_jwt}, json=body) def read_tag(node, group, tag, sync=False): - response = read_tags(node, group, sync) + response = read_tags(node, group, sync, query={"name": tag}) assert 200 == response.status_code x = list(filter(lambda x: x['name'] == tag, response.json()['tags']))