# HG changeset patch # User Mario de Sousa # Date 1611845426 0 # Node ID 5d72a52b8f9c46afd5eb5023e01698449b2f8d6c # Parent 367eb26972b14c965274fc09503e86ad777fe0bb modbus plugin: add (user accessible) transaction bool flags for servers/slaves; FIX BUG in previous commit affecting RTU slaves. diff -r 367eb26972b1 -r 5d72a52b8f9c modbus/mb_runtime.c --- 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: diff -r 367eb26972b1 -r 5d72a52b8f9c modbus/mb_runtime.h --- 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; diff -r 367eb26972b1 -r 5d72a52b8f9c modbus/modbus.py --- 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)