فهرست منبع

Introduce offsets in modbus mappings

This allows to place the coils/registers at any virtual
start address, not only at address 0. This can be useful e.g.
when the server has to fulfill a specification in which
registers are expected at predefined locations.

Signed-off-by: Michael Heimpold <mhei@heimpold.de>
Michael Heimpold 11 سال پیش
والد
کامیت
52ab1bbea7
5فایلهای تغییر یافته به همراه152 افزوده شده و 37 حذف شده
  1. 1 0
      doc/Makefile.am
  2. 4 1
      doc/modbus_mapping_new.txt
  3. 70 0
      doc/modbus_mapping_offset_new.txt
  4. 69 36
      src/modbus.c
  5. 8 0
      src/modbus.h

+ 1 - 0
doc/Makefile.am

@@ -12,6 +12,7 @@ TXT3 = \
         modbus_get_socket.txt \
         modbus_mapping_free.txt \
         modbus_mapping_new.txt \
+        modbus_mapping_offset_new.txt \
         modbus_mask_write_register.txt \
         modbus_new_rtu.txt \
         modbus_new_tcp_pi.txt \

+ 4 - 1
doc/modbus_mapping_new.txt

@@ -18,6 +18,9 @@ The *modbus_mapping_new()* 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.
 
+This function is equivalent to a call of the _modbus_mapping_offset_new()_ function
+with all offsets set to zero.
+
 If it isn't necessary to allocate an array for a specific type of data, you can
 pass the zero value in argument, the associated pointer will be NULL.
 
@@ -40,7 +43,7 @@ EXAMPLE
 -------
 [source,c]
 -------------------
-/* The fist value of each array is accessible from the 0 address. */
+/* The first value of each array is accessible from the 0 address. */
 mb_mapping = modbus_mapping_new(BITS_ADDRESS + BITS_NB,
                                 INPUT_BITS_ADDRESS + INPUT_BITS_NB,
                                 REGISTERS_ADDRESS + REGISTERS_NB,

+ 70 - 0
doc/modbus_mapping_offset_new.txt

@@ -0,0 +1,70 @@
+modbus_mapping_offset_new(3)
+============================
+
+
+NAME
+----
+modbus_mapping_offset_new - allocate four arrays of bits and registers
+
+
+SYNOPSIS
+--------
+*modbus_mapping_t* modbus_mapping_new(int 'nb_bits', int 'offset_bits',
+                                      int 'nb_input_bits', int 'offset_input_bits',
+                                      int 'nb_registers', int 'offset_registers',
+                                      int 'nb_input_registers', int 'offset_input_registers');*
+
+
+DESCRIPTION
+-----------
+The _modbus_mapping_offset_new()_ 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 different offsets make it possible to place the mapping at any address in
+each address space.
+
+If it isn't necessary to allocate an array for a specific type of data, you can
+pass the zero value in argument, the associated pointer will be NULL.
+
+This function is convenient to handle requests in a Modbus server/slave.
+
+
+RETURN VALUE
+------------
+The _modbus_mapping_offset_new()_ function shall return the new allocated structure if
+successful. Otherwise it shall return NULL and set errno.
+
+
+ERRORS
+------
+ENOMEM::
+Not enough memory
+
+
+EXAMPLE
+-------
+[source,c]
+-------------------
+/* The first value of each array is accessible at address 4. */
+mb_mapping = modbus_mapping_offset_new(BITS_ADDRESS + BITS_NB, 4,
+                                INPUT_BITS_ADDRESS + INPUT_BITS_NB, 4,
+                                REGISTERS_ADDRESS + REGISTERS_NB, 4,
+                                INPUT_REGISTERS_ADDRESS + INPUT_REGISTERS_NB, 4);
+if (mb_mapping == NULL) {
+    fprintf(stderr, "Failed to allocate the mapping: %s\n",
+            modbus_strerror(errno));
+    modbus_free(ctx);
+    return -1;
+}
+-------------------
+
+SEE ALSO
+--------
+linkmb:modbus_mapping_free[3]
+
+
+AUTHORS
+-------
+The libmodbus documentation was written by Stéphane Raimbault
+<stephane.raimbault@gmail.com>

+ 69 - 36
src/modbus.c

@@ -706,6 +706,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
     switch (function) {
     case MODBUS_FC_READ_COILS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
+        int addr = address - mb_mapping->offset_bits;
 
         if (nb < 1 || MODBUS_MAX_READ_BITS < nb) {
             if (ctx->debug) {
@@ -718,10 +719,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp);
-        } else if ((address + nb) > mb_mapping->nb_bits) {
+        } else if (address < mb_mapping->offset_bits || (addr + nb) > mb_mapping->nb_bits) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address 0x%0X in read_bits\n",
-                        address + nb);
+                        address < mb_mapping->offset_bits ? address : address + nb);
             }
             rsp_length = response_exception(
                 ctx, &sft,
@@ -729,7 +730,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         } else {
             rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
-            rsp_length = response_io_status(address, nb,
+            rsp_length = response_io_status(addr, nb,
                                             mb_mapping->tab_bits,
                                             rsp, rsp_length);
         }
@@ -739,6 +740,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         /* Similar to coil status (but too many arguments to use a
          * function) */
         int nb = (req[offset + 3] << 8) + req[offset + 4];
+        int addr = address - mb_mapping->offset_input_bits;
 
         if (nb < 1 || MODBUS_MAX_READ_BITS < nb) {
             if (ctx->debug) {
@@ -751,10 +753,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp);
-        } else if ((address + nb) > mb_mapping->nb_input_bits) {
+        } else if (address < mb_mapping->offset_input_bits || (addr + nb) > mb_mapping->nb_input_bits) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address 0x%0X in read_input_bits\n",
-                        address + nb);
+                        address < mb_mapping->offset_input_bits ? address : address + nb);
             }
             rsp_length = response_exception(
                 ctx, &sft,
@@ -762,7 +764,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         } else {
             rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0);
