merge
authorEdouard Tisserant
Fri, 09 Apr 2021 09:45:28 +0200
changeset 2725 2b00f90c6888
parent 2724 3f3f9dce9140 (diff)
parent 2720 971bb3503957 (current diff)
child 2726 c465414017a2
child 3227 d5b2369a103f
merge
--- a/modbus/mb_runtime.c	Tue Feb 23 16:16:12 2021 +0100
+++ b/modbus/mb_runtime.c	Fri Apr 09 09:45:28 2021 +0200
@@ -200,12 +200,42 @@
 }
 
 
-static int __read_inbits   (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes)
-  {return   __pack_bits(((server_mem_t *)mem_map)->ro_bits, start_addr, bit_count, data_bytes);}
-static int __read_outbits  (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes)
-  {return   __pack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes);}
-static int __write_outbits (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes)
-  {return __unpack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes); }
+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 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;
+}
+
+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 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;
+}
+
+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 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;
+}
+
 
 
 
@@ -214,6 +244,10 @@
   if ((start_addr + word_count) > MEM_AREA_SIZE)
     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
 
+  /* 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,
          /* src  */ (void *)&(((server_mem_t *)mem_map)->ro_words[start_addr]),
@@ -228,6 +262,10 @@
   if ((start_addr + word_count) > MEM_AREA_SIZE)
     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
 
+  /* 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,
          /* src  */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]),
@@ -243,6 +281,10 @@
   if ((start_addr + word_count) > MEM_AREA_SIZE)
     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
 
+  /* 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.
    *           The following code cannot be used. memcpy() is used instead.
@@ -545,6 +587,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	Tue Feb 23 16:16:12 2021 +0100
+++ b/modbus/mb_runtime.h	Fri Apr 09 09:45:28 2021 +0200
@@ -41,6 +41,25 @@
 	    u16		rw_bits [MEM_AREA_SIZE];
 	    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.
+             * 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
+             */
+        u8   flag_write_req_flag;
+        u8   flag_read_req_flag;
+        u32  flag_write_req_counter;
+        u32  flag_read_req_counter;
 	} server_mem_t;
 
 
@@ -89,6 +108,8 @@
 	    node_addr_t	node_address;
 	    int		mb_nd;      // modbus library node used for this server 
 	    int		init_state; // store how far along the server's initialization has progressed
+            /* entries from this point forward are not statically initialized when the variable is declared */
+            /* they will be initialized by the  code itself in the init() function */
 	    pthread_t	thread_id;  // thread handling this server
 	    server_mem_t	mem_area;
 	} server_node_t;
--- a/modbus/modbus.py	Tue Feb 23 16:16:12 2021 +0100
+++ b/modbus/modbus.py	Fri Apr 09 09:45:28 2021 +0200
@@ -447,6 +447,82 @@
         """ Return the node's Configuration_Name """
         return self.ModbusServerNode.getConfiguration_Name()
 
