Commit 9a2733e7ecc2c622704753fae7fbf14521de10ea
Committed by
Stéphane Raimbault
1 parent
a2e16a16
New read and write registers function
Showing
6 changed files
with
238 additions
and
4 deletions
src/modbus-private.h
src/modbus.c
| ... | ... | @@ -264,6 +264,7 @@ static unsigned int compute_response_length(modbus_t *ctx, uint8_t *req) |
| 264 | 264 | length = 2 + (nb / 8) + ((nb % 8) ? 1 : 0); |
| 265 | 265 | } |
| 266 | 266 | break; |
| 267 | + case FC_READ_AND_WRITE_REGISTERS: | |
| 267 | 268 | case FC_READ_HOLDING_REGISTERS: |
| 268 | 269 | case FC_READ_INPUT_REGISTERS: |
| 269 | 270 | /* Header + 2 * nb values */ |
| ... | ... | @@ -512,6 +513,8 @@ static uint8_t compute_header_length(int function, msg_type_t msg_type) |
| 512 | 513 | length = 0; |
| 513 | 514 | else |
| 514 | 515 | length = 1; |
| 516 | + } else if (function == FC_READ_AND_WRITE_REGISTERS) { | |
| 517 | + length = 9; | |
| 515 | 518 | } else { |
| 516 | 519 | length = 0; |
| 517 | 520 | } |
| ... | ... | @@ -529,6 +532,8 @@ static int compute_data_length(modbus_t *ctx, uint8_t *msg) |
| 529 | 532 | length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 5]; |
| 530 | 533 | } else if (function == FC_REPORT_SLAVE_ID) { |
| 531 | 534 | length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 1]; |
| 535 | + } else if (function == FC_READ_AND_WRITE_REGISTERS) { | |
| 536 | + length = msg[TAB_HEADER_LENGTH[ctx->type_com] + 9]; | |
| 532 | 537 | } else |
| 533 | 538 | length = 0; |
| 534 | 539 | |
| ... | ... | @@ -789,6 +794,7 @@ static int receive_msg_req(modbus_t *ctx, uint8_t *req, uint8_t *rsp) |
| 789 | 794 | req_nb_value = (req_nb_value / 8) + ((req_nb_value % 8) ? 1 : 0); |
| 790 | 795 | rsp_nb_value = rsp[offset + 1]; |
| 791 | 796 | break; |
| 797 | + case FC_READ_AND_WRITE_REGISTERS: | |
| 792 | 798 | case FC_READ_HOLDING_REGISTERS: |
| 793 | 799 | case FC_READ_INPUT_REGISTERS: |
| 794 | 800 | /* Read functions 1 value = 2 bytes */ |
| ... | ... | @@ -1130,6 +1136,40 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, |
| 1130 | 1136 | errno = ENOPROTOOPT; |
| 1131 | 1137 | return -1; |
| 1132 | 1138 | break; |
| 1139 | + | |
| 1140 | + case FC_READ_AND_WRITE_REGISTERS: { | |
| 1141 | + int nb = (req[offset + 3] << 8) + req[offset + 4]; | |
| 1142 | + uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6]; | |
| 1143 | + int nb_write = (req[offset + 7] << 8) + req[offset + 8]; | |
| 1144 | + | |
| 1145 | + if ((address + nb) > mb_mapping->nb_registers && | |
| 1146 | + (address_write + nb_write) > mb_mapping->nb_registers) { | |
| 1147 | + if (ctx->debug) { | |
| 1148 | + fprintf(stderr, | |
| 1149 | + "Illegal data read address %0X or write address %0X in read_and_write_registers\n", | |
| 1150 | + address + nb, address_write + nb_write); | |
| 1151 | + } | |
| 1152 | + rsp_length = response_exception(ctx, &sft, | |
| 1153 | + MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); | |
| 1154 | + } else { | |
| 1155 | + int i, j; | |
| 1156 | + rsp_length = build_response_basis(ctx, &sft, rsp); | |
| 1157 | + rsp[rsp_length++] = nb << 1; | |
| 1158 | + | |
| 1159 | + for (i = address; i < address + nb; i++) { | |
| 1160 | + rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8; | |
| 1161 | + rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF; | |
| 1162 | + } | |
| 1163 | + | |
| 1164 | + /* 10 and 11 = first value */ | |
| 1165 | + for (i = address_write, j = 10; i < address_write + nb_write; i++, j += 2) { | |
| 1166 | + mb_mapping->tab_registers[i] = | |
| 1167 | + (req[offset + j] << 8) + req[offset + j + 1]; | |
| 1168 | + } | |
| 1169 | + } | |
| 1170 | + } | |
| 1171 | + break; | |
| 1172 | + | |
| 1133 | 1173 | default: |
| 1134 | 1174 | rsp_length = response_exception(ctx, &sft, |
| 1135 | 1175 | MODBUS_EXCEPTION_ILLEGAL_FUNCTION, |
| ... | ... | @@ -1445,8 +1485,73 @@ int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data |
| 1445 | 1485 | return rc; |
| 1446 | 1486 | } |
| 1447 | 1487 | |
| 1488 | +/* Read multiple registers from remote device to dest array and write multiple | |
| 1489 | + registers to remote device from data array. */ | |
| 1490 | +int modbus_read_and_write_holding_registers(modbus_t *ctx, | |
| 1491 | + int read_addr, int read_nb, uint16_t *dest, | |
| 1492 | + int write_addr, int write_nb, const uint16_t *data) | |
| 1493 | +{ | |
| 1494 | + int rc; | |
| 1495 | + int req_length; | |
| 1496 | + int i; | |
| 1497 | + int byte_count; | |
| 1498 | + uint8_t req[MAX_MESSAGE_LENGTH]; | |
| 1499 | + uint8_t rsp[MAX_MESSAGE_LENGTH]; | |
| 1500 | + | |
| 1501 | + if (read_nb > MODBUS_MAX_READ_REGISTERS) { | |
| 1502 | + if (ctx->debug) { | |
| 1503 | + fprintf(stderr, | |
| 1504 | + "ERROR Too many holding registers requested (%d > %d)\n", | |
| 1505 | + read_nb, MODBUS_MAX_READ_REGISTERS); | |
| 1506 | + } | |
| 1507 | + errno = EMBMDATA; | |
| 1508 | + return -1; | |
| 1509 | + } | |
| 1510 | + | |
| 1511 | + if (write_nb > MODBUS_MAX_RW_WRITE_REGISTERS) { | |
| 1512 | + if (ctx->debug) { | |
| 1513 | + fprintf(stderr, | |
| 1514 | + "ERROR Too many holding registers wrote (%d > %d)\n", | |
| 1515 | + write_nb, MODBUS_MAX_RW_WRITE_REGISTERS); | |
| 1516 | + } | |
| 1517 | + errno = EMBMDATA; | |
| 1518 | + return -1; | |
| 1519 | + } | |
| 1520 | + req_length = build_request_basis(ctx, FC_READ_AND_WRITE_REGISTERS, | |
| 1521 | + read_addr, read_nb, req); | |
| 1522 | + | |
| 1523 | + req[req_length++] = write_addr >> 8; | |
| 1524 | + req[req_length++] = write_addr & 0x00ff; | |
| 1525 | + req[req_length++] = write_nb >> 8; | |
| 1526 | + req[req_length++] = write_nb & 0x00ff; | |
| 1527 | + byte_count = write_nb * 2; | |
| 1528 | + req[req_length++] = byte_count; | |
| 1529 | + | |
| 1530 | + for (i = 0; i < write_nb; i++) { | |
| 1531 | + req[req_length++] = data[i] >> 8; | |
| 1532 | + req[req_length++] = data[i] & 0x00FF; | |
| 1533 | + } | |
| 1534 | + | |
| 1535 | + rc = send_msg(ctx, req, req_length); | |
| 1536 | + if (rc > 0) { | |
| 1537 | + int offset; | |
| 1538 | + | |
| 1539 | + rc = receive_msg_req(ctx, req, rsp); | |
| 1540 | + offset = TAB_HEADER_LENGTH[ctx->type_com]; | |
| 1541 | + | |
| 1542 | + /* If rc is negative, the loop is jumped ! */ | |
| 1543 | + for (i = 0; i < rc; i++) { | |
| 1544 | + /* shift reg hi_byte to temp OR with lo_byte */ | |
| 1545 | + dest[i] = (rsp[offset + 2 + (i << 1)] << 8) | | |
| 1546 | + rsp[offset + 3 + (i << 1)]; | |
| 1547 | + } | |
| 1548 | + } | |
| 1549 | + | |
| 1550 | + return rc; | |
| 1551 | +} | |
| 1552 | + | |
| 1448 | 1553 | /* Send a request to get the slave ID of the device (only available in serial |
| 1449 | - * communication). */ | |
| 1554 | + communication). */ | |
| 1450 | 1555 | int modbus_report_slave_id(modbus_t *ctx, uint8_t *data_dest) |
| 1451 | 1556 | { |
| 1452 | 1557 | int rc; | ... | ... |
src/modbus.h
| ... | ... | @@ -182,6 +182,9 @@ int modbus_write_bit(modbus_t *ctx, int coil_addr, int state); |
| 182 | 182 | int modbus_write_register(modbus_t *ctx, int reg_addr, int value); |
| 183 | 183 | int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data); |
| 184 | 184 | int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data); |
| 185 | +int modbus_read_and_write_holding_registers(modbus_t *ctx, int read_addr, | |
| 186 | + int read_nb, uint16_t *dest, int write_addr, | |
| 187 | + int write_nb, const uint16_t *data); | |
| 185 | 188 | int modbus_report_slave_id(modbus_t *ctx, uint8_t *dest); |
| 186 | 189 | |
| 187 | 190 | modbus_mapping_t* modbus_mapping_new(int nb_coil_status, int nb_input_status, | ... | ... |
tests/bandwidth-client.c
| ... | ... | @@ -143,6 +143,43 @@ int main(void) |
| 143 | 143 | printf("* %'d KiB/s\n", rate); |
| 144 | 144 | printf("\n"); |
| 145 | 145 | |
| 146 | + printf("READ AND WRITE REGISTERS\n\n"); | |
| 147 | + | |
| 148 | + nb_points = MODBUS_MAX_RW_WRITE_REGISTERS; | |
| 149 | + start = gettime_ms(); | |
| 150 | + for (i=0; i<NB_LOOPS; i++) { | |
| 151 | + rc = modbus_read_and_write_holding_registers(ctx, 0, nb_points, tab_reg,0, nb_points, tab_reg); | |
| 152 | + if (rc == -1) { | |
| 153 | + fprintf(stderr, "%s\n", modbus_strerror(errno)); | |
| 154 | + return -1; | |
| 155 | + } | |
| 156 | + } | |
| 157 | + end = gettime_ms(); | |
| 158 | + elapsed = end - start; | |
| 159 | + | |
| 160 | + rate = (NB_LOOPS * nb_points) * G_MSEC_PER_SEC / (end - start); | |
| 161 | + printf("Transfert rate in points/seconds:\n"); | |
| 162 | + printf("* %'d registers/s\n", rate); | |
| 163 | + printf("\n"); | |
| 164 | + | |
| 165 | + bytes = NB_LOOPS * nb_points * sizeof(uint16_t); | |
| 166 | + rate = bytes / 1024 * G_MSEC_PER_SEC / (end - start); | |
| 167 | + printf("Values:\n"); | |
| 168 | + printf("* %d x %d values\n", NB_LOOPS, nb_points); | |
| 169 | + printf("* %.3f ms for %d bytes\n", elapsed, bytes); | |
| 170 | + printf("* %'d KiB/s\n", rate); | |
| 171 | + printf("\n"); | |
| 172 | + | |
| 173 | + /* TCP:Query and reponse header and values */ | |
| 174 | + bytes = 12 + 9 + (nb_points * sizeof(uint16_t)); | |
| 175 | + printf("Values and TCP Modbus overhead:\n"); | |
| 176 | + printf("* %d x %d bytes\n", NB_LOOPS, bytes); | |
| 177 | + bytes = NB_LOOPS * bytes; | |
| 178 | + rate = bytes / 1024 * G_MSEC_PER_SEC / (end - start); | |
| 179 | + printf("* %.3f ms for %d bytes\n", elapsed, bytes); | |
| 180 | + printf("* %'d KiB/s\n", rate); | |
| 181 | + printf("\n"); | |
| 182 | + | |
| 146 | 183 | /* Free the memory */ |
| 147 | 184 | free(tab_bit); |
| 148 | 185 | free(tab_reg); | ... | ... |
tests/random-test-client.c
| ... | ... | @@ -56,6 +56,7 @@ int main(void) |
| 56 | 56 | uint8_t *tab_rq_bits; |
| 57 | 57 | uint8_t *tab_rp_bits; |
| 58 | 58 | uint16_t *tab_rq_registers; |
| 59 | + uint16_t *tab_rw_rq_registers; | |
| 59 | 60 | uint16_t *tab_rp_registers; |
| 60 | 61 | |
| 61 | 62 | /* |
| ... | ... | @@ -88,6 +89,9 @@ int main(void) |
| 88 | 89 | tab_rp_registers = (uint16_t *) malloc(nb * sizeof(uint16_t)); |
| 89 | 90 | memset(tab_rp_registers, 0, nb * sizeof(uint16_t)); |
| 90 | 91 | |
| 92 | + tab_rw_rq_registers = (uint16_t *) malloc(nb * sizeof(uint16_t)); | |
| 93 | + memset(tab_rw_rq_registers, 0, nb * sizeof(uint16_t)); | |
| 94 | + | |
| 91 | 95 | nb_loop = nb_fail = 0; |
| 92 | 96 | while (nb_loop++ < LOOP) { |
| 93 | 97 | for (addr = ADDRESS_START; addr <= ADDRESS_END; addr++) { |
| ... | ... | @@ -96,6 +100,7 @@ int main(void) |
| 96 | 100 | /* Random numbers (short) */ |
| 97 | 101 | for (i=0; i<nb; i++) { |
| 98 | 102 | tab_rq_registers[i] = (uint16_t) (65535.0*rand() / (RAND_MAX + 1.0)); |
| 103 | + tab_rw_rq_registers[i] = ~tab_rq_registers[i]; | |
| 99 | 104 | tab_rq_bits[i] = tab_rq_registers[i] % 2; |
| 100 | 105 | } |
| 101 | 106 | nb = ADDRESS_END - addr; |
| ... | ... | @@ -188,7 +193,41 @@ int main(void) |
| 188 | 193 | } |
| 189 | 194 | } |
| 190 | 195 | } |
| 196 | + /* R/W MULTIPLE REGISTERS*/ | |
| 197 | + rc = modbus_read_and_write_holding_registers(ctx, addr, nb, tab_rp_registers, | |
| 198 | + addr, nb, tab_rw_rq_registers); | |
| 199 | + if (rc != nb) { | |
| 200 | + printf("ERROR modbus_read_and_write_registers (%d)\n", rc); | |
| 201 | + printf("Address = %d, nb = %d\n", addr, nb); | |
| 202 | + nb_fail++; | |
| 203 | + } else { | |
| 204 | + for (i=0; i<nb; i++) { | |
| 205 | + if (tab_rq_registers[i] != tab_rp_registers[i]) { | |
| 206 | + printf("ERROR modbus_read_and_write_registers READ\n"); | |
| 207 | + printf("Address = %d, value %d (0x%X) != %d (0x%X)\n", | |
| 208 | + addr, tab_rq_registers[i], tab_rq_registers[i], | |
| 209 | + tab_rp_registers[i], tab_rp_registers[i]); | |
| 210 | + nb_fail++; | |
| 211 | + } | |
| 212 | + } | |
| 191 | 213 | |
| 214 | + rc = modbus_read_registers(ctx, addr, nb, tab_rp_registers); | |
| 215 | + if (rc != nb) { | |
| 216 | + printf("ERROR modbus_read_registers (%d)\n", rc); | |
| 217 | + printf("Address = %d, nb = %d\n", addr, nb); | |
| 218 | + nb_fail++; | |
| 219 | + } else { | |
| 220 | + for (i=0; i<nb; i++) { | |
| 221 | + if (tab_rw_rq_registers[i] != tab_rp_registers[i]) { | |
| 222 | + printf("ERROR modbus_read_and_write_registers WRITE\n"); | |
| 223 | + printf("Address = %d, value %d (0x%X) != %d (0x%X)\n", | |
| 224 | + addr, tab_rw_rq_registers[i], tab_rw_rq_registers[i], | |
| 225 | + tab_rp_registers[i], tab_rp_registers[i]); | |
| 226 | + nb_fail++; | |
| 227 | + } | |
| 228 | + } | |
| 229 | + } | |
| 230 | + } | |
| 192 | 231 | } |
| 193 | 232 | |
| 194 | 233 | printf("Test: "); | ... | ... |
tests/unit-test-client.c
| ... | ... | @@ -204,7 +204,7 @@ int main(void) |
| 204 | 204 | rc = modbus_write_registers(ctx, UT_REGISTERS_ADDRESS, |
| 205 | 205 | UT_REGISTERS_NB_POINTS, |
| 206 | 206 | UT_REGISTERS_TAB); |
| 207 | - printf("1/3 modbus_write_registers: "); | |
| 207 | + printf("1/5 modbus_write_registers: "); | |
| 208 | 208 | if (rc == UT_REGISTERS_NB_POINTS) { |
| 209 | 209 | printf("OK\n"); |
| 210 | 210 | } else { |
| ... | ... | @@ -215,7 +215,7 @@ int main(void) |
| 215 | 215 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, |
| 216 | 216 | UT_REGISTERS_NB_POINTS, |
| 217 | 217 | tab_rp_registers); |
| 218 | - printf("2/3 modbus_read_registers: "); | |
| 218 | + printf("2/5 modbus_read_registers: "); | |
| 219 | 219 | if (rc != UT_REGISTERS_NB_POINTS) { |
| 220 | 220 | printf("FAILED (nb points %d)\n", rc); |
| 221 | 221 | goto close; |
| ... | ... | @@ -233,13 +233,62 @@ int main(void) |
| 233 | 233 | |
| 234 | 234 | rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, |
| 235 | 235 | 0, tab_rp_registers); |
| 236 | - printf("3/3 modbus_read_registers (0): "); | |
| 236 | + printf("3/5 modbus_read_registers (0): "); | |
| 237 | 237 | if (rc != 0) { |
| 238 | 238 | printf("FAILED (nb points %d)\n", rc); |
| 239 | 239 | goto close; |
| 240 | 240 | } |
| 241 | 241 | printf("OK\n"); |
| 242 | 242 | |
| 243 | + nb_points = (UT_REGISTERS_NB_POINTS > | |
| 244 | + UT_INPUT_REGISTERS_NB_POINTS) ? | |
| 245 | + UT_REGISTERS_NB_POINTS : UT_INPUT_REGISTERS_NB_POINTS; | |
| 246 | + memset(tab_rp_registers, 0, nb_points * sizeof(uint16_t)); | |
| 247 | + | |
| 248 | + /* Write registers to zero from tab_rp_registers and read registers to | |
| 249 | + tab_rp_registers. They should be same as UT_REGISTERS_TAB. */ | |
| 250 | + rc = modbus_read_and_write_holding_registers(ctx, | |
| 251 | + UT_REGISTERS_ADDRESS, | |
| 252 | + UT_REGISTERS_NB_POINTS, | |
| 253 | + tab_rp_registers, | |
| 254 | + UT_REGISTERS_ADDRESS, | |
| 255 | + UT_REGISTERS_NB_POINTS, | |
| 256 | + tab_rp_registers); | |
| 257 | + printf("4/5 modbus_read_and_write_registers, read part: "); | |
| 258 | + if (rc != UT_REGISTERS_NB_POINTS) { | |
| 259 | + printf("FAILED (nb points %d)\n", rc); | |
| 260 | + goto close; | |
| 261 | + } | |
| 262 | + | |
| 263 | + for (i=0; i < UT_REGISTERS_NB_POINTS; i++) { | |
| 264 | + if (tab_rp_registers[i] != UT_REGISTERS_TAB[i]) { | |
| 265 | + printf("FAILED (%0X != %0X)\n", | |
| 266 | + tab_rp_registers[i], | |
| 267 | + UT_REGISTERS_TAB[i]); | |
| 268 | + goto close; | |
| 269 | + } | |
| 270 | + } | |
| 271 | + printf("OK\n"); | |
| 272 | + | |
| 273 | + rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, | |
| 274 | + UT_REGISTERS_NB_POINTS, | |
| 275 | + tab_rp_registers); | |
| 276 | + printf("5/5 modbus_read_and_write_registers, write part: "); | |
| 277 | + if (rc != UT_REGISTERS_NB_POINTS) { | |
| 278 | + printf("FAILED (nb points %d)\n", rc); | |
| 279 | + goto close; | |
| 280 | + } | |
| 281 | + | |
| 282 | + for (i=0; i < UT_REGISTERS_NB_POINTS; i++) { | |
| 283 | + if (tab_rp_registers[i] != 0) { | |
| 284 | + printf("FAILED (%0X != %0X)\n", | |
| 285 | + tab_rp_registers[i], | |
| 286 | + UT_REGISTERS_TAB[i]); | |
| 287 | + goto close; | |
| 288 | + } | |
| 289 | + } | |
| 290 | + printf("OK\n"); | |
| 291 | + | |
| 243 | 292 | /* End of many registers */ |
| 244 | 293 | |
| 245 | 294 | ... | ... |