From 1c2f37adbe834afbea1923331c089e913cb96099 Mon Sep 17 00:00:00 2001 From: hxy7yx <1595670487@qq.com> Date: Thu, 12 Jun 2025 20:34:45 -0700 Subject: [PATCH] feat(modbus):support endianess of 8-Byte data --- .github/workflows/check.yml | 2 +- .github/workflows/ci.yml | 12 +++++----- include/neuron/tag.h | 5 +++++ include/neuron/type.h | 36 ++++++++++++++++++++++++++++++ plugins/modbus/modbus-rtu.json | 35 ++++++++++++++++++++++++++--- plugins/modbus/modbus-tcp.json | 35 ++++++++++++++++++++++++++--- plugins/modbus/modbus.h | 7 ++++++ plugins/modbus/modbus_point.c | 40 +++++++++++++++++++++++++++++----- plugins/modbus/modbus_point.h | 10 ++++++--- plugins/modbus/modbus_req.c | 9 +++++++- plugins/modbus/modbus_req.h | 2 ++ plugins/modbus/modbus_rtu.c | 10 +++++++++ plugins/modbus/modbus_tcp.c | 9 ++++++++ src/adapter/driver/driver.c | 32 +++++++++++++++++++++++++++ src/base/tag.c | 40 +++++++++++++++++++++++----------- 15 files changed, 248 insertions(+), 36 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 379967c29..eb9f71634 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -9,7 +9,7 @@ on: jobs: clang_format_check: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: # checkout diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 520467b39..2bd74277e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ env: jobs: cross-build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: matrix: arch: @@ -52,14 +52,14 @@ jobs: run: | ./package-sdk.sh -p ${{ matrix.arch[2] }} -n neuron-sdk-$VERSION - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: neuron-sdk + name: neuron-sdk-${{ matrix.arch[2] }} path: | neuron-sdk-*.tar.gz build-debug-sdk: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: matrix: arch: @@ -83,7 +83,7 @@ jobs: run: | ./package-sdk.sh -p ${{ matrix.arch[2] }} -n neuron-sdk-debug-$VERSION - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: neuron-sdk path: | @@ -102,7 +102,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: neuron-sdk diff --git a/include/neuron/tag.h b/include/neuron/tag.h index e4055f7e3..740a23708 100644 --- a/include/neuron/tag.h +++ b/include/neuron/tag.h @@ -44,6 +44,10 @@ typedef enum { NEU_DATATAG_ENDIAN_BL32 = 5, // #BL 2,1,4,3 NEU_DATATAG_ENDIAN_L64 = 6, // #L 8,7,6,5,4,3,2,1 NEU_DATATAG_ENDIAN_B64 = 7, // #B 1,2,3,4,5,6,7,8 + NEU_DATATAG_ENDIAN_LL64 = 8, + NEU_DATATAG_ENDIAN_LB64 = 9, + NEU_DATATAG_ENDIAN_BB64 = 10, + NEU_DATATAG_ENDIAN_BL64 = 11, } neu_datatag_endian_e; typedef enum { @@ -63,6 +67,7 @@ typedef union { } value32; struct { neu_datatag_endian_e endian; + bool is_default; } value64; struct { uint16_t length; diff --git a/include/neuron/type.h b/include/neuron/type.h index 5add54ae1..11836fd7e 100644 --- a/include/neuron/type.h +++ b/include/neuron/type.h @@ -506,6 +506,42 @@ static inline uint64_t neu_htonll(uint64_t u) return ret; } +static inline uint64_t neu_htonlb(uint64_t u) +{ + uint8_t *in = (uint8_t *) &u; + uint64_t out = 0; + uint8_t *out_bytes = (uint8_t *) &out; + + out_bytes[0] = in[1]; + out_bytes[1] = in[0]; + out_bytes[2] = in[3]; + out_bytes[3] = in[2]; + out_bytes[4] = in[5]; + out_bytes[5] = in[4]; + out_bytes[6] = in[7]; + out_bytes[7] = in[6]; + + return out; +} + +static inline uint64_t neu_htonbl(uint64_t u) +{ + uint8_t *in = (uint8_t *) &u; + uint64_t out = 0; + uint8_t *out_bytes = (uint8_t *) &out; + + out_bytes[0] = in[6]; + out_bytes[1] = in[7]; + out_bytes[2] = in[4]; + out_bytes[3] = in[5]; + out_bytes[4] = in[2]; + out_bytes[5] = in[3]; + out_bytes[6] = in[0]; + out_bytes[7] = in[1]; + + return out; +} + static inline uint64_t neu_ntohll(uint64_t u) { return neu_htonll(u); diff --git a/plugins/modbus/modbus-rtu.json b/plugins/modbus/modbus-rtu.json index 413054c1b..bf464a1bf 100644 --- a/plugins/modbus/modbus-rtu.json +++ b/plugins/modbus/modbus-rtu.json @@ -18,11 +18,11 @@ }, { "type": 7, - "regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$" + "regex": "^[0-9]+![3-4][0-9]+(#B|#L|#BB|#BL|#LL|#LB|)$" }, { "type": 8, - "regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$" + "regex": "^[0-9]+![3-4][0-9]+(#B|#L|#BB|#BL|#LL|#LB|)$" }, { "type": 9, @@ -30,7 +30,7 @@ }, { "type": 10, - "regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$" + "regex": "^[0-9]+![3-4][0-9]+(#B|#L|#BB|#BL|#LL|#LB|)$" }, { "type": 11, @@ -106,6 +106,35 @@ "max": 10000 } }, + "endianess_64": { + "name": "Endianess of 8-Byte Data", + "name_zh": "8 字节数据字节序", + "description": "Tag byte order, applicable to int64, uint64 and double types", + "description_zh": "点位字节序,适用于 int64,uint64 和 double 类型", + "attribute": "required", + "type": "map", + "default": 1, + "valid": { + "map": [ + { + "key": "12 34 56 78", + "value": 1 + }, + { + "key": "21 43 65 87", + "value": 2 + }, + { + "key": "87 65 43 21", + "value": 3 + }, + { + "key": "78 56 34 12", + "value": 4 + } + ] + } + }, "interval": { "name": "Send Interval", "name_zh": "指令发送间隔", diff --git a/plugins/modbus/modbus-tcp.json b/plugins/modbus/modbus-tcp.json index 52139973b..6ff918cf0 100644 --- a/plugins/modbus/modbus-tcp.json +++ b/plugins/modbus/modbus-tcp.json @@ -18,11 +18,11 @@ }, { "type": 7, - "regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$" + "regex": "^[0-9]+![3-4][0-9]+(#B|#L|#BB|#BL|#LL|#LB|)$" }, { "type": 8, - "regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$" + "regex": "^[0-9]+![3-4][0-9]+(#B|#L|#BB|#BL|#LL|#LB|)$" }, { "type": 9, @@ -30,7 +30,7 @@ }, { "type": 10, - "regex": "^[0-9]+![3-4][0-9]+(#B|#L|)$" + "regex": "^[0-9]+![3-4][0-9]+(#B|#L|#BB|#BL|#LL|#LB|)$" }, { "type": 11, @@ -93,6 +93,35 @@ "max": 10000 } }, + "endianess_64": { + "name": "Endianess of 8-Byte Data", + "name_zh": "8 字节数据字节序", + "description": "Tag byte order, applicable to int64, uint64 and double types", + "description_zh": "点位字节序,适用于 int64,uint64 和 double 类型", + "attribute": "required", + "type": "map", + "default": 1, + "valid": { + "map": [ + { + "key": "12 34 56 78", + "value": 1 + }, + { + "key": "21 43 65 87", + "value": 2 + }, + { + "key": "87 65 43 21", + "value": 3 + }, + { + "key": "78 56 34 12", + "value": 4 + } + ] + } + }, "interval": { "name": "Send Interval", "name_zh": "指令发送间隔", diff --git a/plugins/modbus/modbus.h b/plugins/modbus/modbus.h index a512ee220..dc7c957af 100644 --- a/plugins/modbus/modbus.h +++ b/plugins/modbus/modbus.h @@ -66,6 +66,13 @@ typedef enum modbus_area { MODBUS_AREA_HOLD_REGISTER = 4, } modbus_area_e; +typedef enum modbus_endianess_64 { + MODBUS_LL = 1, // 12 34 56 78 + MODBUS_LB = 2, // 21 43 65 87 + MODBUS_BB = 3, // 87 65 43 21 + MODBUS_BL = 4, // 78 56 34 12 +} modbus_endianess_64; + struct modbus_header { uint16_t seq; uint16_t protocol; diff --git a/plugins/modbus/modbus_point.c b/plugins/modbus/modbus_point.c index 817aba0cc..a58442669 100644 --- a/plugins/modbus/modbus_point.c +++ b/plugins/modbus/modbus_point.c @@ -221,7 +221,8 @@ 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 cal_n_byte(int type, neu_value_u *value, neu_datatag_addr_option_u option, + modbus_endianess_64 endianess_64, bool default_tag_endian_64) { int n = 0; switch (type) { @@ -240,6 +241,9 @@ int cal_n_byte(int type, neu_value_u *value, neu_datatag_addr_option_u option) case NEU_TYPE_DOUBLE: case NEU_TYPE_INT64: case NEU_TYPE_UINT64: + if (default_tag_endian_64) { + modbus_convert_endianess_64(value, endianess_64); + } n = sizeof(uint64_t); value->u64 = neu_htonll(value->u64); break; @@ -273,7 +277,8 @@ int cal_n_byte(int type, neu_value_u *value, neu_datatag_addr_option_u option) return n; } -modbus_write_cmd_sort_t *modbus_write_tags_sort(UT_array *tags) +modbus_write_cmd_sort_t * +modbus_write_tags_sort(UT_array *tags, modbus_endianess_64 endianess_64) { neu_tag_sort_result_t *result = neu_tag_sort(tags, tag_sort_write, tag_cmp_write); @@ -295,14 +300,17 @@ modbus_write_cmd_sort_t *modbus_write_tags_sort(UT_array *tags) 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); + n_byte_tag = + cal_n_byte((*tag_s)->point.type, &(*tag_s)->value, + (*tag_s)->point.option, endianess_64, true); 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); + n_byte_tag = + cal_n_byte((*tag_s)->point.type, &(*tag_s)->value, + (*tag_s)->point.option, endianess_64, + (*tag_s)->point.option.value64.is_default); memcpy(sort_result->cmd[i].bytes + 2 * ((*tag_s)->point.start_address - @@ -512,4 +520,24 @@ static bool tag_sort_write(neu_tag_sort_t *sort, void *tag, } return true; +} + +void modbus_convert_endianess_64(neu_value_u * value, + modbus_endianess_64 endianess_64) +{ + switch (endianess_64) { + case MODBUS_LL: + break; + case MODBUS_BB: + value->u64 = neu_htonll(value->u64); + break; + case MODBUS_LB: + value->u64 = neu_htonlb(value->u64); + break; + case MODBUS_BL: + value->u64 = neu_htonbl(value->u64); + break; + default: + break; + } } \ No newline at end of file diff --git a/plugins/modbus/modbus_point.h b/plugins/modbus/modbus_point.h index 0e48155df..5f0497b4e 100644 --- a/plugins/modbus/modbus_point.h +++ b/plugins/modbus/modbus_point.h @@ -79,9 +79,13 @@ typedef struct modbus_write_cmd_sort { 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); +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, modbus_endianess_64 endianess_64); +void modbus_tag_sort_free(modbus_read_cmd_sort_t *cs); + +void modbus_convert_endianess_64(neu_value_u * value, + modbus_endianess_64 endianess_64); #ifdef __cplusplus } diff --git a/plugins/modbus/modbus_req.c b/plugins/modbus/modbus_req.c index 15edf2002..a6ce79269 100644 --- a/plugins/modbus/modbus_req.c +++ b/plugins/modbus/modbus_req.c @@ -411,6 +411,10 @@ int modbus_value_handle(void *ctx, uint8_t slave_id, uint16_t n_byte, case NEU_TYPE_DOUBLE: case NEU_TYPE_INT64: case NEU_TYPE_UINT64: + if ((*p_tag)->option.value64.is_default) { + modbus_convert_endianess_64(&dvalue.value, + plugin->endianess_64); + } dvalue.value.u64 = neu_ntohll(dvalue.value.u64); break; case NEU_TYPE_BIT: { @@ -485,6 +489,9 @@ int modbus_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag, case NEU_TYPE_DOUBLE: case NEU_TYPE_INT64: case NEU_TYPE_UINT64: + if (tag->option.value64.is_default) { + modbus_convert_endianess_64(&value, plugin->endianess_64); + } value.u64 = neu_htonll(value.u64); n_byte = sizeof(uint64_t); break; @@ -552,7 +559,7 @@ int modbus_write_tags(neu_plugin_t *plugin, void *req, UT_array *tags) utarray_push_back(gtags->tags, &p); } - gtags->cmd_sort = modbus_write_tags_sort(gtags->tags); + gtags->cmd_sort = modbus_write_tags_sort(gtags->tags, plugin->endianess_64); for (uint16_t i = 0; i < gtags->cmd_sort->n_cmd; i++) { uint16_t response_size = 0; diff --git a/plugins/modbus/modbus_req.h b/plugins/modbus/modbus_req.h index ca30ca1de..ed6ee3e96 100644 --- a/plugins/modbus/modbus_req.h +++ b/plugins/modbus/modbus_req.h @@ -43,6 +43,8 @@ struct neu_plugin { uint16_t interval; uint16_t retry_interval; uint16_t max_retries; + + modbus_endianess_64 endianess_64; }; void modbus_conn_connected(void *data, int fd); diff --git a/plugins/modbus/modbus_rtu.c b/plugins/modbus/modbus_rtu.c index bd1eaeac0..184335db8 100644 --- a/plugins/modbus/modbus_rtu.c +++ b/plugins/modbus/modbus_rtu.c @@ -167,6 +167,9 @@ static int driver_config(neu_plugin_t *plugin, const char *config) neu_json_elem_t retry_interval = { .name = "retry_interval", .t = NEU_JSON_INT }; + neu_json_elem_t endianess_64 = { .name = "endianess_64", + .t = NEU_JSON_INT }; + ret = neu_parse_param((char *) config, &err_param, 3, &link, &timeout, &interval); if (ret != 0) { @@ -206,9 +209,16 @@ static int driver_config(neu_plugin_t *plugin, const char *config) retry_interval.v.val_int = 0; } + ret = neu_parse_param((char *) config, &err_param, 1, &endianess_64); + if (ret != 0) { + free(err_param); + endianess_64.v.val_int = MODBUS_LL; + } + param.log = plugin->common.log; plugin->max_retries = max_retries.v.val_int; plugin->retry_interval = retry_interval.v.val_int; + plugin->endianess_64 = endianess_64.v.val_int; if (link.v.val_int == 0) { param.type = NEU_CONN_TTY_CLIENT; diff --git a/plugins/modbus/modbus_tcp.c b/plugins/modbus/modbus_tcp.c index 43ab72d53..dfb6d3c99 100644 --- a/plugins/modbus/modbus_tcp.c +++ b/plugins/modbus/modbus_tcp.c @@ -158,6 +158,8 @@ static int driver_config(neu_plugin_t *plugin, const char *config) neu_json_elem_t max_retries = { .name = "max_retries", .t = NEU_JSON_INT }; neu_json_elem_t retry_interval = { .name = "retry_interval", .t = NEU_JSON_INT }; + neu_json_elem_t endianess_64 = { .name = "endianess_64", + .t = NEU_JSON_INT }; ret = neu_parse_param((char *) config, &err_param, 5, &port, &host, &mode, &timeout, &interval); @@ -186,10 +188,17 @@ static int driver_config(neu_plugin_t *plugin, const char *config) retry_interval.v.val_int = 0; } + ret = neu_parse_param((char *) config, &err_param, 1, &endianess_64); + if (ret != 0) { + free(err_param); + endianess_64.v.val_int = MODBUS_LL; + } + param.log = plugin->common.log; plugin->interval = interval.v.val_int; plugin->max_retries = max_retries.v.val_int; plugin->retry_interval = retry_interval.v.val_int; + plugin->endianess_64 = endianess_64.v.val_int; if (mode.v.val_int == 1) { param.type = NEU_CONN_TCP_SERVER; diff --git a/src/adapter/driver/driver.c b/src/adapter/driver/driver.c index da70e922c..0c7b8c99f 100644 --- a/src/adapter/driver/driver.c +++ b/src/adapter/driver/driver.c @@ -725,9 +725,17 @@ static void fix_value(neu_datatag_t *tag, neu_type_e value_type, case NEU_TYPE_LWORD: switch (tag->option.value64.endian) { case NEU_DATATAG_ENDIAN_B64: + case NEU_DATATAG_ENDIAN_BB64: value->value.u64 = neu_ntohll(value->value.u64); break; + case NEU_DATATAG_ENDIAN_LB64: + value->value.u64 = neu_htonlb(value->value.u64); + break; + case NEU_DATATAG_ENDIAN_BL64: + value->value.u64 = neu_htonbl(value->value.u64); + break; case NEU_DATATAG_ENDIAN_L64: + case NEU_DATATAG_ENDIAN_LL64: default: break; } @@ -1908,9 +1916,17 @@ static void read_report_group(int64_t timestamp, int64_t timeout, case NEU_TYPE_UINT64: switch (tag->option.value64.endian) { case NEU_DATATAG_ENDIAN_B64: + case NEU_DATATAG_ENDIAN_BB64: value.value.value.u64 = neu_htonll(value.value.value.u64); break; + case NEU_DATATAG_ENDIAN_LB64: + value.value.value.u64 = neu_htonlb(value.value.value.u64); + break; + case NEU_DATATAG_ENDIAN_BL64: + value.value.value.u64 = neu_htonbl(value.value.value.u64); + break; case NEU_DATATAG_ENDIAN_L64: + case NEU_DATATAG_ENDIAN_LL64: default: break; } @@ -2066,9 +2082,17 @@ static void read_group(int64_t timestamp, int64_t timeout, case NEU_TYPE_UINT64: switch (tag->option.value64.endian) { case NEU_DATATAG_ENDIAN_B64: + case NEU_DATATAG_ENDIAN_BB64: value.value.value.u64 = neu_htonll(value.value.value.u64); break; + case NEU_DATATAG_ENDIAN_LB64: + value.value.value.u64 = neu_htonlb(value.value.value.u64); + break; + case NEU_DATATAG_ENDIAN_BL64: + value.value.value.u64 = neu_htonbl(value.value.value.u64); + break; case NEU_DATATAG_ENDIAN_L64: + case NEU_DATATAG_ENDIAN_LL64: default: break; } @@ -2241,9 +2265,17 @@ static void read_group_paginate(int64_t timestamp, int64_t timeout, case NEU_TYPE_UINT64: switch (tag->option.value64.endian) { case NEU_DATATAG_ENDIAN_B64: + case NEU_DATATAG_ENDIAN_BB64: value.value.value.u64 = neu_htonll(value.value.value.u64); break; + case NEU_DATATAG_ENDIAN_LB64: + value.value.value.u64 = neu_htonlb(value.value.value.u64); + break; + case NEU_DATATAG_ENDIAN_BL64: + value.value.value.u64 = neu_htonbl(value.value.value.u64); + break; case NEU_DATATAG_ENDIAN_L64: + case NEU_DATATAG_ENDIAN_LL64: default: break; } diff --git a/src/base/tag.c b/src/base/tag.c index 4d1e60e17..31b86450f 100644 --- a/src/base/tag.c +++ b/src/base/tag.c @@ -254,21 +254,35 @@ int neu_datatag_parse_addr_option(const neu_datatag_t * datatag, case NEU_TYPE_UINT64: { char *op = find_last_character(datatag->address, '#'); - option->value64.endian = NEU_DATATAG_ENDIAN_L64; + option->value64.endian = NEU_DATATAG_ENDIAN_L64; + option->value64.is_default = true; if (op != NULL) { - char e = 0; - sscanf(op, "#%c", &e); + char e1 = 0; + char e2 = 0; + int n = sscanf(op, "#%c%c", &e1, &e2); - switch (e) { - case 'B': - option->value64.endian = NEU_DATATAG_ENDIAN_B64; - break; - case 'L': - option->value64.endian = NEU_DATATAG_ENDIAN_L64; - break; - default: - option->value64.endian = NEU_DATATAG_ENDIAN_L64; - break; + if (n == 2) { + if (e1 == 'B' && e2 == 'B') { + option->value64.endian = NEU_DATATAG_ENDIAN_BB64; + option->value64.is_default = false; + } else if (e1 == 'B' && e2 == 'L') { + option->value64.endian = NEU_DATATAG_ENDIAN_BL64; + option->value64.is_default = false; + } else if (e1 == 'L' && e2 == 'L') { + option->value64.endian = NEU_DATATAG_ENDIAN_LL64; + option->value64.is_default = false; + } else if (e1 == 'L' && e2 == 'B') { + option->value64.endian = NEU_DATATAG_ENDIAN_LB64; + option->value64.is_default = false; + } + } else if (n == 1) { + if (e1 == 'B') { + option->value64.endian = NEU_DATATAG_ENDIAN_B64; + option->value64.is_default = false; + } else if (e1 == 'L') { + option->value64.endian = NEU_DATATAG_ENDIAN_L64; + option->value64.is_default = false; + } } }