modbus plugin: add (user accessible) transaction bool flags for servers/slaves; FIX BUG in previous commit affecting RTU slaves.
--- 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)