From 0b19e0b17b0ad3d2058c0e691b064aa66fbc890b Mon Sep 17 00:00:00 2001 From: hxy7yx <1595670487@qq.com> Date: Wed, 15 May 2024 16:28:10 +0800 Subject: [PATCH] check if the written value conforms to the standard --- include/neuron/errcodes.h | 1 + src/adapter/driver/driver.c | 225 ++++++++++++++++++++++++++++++++- src/utils/http.c | 3 +- tests/ft/app/test_azure.py | 2 +- tests/ft/app/test_ekuiper.py | 4 +- tests/ft/app/test_mqtt.py | 2 +- tests/ft/driver/test_modbus.py | 158 ++++++++++++++++++++++- tests/ft/neuron/error.py | 1 + 8 files changed, 386 insertions(+), 10 deletions(-) diff --git a/include/neuron/errcodes.h b/include/neuron/errcodes.h index 131ca3e0..7a4bba55 100644 --- a/include/neuron/errcodes.h +++ b/include/neuron/errcodes.h @@ -136,6 +136,7 @@ typedef enum { NEU_ERR_PLUGIN_NOT_SUPPORT_WRITE_TAGS = 3017, NEU_ERR_PLUGIN_NOT_SUPPORT_READ_SYNC = 3018, NEU_ERR_PLUGIN_TYPE_NOT_SUPPORT = 3019, + NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE = 3020, NEU_ERR_MQTT_FAILURE = 4000, NEU_ERR_MQTT_NO_CERTFILESET = 4001, diff --git a/src/adapter/driver/driver.c b/src/adapter/driver/driver.c index f294a9f3..4b72bfa9 100644 --- a/src/adapter/driver/driver.c +++ b/src/adapter/driver/driver.c @@ -17,11 +17,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include +#include #include #include #include #include +#define EPSILON 1e-9 + #include "event/event.h" #include "utils/log.h" #include "utils/utextend.h" @@ -667,6 +670,158 @@ static void cal_decimal(neu_type_e tag_type, neu_type_e value_type, } } +int almost_equal(double a, double b, double epsilon) +{ + return fabs(a - b) < epsilon; +} + +int check_value_decimal(neu_type_e write_type, double result, int64_t value_min, + int64_t value_max) +{ + if (write_type != NEU_TYPE_INT64 && write_type != NEU_TYPE_DOUBLE) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else if (result < value_min || result > value_max) { + return NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE; + } else if (almost_equal(ceil(result), result, EPSILON)) { + return NEU_ERR_SUCCESS; + } else { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } +} + +int check_value(neu_type_e write_type, int64_t value, int64_t value_min, + int64_t value_max) +{ + if (write_type != NEU_TYPE_INT64) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else if (value >= value_min && value <= value_max) { + return NEU_ERR_SUCCESS; + } else { + return NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE; + } +} + +int is_value_in_range(neu_type_e tag_type, int64_t value, double value_d, + neu_type_e write_type, double decimal) +{ + if (decimal != 0) { + double result; + + if (write_type == NEU_TYPE_INT64) { + result = value / decimal; + } else { + result = value_d / decimal; + } + + switch (tag_type) { + case NEU_TYPE_INT8: + return check_value_decimal(write_type, result, INT8_MIN, INT8_MAX); + case NEU_TYPE_UINT8: + return check_value_decimal(write_type, result, 0, UINT8_MAX); + case NEU_TYPE_INT16: + return check_value_decimal(write_type, result, INT16_MIN, + INT16_MAX); + case NEU_TYPE_UINT16: + return check_value_decimal(write_type, result, 0, UINT16_MAX); + case NEU_TYPE_INT32: + return check_value_decimal(write_type, result, INT32_MIN, + INT32_MAX); + case NEU_TYPE_UINT32: + return check_value_decimal(write_type, result, 0, UINT32_MAX); + case NEU_TYPE_INT64: + case NEU_TYPE_UINT64: + if (write_type != NEU_TYPE_INT64 && write_type != NEU_TYPE_DOUBLE) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else if (almost_equal(ceil(result), result, EPSILON)) { + return NEU_ERR_SUCCESS; + } else { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } + case NEU_TYPE_FLOAT: + if (write_type != NEU_TYPE_DOUBLE) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else if (result < (int64_t) -FLT_MAX || + result > (int64_t) FLT_MAX) { + return NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE; + } else { + return NEU_ERR_SUCCESS; + } + case NEU_TYPE_DOUBLE: + if (write_type != NEU_TYPE_DOUBLE) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else { + return NEU_ERR_SUCCESS; + } + return check_value(write_type, result, INT16_MIN, INT16_MAX); + case NEU_TYPE_STRING: + case NEU_TYPE_BOOL: + case NEU_TYPE_BIT: + return NEU_ERR_PARAM_IS_WRONG; + default: + return NEU_ERR_SUCCESS; + } + } else { + switch (tag_type) { + case NEU_TYPE_BOOL: + if (write_type == NEU_TYPE_BOOL) { + return NEU_ERR_SUCCESS; + } else { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } + case NEU_TYPE_BIT: + if (write_type != NEU_TYPE_INT64) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else if (value != 1 && value != 0) { + return NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE; + } else { + return NEU_ERR_SUCCESS; + } + case NEU_TYPE_INT8: + return check_value(write_type, value, INT8_MIN, INT8_MAX); + case NEU_TYPE_UINT8: + return check_value(write_type, value, 0, UINT8_MAX); + case NEU_TYPE_INT16: + return check_value(write_type, value, INT16_MIN, INT16_MAX); + case NEU_TYPE_UINT16: + return check_value(write_type, value, 0, UINT16_MAX); + case NEU_TYPE_INT32: + return check_value(write_type, value, INT32_MIN, INT32_MAX); + case NEU_TYPE_UINT32: + return check_value(write_type, value, 0, UINT32_MAX); + case NEU_TYPE_INT64: + case NEU_TYPE_UINT64: + if (write_type == NEU_TYPE_INT64) { + return NEU_ERR_SUCCESS; + } else { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } + case NEU_TYPE_FLOAT: + if (write_type != NEU_TYPE_DOUBLE) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else if (value < (int64_t) -FLT_MAX || + value > (int64_t) FLT_MAX) { + return NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE; + } else { + return NEU_ERR_SUCCESS; + } + case NEU_TYPE_DOUBLE: + if (write_type != NEU_TYPE_DOUBLE) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else { + return NEU_ERR_SUCCESS; + } + case NEU_TYPE_STRING: + if (write_type != NEU_TYPE_STRING) { + return NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH; + } else { + return NEU_ERR_SUCCESS; + } + default: + return NEU_ERR_SUCCESS; + } + } +} + void neu_adapter_driver_write_tags(neu_adapter_driver_t *driver, neu_reqresp_head_t * req) { @@ -695,10 +850,24 @@ void neu_adapter_driver_write_tags(neu_adapter_driver_t *driver, UT_array *tags = NULL; UT_icd icd = { sizeof(neu_plugin_tag_value_t), NULL, NULL, NULL }; utarray_new(tags, &icd); + int value_err = NEU_ERR_SUCCESS; + int value_check; + for (int i = 0; i < cmd->n_tag; i++) { neu_datatag_t *tag = neu_group_find_tag(g->group, cmd->tags[i].tag); + + if (tag != NULL) { + value_check = + is_value_in_range(tag->type, cmd->tags[i].value.value.i64, + cmd->tags[i].value.value.d64, + cmd->tags[i].value.type, tag->decimal); + } else { + value_check = NEU_ERR_TAG_NOT_EXIST; + } + if (tag != NULL && neu_tag_attribute_test(tag, NEU_ATTRIBUTE_WRITE) && - neu_tag_attribute_test(tag, NEU_ATTRIBUTE_STATIC) == false) { + neu_tag_attribute_test(tag, NEU_ATTRIBUTE_STATIC) == false && + value_check == NEU_ERR_SUCCESS) { if (tag->type == NEU_TYPE_FLOAT || tag->type == NEU_TYPE_DOUBLE) { if (cmd->tags[i].value.type == NEU_TYPE_INT64) { cmd->tags[i].value.value.d64 = @@ -716,12 +885,25 @@ void neu_adapter_driver_write_tags(neu_adapter_driver_t *driver, .value = cmd->tags[i].value.value, }; utarray_push_back(tags, &tv); + } else { + value_err = value_check; } if (tag != NULL) { neu_tag_free(tag); } } + if (value_err != NEU_ERR_SUCCESS) { + driver->adapter.cb_funs.driver.write_response(&driver->adapter, req, + value_err); + utarray_foreach(tags, neu_plugin_tag_value_t *, tv) + { + neu_tag_free(tv->tag); + } + utarray_free(tags); + return; + } + if (utarray_len(tags) != (unsigned int) cmd->n_tag) { driver->adapter.cb_funs.driver.write_response(&driver->adapter, req, NEU_ERR_TAG_NOT_EXIST); @@ -776,6 +958,8 @@ void neu_adapter_driver_write_gtags(neu_adapter_driver_t *driver, UT_array *tags = NULL; UT_icd icd = { sizeof(neu_plugin_tag_value_t), NULL, NULL, NULL }; utarray_new(tags, &icd); + int value_err = NEU_ERR_SUCCESS; + int value_check; for (int i = 0; i < cmd->n_group; i++) { group_t *g = find_group(driver, cmd->groups[i].group); @@ -783,10 +967,20 @@ void neu_adapter_driver_write_gtags(neu_adapter_driver_t *driver, for (int k = 0; k < cmd->groups[i].n_tag; k++) { neu_datatag_t *tag = neu_group_find_tag(g->group, cmd->groups[i].tags[k].tag); + + if (tag != NULL) { + value_check = is_value_in_range( + tag->type, cmd->groups[i].tags[k].value.value.i64, + cmd->groups[i].tags[k].value.value.d64, + cmd->groups[i].tags[k].value.type, tag->decimal); + } else { + value_check = NEU_ERR_TAG_NOT_EXIST; + } + if (tag != NULL && neu_tag_attribute_test(tag, NEU_ATTRIBUTE_WRITE) && - neu_tag_attribute_test(tag, NEU_ATTRIBUTE_STATIC) == false) { - + neu_tag_attribute_test(tag, NEU_ATTRIBUTE_STATIC) == false && + value_check == NEU_ERR_SUCCESS) { if (tag->type == NEU_TYPE_FLOAT || tag->type == NEU_TYPE_DOUBLE) { if (cmd->groups[i].tags[k].value.type == NEU_TYPE_INT64) { @@ -807,6 +1001,8 @@ void neu_adapter_driver_write_gtags(neu_adapter_driver_t *driver, .value = cmd->groups[i].tags[k].value.value, }; utarray_push_back(tags, &tv); + } else { + value_err = value_check; } if (tag != NULL) { neu_tag_free(tag); @@ -814,6 +1010,17 @@ void neu_adapter_driver_write_gtags(neu_adapter_driver_t *driver, } } + if (value_err != NEU_ERR_SUCCESS) { + driver->adapter.cb_funs.driver.write_response(&driver->adapter, req, + value_err); + utarray_foreach(tags, neu_plugin_tag_value_t *, tv) + { + neu_tag_free(tv->tag); + } + utarray_free(tags); + return; + } + uint32_t n_tag = 0; for (int i = 0; i < cmd->n_group; i++) { n_tag += cmd->groups[i].n_tag; @@ -867,6 +1074,18 @@ void neu_adapter_driver_write_tag(neu_adapter_driver_t *driver, neu_tag_free(tag); return; } + + int value_check = is_value_in_range(tag->type, cmd->value.value.i64, + cmd->value.value.d64, + cmd->value.type, tag->decimal); + + if (value_check != NEU_ERR_SUCCESS) { + driver->adapter.cb_funs.driver.write_response(&driver->adapter, req, + value_check); + neu_tag_free(tag); + return; + } + if (tag->type == NEU_TYPE_FLOAT || tag->type == NEU_TYPE_DOUBLE) { if (cmd->value.type == NEU_TYPE_INT64) { cmd->value.value.d64 = (double) cmd->value.value.i64; diff --git a/src/utils/http.c b/src/utils/http.c index b10b6c18..13e5b6cd 100644 --- a/src/utils/http.c +++ b/src/utils/http.c @@ -254,7 +254,6 @@ int neu_http_response(nng_aio *aio, neu_err_code_e code, char *content) case NEU_ERR_LICENSE_MAX_NODES: case NEU_ERR_LICENSE_TOKEN_NOT_MATCH: case NEU_ERR_GROUP_ALREADY_SUBSCRIBED: - case NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH: case NEU_ERR_PLUGIN_PROTOCOL_DECODE_FAILURE: case NEU_ERR_PLUGIN_NOT_RUNNING: case NEU_ERR_PLUGIN_TAG_NOT_READY: @@ -320,6 +319,8 @@ int neu_http_response(nng_aio *aio, neu_err_code_e code, char *content) case NEU_ERR_TAG_PRECISION_INVALID: case NEU_ERR_TAG_NAME_TOO_LONG: case NEU_ERR_TAG_ADDRESS_TOO_LONG: + case NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH: + case NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE: case NEU_ERR_TAG_BIAS_INVALID: case NEU_ERR_GROUP_MAX_GROUPS: case NEU_ERR_LICENSE_MAX_TAGS: diff --git a/tests/ft/app/test_azure.py b/tests/ft/app/test_azure.py index f9567670..377b5d73 100644 --- a/tests/ft/app/test_azure.py +++ b/tests/ft/app/test_azure.py @@ -455,7 +455,7 @@ class TestAzure: }, { "tag": "tag1", - "value": True, + "value": 1, }, { "tag": "tag2", diff --git a/tests/ft/app/test_ekuiper.py b/tests/ft/app/test_ekuiper.py index b910c4fb..809178a0 100644 --- a/tests/ft/app/test_ekuiper.py +++ b/tests/ft/app/test_ekuiper.py @@ -377,7 +377,7 @@ class TesteKuiper: } req_tags = { TAGS[0]["name"]: 1234, - TAGS[1]["name"]: True, + TAGS[1]["name"]: 1, TAGS[2]["name"]: 3.14, TAGS[3]["name"]: "hello", TAGS[4]["name"]: [1, 2, 3, 4], @@ -407,7 +407,7 @@ class TesteKuiper: req_tags = { TAGS[0]["name"]: 5678, - TAGS[1]["name"]: False, + TAGS[1]["name"]: 0, TAGS[2]["name"]: 6.28, TAGS[3]["name"]: "mulitple", TAGS[4]["name"]: [9, 8, 7, 6], diff --git a/tests/ft/app/test_mqtt.py b/tests/ft/app/test_mqtt.py index 03065311..c4fa52e0 100644 --- a/tests/ft/app/test_mqtt.py +++ b/tests/ft/app/test_mqtt.py @@ -657,7 +657,7 @@ class TestMQTT: }, { "tag": "tag1", - "value": True, + "value": 1, }, { "tag": "tag2", diff --git a/tests/ft/driver/test_modbus.py b/tests/ft/driver/test_modbus.py index 96ad4123..aaacfa27 100644 --- a/tests/ft/driver/test_modbus.py +++ b/tests/ft/driver/test_modbus.py @@ -199,6 +199,8 @@ hold_string_s = [{"name": "hold_string_s", "address": "1!400870.2", hold_int16_decimal = [{"name": "hold_int16_decimal", "address": "1!40103", "attribute": config.NEU_TAG_ATTRIBUTE_RW, "type": config.NEU_TYPE_INT16, "decimal": 0.1}] +hold_int16_decimal_w = [{"name": "hold_int16_decimal_w", "address": "1!40103", + "attribute": config.NEU_TAG_ATTRIBUTE_RW, "type": config.NEU_TYPE_INT16, "decimal": 0.1}] hold_int16_decimal_neg = [{"name": "int16_decimal_neg", "address": "1!40140", "attribute": config.NEU_TAG_ATTRIBUTE_RW, "type": config.NEU_TYPE_INT16, "decimal": -1}] hold_uint16_decimal = [{"name": "hold_uint16_decimal", "address": "1!400105", @@ -524,6 +526,102 @@ class TestModbus: assert 400 == response.status_code assert error.NEU_ERR_TAG_ADDRESS_FORMAT_INVALID == response.json()[ 'error'] + + @description(given="created modbus node and tags", when="write tags with invalid value", then="write failed") + def test_write_tags_invalid_value(self, param): + response = api.write_tag(node=param[0], group='group', tag=hold_int16[0]['name'], value=32768) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + response = api.write_tag(node=param[0], group='group', tag=hold_uint16[0]['name'], value=-1) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + response = api.write_tag(node=param[0], group='group', tag=hold_int32[0]['name'], value=2147483648) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + response = api.write_tag(node=param[0], group='group', tag=hold_uint32[0]['name'], value=-1) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + response = api.write_tag(node=param[0], group='group', tag=hold_string[0]['name'], value=1) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH == response.json()['error'] + + response = api.write_tag(node=param[0], group='group', tag=coil_bit_1[0]['name'], value=2) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + response = api.write_tag(node=param[0], group='group', tag=coil_bit_1[0]['name'], value=True) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH == response.json()['error'] + + api.add_tags_check(node=param[0], group='group', tags=hold_int16_decimal_w) + + response = api.write_tag(node=param[0], group='group', tag=hold_int16_decimal_w[0]['name'], value=1.11) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH == response.json()['error'] + + response = api.write_tag(node=param[0], group='group', tag=hold_float[0]['name'], value=1) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_TYPE_MISMATCH == response.json()['error'] + + response = api.write_tags(node=param[0], group='group', tag_values=[ + {"tag": hold_int16[0]['name'], "value": 32768}, + {"tag": hold_uint16[0]['name'], "value": 2}, + {"tag": hold_int32[0]['name'], "value": 3}, + {"tag": hold_uint32[0]['name'], "value": 4}]) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + response = api.write_tags(node=param[0], group='group', tag_values=[ + {"tag": hold_int16[0]['name'], "value": 1}, + {"tag": hold_uint16[0]['name'], "value": -1}, + {"tag": hold_int32[0]['name'], "value": 3}, + {"tag": hold_uint32[0]['name'], "value": 4}]) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + response = api.write_tags(node=param[0], group='group', tag_values=[ + {"tag": hold_int16[0]['name'], "value": 1}, + {"tag": hold_uint16[0]['name'], "value": 2}, + {"tag": hold_int32[0]['name'], "value": 2147483648}, + {"tag": hold_uint32[0]['name'], "value": 4}]) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + response = api.write_tags(node=param[0], group='group', tag_values=[ + {"tag": hold_int16[0]['name'], "value": 1}, + {"tag": hold_uint16[0]['name'], "value": 2}, + {"tag": hold_int32[0]['name'], "value": 3}, + {"tag": hold_uint32[0]['name'], "value": -1}]) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + modbus_write_gtags = {"node": param[0], "groups": [{"group": "group", "tags" : [{"tag": "hold_int16", "value": 32768}, + {"tag": "hold_uint16", "value": 2}, {"tag": "hold_int32", "value": 3}, {"tag": "hold_uint32", "value": 4}]}]} + response = api.write_gtags(json=modbus_write_gtags) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + modbus_write_gtags = {"node": param[0], "groups": [{"group": "group", "tags" : [{"tag": "hold_int16", "value": 1}, + {"tag": "hold_uint16", "value": -1}, {"tag": "hold_int32", "value": 3}, {"tag": "hold_uint32", "value": 4}]}]} + response = api.write_gtags(json=modbus_write_gtags) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + modbus_write_gtags = {"node": param[0], "groups": [{"group": "group", "tags" : [{"tag": "hold_int16", "value": 1}, + {"tag": "hold_uint16", "value": 2}, {"tag": "hold_int32", "value": 2147483648}, {"tag": "hold_uint32", "value": 4}]}]} + response = api.write_gtags(json=modbus_write_gtags) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] + + modbus_write_gtags = {"node": param[0], "groups": [{"group": "group", "tags" : [{"tag": "hold_int16", "value": 1}, + {"tag": "hold_uint16", "value": 2}, {"tag": "hold_int32", "value": 3}, {"tag": "hold_uint32", "value": -1}]}]} + response = api.write_gtags(json=modbus_write_gtags) + assert 400 == response.status_code + assert error.NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE == response.json()['error'] @description(given="created modbus node and tags", when="write/read tags", then="write/read success") def test_write_read_tags(self, param): @@ -888,7 +986,7 @@ class TestModbus: node=param[0], group='group', tag=hold_string_utf8[0]['name']) @description(given="created modbus node/tag", when="read/write decimal/bias tag", then="read success") - def test_read_decimal_bias_tag(self, param): + def test_read_decimal_bias_tag_i(self, param): tags = [ hold_int16[0], hold_int16_L[0], @@ -912,6 +1010,62 @@ class TestModbus: hold_uint64[0], hold_uint64_L[0], hold_uint64_B[0], + ] + + tags_bias = [ + { + **tag, + "bias": random.randint(1, 1000), + "attribute": tag["attribute"] & ~config.NEU_TAG_ATTRIBUTE_WRITE, + } + for tag in tags + ] + + tags_decimal_bias = [ + { + **tag, + "decimal": 0.1, + } + for tag in tags_bias + ] + + tag_values = [ + { + "tag": tag['name'], + "value": random.randint(1, 10) + } + for tag in tags + ] + + try: + # 1. prepare tags + grp = "group-bias" + api.add_group_check(node=param[0], group=grp, interval=100) + api.add_tags_check(node=param[0], group=grp, tags=tags) + api.write_tags_check(node=param[0], group=grp, tag_values=tag_values) + # 2. read value + bias + api.update_tags_check(node=param[0], group=grp, tags=tags_bias) + time.sleep(0.3) + resp_values = api.read_tags(node=param[0], group=grp).json()['tags'] + assert len(tag_values) == len(resp_values) + for tag, value, resp in zip(tags_bias, tag_values, resp_values): + expected = value['value'] + tag['bias'] + assert compare_float(expected, resp['value']) + # 3. read value * decimal + bias + api.update_tags_check( + node=param[0], group=grp, tags=tags_decimal_bias) + time.sleep(0.3) + resp_values = api.read_tags(node=param[0], group=grp).json()['tags'] + assert len(tag_values) == len(resp_values) + for tag, value, resp in zip(tags_decimal_bias, tag_values, resp_values): + expected = value['value'] * tag['decimal'] + tag['bias'] + assert compare_float(expected, resp['value']) + finally: + api.del_group(node=param[0], group=grp) + + @description(given="created modbus node/tag", when="read/write decimal/bias tag", then="read success") + def test_read_decimal_bias_tag_f(self, param): + tags = [ hold_float[0], hold_float_LL[0], hold_float_LB[0], @@ -942,7 +1096,7 @@ class TestModbus: tag_values = [ { "tag": tag['name'], - "value": random.randint(1, 10) + "value": random.uniform(1.0, 10.0) } for tag in tags ] diff --git a/tests/ft/neuron/error.py b/tests/ft/neuron/error.py index 0b48cd6b..e27f6cbb 100644 --- a/tests/ft/neuron/error.py +++ b/tests/ft/neuron/error.py @@ -106,5 +106,6 @@ NEU_ERR_PLUGIN_NOT_SUPPORT_TEMPLATE = 3016 NEU_ERR_PLUGIN_NOT_SUPPORT_WRITE_TAGS = 3017 NEU_ERR_PLUGIN_NOT_SUPPORT_READ_SYNC = 3018 NEU_ERR_PLUGIN_TYPE_NOT_SUPPORT = 3019 +NEU_ERR_PLUGIN_TAG_VALUE_OUT_OF_RANGE = 3020 NEU_ERR_MQTT_SUBSCRIBE_FAILURE = 4010 \ No newline at end of file