modbus plugin: add (user accessible) transaction bool flags for servers/slaves; FIX BUG in previous commit affecting RTU slaves.
authorMario de Sousa <msousa@fe.up.pt>
Thu, 28 Jan 2021 14:50:26 +0000
changeset 2722 5d72a52b8f9c
parent 2721 367eb26972b1
child 2723 cde2e410b874
modbus plugin: add (user accessible) transaction bool flags for servers/slaves; FIX BUG in previous commit affecting RTU slaves.
modbus/mb_runtime.c
modbus/mb_runtime.h
modbus/modbus.py
--- a/modbus/mb_runtime.c	Thu Jan 28 14:17:40 2021 +0000
+++ b/modbus/mb_runtime.c	Thu Jan 28 14:50:26 2021 +0000
@@ -201,9 +201,11 @@
 static int __read_inbits   (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes) {
   int res = __pack_bits(((server_mem_t *)mem_map)->ro_bits, start_addr, bit_count, data_bytes);
   
-  if (res >= 0)
-    /* update the counter of Modbus requests we have processed. */
+  if (res >= 0) {
+    /* update the flag and counter of Modbus requests we have processed. */
     ((server_mem_t *)mem_map)->flag_read_req_counter++;
+    ((server_mem_t *)mem_map)->flag_read_req_flag = 1;
+  }
 
   return res;
 }
@@ -211,9 +213,11 @@
 static int __read_outbits  (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes) {
   int res = __pack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes);
   
-  if (res >= 0)
-    /* update the counter of Modbus requests we have processed. */
+  if (res >= 0) {
+    /* update the flag and counter of Modbus requests we have processed. */
     ((server_mem_t *)mem_map)->flag_read_req_counter++;
+    ((server_mem_t *)mem_map)->flag_read_req_flag = 1;
+  }
 
   return res;
 }
@@ -221,9 +225,11 @@
 static int __write_outbits (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes) {
   int res = __unpack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes);
   
-  if (res >= 0)
-    /* update the counter of Modbus requests we have processed. */
+  if (res >= 0) {
+    /* update the flag and counter of Modbus requests we have processed. */
     ((server_mem_t *)mem_map)->flag_write_req_counter++;
+    ((server_mem_t *)mem_map)->flag_write_req_flag = 1;
+  }
 
   return res;
 }
@@ -236,8 +242,9 @@
   if ((start_addr + word_count) > MEM_AREA_SIZE)
     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
 
-  /* update the counter of Modbus requests we have processed. */
+  /* update the flag and counter of Modbus requests we have processed. */
   ((server_mem_t *)mem_map)->flag_read_req_counter++;
+  ((server_mem_t *)mem_map)->flag_read_req_flag = 1;
 
   /* use memcpy() because loop with pointers (u16 *) caused alignment problems */
   memcpy(/* dest */ (void *)data_words,
@@ -253,8 +260,9 @@
   if ((start_addr + word_count) > MEM_AREA_SIZE)
     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
 
-  /* update the counter of Modbus requests we have processed. */
+  /* update the flag and counter of Modbus requests we have processed. */
   ((server_mem_t *)mem_map)->flag_read_req_counter++;
+  ((server_mem_t *)mem_map)->flag_read_req_flag = 1;
 
   /* use memcpy() because loop with pointers (u16 *) caused alignment problems */
   memcpy(/* dest */ (void *)data_words,
@@ -271,8 +279,9 @@
   if ((start_addr + word_count) > MEM_AREA_SIZE)
     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
 
-  /* update the counter of Modbus requests we have processed. */
+  /* update the flag and counter of Modbus requests we have processed. */
   ((server_mem_t *)mem_map)->flag_write_req_counter++;
+  ((server_mem_t *)mem_map)->flag_write_req_flag = 1;
 
   /* WARNING: The data returned in the data_words[] array is not guaranteed to be 16 bit aligned.
    *           It is not therefore safe to cast it to an u16 data type.
@@ -558,8 +567,10 @@
 		//   -1  -->    modbus node created!; no thread  created
 		//  >=0  -->    modbus node created!;    thread  created!
 		server_nodes[index].mb_nd = -2; 
+		server_nodes[index].mem_area.flag_write_req_flag    = 0; 
 		server_nodes[index].mem_area.flag_write_req_counter = 0; 
 		server_nodes[index].mem_area.flag_read_req_counter  = 0; 
+		server_nodes[index].mem_area.flag_read_req_flag     = 0; 
         /* see comment in mb_runtime.h to understad why we need to initialize these entries */
         switch (server_nodes[index].node_address.naf) {
             case naf_tcp:
--- a/modbus/mb_runtime.h	Thu Jan 28 14:17:40 2021 +0000
+++ b/modbus/mb_runtime.h	Thu Jan 28 14:50:26 2021 +0000
@@ -42,14 +42,24 @@
 	    u16		ro_words[MEM_AREA_SIZE];
 	    u16		rw_words[MEM_AREA_SIZE];
             /* Two flags to count the number of Modbus requests (read and write) we have 
-             * successfully received from any remote Modbus master
-             * These two flags will be mapped onto located variables
+             * successfully received from any remote Modbus master.
+             * Two boolean flags that are set whenever we successfully process a
+             * Modbus request sent from a remote client.
+             * These flags will be mapped onto located variables
              * so the user's IEC 61131-3 code can check whether we are being
              * polled by a Modbus master.
              * The counters will roll over to 0 upon reaching maximum value.
+             * The user will probably periodically reset the boolean flags to false,
+             * and use this as a communication timeout 
+             * (when it remains false in two consecutive periods)
+             * 
+             * u8  for BOOL  variable/flag
+             * u32 for UDINT variable/counter
              */
-            u32         flag_write_req_counter;
-            u32         flag_read_req_counter;
+        u8   flag_write_req_flag;
+        u8   flag_read_req_flag;
+        u32  flag_write_req_counter;
+        u32  flag_read_req_counter;
 	} server_mem_t;
 
 
--- a/modbus/modbus.py	Thu Jan 28 14:17:40 2021 +0000
+++ b/modbus/modbus.py	Thu Jan 28 14:50:26 2021 +0000
@@ -452,10 +452,10 @@
         # execute the Modbus request.
         # NOTE: If the Modbus slave has a 'current_location' of
         #          %QX1.2
-        #       then the "Modbus Read Request Counter" will be
-        #          %MD1.2.0
-        #       then the "Modbus Write Request Counter" will be
-        #          %MD1.2.1
+        #       then the "Modbus Read Request Counter"  will be %MD1.2.0
+        #       then the "Modbus Write Request Counter" will be %MD1.2.1
+        #       then the "Modbus Read Request Flag"     will be %MD1.2.2
+        #       then the "Modbus Write Request Flag"    will be %MD1.2.3
         #
         # Note that any MemoryArea contained under this server/slave
         # will ocupy the locations of type
@@ -467,6 +467,8 @@
         # and therefore never ocupy the locations
         #           %M1.2.0
         #           %M1.2.1
+        #           %M1.2.2
+        #           %M1.2.3
         # used by the following flags/counters.
         entries = []
         entries.append({
@@ -487,6 +489,24 @@
             "location": "D" + ".".join([str(i) for i in current_location]) + ".1",
             "description": "Modbus write request counter",
             "children": []})        
+        entries.append({
+            "name": "Modbus Read Request Flag",
+            "type": LOCATION_VAR_MEMORY,
+            "size": 1,            # BOOL flag
+            "IEC_type": "BOOL",   # BOOL flag
+            "var_name": "var_name",
+            "location": "X" + ".".join([str(i) for i in current_location]) + ".2",
+            "description": "Modbus read request flag",
+            "children": []})        
+        entries.append({
+            "name": "Modbus write Request Flag",
+            "type": LOCATION_VAR_MEMORY,
+            "size": 1,            # BOOL flag
+            "IEC_type": "BOOL",   # BOOL flag
+            "var_name": "var_name",
+            "location": "X" + ".".join([str(i) for i in current_location]) + ".3",
+            "description": "Modbus write request flag",
+            "children": []})        
         # recursively call all the Memory Areas under this Modbus server/save
         # i.e., all the children objects which will be of class _MemoryAreaPlug
         for child in self.IECSortedChildren():
@@ -689,10 +709,10 @@
         # execute the Modbus request.
         # NOTE: If the Modbus slave has a 'current_location' of
         #          %QX1.2
-        #       then the "Modbus Read Request Counter" will be
-        #          %MD1.2.0
-        #       then the "Modbus Write Request Counter" will be
-        #          %MD1.2.1
+        #       then the "Modbus Read Request Counter"  will be %MD1.2.0
+        #       then the "Modbus Write Request Counter" will be %MD1.2.1
+        #       then the "Modbus Read Request Flag"     will be %MD1.2.2
+        #       then the "Modbus Write Request Flag"    will be %MD1.2.3
         #
         # Note that any MemoryArea contained under this server/slave
         # will ocupy the locations of type
@@ -704,6 +724,8 @@
         # and therefore never ocupy the locations
         #           %M1.2.0
         #           %M1.2.1
+        #           %M1.2.2
+        #           %M1.2.3
         # used by the following flags/counters.
         entries = []
         entries.append({
@@ -724,6 +746,24 @@
             "location": "D" + ".".join([str(i) for i in current_location]) + ".1",
             "description": "Modbus write request counter",
             "children": []})        
+        entries.append({
+            "name": "Modbus Read Request Flag",
+            "type": LOCATION_VAR_MEMORY,
+            "size": 1,            # BOOL flag
+            "IEC_type": "BOOL",   # BOOL flag
+            "var_name": "var_name",
+            "location": "X" + ".".join([str(i) for i in current_location]) + ".2",
+            "description": "Modbus read request flag",
+            "children": []})        
+        entries.append({
+            "name": "Modbus write Request Flag",
+            "type": LOCATION_VAR_MEMORY,
+            "size": 1,            # BOOL flag
+            "IEC_type": "BOOL",   # BOOL flag
+            "var_name": "var_name",
+            "location": "X" + ".".join([str(i) for i in current_location]) + ".3",
+            "description": "Modbus write request flag",
+            "children": []})        
         # recursively call all the Memory Areas under this Modbus server/save
         # i.e., all the children objects which will be of class _MemoryAreaPlug
         for child in self.IECSortedChildren():
@@ -924,17 +964,11 @@
                 if new_node is None:
                     return [], "", False
                 server_node_list.append(new_node)                
-                #        We currently add 2 flags/counters to each Modbus server/slave
+                #        We currently add 4 flags/counters to each Modbus server/slave
                 #
-                # TODO: fix comment
-                #        We add the "Execution Control Flag" to each client request (one flag per request)
-                #        to allow the user program to control when to execute the request (if not executed periodically)
-                #        While all Modbus registers/coils are mapped onto a location
-                #        with 4 numbers (e.g. %QX0.1.2.55), this control flag is mapped
-                #        onto a location with 4 numbers (e.g. %QX0.1.2.0.0), where the last
-                #        two numbers are always '0.0', and the first two identify the request.
-                #        In the following if, we check for this condition by checking
-                #        if there are at least 4 or more number in the location's address.
+                #        We add the Modbus read/write counter/flag to each Modbus slave/server
+                #        to allow the user program to determine if the slave is being actively
+                #        read from or written by by a remote Modbus client.
                 for iecvar in child.GetLocations():
                     #print "child" + repr(iecvar)
                     if (len(iecvar["LOC"]) == 3) and (str(iecvar["NAME"]) not in loc_vars_list):
@@ -946,6 +980,14 @@
                         if iecvar["LOC"][2] == 1:
                             loc_vars.append("u32 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.flag_write_req_counter;" % (server_id))
                             loc_vars_list.append(str(iecvar["NAME"]))
+                        # Add if it is a "Modbus Read Request Flag" (mapped onto %MDa.b.2), so last number is a '2'
+                        if iecvar["LOC"][2] == 2:
+                            loc_vars.append("u8 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.flag_read_req_flag;" % (server_id))
+                            loc_vars_list.append(str(iecvar["NAME"]))
+                        # Add if it is a "Modbus Write Request Counter" (mapped onto %MDa.b.3), so last number is a '3'
+                        if iecvar["LOC"][2] == 3:
+                            loc_vars.append("u8 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.flag_write_req_flag;" % (server_id))
+                            loc_vars_list.append(str(iecvar["NAME"]))
                 
                 for subchild in child.IECSortedChildren():
                     new_memarea = GetTCPServerMemAreaPrinted(self, subchild, nodeid)
@@ -975,7 +1017,31 @@
                 if new_node is None:
                     return [], "", False
                 server_node_list.append(new_node)
+                #        We currently add 4 flags/counters to each Modbus server/slave
                 #
+                #        We add the Modbus read/write counter/flag to each Modbus slave/server
+                #        to allow the user program to determine if the slave is being actively
+                #        read from or written by by a remote Modbus client.
+                for iecvar in child.GetLocations():
+                    #print "child" + repr(iecvar)
+                    if (len(iecvar["LOC"]) == 3) and (str(iecvar["NAME"]) not in loc_vars_list):
+                        # Add if it is a "Modbus Read Request Counter" (mapped onto %MDa.b.0), so last number is a '0'
+                        if iecvar["LOC"][2] == 0:
+                            loc_vars.append("u32 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.flag_read_req_counter;" % (server_id))
+                            loc_vars_list.append(str(iecvar["NAME"]))
+                        # Add if it is a "Modbus Write Request Counter" (mapped onto %MDa.b.1), so last number is a '1'
+                        if iecvar["LOC"][2] == 1:
+                            loc_vars.append("u32 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.flag_write_req_counter;" % (server_id))
+                            loc_vars_list.append(str(iecvar["NAME"]))
+                        # Add if it is a "Modbus Read Request Flag" (mapped onto %MDa.b.2), so last number is a '2'
+                        if iecvar["LOC"][2] == 2:
+                            loc_vars.append("u8 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.flag_read_req_flag;" % (server_id))
+                            loc_vars_list.append(str(iecvar["NAME"]))
+                        # Add if it is a "Modbus Write Request Counter" (mapped onto %MDa.b.3), so last number is a '3'
+                        if iecvar["LOC"][2] == 3:
+                            loc_vars.append("u8 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.flag_write_req_flag;" % (server_id))
+                            loc_vars_list.append(str(iecvar["NAME"]))
+
                 for subchild in child.IECSortedChildren():
                     new_memarea = GetTCPServerMemAreaPrinted(
                         self, subchild, nodeid)