From 617e4550bc90515ebff784dbd9b0c75066e477a3 Mon Sep 17 00:00:00 2001 From: hxy7yx <1595670487@qq.com> Date: Mon, 14 Aug 2023 16:09:22 +0800 Subject: [PATCH] feat(modbus):write multiple tags --- ft/modbus_rtu_test.robot | 13 +++ ft/modbus_tcp_test.robot | 13 +++ ft/resource/api.resource | 6 + ft/resource/driver.resource | 34 ++++++ plugins/modbus/modbus-rtu.json | 6 +- plugins/modbus/modbus-tcp.json | 6 +- plugins/modbus/modbus_point.c | 207 ++++++++++++++++++++++++++++++++- plugins/modbus/modbus_point.h | 28 ++++- plugins/modbus/modbus_req.c | 82 ++++++++++++- plugins/modbus/modbus_req.h | 5 +- plugins/modbus/modbus_rtu.c | 10 +- plugins/modbus/modbus_stack.c | 36 ++++-- plugins/modbus/modbus_stack.h | 2 +- plugins/modbus/modbus_tcp.c | 18 ++- 14 files changed, 434 insertions(+), 32 deletions(-) diff --git a/ft/modbus_rtu_test.robot b/ft/modbus_rtu_test.robot index d75e9978..3f52f311 100644 --- a/ft/modbus_rtu_test.robot +++ b/ft/modbus_rtu_test.robot @@ -48,6 +48,13 @@ ${input_bit_1_name_wrong} {"name": "input_bit", "address": "1!100001", ${input_bit_1_attribute_unmatch} {"name": "input_bit_1", "address": "1!100001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_BIT}} ${input_bit_1_type_unmatch} {"name": "input_bit_1", "address": "1!100001", "attribute": ${TAG_ATTRIBUTE_READ}, "type": ${TAG_TYPE_INT16}} +${hold_int16_1} {"name": "hold_int16_1", "address": "1!400001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}} +${hold_int16_2} {"name": "hold_int16_2", "address": "1!400002", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}} +${hold_int16_3} {"name": "hold_int16_3", "address": "1!400003", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}} +${hold_string_tags_1} {"name": "hold_string_tags_1", "address": "1!40001.4", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_STRING}} +${hold_string_tags_2} {"name": "hold_string_tags_2", "address": "1!40003.4", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_STRING}} +${hold_string_tags_3} {"name": "hold_string_tags_3", "address": "1!40005.4", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_STRING}} + *** Test Cases *** Set a node with right settings, it should be success. [Template] Set a ${node} with right ${node_settings}, it will be success. @@ -146,6 +153,12 @@ Update a tag with unmatch address and types and attribute, it should be failure. ${input_bit_1} ${input_bit_1_type_unmatch} ${modbus_rtu_node} 200 ${NEU_ERR_TAG_TYPE_NOT_SUPPORT} ${input_bit_1} ${input_bit_1_name_wrong} ${modbus_rtu_node} 200 ${NEU_ERR_TAG_NOT_EXIST} +Write 3 tags 1 time, it should return success + [Template] Write and Read ${tag_data1}, ${tag_data2} and ${tag_data3} named ${tag1}, ${tag2} and ${tag3} from ${node}, using ${check} to check the ${value1}, ${value2} and ${value3}, it will be success. + ${hold_int16_1} ${hold_int16_2} ${hold_int16_3} hold_int16_1 hold_int16_2 hold_int16_3 ${modbus_rtu_node} Compare Tag Value As Int 1 2 3 + ${coil_bit_1} ${coil_bit_2} ${coil_bit_3} coil_bit_1 coil_bit_2 coil_bit_3 ${modbus_rtu_node} Compare Tag Value As Int 1 0 1 + ${hold_string_tags_1} ${hold_string_tags_2} ${hold_string_tags_3} hold_string_tags_1 hold_string_tags_2 hold_string_tags_3 ${modbus_rtu_node} Compare Tag Value As Strings "a" "b" "c" + Write a value to a tag that don't exit in plc or neuron, it will be failure. [Template] Write a ${value} to a ${tag} with named ${tag_name} that don't exist in neuron or plc from ${node}, it will be failure. 12 ${input_bit_1} input ${modbus_rtu_node} diff --git a/ft/modbus_tcp_test.robot b/ft/modbus_tcp_test.robot index f60484ec..901a9a1d 100644 --- a/ft/modbus_tcp_test.robot +++ b/ft/modbus_tcp_test.robot @@ -69,6 +69,13 @@ ${hold_double_decimal} {"name": "hold_double_decimal", "address": "1!400027", ${hold_float_precision} {"name": "hold_float_precision", "address": "1!400030", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_FLOAT}, "precision": 2} ${hold_double_precision} {"name": "hold_double_precision", "address": "1!400040", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_DOUBLE}, "precision": 2} +${hold_int16_1} {"name": "hold_int16_1", "address": "1!400001", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}} +${hold_int16_2} {"name": "hold_int16_2", "address": "1!400002", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}} +${hold_int16_3} {"name": "hold_int16_3", "address": "1!400003", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_INT16}} +${hold_string_tags_1} {"name": "hold_string_tags_1", "address": "1!40001.4", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_STRING}} +${hold_string_tags_2} {"name": "hold_string_tags_2", "address": "1!40003.4", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_STRING}} +${hold_string_tags_3} {"name": "hold_string_tags_3", "address": "1!40005.4", "attribute": ${TAG_ATTRIBUTE_RW}, "type": ${TAG_TYPE_STRING}} + *** Test Cases *** Set a node with right settings, it should be success. [Template] Set a ${node} with right ${node_settings}, it will be success. @@ -167,6 +174,12 @@ Update a tag with right address and types and attribute, it will be success ${hold_int16} ${hold_int16_address_update} hold_int16 ${modbus_tcp_node} Compare Tag Value As Int 120 ${hold_int16} ${hold_int16_type_update} hold_int16 ${modbus_tcp_node} Compare Tag Value As Int 120 +Write 3 tags 1 time, it should return success + [Template] Write and Read ${tag_data1}, ${tag_data2} and ${tag_data3} named ${tag1}, ${tag2} and ${tag3} from ${node}, using ${check} to check the ${value1}, ${value2} and ${value3}, it will be success. + ${hold_int16_1} ${hold_int16_2} ${hold_int16_3} hold_int16_1 hold_int16_2 hold_int16_3 ${modbus_tcp_node} Compare Tag Value As Int 1 2 3 + ${coil_bit_1} ${coil_bit_2} ${coil_bit_3} coil_bit_1 coil_bit_2 coil_bit_3 ${modbus_tcp_node} Compare Tag Value As Int 1 0 1 + ${hold_string_tags_1} ${hold_string_tags_2} ${hold_string_tags_3} hold_string_tags_1 hold_string_tags_2 hold_string_tags_3 ${modbus_tcp_node} Compare Tag Value As Strings "a" "b" "c" + Add a tag with decimal, it should return success [Template] Add a ${tag} with right address and type and attribute from ${node}, it will be success. ${hold_int16_decimal} ${modbus_tcp_node} diff --git a/ft/resource/api.resource b/ft/resource/api.resource index 66b7516e..bcb2b504 100644 --- a/ft/resource/api.resource +++ b/ft/resource/api.resource @@ -186,6 +186,12 @@ Write Tags ${res} = Object response body [RETURN] ${res}[0] +Write 3 Tags 1 Time + [Arguments] ${node} ${group} ${tag1} ${value1} ${tag2} ${value2} ${tag3} ${value3} + POST /api/v2/write/tags {"node": "${node}", "group": "${group}", "tags": [{"tag": "${tag1}", "value": ${value1}}, {"tag": "${tag2}", "value": ${value2}}, {"tag": "${tag3}", "value": ${value3}}]} + ${res} = Object response body + [RETURN] ${res}[0] + Add Template [Arguments] ${name} ${plugin} ${groups} ${data} = Evaluate json.dumps(${groups}) json diff --git a/ft/resource/driver.resource b/ft/resource/driver.resource index dc30de5a..7f648b88 100644 --- a/ft/resource/driver.resource +++ b/ft/resource/driver.resource @@ -293,3 +293,37 @@ Write and read a ${value} to a ${tag} named ${tag_name} when the ${node} is stop Check Tag Error As Int ${res}[tags] ${tag_name} ${NEU_ERR_PLUGIN_NOT_RUNNING} [Teardown] Del Group ${node} group + +Write and Read ${tag_data1}, ${tag_data2} and ${tag_data3} named ${tag1}, ${tag2} and ${tag3} from ${node}, using ${check} to check the ${value1}, ${value2} and ${value3}, it will be success. + [Tags] Driver 18 + + ${res}= Add Group ${node} group 100 + Check Response Status ${res} 200 + Check Error Code ${res} ${NEU_ERR_SUCCESS} + + ${res}= Add Tags ${node} group ${tag_data1} + Check Response Status ${res} 200 + Check Error Code ${res} ${NEU_ERR_SUCCESS} + + ${res}= Add Tags ${node} group ${tag_data2} + Check Response Status ${res} 200 + Check Error Code ${res} ${NEU_ERR_SUCCESS} + + ${res}= Add Tags ${node} group ${tag_data3} + Check Response Status ${res} 200 + Check Error Code ${res} ${NEU_ERR_SUCCESS} + + Sleep 290ms + ${res}= Write 3 Tags 1 Time ${node} group ${tag1} ${value1} ${tag2} ${value2} ${tag3} ${value3} + Check Response Status ${res} 200 + Check Error Code ${res} ${NEU_ERR_SUCCESS} + + Sleep 100ms + ${res}= Read Tags ${node} group + Check Response Status ${res} 200 + + Run Keyword ${check} ${res}[tags] ${tag1} ${value1} + Run Keyword ${check} ${res}[tags] ${tag2} ${value2} + Run Keyword ${check} ${res}[tags] ${tag3} ${value3} + + [Teardown] Del Group ${node} group \ No newline at end of file diff --git a/plugins/modbus/modbus-rtu.json b/plugins/modbus/modbus-rtu.json index 46c7940b..4fbc5622 100644 --- a/plugins/modbus/modbus-rtu.json +++ b/plugins/modbus/modbus-rtu.json @@ -80,7 +80,7 @@ "name": "Maximum Retry Times", "name_zh": "最大重试次数", "description": "The maximum number of retries after a failed attempt to send a read command", - "description_zh": "发送读取指令失败后最大重试次数", + "description_zh": "发送读指令失败后最大重试次数", "attribute": "required", "type": "int", "default": 0, @@ -93,7 +93,7 @@ "name": "Retry Interval", "name_zh": "指令重新发送间隔", "description": "Resend reading instruction interval(ms) after a failed attempt to send a read command", - "description_zh": "发送读取指令失败后重新发送读指令时间间隔,单位为毫秒", + "description_zh": "发送读指令失败后重新发送读指令时间间隔,单位为毫秒", "attribute": "required", "type": "int", "default": 0, @@ -106,7 +106,7 @@ "name": "Send Interval", "name_zh": "指令发送间隔", "description": "Send reading instruction interval(ms)", - "description_zh": "发送读指令时间间隔,单位为毫秒", + "description_zh": "发送指令时间间隔,单位为毫秒", "attribute": "required", "type": "int", "default": 20, diff --git a/plugins/modbus/modbus-tcp.json b/plugins/modbus/modbus-tcp.json index f9a7b993..1a84bb51 100644 --- a/plugins/modbus/modbus-tcp.json +++ b/plugins/modbus/modbus-tcp.json @@ -92,7 +92,7 @@ "name": "Maximum Retry Times", "name_zh": "最大重试次数", "description": "The maximum number of retries after a failed attempt to send a read command", - "description_zh": "发送读取指令失败后最大重试次数", + "description_zh": "发送读指令失败后最大重试次数", "attribute": "required", "type": "int", "default": 0, @@ -105,7 +105,7 @@ "name": "Retry Interval", "name_zh": "指令重新发送间隔", "description": "Resend reading instruction interval(ms) after a failed attempt to send a read command", - "description_zh": "发送读取指令失败后重新发送读指令时间间隔,单位为毫秒", + "description_zh": "发送读指令失败后重新发送读指令时间间隔,单位为毫秒", "attribute": "required", "type": "int", "default": 0, @@ -118,7 +118,7 @@ "name": "Send Interval", "name_zh": "指令发送间隔", "description": "Send reading instruction interval(ms)", - "description_zh": "发送读指令时间间隔,单位为毫秒", + "description_zh": "发送指令时间间隔,单位为毫秒", "attribute": "required", "type": "int", "default": 20, diff --git a/plugins/modbus/modbus_point.c b/plugins/modbus/modbus_point.c index 5ac03347..ab2bfb04 100644 --- a/plugins/modbus/modbus_point.c +++ b/plugins/modbus/modbus_point.c @@ -31,6 +31,9 @@ static __thread uint16_t modbus_read_max_byte = 250; static int tag_cmp(neu_tag_sort_elem_t *tag1, neu_tag_sort_elem_t *tag2); static bool tag_sort(neu_tag_sort_t *sort, void *tag, void *tag_to_be_sorted); +static int tag_cmp_write(neu_tag_sort_elem_t *tag1, neu_tag_sort_elem_t *tag2); +static bool tag_sort_write(neu_tag_sort_t *sort, void *tag, + void *tag_to_be_sorted); int modbus_tag_to_point(const neu_datatag_t *tag, modbus_point_t *point) { @@ -165,6 +168,15 @@ int modbus_tag_to_point(const neu_datatag_t *tag, modbus_point_t *point) return ret; } +int modbus_write_tag_to_point(const neu_plugin_tag_value_t *tag, + modbus_point_write_t * point) +{ + int ret = NEU_ERR_SUCCESS; + ret = modbus_tag_to_point(tag->tag, &point->point); + point->value = tag->value; + return ret; +} + modbus_read_cmd_sort_t *modbus_tag_sort(UT_array *tags, uint16_t max_byte) { modbus_read_max_byte = max_byte; @@ -193,6 +205,112 @@ modbus_read_cmd_sort_t *modbus_tag_sort(UT_array *tags, uint16_t max_byte) return sort_result; } +int cal_n_byte(int type, neu_value_u *value, neu_datatag_addr_option_u option) +{ + int n = 0; + switch (type) { + case NEU_TYPE_UINT16: + case NEU_TYPE_INT16: + n = sizeof(uint16_t); + value->u16 = htons(value->u16); + break; + case NEU_TYPE_FLOAT: + case NEU_TYPE_UINT32: + case NEU_TYPE_INT32: + n = sizeof(uint32_t); + value->u32 = htonl(value->u32); + break; + + case NEU_TYPE_DOUBLE: + case NEU_TYPE_INT64: + case NEU_TYPE_UINT64: + n = sizeof(uint64_t); + value->u64 = neu_htonll(value->u64); + break; + case NEU_TYPE_BIT: { + n = sizeof(uint8_t); + break; + } + case NEU_TYPE_STRING: { + n = option.string.length; + switch (option.string.type) { + case NEU_DATATAG_STRING_TYPE_H: + break; + case NEU_DATATAG_STRING_TYPE_L: + neu_datatag_string_ltoh(value->str, option.string.length); + break; + case NEU_DATATAG_STRING_TYPE_D: + break; + case NEU_DATATAG_STRING_TYPE_E: + break; + } + break; + } + default: + assert(1 == 0); + break; + } + return n; +} + +modbus_write_cmd_sort_t *modbus_write_tags_sort(UT_array *tags) +{ + neu_tag_sort_result_t *result = + neu_tag_sort(tags, tag_sort_write, tag_cmp_write); + + modbus_write_cmd_sort_t *sort_result = + calloc(1, sizeof(modbus_write_cmd_sort_t)); + sort_result->n_cmd = result->n_sort; + sort_result->cmd = calloc(result->n_sort, sizeof(modbus_write_cmd_t)); + for (uint16_t i = 0; i < result->n_sort; i++) { + modbus_point_write_t *tag = + *(modbus_point_write_t **) utarray_front(result->sorts[i].tags); + struct modbus_sort_ctx *ctx = result->sorts[i].info.context; + + int num_tags = utarray_len(result->sorts[i].tags); + sort_result->cmd[i].bytes = calloc(num_tags, sizeof(neu_value_u)); + int n_byte = 0, n_byte_tag = 0; + uint8_t *data_bit = calloc((num_tags + 7) / 8, sizeof(uint8_t)); + int k = 0; + utarray_foreach(result->sorts[i].tags, modbus_point_write_t **, tag_s) + { + if ((*tag_s)->point.area == MODBUS_AREA_COIL) { + n_byte_tag = cal_n_byte((*tag_s)->point.type, &(*tag_s)->value, + (*tag_s)->point.option); + data_bit[k / 8] += ((*tag_s)->value.i8) << k % 8; + n_byte += n_byte_tag; + k++; + } else { + n_byte_tag = cal_n_byte((*tag_s)->point.type, &(*tag_s)->value, + (*tag_s)->point.option); + memcpy(sort_result->cmd[i].bytes + + 2 * + ((*tag_s)->point.start_address - + tag->point.start_address), + &((*tag_s)->value), n_byte_tag); + n_byte += n_byte_tag; + } + } + if ((*(modbus_point_write_t **) utarray_front(result->sorts[i].tags)) + ->point.area == MODBUS_AREA_COIL) { + memcpy(sort_result->cmd[i].bytes, data_bit, (k + 7) / 8); + } + + sort_result->cmd[i].tags = utarray_clone(result->sorts[i].tags); + sort_result->cmd[i].slave_id = tag->point.slave_id; + sort_result->cmd[i].area = tag->point.area; + sort_result->cmd[i].start_address = tag->point.start_address; + sort_result->cmd[i].n_register = ctx->end - ctx->start; + sort_result->cmd[i].n_byte = n_byte; + + free(data_bit); + free(result->sorts[i].info.context); + } + + neu_tag_sort_free(result); + return sort_result; +} + void modbus_tag_sort_free(modbus_read_cmd_sort_t *cs) { for (uint16_t i = 0; i < cs->n_cmd; i++) { @@ -266,7 +384,7 @@ static bool tag_sort(neu_tag_sort_t *sort, void *tag, void *tag_to_be_sorted) switch (t1->area) { case MODBUS_AREA_COIL: case MODBUS_AREA_INPUT: - if ((ctx->end - ctx->start) / 8 >= modbus_read_max_byte - 1) { + if ((ctx->end - ctx->start + 7) / 8 >= modbus_read_max_byte) { return false; } break; @@ -288,3 +406,90 @@ static bool tag_sort(neu_tag_sort_t *sort, void *tag, void *tag_to_be_sorted) return true; } + +static int tag_cmp_write(neu_tag_sort_elem_t *tag1, neu_tag_sort_elem_t *tag2) +{ + modbus_point_write_t *p_t1 = (modbus_point_write_t *) tag1->tag; + modbus_point_write_t *p_t2 = (modbus_point_write_t *) tag2->tag; + + if (p_t1->point.slave_id > p_t2->point.slave_id) { + return 1; + } else if (p_t1->point.slave_id < p_t2->point.slave_id) { + return -1; + } + + if (p_t1->point.area > p_t2->point.area) { + return 1; + } else if (p_t1->point.area < p_t2->point.area) { + return -1; + } + + if (p_t1->point.start_address > p_t2->point.start_address) { + return 1; + } else if (p_t1->point.start_address < p_t2->point.start_address) { + return -1; + } + + if (p_t1->point.n_register > p_t2->point.n_register) { + return 1; + } else if (p_t1->point.n_register < p_t2->point.n_register) { + return -1; + } + + return 0; +} + +static bool tag_sort_write(neu_tag_sort_t *sort, void *tag, + void *tag_to_be_sorted) +{ + modbus_point_write_t * t1 = (modbus_point_write_t *) tag; + modbus_point_write_t * t2 = (modbus_point_write_t *) tag_to_be_sorted; + struct modbus_sort_ctx *ctx = NULL; + + if (sort->info.context == NULL) { + sort->info.context = calloc(1, sizeof(struct modbus_sort_ctx)); + ctx = (struct modbus_sort_ctx *) sort->info.context; + ctx->start = t1->point.start_address; + ctx->end = t1->point.start_address + t1->point.n_register; + return true; + } + + ctx = (struct modbus_sort_ctx *) sort->info.context; + + if (t1->point.slave_id != t2->point.slave_id) { + return false; + } + + if (t1->point.area != t2->point.area) { + return false; + } + + if (t2->point.start_address > ctx->end) { + return false; + } + + switch (t1->point.area) { + case MODBUS_AREA_COIL: + case MODBUS_AREA_INPUT: + if ((ctx->end - ctx->start) / 8 >= modbus_read_max_byte - 1) { + return false; + } + break; + case MODBUS_AREA_INPUT_REGISTER: + case MODBUS_AREA_HOLD_REGISTER: { + uint16_t now_bytes = (ctx->end - ctx->start) * 2; + uint16_t add_now = now_bytes + t2->point.n_register * 2; + if (add_now >= modbus_read_max_byte) { + return false; + } + + break; + } + } + + if (t2->point.start_address + t2->point.n_register > ctx->end) { + ctx->end = t2->point.start_address + t2->point.n_register; + } + + return true; +} \ No newline at end of file diff --git a/plugins/modbus/modbus_point.h b/plugins/modbus/modbus_point.h index b3f219d4..0e48155d 100644 --- a/plugins/modbus/modbus_point.h +++ b/plugins/modbus/modbus_point.h @@ -40,7 +40,14 @@ typedef struct modbus_point { char name[NEU_TAG_NAME_LEN]; } modbus_point_t; +typedef struct modbus_point_write { + modbus_point_t point; + neu_value_u value; +} modbus_point_write_t; + int modbus_tag_to_point(const neu_datatag_t *tag, modbus_point_t *point); +int modbus_write_tag_to_point(const neu_plugin_tag_value_t *tag, + modbus_point_write_t * point); typedef struct modbus_read_cmd { uint8_t slave_id; @@ -56,8 +63,25 @@ typedef struct modbus_read_cmd_sort { modbus_read_cmd_t *cmd; } modbus_read_cmd_sort_t; -modbus_read_cmd_sort_t *modbus_tag_sort(UT_array *tags, uint16_t max_byte); -void modbus_tag_sort_free(modbus_read_cmd_sort_t *cs); +typedef struct modbus_write_cmd { + uint8_t slave_id; + modbus_area_e area; + uint16_t start_address; + uint16_t n_register; + uint8_t n_byte; + uint8_t * bytes; + + UT_array *tags; +} modbus_write_cmd_t; + +typedef struct modbus_write_cmd_sort { + uint16_t n_cmd; + modbus_write_cmd_t *cmd; +} modbus_write_cmd_sort_t; + +modbus_read_cmd_sort_t * modbus_tag_sort(UT_array *tags, uint16_t max_byte); +modbus_write_cmd_sort_t *modbus_write_tags_sort(UT_array *tags); +void modbus_tag_sort_free(modbus_read_cmd_sort_t *cs); #ifdef __cplusplus } diff --git a/plugins/modbus/modbus_req.c b/plugins/modbus/modbus_req.c index 462d9474..226dc8b0 100644 --- a/plugins/modbus/modbus_req.c +++ b/plugins/modbus/modbus_req.c @@ -29,6 +29,11 @@ struct modbus_group_data { modbus_read_cmd_sort_t *cmd_sort; }; +struct modbus_write_tags_data { + UT_array * tags; + modbus_write_cmd_sort_t *cmd_sort; +}; + static void plugin_group_free(neu_plugin_group_t *pgp); static int process_protocol_buf(neu_plugin_t *plugin, uint8_t slave_id, uint16_t response_size); @@ -445,7 +450,7 @@ int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte, } int modbus_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, - neu_value_u value) + neu_value_u value, bool response) { modbus_point_t point = { 0 }; int ret = modbus_tag_to_point(tag, &point); @@ -498,12 +503,83 @@ int modbus_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, uint16_t response_size = 0; ret = modbus_stack_write(plugin->stack, req, point.slave_id, point.area, point.start_address, point.n_register, value.bytes, - n_byte, &response_size); + n_byte, &response_size, response); if (ret > 0) { process_protocol_buf(plugin, point.slave_id, response_size); } - return 0; + return ret; +} + +int modbus_write_tag(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, + neu_value_u value) +{ + int ret = 0; + ret = modbus_write(plugin, req, tag, value, true); + return ret; +} + +int modbus_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags) +{ + struct modbus_write_tags_data *gtags = NULL; + int ret = 0; + int rv = 0; + + gtags = calloc(1, sizeof(struct modbus_write_tags_data)); + + utarray_new(gtags->tags, &ut_ptr_icd); + utarray_foreach(tags, neu_plugin_tag_value_t *, tag) + { + modbus_point_write_t *p = calloc(1, sizeof(modbus_point_write_t)); + ret = modbus_write_tag_to_point(tag, p); + assert(ret == 0); + + utarray_push_back(gtags->tags, &p); + } + gtags->cmd_sort = modbus_write_tags_sort(gtags->tags); + for (uint16_t i = 0; i < gtags->cmd_sort->n_cmd; i++) { + uint16_t response_size = 0; + + ret = modbus_stack_write( + plugin->stack, req, gtags->cmd_sort->cmd[i].slave_id, + gtags->cmd_sort->cmd[i].area, gtags->cmd_sort->cmd[i].start_address, + gtags->cmd_sort->cmd[i].n_register, gtags->cmd_sort->cmd[i].bytes, + gtags->cmd_sort->cmd[i].n_byte, &response_size, false); + if (ret > 0) { + process_protocol_buf(plugin, gtags->cmd_sort->cmd[i].slave_id, + response_size); + } + + if (ret <= 0) { + rv = 1; + } + if (plugin->interval > 0) { + struct timespec t1 = { .tv_sec = plugin->interval / 1000, + .tv_nsec = 1000 * 1000 * + (plugin->interval % 1000) }; + struct timespec t2 = { 0 }; + nanosleep(&t1, &t2); + } + } + + if (rv == 0) { + plugin->common.adapter_callbacks->driver.write_response( + plugin->common.adapter, req, NEU_ERR_SUCCESS); + } else { + plugin->common.adapter_callbacks->driver.write_response( + plugin->common.adapter, req, NEU_ERR_PLUGIN_DISCONNECTED); + } + + for (uint16_t i = 0; i < gtags->cmd_sort->n_cmd; i++) { + utarray_free(gtags->cmd_sort->cmd[i].tags); + free(gtags->cmd_sort->cmd[i].bytes); + } + free(gtags->cmd_sort->cmd); + free(gtags->cmd_sort); + utarray_foreach(gtags->tags, modbus_point_write_t **, tag) { free(*tag); } + utarray_free(gtags->tags); + free(gtags); + return ret; } int modbus_write_resp(void *ctx, void *req, int error) diff --git a/plugins/modbus/modbus_req.h b/plugins/modbus/modbus_req.h index 30c666a1..ca30ca1d 100644 --- a/plugins/modbus/modbus_req.h +++ b/plugins/modbus/modbus_req.h @@ -58,7 +58,10 @@ int modbus_send_msg(void *ctx, uint16_t n_byte, uint8_t *bytes); int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte, uint8_t *bytes, int error); int modbus_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, - neu_value_u value); + neu_value_u value, bool response); +int modbus_write_tag(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, + neu_value_u value); +int modbus_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags); int modbus_write_resp(void *ctx, void *req, int error); #endif diff --git a/plugins/modbus/modbus_rtu.c b/plugins/modbus/modbus_rtu.c index 7321fdb6..bea6cd3b 100644 --- a/plugins/modbus/modbus_rtu.c +++ b/plugins/modbus/modbus_rtu.c @@ -42,6 +42,7 @@ static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag); static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group); static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, neu_value_u value); +static int driver_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags); static const neu_plugin_intf_funs_t plugin_intf_funs = { .open = driver_open, @@ -57,7 +58,7 @@ static const neu_plugin_intf_funs_t plugin_intf_funs = { .driver.group_timer = driver_group_timer, .driver.write_tag = driver_write, .driver.tag_validator = driver_tag_validator, - .driver.write_tags = NULL, + .driver.write_tags = driver_write_tags, }; const neu_plugin_module_t neu_plugin_module = { @@ -312,5 +313,10 @@ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group) static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, neu_value_u value) { - return modbus_write(plugin, req, tag, value); + return modbus_write_tag(plugin, req, tag, value); +} + +static int driver_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags) +{ + return modbus_write_tags(plugin, req, tags); } \ No newline at end of file diff --git a/plugins/modbus/modbus_stack.c b/plugins/modbus/modbus_stack.c index 4921e801..94f4589f 100644 --- a/plugins/modbus/modbus_stack.c +++ b/plugins/modbus/modbus_stack.c @@ -225,7 +225,7 @@ int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id, int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id, enum modbus_area area, uint16_t start_address, uint16_t n_reg, uint8_t *bytes, uint8_t n_byte, - uint16_t *response_size) + uint16_t *response_size, bool response) { static __thread neu_protocol_pack_buf_t pbuf = { 0 }; modbus_action_e m_action = MODBUS_ACTION_DEFAULT; @@ -239,13 +239,21 @@ int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id, switch (area) { case MODBUS_AREA_COIL: - if (*bytes > 0) { - modbus_address_wrap(&pbuf, start_address, 0xff00, m_action); + if (n_byte > 1) { + n_reg = n_byte; + modbus_data_wrap(&pbuf, (n_byte + 7) / 8, bytes, m_action); + modbus_address_wrap(&pbuf, start_address, n_reg, m_action); + modbus_code_wrap(&pbuf, slave_id, MODBUS_WRITE_M_COIL); + break; } else { - modbus_address_wrap(&pbuf, start_address, 0, m_action); + if (*bytes > 0) { + modbus_address_wrap(&pbuf, start_address, 0xff00, m_action); + } else { + modbus_address_wrap(&pbuf, start_address, 0, m_action); + } + modbus_code_wrap(&pbuf, slave_id, MODBUS_WRITE_S_COIL); + break; } - modbus_code_wrap(&pbuf, slave_id, MODBUS_WRITE_S_COIL); - break; case MODBUS_AREA_HOLD_REGISTER: m_action = MODBUS_ACTION_HOLD_REG_WRITE; modbus_data_wrap(&pbuf, n_byte, bytes, m_action); @@ -275,13 +283,17 @@ int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id, int ret = stack->send_fn(stack->ctx, neu_protocol_pack_buf_used_size(&pbuf), neu_protocol_pack_buf_get(&pbuf)); if (ret > 0) { - stack->write_resp(stack->ctx, req, NEU_ERR_SUCCESS); - plog_notice((neu_plugin_t *) stack->ctx, "send write req, %hhu!%hu", - slave_id, start_address); + if (response) { + stack->write_resp(stack->ctx, req, NEU_ERR_SUCCESS); + plog_notice((neu_plugin_t *) stack->ctx, "send write req, %hhu!%hu", + slave_id, start_address); + } } else { - stack->write_resp(stack->ctx, req, NEU_ERR_PLUGIN_DISCONNECTED); - plog_warn((neu_plugin_t *) stack->ctx, "send write req fail, %hhu!%hu", - slave_id, start_address); + if (response) { + stack->write_resp(stack->ctx, req, NEU_ERR_PLUGIN_DISCONNECTED); + plog_warn((neu_plugin_t *) stack->ctx, + "send write req fail, %hhu!%hu", slave_id, start_address); + } } return ret; } diff --git a/plugins/modbus/modbus_stack.h b/plugins/modbus/modbus_stack.h index 3334b0ee..8e1df0a7 100644 --- a/plugins/modbus/modbus_stack.h +++ b/plugins/modbus/modbus_stack.h @@ -52,7 +52,7 @@ int modbus_stack_read(modbus_stack_t *stack, uint8_t slave_id, int modbus_stack_write(modbus_stack_t *stack, void *req, uint8_t slave_id, enum modbus_area area, uint16_t start_address, uint16_t n_reg, uint8_t *bytes, uint8_t n_byte, - uint16_t *response_size); + uint16_t *response_size, bool response); bool modbus_stack_is_rtu(modbus_stack_t *stack); #endif \ No newline at end of file diff --git a/plugins/modbus/modbus_tcp.c b/plugins/modbus/modbus_tcp.c index 5e164ea0..29a5075d 100644 --- a/plugins/modbus/modbus_tcp.c +++ b/plugins/modbus/modbus_tcp.c @@ -42,6 +42,7 @@ static int driver_validate_tag(neu_plugin_t *plugin, neu_datatag_t *tag); static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group); static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, neu_value_u value); +static int driver_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags); static const neu_plugin_intf_funs_t plugin_intf_funs = { .open = driver_open, @@ -57,7 +58,7 @@ static const neu_plugin_intf_funs_t plugin_intf_funs = { .driver.group_timer = driver_group_timer, .driver.write_tag = driver_write, .driver.tag_validator = driver_tag_validator, - .driver.write_tags = NULL, + .driver.write_tags = driver_write_tags, }; const neu_plugin_module_t neu_plugin_module = { @@ -144,7 +145,9 @@ static int driver_config(neu_plugin_t *plugin, const char *config) char * err_param = NULL; neu_json_elem_t port = { .name = "port", .t = NEU_JSON_INT }; neu_json_elem_t timeout = { .name = "timeout", .t = NEU_JSON_INT }; - neu_json_elem_t host = { .name = "host", .t = NEU_JSON_STR }; + neu_json_elem_t host = { .name = "host", + .t = NEU_JSON_STR, + .v.val_str = NULL }; neu_json_elem_t interval = { .name = "interval", .t = NEU_JSON_INT }; neu_json_elem_t mode = { .name = "connection_mode", .t = NEU_JSON_INT }; neu_json_elem_t tmode = { .name = "transport_mode", .t = NEU_JSON_INT }; @@ -159,7 +162,9 @@ static int driver_config(neu_plugin_t *plugin, const char *config) if (ret != 0) { plog_error(plugin, "config: %s, decode error: %s", config, err_param); free(err_param); - free(host.v.val_str); + if (host.v.val_str != NULL) { + free(host.v.val_str); + } return -1; } @@ -275,5 +280,10 @@ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group) static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, neu_value_u value) { - return modbus_write(plugin, req, tag, value); + return modbus_write_tag(plugin, req, tag, value); +} + +static int driver_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags) +{ + return modbus_write_tags(plugin, req, tags); } \ No newline at end of file