# HG changeset patch # User Mario de Sousa # Date 1611845476 0 # Node ID cde2e410b874e44242c0d36cca9541d587b90261 # Parent 76e8ec46828a1d5390cd2c92cb4eab07ae7b0b74# Parent 5d72a52b8f9c46afd5eb5023e01698449b2f8d6c merge diff -r 76e8ec46828a -r cde2e410b874 modbus/mb_runtime.c --- a/modbus/mb_runtime.c Fri Dec 25 17:12:02 2020 +0000 +++ b/modbus/mb_runtime.c Thu Jan 28 14:51:16 2021 +0000 @@ -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: diff -r 76e8ec46828a -r cde2e410b874 modbus/mb_runtime.h --- a/modbus/mb_runtime.h Fri Dec 25 17:12:02 2020 +0000 +++ b/modbus/mb_runtime.h Thu Jan 28 14:51:16 2021 +0000 @@ -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; diff -r 76e8ec46828a -r cde2e410b874 modbus/modbus.py --- a/modbus/modbus.py Fri Dec 25 17:12:02 2020 +0000 +++ b/modbus/modbus.py Thu Jan 28 14:51:16 2021 +0000 @@ -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":