-            rsp_length = response_io_status(address, nb,
+            rsp_length = response_io_status(addr, nb,
                                             mb_mapping->tab_input_bits,
                                             rsp, rsp_length);
         }
@@ -770,6 +772,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         break;
     case MODBUS_FC_READ_HOLDING_REGISTERS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
+        int addr = address - mb_mapping->offset_registers;
 
         if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) {
             if (ctx->debug) {
@@ -782,10 +785,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp);
-        } else if ((address + nb) > mb_mapping->nb_registers) {
+        } else if (address < mb_mapping->offset_registers || (addr + nb) > mb_mapping->nb_registers) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address 0x%0X in read_registers\n",
-                        address + nb);
+                        address < mb_mapping->offset_registers ? address : address + nb);
             }
             rsp_length = response_exception(
                 ctx, &sft,
@@ -795,7 +798,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
 
             rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = nb << 1;
-            for (i = address; i < address + nb; i++) {
+            for (i = addr; i < addr + nb; i++) {
                 rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8;
                 rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF;
             }
@@ -806,6 +809,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         /* Similar to holding registers (but too many arguments to use a
          * function) */
         int nb = (req[offset + 3] << 8) + req[offset + 4];
+        int addr = address - mb_mapping->offset_input_registers;
 
         if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) {
             if (ctx->debug) {
@@ -818,10 +822,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp);
-        } else if ((address + nb) > mb_mapping->nb_input_registers) {
+        } else if (address < mb_mapping->offset_input_registers || (addr + nb) > mb_mapping->nb_input_registers) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address 0x%0X in read_input_registers\n",
-                        address + nb);
+                        address < mb_mapping->offset_input_registers ? address : address + nb);
             }
             rsp_length = response_exception(
                 ctx, &sft,
@@ -831,15 +835,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
 
             rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             rsp[rsp_length++] = nb << 1;
-            for (i = address; i < address + nb; i++) {
+            for (i = addr; i < addr + nb; i++) {
                 rsp[rsp_length++] = mb_mapping->tab_input_registers[i] >> 8;
                 rsp[rsp_length++] = mb_mapping->tab_input_registers[i] & 0xFF;
             }
         }
     }
         break;
