mirror of
https://github.com/emqx/neuron.git
synced 2026-10-27 12:28:44 +08:00
Merge pull request #1452 from hxy7yx/v2.5-1
feat(modbus):write multiple tags
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
.driver.add_tags = NULL,
|
||||
.driver.load_tags = NULL,
|
||||
.driver.del_tags = NULL,
|
||||
@@ -316,5 +317,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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
.driver.add_tags = NULL,
|
||||
.driver.load_tags = NULL,
|
||||
.driver.del_tags = NULL,
|
||||
@@ -148,7 +149,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 };
|
||||
@@ -163,7 +166,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;
|
||||
}
|
||||
|
||||
@@ -279,5 +284,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);
|
||||
}
|
||||
Reference in New Issue
Block a user