diff --git a/src/base/group.c b/src/base/group.c index 84640b62..67155936 100644 --- a/src/base/group.c +++ b/src/base/group.c @@ -29,6 +29,7 @@ typedef struct tag_elem { char *name; neu_datatag_t *tag; + uint64_t order; UT_hash_handle hh; } tag_elem_t; @@ -37,15 +38,22 @@ struct neu_group { char *name; tag_elem_t *tags; + uint64_t next_tag_order; uint32_t interval; int64_t timestamp; pthread_mutex_t mtx; }; +static int tag_order_cmp(tag_elem_t *left, tag_elem_t *right); static UT_array *to_array(tag_elem_t *tags); static void update_timestamp(neu_group_t *group); +static int tag_order_cmp(tag_elem_t *left, tag_elem_t *right) +{ + return (left->order > right->order) - (left->order < right->order); +} + neu_group_t *neu_group_new(const char *name, uint32_t interval) { neu_group_t *group = calloc(1, sizeof(neu_group_t)); @@ -166,6 +174,7 @@ int neu_group_add_tag(neu_group_t *group, const neu_datatag_t *tag) return NEU_ERR_EINTERNAL; } + el->order = group->next_tag_order++; HASH_ADD_STR(group->tags, name, el); update_timestamp(group); pthread_mutex_unlock(&group->mtx); @@ -232,7 +241,8 @@ int neu_group_rename_tag(neu_group_t *group, const char *old_name, el->name = new_el_name; free(el->tag->name); el->tag->name = new_tag_name; - HASH_ADD_STR(group->tags, name, el); + HASH_ADD_KEYPTR_INORDER(hh, group->tags, el->name, strlen(el->name), el, + tag_order_cmp); update_timestamp(group); ret = NEU_ERR_SUCCESS; diff --git a/tests/ft/tag/test_tag.py b/tests/ft/tag/test_tag.py index 074c898f..b7884b1d 100644 --- a/tests/ft/tag/test_tag.py +++ b/tests/ft/tag/test_tag.py @@ -826,24 +826,39 @@ class TestTag: assert NEU_ERR_TAG_DECIMAL_INVALID == response.json()['error'] assert 1 == response.json()['index'] - @description(given="an existing tag", when="renaming to a new name", then="successfully renamed") + @description(given="three existing tags", when="renaming the middle tag", then="successfully renamed without changing tag order") def test_rename_tag_success(self): - tag = [{"name": "rename_test_tag", "address": "1!400001", - "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}] - api.add_tags(node="modbus-tcp", group="group1", tags=tag) + tags = [ + {"name": "rename_first_tag", "address": "1!400001", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}, + {"name": "rename_middle_tag", "address": "1!400002", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}, + {"name": "rename_last_tag", "address": "1!400003", + "attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}, + ] + api.add_tags(node="modbus-tcp", group="group1", tags=tags) response = api.rename_tag(node="modbus-tcp", group="group1", - old_name="rename_test_tag", new_name="renamed_tag") + old_name="rename_middle_tag", + new_name="renamed_middle_tag") assert 200 == response.status_code assert NEU_ERR_SUCCESS == response.json()['error'] response = api.get_tags(node="modbus-tcp", group="group1") assert 200 == response.status_code tag_names = [t['name'] for t in response.json()['tags']] - assert "renamed_tag" in tag_names - assert "rename_test_tag" not in tag_names + renamed_tags = [ + name for name in tag_names + if name in {"rename_first_tag", "renamed_middle_tag", + "rename_last_tag"} + ] + assert ["rename_first_tag", "renamed_middle_tag", + "rename_last_tag"] == renamed_tags + assert "rename_middle_tag" not in tag_names - api.del_tags(node="modbus-tcp", group="group1", tags=["renamed_tag"]) + api.del_tags(node="modbus-tcp", group="group1", + tags=["rename_first_tag", "renamed_middle_tag", + "rename_last_tag"]) @description(given="an existing tag", when="renaming to a name that already exists", then="fail with conflict error") def test_rename_tag_conflict(self):