+    def GetVariableLocationTree(self):
+        current_location = self.GetCurrentLocation()
+        name             = self.BaseParams.getName()
+        # start off with flags that count the number of Modbus requests/transactions
+        # handled by this Modbus server/slave.
+        # These flags are mapped onto located variables and therefore available to the user programs
+        # May be used to detect communication errors.
+        # 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 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
+        #          %MX or %MW
+        # which will never clash with the %MD used here.
+        # Additionaly, any MemoryArea contained under this server/slave
+        # will ocupy locations with
+        #           %M1.2.a.b (with a and b being numbers in range 0, 1, ...)
+        # 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({
+            "name": "Modbus Read Request Counter",
+            "type": LOCATION_VAR_MEMORY,
+            "size": 32,           # UDINT flag
+            "IEC_type": "UDINT",  # UDINT flag
+            "var_name": "var_name",
+            "location": "D" + ".".join([str(i) for i in current_location]) + ".0",
+            "description": "Modbus read request counter",
+            "children": []})        
+        entries.append({
+            "name": "Modbus Write Request Counter",
+            "type": LOCATION_VAR_MEMORY,
+            "size": 32,           # UDINT flag
+            "IEC_type": "UDINT",  # UDINT flag
+            "var_name": "var_name",
+            "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():
+            entries.append(child.GetVariableLocationTree())
+
+        return {"name": name,
+                "type": LOCATION_CONFNODE,
+                "location": ".".join([str(i) for i in current_location]) + ".x",
+                "children": entries}
+
+
     def CTNGenerate_C(self, buildpath, locations):
         """
         Generate C code
@@ -631,6 +707,82 @@
         """ Return the node's Configuration_Name """
         return self.ModbusRTUslave.getConfiguration_Name()
 
+    def GetVariableLocationTree(self):
+        current_location = self.GetCurrentLocation()
+        name             = self.BaseParams.getName()
+        # start off with flags that count the number of Modbus requests/transactions
+        # handled by this Modbus server/slave.
+        # These flags are mapped onto located variables and therefore available to the user programs
+        # May be used to detect communication errors.
+        # 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 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
+        #          %MX or %MW
+        # which will never clash with the %MD used here.
+        # Additionaly, any MemoryArea contained under this server/slave
+        # will ocupy locations with
+        #           %M1.2.a.b (with a and b being numbers in range 0, 1, ...)
+        # 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({
+            "name": "Modbus Read Request Counter",
+            "type": LOCATION_VAR_MEMORY,
+            "size": 32,           # UDINT flag
+            "IEC_type": "UDINT",  # UDINT flag
+            "var_name": "var_name",
+            "location": "D" + ".".join([str(i) for i in current_location]) + ".0",
+            "description": "Modbus read request counter",
+            "children": []})        
+        entries.append({
+            "name": "Modbus Write Request Counter",
+            "type": LOCATION_VAR_MEMORY,
+            "size": 32,           # UDINT flag
+            "IEC_type": "UDINT",  # UDINT flag
+            "var_name": "var_name",
+            "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():
+            entries.append(child.GetVariableLocationTree())
+
+        return {"name": name,
+                "type": LOCATION_CONFNODE,
+                "location": ".".join([str(i) for i in current_location]) + ".x",
+                "children": entries}
+
+
     def CTNGenerate_C(self, buildpath, locations):
         """
         Generate C code
@@ -819,29 +971,52 @@
                 new_node = GetTCPServerNodePrinted(self, child)
                 if new_node is None:
                     return [], "", False
-                server_node_list.append(new_node)
+                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)
+                    new_memarea = GetTCPServerMemAreaPrinted(self, subchild, nodeid)
                     if new_memarea is None:
                         return [], "", False
                     server_memarea_list.append(new_memarea)
-                    function = subchild.GetParamsAttributes()[
-                        0]["children"][0]["value"]
+                    function = subchild.GetParamsAttributes()[0]["children"][0]["value"]
                     # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words'
                     memarea = modbus_memtype_dict[function][1]
                     for iecvar in subchild.GetLocations():
-                        # print repr(iecvar)
-                        absloute_address = iecvar["LOC"][3]
-                        start_address = int(GetCTVal(subchild, 2))
-                        relative_addr = absloute_address - start_address
-                        # test if relative address in request specified range
-                        if relative_addr in xrange(int(GetCTVal(subchild, 1))):
-                            if str(iecvar["NAME"]) not in loc_vars_list:
-                                loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % (
-                                    server_id, memarea, absloute_address))
-                                loc_vars_list.append(str(iecvar["NAME"]))
+                        if len(iecvar["LOC"]) == 4:
+                            #print "subchild" + repr(iecvar)
+                            absloute_address = iecvar["LOC"][3]
+                            start_address = int(GetCTVal(subchild, 2))
+                            relative_addr = absloute_address - start_address
+                            # test if relative address in request specified range
+                            if relative_addr in xrange(int(GetCTVal(subchild, 1))):
+                                if str(iecvar["NAME"]) not in loc_vars_list:
+                                    loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % (
+                                        server_id, memarea, absloute_address))
+                                    loc_vars_list.append(str(iecvar["NAME"]))
                 server_id += 1
             #
             if child.PlugType == "ModbusRTUslave":
@@ -850,28 +1025,52 @@
                 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)
                     if new_memarea is None:
                         return [], "", False
                     server_memarea_list.append(new_memarea)
-                    function = subchild.GetParamsAttributes()[
-                        0]["children"][0]["value"]
+                    function = subchild.GetParamsAttributes()[0]["children"][0]["value"]
                     # 'ro_bits', 'rw_bits', 'ro_words' or 'rw_words'
                     memarea = modbus_memtype_dict[function][1]
                     for iecvar in subchild.GetLocations():
-                        # print repr(iecvar)
-                        absloute_address = iecvar["LOC"][3]
-                        start_address = int(GetCTVal(subchild, 2))
-                        relative_addr = absloute_address - start_address
-                        # test if relative address in request specified range
-                        if relative_addr in xrange(int(GetCTVal(subchild, 1))):
-                            if str(iecvar["NAME"]) not in loc_vars_list:
-                                loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % (
-                                    server_id, memarea, absloute_address))
-                                loc_vars_list.append(str(iecvar["NAME"]))
+                        if len(iecvar["LOC"]) == 4:
+                            # print repr(iecvar)
+                            absloute_address = iecvar["LOC"][3]
+                            start_address = int(GetCTVal(subchild, 2))
+                            relative_addr = absloute_address - start_address
+                            # test if relative address in request specified range
+                            if relative_addr in xrange(int(GetCTVal(subchild, 1))):
+                                if str(iecvar["NAME"]) not in loc_vars_list:
+                                    loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &server_nodes[%d].mem_area.%s[%d];" % (
+                                        server_id, memarea, absloute_address))
+                                    loc_vars_list.append(str(iecvar["NAME"]))
                 server_id += 1
             #
             if child.PlugType == "ModbusTCPclient":