From add0a3bfc2f2f04341c8536527f5b0910a45c772 Mon Sep 17 00:00:00 2001 From: Kamil Wcislo Date: Thu, 27 Apr 2017 16:19:34 +0200 Subject: [PATCH 1/8] Add write_file_record and read_file_record functions --- src/modbus.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/modbus.h | 9 ++++ 2 files changed, 134 insertions(+) diff --git a/src/modbus.c b/src/modbus.c index 03d8da248..4a30cc73b 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -600,6 +600,12 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, /* Report slave ID (bytes received) */ req_nb_value = rsp_nb_value = rsp[offset + 1]; break; + case MODBUS_FC_READ_FILE_RECORD: + req_nb_value = (req[offset + 7] << 8) + req[offset + 8]; + rsp_nb_value = (rsp[offset + 2] - 1) / 2; + case MODBUS_FC_WRITE_FILE_RECORD: + req_nb_value = (req[offset + 7] << 8) + req[offset + 8]; + rsp_nb_value = (rsp[offset + 7] << 8) + rsp[offset + 8]; default: /* 1 Write functions & others */ req_nb_value = rsp_nb_value = 1; @@ -1234,6 +1240,67 @@ int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, return status; } +int modbus_read_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, uint16_t *dest) +{ + int rc; + int req_length; + uint8_t req[_MIN_REQ_LENGTH]; + uint8_t rsp[MAX_MESSAGE_LENGTH]; + + if (ctx == NULL) { + errno = EINVAL; + return -1; + } + + if (sub_addr > MODBUS_MAX_FILE_RECORD_NUMBER) { + if (ctx->debug) { + fprintf(stderr, + "ERROR Too big file record number (%d > %d)\n", + sub_addr, MODBUS_MAX_FILE_RECORD_NUMBER); + } + errno = EMBMDATA; + return -1; + } + + req_length = ctx->backend->build_request_basis(ctx, + MODBUS_FC_READ_FILE_RECORD, + 0, 0, req); + + req_length -= 4; + req[req_length++] = 7; // one request, so 7 bytes + req[req_length++] = 6; + req[req_length++] = addr >> 8; + req[req_length++] = addr & 0x00ff; + req[req_length++] = sub_addr >> 8; + req[req_length++] = sub_addr & 0x00ff; + req[req_length++] = nb >> 8; + req[req_length++] = nb * 0x00ff; + + rc = send_msg(ctx, req, req_length); + if (rc > 0) { + int offset; + int i; + + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); + if (rc == -1) + return -1; + + rc = check_confirmation(ctx, req, rsp, rc); + if (rc == -1) + return -1; + + offset = ctx->backend->header_length; + + for (i = 0; i < rc; i++) { + + dest[i] = (rsp[offset + 4 + (i << 1)] << 8) | + rsp[offset + 5 + (i << 1)]; + } + } + + return rc; +} + /* Write a value to the specified register of the remote device. Used by write_bit and write_register */ static int write_single(modbus_t *ctx, int function, int addr, int value) @@ -1399,6 +1466,64 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *src) return rc; } +int modbus_write_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, const uint16_t *src) +{ + int rc; + int i; + int req_length; + int byte_count; + uint8_t req[MAX_MESSAGE_LENGTH]; + + if (ctx == NULL) { + errno = EINVAL; + return -1; + } + + if (sub_addr > MODBUS_MAX_FILE_RECORD_NUMBER) { + if (ctx->debug) { + fprintf(stderr, + "ERROR Too big file record number (%d > %d)\n", + sub_addr, MODBUS_MAX_FILE_RECORD_NUMBER); + } + errno = EMBMDATA; + return -1; + } + + req_length = ctx->backend->build_request_basis(ctx, + MODBUS_FC_WRITE_FILE_RECORD, + 0, 0, req); + + byte_count = nb * 2; + req_length -= 4; + req[req_length++] = 7 + byte_count; // one request header + bytes to write + req[req_length++] = 6; + req[req_length++] = addr >> 8; + req[req_length++] = addr & 0x00ff; + req[req_length++] = sub_addr >> 8; + req[req_length++] = sub_addr & 0x00ff; + req[req_length++] = nb >> 8; + req[req_length++] = nb * 0x00ff; + + for (i = 0; i < nb; i++) { + req[req_length++] = src[i] >> 8; + req[req_length++] = src[i] & 0x00FF; + } + + rc = send_msg(ctx, req, req_length); + if (rc > 0) { + uint8_t rsp[MAX_MESSAGE_LENGTH]; + + rc = _modbus_receive_msg(ctx, rsp, MSG_CONFIRMATION); + if (rc == -1) + return -1; + + rc = check_confirmation(ctx, req, rsp, rc); + } + + return rc; +} + + int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask) { int rc; diff --git a/src/modbus.h b/src/modbus.h index ab87e311a..5983e68fd 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -68,6 +68,8 @@ MODBUS_BEGIN_DECLS #define MODBUS_FC_WRITE_MULTIPLE_COILS 0x0F #define MODBUS_FC_WRITE_MULTIPLE_REGISTERS 0x10 #define MODBUS_FC_REPORT_SLAVE_ID 0x11 +#define MODBUS_FC_READ_FILE_RECORD 0x14 +#define MODBUS_FC_WRITE_FILE_RECORD 0x15 #define MODBUS_FC_MASK_WRITE_REGISTER 0x16 #define MODBUS_FC_WRITE_AND_READ_REGISTERS 0x17 @@ -81,6 +83,11 @@ MODBUS_BEGIN_DECLS #define MODBUS_MAX_READ_BITS 2000 #define MODBUS_MAX_WRITE_BITS 1968 +/* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 14 page 33) + * Sub-Req. x, Record Number (2 bytes): 1 to 9999 (0x270F) + */ +#define MODBUS_MAX_FILE_RECORD_NUMBER 9999 + /* Modbus_Application_Protocol_V1_1b.pdf (chapter 6 section 3 page 15) * Quantity of Registers to read (2 bytes): 1 to 125 (0x7D) * (chapter 6 section 12 page 31) @@ -211,6 +218,8 @@ MODBUS_API int modbus_write_bit(modbus_t *ctx, int coil_addr, int status); MODBUS_API int modbus_write_register(modbus_t *ctx, int reg_addr, int value); MODBUS_API int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data); MODBUS_API int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data); +MODBUS_API int modbus_write_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, const uint16_t *src); +MODBUS_API int modbus_read_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, uint16_t *dest); MODBUS_API int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask); MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb, const uint16_t *src, int read_addr, int read_nb, From 6c08f48f005514949e283cc79393b8587f762b8a Mon Sep 17 00:00:00 2001 From: Kamil Wcislo Date: Thu, 27 Apr 2017 18:27:57 +0200 Subject: [PATCH 2/8] Add modbus_file_t and update mapping functions --- src/modbus-private.h | 1 + src/modbus.c | 83 ++++++++++++++++++++++++++++++-- src/modbus.h | 16 +++++- tests/bandwidth-server-many-up.c | 3 +- tests/bandwidth-server-one.c | 3 +- tests/random-test-server.c | 2 +- tests/unit-test-server.c | 3 +- 7 files changed, 102 insertions(+), 9 deletions(-) diff --git a/src/modbus-private.h b/src/modbus-private.h index 2c601c495..3e854f117 100644 --- a/src/modbus-private.h +++ b/src/modbus-private.h @@ -106,6 +106,7 @@ struct _modbus { void _modbus_init_common(modbus_t *ctx); void _error_print(modbus_t *ctx, const char *context); int _modbus_receive_msg(modbus_t *ctx, uint8_t *msg, msg_type_t msg_type); +void _modbus_free_files(modbus_file_t *files, int nb_files); #ifndef HAVE_STRLCPY size_t strlcpy(char *dest, const char *src, size_t dest_size); diff --git a/src/modbus.c b/src/modbus.c index 4a30cc73b..163170223 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -1893,9 +1893,12 @@ modbus_mapping_t* modbus_mapping_new_start_address( unsigned int start_bits, unsigned int nb_bits, unsigned int start_input_bits, unsigned int nb_input_bits, unsigned int start_registers, unsigned int nb_registers, - unsigned int start_input_registers, unsigned int nb_input_registers) + unsigned int start_input_registers, unsigned int nb_input_registers, + unsigned int start_files, unsigned int nb_files, + unsigned int nb_records, unsigned int record_size) { modbus_mapping_t *mb_mapping; + unsigned int i, j; mb_mapping = (modbus_mapping_t *)malloc(sizeof(modbus_mapping_t)); if (mb_mapping == NULL) { @@ -1970,16 +1973,88 @@ modbus_mapping_t* modbus_mapping_new_start_address( nb_input_registers * sizeof(uint16_t)); } + /* 5X */ + mb_mapping->nb_files = nb_files; + mb_mapping->start_files = start_files; + if (nb_files == 0) { + mb_mapping->files = NULL; + } else { + mb_mapping->files = (modbus_file_t *) malloc(nb_files * sizeof(modbus_file_t)); + if (mb_mapping->files == NULL) { + free(mb_mapping->tab_input_registers); + free(mb_mapping->tab_registers); + free(mb_mapping->tab_input_bits); + free(mb_mapping->tab_bits); + free(mb_mapping); + return NULL; + } + memset(mb_mapping->files, 0, + nb_files * sizeof(modbus_file_t)); + + for (i = 0; i < nb_files; i++) { + mb_mapping->files[i].nb_records = nb_records; + mb_mapping->files[i].record_size = record_size; + mb_mapping->files[i].records = (uint16_t **) malloc(nb_records * sizeof(uint16_t *)); + if (mb_mapping->files[i].records == NULL) { + while (i) { + free(mb_mapping->files[i--].records); + } + free(mb_mapping->files); + free(mb_mapping->tab_input_registers); + free(mb_mapping->tab_registers); + free(mb_mapping->tab_input_bits); + free(mb_mapping->tab_bits); + free(mb_mapping); + return NULL; + } + for (j = 0; j < nb_records; j++) { + mb_mapping->files[i].records[j] = (uint16_t *) malloc(record_size * sizeof(uint16_t)); + if (mb_mapping->files[i].records[j] == NULL) { + while (j) { + free(mb_mapping->files[i].records[j--]); + } + while (i) { + free(mb_mapping->files[i--].records); + } + free(mb_mapping->files); + free(mb_mapping->tab_input_registers); + free(mb_mapping->tab_registers); + free(mb_mapping->tab_input_bits); + free(mb_mapping->tab_bits); + free(mb_mapping); + return NULL; + } + } + } + } + return mb_mapping; } modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, - int nb_registers, int nb_input_registers) + int nb_registers, int nb_input_registers, + int nb_files, int nb_records, int record_size) { return modbus_mapping_new_start_address( - 0, nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers); + 0, nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers, + 0, nb_files, nb_records, record_size); } +void _modbus_free_files(modbus_file_t *files, int nb_files) +{ + int i, j; + + if (files == NULL) { + return; + } + + for (i = 0; i < nb_files; i++) { + for (j = 0; j < files[i].nb_records; j++) { + free(files[i].records[j]); + } + free(files[i].records); + } +} /* Frees the 4 arrays */ void modbus_mapping_free(modbus_mapping_t *mb_mapping) { @@ -1991,6 +2066,8 @@ void modbus_mapping_free(modbus_mapping_t *mb_mapping) free(mb_mapping->tab_registers); free(mb_mapping->tab_input_bits); free(mb_mapping->tab_bits); + _modbus_free_files(mb_mapping->files, mb_mapping->nb_files); + free(mb_mapping->files); free(mb_mapping); } diff --git a/src/modbus.h b/src/modbus.h index 5983e68fd..fd63146d9 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -161,6 +161,12 @@ extern const unsigned int libmodbus_version_micro; typedef struct _modbus modbus_t; +typedef struct _modbus_file_t { + int nb_records; + int record_size; + uint16_t **records; +} modbus_file_t; + typedef struct _modbus_mapping_t { int nb_bits; int start_bits; @@ -170,10 +176,13 @@ typedef struct _modbus_mapping_t { int start_input_registers; int nb_registers; int start_registers; + int nb_files; + int start_files; uint8_t *tab_bits; uint8_t *tab_input_bits; uint16_t *tab_input_registers; uint16_t *tab_registers; + modbus_file_t *files; } modbus_mapping_t; typedef enum @@ -230,10 +239,13 @@ MODBUS_API modbus_mapping_t* modbus_mapping_new_start_address( unsigned int start_bits, unsigned int nb_bits, unsigned int start_input_bits, unsigned int nb_input_bits, unsigned int start_registers, unsigned int nb_registers, - unsigned int start_input_registers, unsigned int nb_input_registers); + unsigned int start_input_registers, unsigned int nb_input_registers, + unsigned int start_files, unsigned int nb_files, + unsigned int nb_records, unsigned int record_size); MODBUS_API modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, - int nb_registers, int nb_input_registers); + int nb_registers, int nb_input_registers, + int nb_files, int nb_records, int record_size); MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping); MODBUS_API int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length); diff --git a/tests/bandwidth-server-many-up.c b/tests/bandwidth-server-many-up.c index 0f00f3a2f..eb86db831 100644 --- a/tests/bandwidth-server-many-up.c +++ b/tests/bandwidth-server-many-up.c @@ -53,7 +53,8 @@ int main(void) ctx = modbus_new_tcp("127.0.0.1", 1502); mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, - MODBUS_MAX_READ_REGISTERS, 0); + MODBUS_MAX_READ_REGISTERS, 0, + 0, 0, 0); if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); diff --git a/tests/bandwidth-server-one.c b/tests/bandwidth-server-one.c index 8971d0763..4519471f8 100644 --- a/tests/bandwidth-server-one.c +++ b/tests/bandwidth-server-one.c @@ -58,7 +58,8 @@ int main(int argc, char *argv[]) } mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0, - MODBUS_MAX_READ_REGISTERS, 0); + MODBUS_MAX_READ_REGISTERS, 0, + 0, 0, 0); if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); diff --git a/tests/random-test-server.c b/tests/random-test-server.c index 03bd87056..e5b97a9ca 100644 --- a/tests/random-test-server.c +++ b/tests/random-test-server.c @@ -22,7 +22,7 @@ int main(void) ctx = modbus_new_tcp("127.0.0.1", 1502); /* modbus_set_debug(ctx, TRUE); */ - mb_mapping = modbus_mapping_new(500, 500, 500, 500); + mb_mapping = modbus_mapping_new(500, 500, 500, 500, 0, 0, 0); if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); diff --git a/tests/unit-test-server.c b/tests/unit-test-server.c index 7002b10c6..834105d20 100644 --- a/tests/unit-test-server.c +++ b/tests/unit-test-server.c @@ -75,7 +75,8 @@ int main(int argc, char*argv[]) UT_BITS_ADDRESS, UT_BITS_NB, UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_MAX, - UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB); + UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB, + 0, 0, 0, 0); if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); From 847465e69a28e78055d2d4e11773d5479077f612 Mon Sep 17 00:00:00 2001 From: Kamil Wcislo Date: Thu, 27 Apr 2017 18:28:57 +0200 Subject: [PATCH 3/8] Update compute length for file handling functions --- src/modbus.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/modbus.c b/src/modbus.c index 163170223..5481abfcf 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -152,6 +152,12 @@ static unsigned int compute_response_length_from_request(modbus_t *ctx, uint8_t /* The response is device specific (the header provides the length) */ return MSG_LENGTH_UNDEFINED; + case MODBUS_FC_READ_FILE_RECORD: + length = 4 + 2 * (req[offset + 7] << 8 | req[offset + 8]); + break; + case MODBUS_FC_WRITE_FILE_RECORD: + length = 9 + 2 * (req[offset + 7] << 8 | req[offset + 8]); + break; case MODBUS_FC_MASK_WRITE_REGISTER: length = 7; break; @@ -260,6 +266,9 @@ static uint8_t compute_meta_length_after_function(int function, } else if (function == MODBUS_FC_WRITE_MULTIPLE_COILS || function == MODBUS_FC_WRITE_MULTIPLE_REGISTERS) { length = 5; + } else if (function == MODBUS_FC_READ_FILE_RECORD || + function == MODBUS_FC_WRITE_FILE_RECORD) { + length = 8; } else if (function == MODBUS_FC_MASK_WRITE_REGISTER) { length = 6; } else if (function == MODBUS_FC_WRITE_AND_READ_REGISTERS) { @@ -280,6 +289,12 @@ static uint8_t compute_meta_length_after_function(int function, case MODBUS_FC_MASK_WRITE_REGISTER: length = 6; break; + case MODBUS_FC_READ_FILE_RECORD: + length = 3; + break; + case MODBUS_FC_WRITE_FILE_RECORD: + length = 8; + break; default: length = 1; } @@ -304,6 +319,10 @@ static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, case MODBUS_FC_WRITE_AND_READ_REGISTERS: length = msg[ctx->backend->header_length + 9]; break; + case MODBUS_FC_READ_FILE_RECORD: + case MODBUS_FC_WRITE_FILE_RECORD: + length = msg[ctx->backend->header_length + 1] - 7; + break; default: length = 0; } @@ -313,6 +332,10 @@ static int compute_data_length_after_meta(modbus_t *ctx, uint8_t *msg, function == MODBUS_FC_REPORT_SLAVE_ID || function == MODBUS_FC_WRITE_AND_READ_REGISTERS) { length = msg[ctx->backend->header_length + 1]; + } else if (function == MODBUS_FC_READ_FILE_RECORD) { + length = msg[ctx->backend->header_length + 2] - 1; + } else if (function == MODBUS_FC_WRITE_FILE_RECORD) { + length = msg[ctx->backend->header_length + 1] - 7; } else { length = 0; } From 4e59d68d9b9afeade317b2f072d20f2af28baf1a Mon Sep 17 00:00:00 2001 From: Kamil Wcislo Date: Thu, 27 Apr 2017 18:57:06 +0200 Subject: [PATCH 4/8] Add reply handling for read/write file record functions --- src/modbus.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/src/modbus.c b/src/modbus.c index 5481abfcf..7bbc46c98 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -626,9 +626,11 @@ static int check_confirmation(modbus_t *ctx, uint8_t *req, case MODBUS_FC_READ_FILE_RECORD: req_nb_value = (req[offset + 7] << 8) + req[offset + 8]; rsp_nb_value = (rsp[offset + 2] - 1) / 2; + break; case MODBUS_FC_WRITE_FILE_RECORD: req_nb_value = (req[offset + 7] << 8) + req[offset + 8]; rsp_nb_value = (rsp[offset + 7] << 8) + rsp[offset + 8]; + break; default: /* 1 Write functions & others */ req_nb_value = rsp_nb_value = 1; @@ -1016,7 +1018,79 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, } } break; + case MODBUS_FC_READ_FILE_RECORD: { + address = (req[offset + 3] << 8) + req[offset + 4]; + int mapping_address = address - mb_mapping->start_files; + uint16_t record_addr = (req[offset + 5] << 8) + req[offset + 6]; + uint16_t record_len = (req[offset + 7] << 8) + req[offset + 8]; + + if (address < 1 || mapping_address < 0 || mapping_address > mb_mapping->nb_files) { + rsp_length = response_exception( + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, + "Illegal file number %d in read_file_record\n", address); + } else if (record_addr > MODBUS_MAX_FILE_RECORD_NUMBER || + record_addr >= mb_mapping->files[mapping_address].nb_records || + record_len >= mb_mapping->files[mapping_address].record_size) { + rsp_length = response_exception( + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE, + "Illegal record %d, len 0x%0X (max %d) in read_file_record\n", + record_addr, record_len, mb_mapping->files[mapping_address].nb_records); + } else { + int i; + + rsp_length = ctx->backend->build_response_basis(&sft, rsp); + rsp[rsp_length++] = record_len * 2 + 2; // data len + rsp[rsp_length++] = record_len * 2 + 1; // file resp len + rsp[rsp_length++] = 6; // reference type + for (i = 0; i < record_len; i++) { + rsp[rsp_length++] = mb_mapping->files[mapping_address].records[record_addr][i] >> 8; + rsp[rsp_length++] = mb_mapping->files[mapping_address].records[record_addr][i] & 0xFF; + } + } + } + break; + case MODBUS_FC_WRITE_FILE_RECORD: { + address = (req[offset + 3] << 8) + req[offset + 4]; + int mapping_address = address - mb_mapping->start_files; + uint16_t record_addr = (req[offset + 5] << 8) + req[offset + 6]; + uint16_t record_len = (req[offset + 7] << 8) + req[offset + 8]; + if (address < 1 || mapping_address < 0 || mapping_address > mb_mapping->nb_files) { + rsp_length = response_exception( + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp, FALSE, + "Illegal file number %d in write_file_record\n", address); + } else if (record_addr > MODBUS_MAX_FILE_RECORD_NUMBER || + record_addr >= mb_mapping->files[mapping_address].nb_records || + record_len >= mb_mapping->files[mapping_address].record_size) { + rsp_length = response_exception( + ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp, TRUE, + "Illegal record %d, len 0x%0X (max %d) in write_file_record\n", + record_addr, record_len, mb_mapping->files[mapping_address].nb_records); + } else { + int i; + + for (i = 0; i < record_len; i++) { + mb_mapping->files[mapping_address].records[record_addr][i] = (req[offset + 9 + (i *2)] << 8) | + (req[offset + 10 + (i *2)] & 0xFF); + } + + rsp_length = ctx->backend->build_response_basis(&sft, rsp); + rsp[rsp_length++] = record_len * 2 + 7; // data len + rsp[rsp_length++] = 6; // reference type + rsp[rsp_length++] = address >> 8; // file nb + rsp[rsp_length++] = address & 0xFF; + rsp[rsp_length++] = record_addr >> 8; // record nb + rsp[rsp_length++] = record_addr & 0xFF; + rsp[rsp_length++] = record_len >> 8; // record len + rsp[rsp_length++] = record_len & 0xFF; + + for (i = 0; i < record_len; i++) { + rsp[rsp_length++] = mb_mapping->files[mapping_address].records[record_addr][i] >> 8; + rsp[rsp_length++] = mb_mapping->files[mapping_address].records[record_addr][i] & 0xFF; + } + } + } + break; default: rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_FUNCTION, rsp, TRUE, @@ -1297,7 +1371,7 @@ int modbus_read_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, uint1 req[req_length++] = sub_addr >> 8; req[req_length++] = sub_addr & 0x00ff; req[req_length++] = nb >> 8; - req[req_length++] = nb * 0x00ff; + req[req_length++] = nb & 0x00ff; rc = send_msg(ctx, req, req_length); if (rc > 0) { @@ -1525,7 +1599,7 @@ int modbus_write_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, cons req[req_length++] = sub_addr >> 8; req[req_length++] = sub_addr & 0x00ff; req[req_length++] = nb >> 8; - req[req_length++] = nb * 0x00ff; + req[req_length++] = nb & 0x00ff; for (i = 0; i < nb; i++) { req[req_length++] = src[i] >> 8; From ee9a2a2c754913621410e775bc7315906c33faab Mon Sep 17 00:00:00 2001 From: Kamil Wcislo Date: Mon, 10 Sep 2018 18:23:30 +0200 Subject: [PATCH 5/8] Add documentation for file operations functions --- doc/Makefile.am | 2 ++ doc/modbus_mapping_new.txt | 12 ++++--- doc/modbus_mapping_new_start_address.txt | 17 ++++++---- doc/modbus_read_file_record.txt | 42 ++++++++++++++++++++++++ doc/modbus_write_file_record.txt | 37 +++++++++++++++++++++ 5 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 doc/modbus_read_file_record.txt create mode 100644 doc/modbus_write_file_record.txt diff --git a/doc/Makefile.am b/doc/Makefile.am index 5a52c0409..c7d2c973d 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -25,6 +25,7 @@ TXT3 = \ modbus_read_input_bits.txt \ modbus_read_input_registers.txt \ modbus_read_registers.txt \ + modbus_read_file_record.txt \ modbus_receive_confirmation.txt \ modbus_receive.txt \ modbus_reply_exception.txt \ @@ -60,6 +61,7 @@ TXT3 = \ modbus_write_bits.txt \ modbus_write_bit.txt \ modbus_write_registers.txt \ + modbus_write_file_record.txt \ modbus_write_register.txt TXT7 = libmodbus.txt diff --git a/doc/modbus_mapping_new.txt b/doc/modbus_mapping_new.txt index 71cde1d63..ffdaa93c6 100644 --- a/doc/modbus_mapping_new.txt +++ b/doc/modbus_mapping_new.txt @@ -9,13 +9,15 @@ modbus_mapping_new - allocate four arrays of bits and registers SYNOPSIS -------- -*modbus_mapping_t* modbus_mapping_new(int 'nb_bits', int 'nb_input_bits', int 'nb_registers', int 'nb_input_registers');* +*modbus_mapping_t* modbus_mapping_new(int 'nb_bits', int 'nb_input_bits', + int 'nb_registers', int 'nb_input_registers', + int 'nb_files', int 'nb_records', int 'record_size');* DESCRIPTION ----------- -The *modbus_mapping_new()* function shall allocate four arrays to store bits, -input bits, registers and inputs registers. The pointers are stored in +The *modbus_mapping_new()* function shall allocate six arrays to store bits, +input bits, registers, inputs registers, files and file records. The pointers are stored in modbus_mapping_t structure. All values of the arrays are initialized to zero. This function is equivalent to a call of the @@ -48,7 +50,9 @@ EXAMPLE mb_mapping = modbus_mapping_new(BITS_ADDRESS + BITS_NB, INPUT_BITS_ADDRESS + INPUT_BITS_NB, REGISTERS_ADDRESS + REGISTERS_NB, - INPUT_REGISTERS_ADDRESS + INPUT_REGISTERS_NB); + INPUT_REGISTERS_ADDRESS + INPUT_REGISTERS_NB, + FILES_ADDRESS + FILES_NB, + RECORDS_NB, RECORDS_SIZE); if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); diff --git a/doc/modbus_mapping_new_start_address.txt b/doc/modbus_mapping_new_start_address.txt index ec7bfdb35..2612f5dce 100644 --- a/doc/modbus_mapping_new_start_address.txt +++ b/doc/modbus_mapping_new_start_address.txt @@ -12,14 +12,17 @@ SYNOPSIS *modbus_mapping_t* modbus_mapping_new_start_address(int 'start_bits', int 'nb_bits', int 'start_input_bits', int 'nb_input_bits', int 'start_registers', int 'nb_registers', - int 'start_input_registers', int 'nb_input_registers');* + int 'start_input_registers', int 'nb_input_registers', + int 'start_files', int 'nb_files', + int 'nb_records', int 'record_size');* DESCRIPTION ----------- -The _modbus_mapping_new_start_address()_ function shall allocate four arrays to -store bits, input bits, registers and inputs registers. The pointers are stored -in modbus_mapping_t structure. All values of the arrays are initialized to zero. +The _modbus_mapping_new_start_address()_ function shall allocate six arrays to +store bits, input bits, registers, inputs registers, files and file records. +The pointers are stored in modbus_mapping_t structure. All values of the arrays +are initialized to zero. The different starting adresses make it possible to place the mapping at any address in each address space. This way, you can give access to values stored @@ -28,7 +31,7 @@ make available registers from 10000 to 10009, you can use: [source,c] ------------------- -mb_mapping = modbus_mapping_new_start_address(0, 0, 0, 0, 10000, 10, 0, 0); +mb_mapping = modbus_mapping_new_start_address(0, 0, 0, 0, 10000, 10, 0, 0, 0, 0, 0, 0); ------------------- With this code, only 10 registers (`uint16_t`) are allocated. @@ -60,7 +63,9 @@ EXAMPLE mb_mapping = modbus_mapping_new_start_address(BITS_ADDRESS, BITS_NB, INPUT_BITS_ADDRESS, INPUT_BITS_NB, REGISTERS_ADDRESS, REGISTERS_NB, - INPUT_REGISTERS_ADDRESS, INPUT_REGISTERS_NB); + INPUT_REGISTERS_ADDRESS, INPUT_REGISTERS_NB, + FILES_ADDRESS, FILES_NB, + RECORDS_NB, RECORDS_SIZE); if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); diff --git a/doc/modbus_read_file_record.txt b/doc/modbus_read_file_record.txt new file mode 100644 index 000000000..10cef3d7f --- /dev/null +++ b/doc/modbus_read_file_record.txt @@ -0,0 +1,42 @@ +modbus_read_file_record(3) +========================== + + +NAME +---- +modbus_read_file_record - read many input registers + + +SYNOPSIS +-------- +*int modbus_read_file_record(modbus_t *'ctx', int 'addr', int 'sub_addr', int 'nb', uint16_t *'dest');* + + +DESCRIPTION +----------- +The *modbus_read_registers()* function shall read the content of the _nb_ +registers stored in a record at _sub_addr_ in a file at _addr_ of the remote device. +The result of reading is stored in _dest_ array as word values (16 bits). + +You must take care to allocate enough memory to store the results in _dest_ +(at least _nb_ * sizeof(uint16_t)). + +The function uses the Modbus function code 0x14 (read file record). + + +RETURN VALUE +------------ +The function shall return the number of read registers if +successful. Otherwise it shall return -1 and set errno. + + +SEE ALSO +-------- +linkmb:modbus_write_file_record[3] + + +AUTHORS +------- +Kamil Wcislo +The libmodbus documentation was written by Stéphane Raimbault + diff --git a/doc/modbus_write_file_record.txt b/doc/modbus_write_file_record.txt new file mode 100644 index 000000000..bb1196d9b --- /dev/null +++ b/doc/modbus_write_file_record.txt @@ -0,0 +1,37 @@ +modbus_write_file_record(3) +=========================== + + +NAME +---- +modbus_write_file_record - read registers from record in a file + + +SYNOPSIS +-------- +*int modbus_write_file_record(modbus_t *'ctx', int 'addr', int 'sub_addr', int 'nb', uint16_t *'src');* + +DESCRIPTION +----------- +The *modbus_write_file_record()* function shall write the content of the _nb_ +holding registers from the array _src_ to record at _sub_addr_ of the file at +address _addr_ of the remote device. + +The function uses the Modbus function code 0x15 (write file record). + +RETURN VALUE +------------ +The function shall return the number of written registers if +successful. Otherwise it shall return -1 and set errno. + + +SEE ALSO +-------- +linkmb:modbus_read_file_record[3] + + +AUTHORS +------- +Kamil Wcislo +The libmodbus documentation was written by Stéphane Raimbault + From 56bb1eaeaf7f60902a4d0b131a304806de7b5a6a Mon Sep 17 00:00:00 2001 From: Kamil Wcislo Date: Mon, 10 Sep 2018 19:55:10 +0200 Subject: [PATCH 6/8] Fix request length allocation for modbus_read_file_record --- src/modbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modbus.c b/src/modbus.c index 7bbc46c98..5d23e9a00 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -1341,7 +1341,7 @@ int modbus_read_file_record(modbus_t *ctx, int addr, int sub_addr, int nb, uint1 { int rc; int req_length; - uint8_t req[_MIN_REQ_LENGTH]; + uint8_t req[_MIN_REQ_LENGTH + 4]; uint8_t rsp[MAX_MESSAGE_LENGTH]; if (ctx == NULL) { From 0ad89a37a5406135030fb7af7c9fdf6b08251caf Mon Sep 17 00:00:00 2001 From: Kamil Wcislo Date: Mon, 10 Sep 2018 19:55:38 +0200 Subject: [PATCH 7/8] Add unit tests for file operations functions --- tests/unit-test-client.c | 18 ++++++++++++++++++ tests/unit-test-server.c | 2 +- tests/unit-test.h.in | 6 ++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/unit-test-client.c b/tests/unit-test-client.c index fdf5c26ed..e5e70e204 100644 --- a/tests/unit-test-client.c +++ b/tests/unit-test-client.c @@ -273,6 +273,24 @@ int main(int argc, char *argv[]) tab_rp_registers[i], UT_INPUT_REGISTERS_TAB[i]); } + /** FILE OPERATIONS **/ + printf("\nTEST FILE OPERATIONS\n"); + rc = modbus_write_file_record(ctx, 0x1, UT_RECORD_ADDRESS, + UT_REGISTERS_NB, UT_REGISTERS_TAB); + printf("1/2 modbus_write_file_record: "); + ASSERT_TRUE(rc == UT_REGISTERS_NB, "FAILED (nb points %d)\n", rc); + + rc = modbus_read_file_record(ctx, 0x1, UT_RECORD_ADDRESS, + UT_REGISTERS_NB, tab_rp_registers); + printf("2/2 modbus_read_file_record: "); + ASSERT_TRUE(rc == UT_REGISTERS_NB, "FAILED (nb points %d)\n", rc); + + for (i=0; i < UT_REGISTERS_NB; i++) { + ASSERT_TRUE(tab_rp_registers[i] == UT_REGISTERS_TAB[i], + "%d: FAILED (%0X != %0X)\n", + i, tab_rp_registers[i], UT_REGISTERS_TAB[i]); + } + /* MASKS */ printf("1/1 Write mask: "); rc = modbus_write_register(ctx, UT_REGISTERS_ADDRESS, 0x12); diff --git a/tests/unit-test-server.c b/tests/unit-test-server.c index 834105d20..0776f6ee8 100644 --- a/tests/unit-test-server.c +++ b/tests/unit-test-server.c @@ -76,7 +76,7 @@ int main(int argc, char*argv[]) UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB_MAX, UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB, - 0, 0, 0, 0); + UT_FILE_ADDRESS, UT_FILE_NB, UT_RECORDS_NB, UT_RECORDS_SIZE); if (mb_mapping == NULL) { fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno)); diff --git a/tests/unit-test.h.in b/tests/unit-test.h.in index dca826f46..ce5e34190 100644 --- a/tests/unit-test.h.in +++ b/tests/unit-test.h.in @@ -56,6 +56,12 @@ const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x108; const uint16_t UT_INPUT_REGISTERS_NB = 0x1; const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A }; +const uint16_t UT_FILE_ADDRESS = 0x0; +const uint16_t UT_FILE_NB = 0x2; +const uint16_t UT_RECORDS_NB = 0x3; +const uint16_t UT_RECORDS_SIZE = 0x20; +const uint16_t UT_RECORD_ADDRESS = 0x2; + const float UT_REAL = 123456.00; const uint32_t UT_IREAL_ABCD = 0x0020F147; From 2774f801c8f31305d1c87d3ae0b6c2f1f87b12ad Mon Sep 17 00:00:00 2001 From: Kamil Wcislo Date: Fri, 14 Sep 2018 09:39:38 +0200 Subject: [PATCH 8/8] doc/modbus_write_file_record.txt: add const to function definition --- doc/modbus_write_file_record.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/modbus_write_file_record.txt b/doc/modbus_write_file_record.txt index bb1196d9b..9b17410c1 100644 --- a/doc/modbus_write_file_record.txt +++ b/doc/modbus_write_file_record.txt @@ -9,7 +9,7 @@ modbus_write_file_record - read registers from record in a file SYNOPSIS -------- -*int modbus_write_file_record(modbus_t *'ctx', int 'addr', int 'sub_addr', int 'nb', uint16_t *'src');* +*int modbus_write_file_record(modbus_t *'ctx', int 'addr', int 'sub_addr', int 'nb', const uint16_t *'src');* DESCRIPTION -----------