-    case MODBUS_FC_WRITE_SINGLE_COIL:
-        if (address >= mb_mapping->nb_bits) {
+    case MODBUS_FC_WRITE_SINGLE_COIL: {
+        int addr = address - mb_mapping->offset_bits;
+
+        if (address < mb_mapping->offset_bits || addr >= mb_mapping->nb_bits) {
             if (ctx->debug) {
                 fprintf(stderr,
                         "Illegal data address 0x%0X in write_bit\n",
@@ -852,7 +858,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             int data = (req[offset + 3] << 8) + req[offset + 4];
 
             if (data == 0xFF00 || data == 0x0) {
-                mb_mapping->tab_bits[address] = (data) ? ON : OFF;
+                mb_mapping->tab_bits[addr] = (data) ? ON : OFF;
                 memcpy(rsp, req, req_length);
                 rsp_length = req_length;
             } else {
@@ -866,9 +872,12 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
                     MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp);
             }
         }
+    }
         break;
-    case MODBUS_FC_WRITE_SINGLE_REGISTER:
-        if (address >= mb_mapping->nb_registers) {
+    case MODBUS_FC_WRITE_SINGLE_REGISTER: {
+        int addr = address - mb_mapping->offset_registers;
+
+        if (address < mb_mapping->offset_registers || addr >= mb_mapping->nb_registers) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address 0x%0X in write_register\n",
                         address);
@@ -879,13 +888,15 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         } else {
             int data = (req[offset + 3] << 8) + req[offset + 4];
 
-            mb_mapping->tab_registers[address] = data;
+            mb_mapping->tab_registers[addr] = data;
             memcpy(rsp, req, req_length);
             rsp_length = req_length;
         }
+    }
         break;
     case MODBUS_FC_WRITE_MULTIPLE_COILS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
+        int addr = address - mb_mapping->offset_bits;
 
         if (nb < 1 || MODBUS_MAX_WRITE_BITS < nb) {
             if (ctx->debug) {
@@ -901,17 +912,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp);
-        } else if ((address + nb) > mb_mapping->nb_bits) {
+        } else if (address < mb_mapping->offset_bits || (addr + nb) > mb_mapping->nb_bits) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address 0x%0X in write_bits\n",
-                        address + nb);
+                        address < mb_mapping->offset_bits ? address : address + nb);
             }
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
         } else {
             /* 6 = byte count */
-            modbus_set_bits_from_bytes(mb_mapping->tab_bits, address, nb, &req[offset + 6]);
+            modbus_set_bits_from_bytes(mb_mapping->tab_bits, addr, nb, &req[offset + 6]);
 
             rsp_length = ctx->backend->build_response_basis(&sft, rsp);
             /* 4 to copy the bit address (2) and the quantity of bits */
@@ -922,6 +933,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         break;
     case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
+        int addr = address - mb_mapping->offset_registers;
+
         if (nb < 1 || MODBUS_MAX_WRITE_REGISTERS < nb) {
             if (ctx->debug) {
                 fprintf(stderr,
@@ -936,17 +949,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp);
-        } else if ((address + nb) > mb_mapping->nb_registers) {
+        } else if (address < mb_mapping->offset_registers || (addr + nb) > mb_mapping->nb_registers) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address 0x%0X in write_registers\n",
-                        address + nb);
+                        address < mb_mapping->offset_registers ? address : address + nb);
             }
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
         } else {
             int i, j;
-            for (i = address, j = 6; i < address + nb; i++, j += 2) {
+            for (i = addr, j = 6; i < addr + nb; i++, j += 2) {
                 /* 6 and 7 = first value */
                 mb_mapping->tab_registers[i] =
                     (req[offset + j] << 8) + req[offset + j + 1];
@@ -983,8 +996,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
         errno = ENOPROTOOPT;
         return -1;
         break;
-    case MODBUS_FC_MASK_WRITE_REGISTER:
-        if (address >= mb_mapping->nb_registers) {
+    case MODBUS_FC_MASK_WRITE_REGISTER: {
+        int addr = address - mb_mapping->offset_registers;
+
+        if (address < mb_mapping->offset_registers || addr >= mb_mapping->nb_registers) {
             if (ctx->debug) {
                 fprintf(stderr, "Illegal data address 0x%0X in write_register\n",
                         address);
@@ -993,21 +1008,24 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
         } else {
-            uint16_t data = mb_mapping->tab_registers[address];
+            uint16_t data = mb_mapping->tab_registers[addr];
             uint16_t and = (req[offset + 3] << 8) + req[offset + 4];
             uint16_t or = (req[offset + 5] << 8) + req[offset + 6];
 
             data = (data & and) | (or & (~and));
-            mb_mapping->tab_registers[address] = data;
+            mb_mapping->tab_registers[addr] = data;
             memcpy(rsp, req, req_length);
             rsp_length = req_length;
         }
+    }
         break;
     case MODBUS_FC_WRITE_AND_READ_REGISTERS: {
         int nb = (req[offset + 3] << 8) + req[offset + 4];
         uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6];
         int nb_write = (req[offset + 7] << 8) + req[offset + 8];
         int nb_write_bytes = req[offset + 9];
+        int addr = address - mb_mapping->offset_registers;
+        int addr_write = address_write - mb_mapping->offset_registers;
 
         if (nb_write < 1 || MODBUS_MAX_WR_WRITE_REGISTERS < nb_write ||
             nb < 1 || MODBUS_MAX_WR_READ_REGISTERS < nb ||
@@ -1023,12 +1041,15 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
             rsp_length = response_exception(
                 ctx, &sft,
                 MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp);
-        } else if ((address + nb) > mb_mapping->nb_registers ||
-                   (address_write + nb_write) > mb_mapping->nb_registers) {
+        } else if (address < mb_mapping->offset_registers ||
+                   (addr + nb) > mb_mapping->nb_registers ||
+                   address_write < mb_mapping->offset_registers ||
+                   (addr_write + nb_write) > mb_mapping->nb_registers) {
             if (ctx->debug) {
                 fprintf(stderr,
                         "Illegal data read address 0x%0X or write address 0x%0X write_and_read_registers\n",
-                        address + nb, address_write + nb_write);
+                        address < mb_mapping->offset_registers ? address : address + nb,
+                        address_write < mb_mapping->offset_registers ? address_write : address_write + nb_write);
             }
             rsp_length = response_exception(ctx, &sft,
                                             MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp);
@@ -1039,13 +1060,13 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req,
 
             /* Write first.
                10 and 11 are the offset of the first values to write */
-            for (i = address_write, j = 10; i < address_write + nb_write; i++, j += 2) {
+            for (i = addr_write, j = 10; i < addr_write + nb_write; i++, j += 2) {
                 mb_mapping->tab_registers[i] =
                     (req[offset + j] << 8) + req[offset + j + 1];
             }
 
             /* and read the data for the response */
-            for (i = address; i < address + nb; i++) {
+            for (i = addr; i < addr + nb; i++) {
                 rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8;
                 rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF;
             }
@@ -1786,10 +1807,12 @@ int modbus_set_debug(modbus_t *ctx, int flag)
 /* Allocates 4 arrays to store bits, input bits, registers and inputs
    registers. The pointers are stored in modbus_mapping structure.
 
-   The modbus_mapping_new() function shall return the new allocated structure if
+   The modbus_mapping_offset_new() function shall return the new allocated structure if
    successful. Otherwise it shall return NULL and set errno to ENOMEM. */
-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_offset_new(int nb_bits, int offset_bits,
+                                            int nb_input_bits, int offset_input_bits,
+                                            int nb_registers, int offset_registers,
+                                            int nb_input_registers, int offset_input_registers)
 {
     modbus_mapping_t *mb_mapping;
 
@@ -1800,6 +1823,7 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
 
     /* 0X */
     mb_mapping->nb_bits = nb_bits;
+    mb_mapping->offset_bits = offset_bits;
     if (nb_bits == 0) {
         mb_mapping->tab_bits = NULL;
     } else {
@@ -1815,6 +1839,7 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
 
     /* 1X */
     mb_mapping->nb_input_bits = nb_input_bits;
+    mb_mapping->offset_input_bits = offset_input_bits;
     if (nb_input_bits == 0) {
         mb_mapping->tab_input_bits = NULL;
     } else {
@@ -1830,6 +1855,7 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
 
     /* 4X */
     mb_mapping->nb_registers = nb_registers;
+    mb_mapping->offset_registers = offset_registers;
     if (nb_registers == 0) {
         mb_mapping->tab_registers = NULL;
     } else {
@@ -1846,6 +1872,7 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
 
     /* 3X */
     mb_mapping->nb_input_registers = nb_input_registers;
+    mb_mapping->offset_input_registers = offset_input_registers;
     if (nb_input_registers == 0) {
         mb_mapping->tab_input_registers = NULL;
     } else {
@@ -1865,6 +1892,12 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
     return mb_mapping;
 }
 
+modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
+                                     int nb_registers, int nb_input_registers)
+{
+    return modbus_mapping_offset_new(nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers, 0);
+}
+
 /* Frees the 4 arrays */
 void modbus_mapping_free(modbus_mapping_t *mb_mapping)
 {

+ 8 - 0
src/modbus.h

@@ -156,9 +156,13 @@ typedef struct _modbus modbus_t;
 
 typedef struct {
     int nb_bits;
+    int offset_bits;
     int nb_input_bits;
+    int offset_input_bits;
     int nb_input_registers;
+    int offset_input_registers;
     int nb_registers;
+    int offset_registers;
     uint8_t *tab_bits;
     uint8_t *tab_input_bits;
     uint16_t *tab_input_registers;
@@ -209,6 +213,10 @@ MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, in
                                                uint16_t *dest);
 MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest);
 
+MODBUS_API modbus_mapping_t* modbus_mapping_offset_new(int nb_bits, int offset_bits,
+                                            int nb_input_bits, int offset_input_bits,
+                                            int nb_registers, int offset_registers,
+                                            int nb_input_registers, int offset_input_registers);
 MODBUS_API modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits,
                                             int nb_registers, int nb_input_registers);
 MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping);