Merged Mario's modbus branch. Fixed line endings.
authorEdouard Tisserant
Tue, 30 Jan 2018 16:06:58 +0100
changeset 1912 8391c11477f4
parent 1908 d0b1ffcb9368 (current diff)
parent 1911 c1298e7ffe3a (diff)
child 1913 338e2f51b685
Merged Mario's modbus branch. Fixed line endings.
.hgignore
modbus/mb_runtime.c
modbus/mb_runtime.h
--- a/features.py	Mon Jan 29 13:31:14 2018 +0100
+++ b/features.py	Tue Jan 30 16:06:58 2018 +0100
@@ -29,6 +29,7 @@
 
 catalog = [
     ('canfestival', _('CANopen support'), _('Map located variables over CANopen'), 'canfestival.canfestival.RootClass'),
+    ('modbus', _('Modbus support'), _('Map located variables over Modbus'), 'modbus.modbus.RootClass'),
     ('c_ext', _('C extension'), _('Add C code accessing located variables synchronously'), 'c_ext.CFile'),
     ('py_ext', _('Python file'), _('Add Python code executed asynchronously'), 'py_ext.PythonFile'),
     ('wxglade_hmi', _('WxGlade GUI'), _('Add a simple WxGlade based GUI.'), 'wxglade_hmi.WxGladeHMI'),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modbus/README	Tue Jan 30 16:06:58 2018 +0100
@@ -0,0 +1,1 @@
+Modbus
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modbus/__init__.py	Tue Jan 30 16:06:58 2018 +0100
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz, a Integrated Development Environment for
+# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
+#
+# Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#    
+# This code is made available on the understanding that it will not be
+# used in safety-critical situations without a full and competent review.
+
+
+from modbus import *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modbus/mb_runtime.c	Tue Jan 30 16:06:58 2018 +0100
@@ -0,0 +1,615 @@
+/* File generated by Beremiz (PlugGenerate_C method of Modbus plugin) */
+
+/*
+ * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+
+#include <stdio.h>
+#include <string.h>  /* required for memcpy() */
+#include "mb_slave_and_master.h"
+#include "MB_%(locstr)s.h"
+
+
+#define MAX_MODBUS_ERROR_CODE 11
+static const char *modbus_error_messages[MAX_MODBUS_ERROR_CODE+1] = {
+    /* 0 */ "",                             /* un-used -> no error! */
+    /* 1 */ "illegal/unsuported function",
+    /* 2 */ "illegal data address",
+    /* 3 */ "illegal data value",
+    /* 4 */ "slave device failure",
+    /* 5 */ "acknowledge -> slave intends to reply later",
+    /* 6 */ "slave device busy",
+    /* 7 */ "negative acknowledge",
+    /* 8 */ "memory parity error",
+    /* 9 */ "",                             /* undefined by Modbus */
+    /* 10*/ "gateway path unavalilable",
+    /* 11*/ "gateway target device failed to respond"
+};    
+
+
+/* Execute a modbus client transaction/request */
+static int __execute_mb_request(int request_id){
+	switch (client_requests[request_id].mb_function){
+	
+	case  1: /* read coils */
+		return read_output_bits(client_requests[request_id].slave_id,
+					client_requests[request_id].address,
+					client_requests[request_id].count,
+					client_requests[request_id].coms_buffer,
+					(int) client_requests[request_id].count,
+					client_nodes[client_requests[request_id].client_node_id].mb_nd,
+					client_requests[request_id].retries,
+					&(client_requests[request_id].error_code),
+					&(client_requests[request_id].resp_timeout),
+					&(client_requests[request_id].coms_buf_mutex));
+
+	case  2: /* read discrete inputs */
+		return read_input_bits( client_requests[request_id].slave_id,
+					client_requests[request_id].address,
+					client_requests[request_id].count,
+					client_requests[request_id].coms_buffer,
+					(int) client_requests[request_id].count,
+					client_nodes[client_requests[request_id].client_node_id].mb_nd,
+					client_requests[request_id].retries,
+					&(client_requests[request_id].error_code),
+					&(client_requests[request_id].resp_timeout),
+					&(client_requests[request_id].coms_buf_mutex));
+
+	case  3: /* read holding registers */
+		return read_output_words(client_requests[request_id].slave_id,
+					client_requests[request_id].address,
+					client_requests[request_id].count,
+					client_requests[request_id].coms_buffer,
+					(int) client_requests[request_id].count,
+					client_nodes[client_requests[request_id].client_node_id].mb_nd,
+					client_requests[request_id].retries,
+					&(client_requests[request_id].error_code),
+					&(client_requests[request_id].resp_timeout),
+					&(client_requests[request_id].coms_buf_mutex));
+	
+	case  4: /* read input registers */
+		return read_input_words(client_requests[request_id].slave_id,
+					client_requests[request_id].address,
+					client_requests[request_id].count,
+					client_requests[request_id].coms_buffer,
+					(int) client_requests[request_id].count,
+					client_nodes[client_requests[request_id].client_node_id].mb_nd,
+					client_requests[request_id].retries,
+					&(client_requests[request_id].error_code),
+					&(client_requests[request_id].resp_timeout),
+					&(client_requests[request_id].coms_buf_mutex));
+
+	case  5: /* write single coil */
+		return write_output_bit(client_requests[request_id].slave_id,
+					client_requests[request_id].address,
+					client_requests[request_id].coms_buffer[0],
+					client_nodes[client_requests[request_id].client_node_id].mb_nd,
+					client_requests[request_id].retries,
+					&(client_requests[request_id].error_code),
+					&(client_requests[request_id].resp_timeout),
+					&(client_requests[request_id].coms_buf_mutex));
+
+	case  6: /* write single register */
+		return write_output_word(client_requests[request_id].slave_id,
+					client_requests[request_id].address,
+					client_requests[request_id].coms_buffer[0],
+					client_nodes[client_requests[request_id].client_node_id].mb_nd,
+					client_requests[request_id].retries,
+					&(client_requests[request_id].error_code),
+					&(client_requests[request_id].resp_timeout),
+					&(client_requests[request_id].coms_buf_mutex));
+
+	case  7: break; /* function not yet supported */
+	case  8: break; /* function not yet supported */
+	case  9: break; /* function not yet supported */
+	case 10: break; /* function not yet supported */
+	case 11: break; /* function not yet supported */
+	case 12: break; /* function not yet supported */
+	case 13: break; /* function not yet supported */
+	case 14: break; /* function not yet supported */
+	
+	case 15: /* write multiple coils */
+		return write_output_bits(client_requests[request_id].slave_id,
+					 client_requests[request_id].address,
+					 client_requests[request_id].count,
+					 client_requests[request_id].coms_buffer,
+					 client_nodes[client_requests[request_id].client_node_id].mb_nd,
+					 client_requests[request_id].retries,
+					 &(client_requests[request_id].error_code),
+					 &(client_requests[request_id].resp_timeout),
+					 &(client_requests[request_id].coms_buf_mutex));
+
+	case 16: /* write multiple registers */
+		return write_output_words(client_requests[request_id].slave_id,
+					client_requests[request_id].address,
+					client_requests[request_id].count,
+					client_requests[request_id].coms_buffer,
+					client_nodes[client_requests[request_id].client_node_id].mb_nd,
+					client_requests[request_id].retries,
+					&(client_requests[request_id].error_code),
+					&(client_requests[request_id].resp_timeout),
+					&(client_requests[request_id].coms_buf_mutex));
+	
+	default: break;  /* should never occur, if file generation is correct */
+	}
+
+	fprintf(stderr, "Modbus plugin: Modbus function %%d not supported\n", request_id); /* should never occur, if file generation is correct */
+	return -1;
+}
+
+
+
+/* pack bits from unpacked_data to packed_data */
+static inline int __pack_bits(u16 *unpacked_data, u16 start_addr, u16 bit_count,  u8  *packed_data) {
+  u8 bit;
+  u16 byte, coils_processed;
+
+  if ((0 == bit_count) || (65535-start_addr < bit_count-1))
+    return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
+  
+  for( byte = 0, coils_processed = 0; coils_processed < bit_count; byte++) {
+    packed_data[byte] = 0;
+    for( bit = 0x01; (bit & 0xFF) && (coils_processed < bit_count); bit <<= 1, coils_processed++ ) {
+      if(unpacked_data[start_addr + coils_processed])
+            packed_data[byte] |=  bit; /*   set bit */
+      else  packed_data[byte] &= ~bit; /* reset bit */
+    }
+  }
+  return 0;
+}
+
+
+/* unpack bits from packed_data to unpacked_data */
+static inline int __unpack_bits(u16 *unpacked_data, u16 start_addr, u16 bit_count,  u8  *packed_data) {
+  u8  temp, bit;
+  u16 byte, coils_processed;
+
+  if ((0 == bit_count) || (65535-start_addr < bit_count-1))
+    return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
+  
+  for(byte = 0, coils_processed = 0; coils_processed < bit_count; byte++) {
+    temp = packed_data[byte] ;
+    for(bit = 0x01; (bit & 0xff) && (coils_processed < bit_count); bit <<= 1, coils_processed++) {
+      unpacked_data[start_addr + coils_processed] = (temp & bit)?1:0;
+    }
+  }
+  return 0;
+}
+
+
+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_inwords  (void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) {
+  u16 count;
+  // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */
+  
+  if ((start_addr + word_count) > MEM_AREA_SIZE)
+    return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
+  
+  /* use memcpy() as it is more efficient...
+  for (count = 0; count < word_count ; count++)
+    data_words[count] = ((server_mem_t *)mem_map)->ro_words[count + start_addr];
+  */
+  memcpy(/* dest */ (void *)data_words,
+         /* src  */ (void *)&(((server_mem_t *)mem_map)->ro_words[start_addr]),
+         /* size */ word_count * 2);
+  return 0;
+}
+
+
+
+static int __read_outwords (void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) {
+  u16 count;
+  // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */
+  
+  if ((start_addr + word_count) > MEM_AREA_SIZE)
+    return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
+  
+  /* use memcpy() as it is more efficient...
+  for (count = 0; count < word_count ; count++)
+    data_words[count] = ((server_mem_t *)mem_map)->rw_words[count + start_addr];
+  */
+  memcpy(/* dest */ (void *)data_words,
+         /* src  */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]),
+         /* size */ word_count * 2);
+  return 0;
+}
+
+
+
+
+static int __write_outwords(void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) {
+  u16 count;
+  // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */
+  
+  if ((start_addr + word_count) > MEM_AREA_SIZE)
+    return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
+
+  /* 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.
+   */
+  /*
+  for (count = 0; count < word_count ; count++)
+    ((server_mem_t *)mem_map)->rw_words[count + start_addr] = data_words[count];
+  */
+  memcpy(/* dest */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]),
+         /* src  */ (void *)data_words,
+         /* size */ word_count * 2);
+  return 0;
+}
+
+
+
+
+#include <pthread.h>
+
+static void *__mb_server_thread(void *_server_node)  {
+	server_node_t *server_node = _server_node;
+	mb_slave_callback_t callbacks = { 
+			&__read_inbits,
+			&__read_outbits,
+			&__write_outbits,
+			&__read_inwords,
+			&__read_outwords,
+			&__write_outwords,
+			(void *)&(server_node->mem_area)
+			};  
+	
+	// Enable thread cancelation. Enabled is default, but set it anyway to be safe.
+	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+
+	// mb_slave_run() should never return!
+	mb_slave_run(server_node->mb_nd /* nd */, callbacks, server_node->slave_id);
+	fprintf(stderr, "Modbus plugin: Modbus server for node %%s died unexpectedly!\n", server_node->location); /* should never occur */
+	return NULL;
+}
+
+
+
+static void *__mb_client_thread(void *_index)  {
+	int client_node_id = (char *)_index - (char *)NULL; // Use pointer arithmetic (more portable than cast)
+	struct timespec next_cycle;
+	int period_sec  =  client_nodes[client_node_id].comm_period / 1000;          /* comm_period is in ms */
+	int period_nsec = (client_nodes[client_node_id].comm_period %%1000)*1000000; /* comm_period is in ms */
+
+	// Enable thread cancelation. Enabled is default, but set it anyway to be safe.
+	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+	
+	// get the current time
+	clock_gettime(CLOCK_MONOTONIC, &next_cycle);
+
+	// loop the communication with the client
+	while (1) {
+		/*
+		struct timespec cur_time;
+		clock_gettime(CLOCK_MONOTONIC, &cur_time);
+		fprintf(stderr, "Modbus client thread - new cycle (%%ld:%%ld)!\n", cur_time.tv_sec, cur_time.tv_nsec);
+		*/
+		int req;
+		for (req=0; req < NUMBER_OF_CLIENT_REQTS; req ++){
+			/*just do the requests belonging to the client */
+			if (client_requests[req].client_node_id != client_node_id)
+				continue;
+			int res_tmp = __execute_mb_request(req);
+			switch (res_tmp) {
+			  case PORT_FAILURE: {
+				if (res_tmp != client_nodes[client_node_id].prev_error)
+					fprintf(stderr, "Modbus plugin: Error connecting Modbus client %%s to remote server.\n", client_nodes[client_node_id].location);
+				client_nodes[client_node_id].prev_error = res_tmp;
+				break;
+			  }
+			  case INVALID_FRAME: {
+				if ((res_tmp != client_requests[req].prev_error) && (0 == client_nodes[client_node_id].prev_error))
+					fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s was unsuccesful. Server/slave returned an invalid/corrupted frame.\n", client_requests[req].location);
+				client_requests[req].prev_error = res_tmp;
+				break;
+			  }
+			  case TIMEOUT: {
+				if ((res_tmp != client_requests[req].prev_error) && (0 == client_nodes[client_node_id].prev_error))
+					fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s timed out waiting for reply from server.\n", client_requests[req].location);
+				client_requests[req].prev_error = res_tmp;
+				break;
+			  }
+			  case MODBUS_ERROR: {
+				if (client_requests[req].prev_error != client_requests[req].error_code) {
+					fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s was unsuccesful. Server/slave returned error code 0x%%2x", client_requests[req].location, client_requests[req].error_code);
+					if (client_requests[req].error_code <= MAX_MODBUS_ERROR_CODE ) {
+						fprintf(stderr, "(%%s)", modbus_error_messages[client_requests[req].error_code]);
+						fprintf(stderr, ".\n");
+					}
+				}
+				client_requests[req].prev_error = client_requests[req].error_code;
+				break;
+			  }
+			  default: {
+				if ((res_tmp >= 0) && (client_nodes[client_node_id].prev_error != 0)) {
+					fprintf(stderr, "Modbus plugin: Modbus client %%s has reconnected to server/slave.\n", client_nodes[client_node_id].location);
+				}
+				if ((res_tmp >= 0) && (client_requests[req]        .prev_error != 0)) {
+					fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s has succesfully resumed comunication.\n", client_requests[req].location);
+				}
+				client_nodes[client_node_id].prev_error = 0;
+				client_requests[req]        .prev_error = 0;
+				break;
+			  }
+			}
+		}
+		// Determine absolute time instant for starting the next cycle
+		// struct timespec prev_cycle;
+		// prev_cycle = next_cycle;
+		next_cycle.tv_sec  += period_sec;
+		next_cycle.tv_nsec += period_nsec;
+		if (next_cycle.tv_nsec >= 1000000000) {
+			next_cycle.tv_sec  ++;
+			next_cycle.tv_nsec -= 1000000000;
+		}
+		/* It probably does not make sense to check for overflow of timer.
+		 * Even in 32 bit systems this will take at least 68 years since the computer booted
+		 * (remember, we are using CLOCK_MONOTONIC, which should start counting from 0
+		 * every time the system boots). On 64 bit systems, it will take over 
+		 * 10^11 years to overflow.
+		 */
+		/*
+		if (next_cycle.tv_sec) < prev_cycle.tv_sec) {
+			// we will lose some precision by reading the time again, 
+			// but it is better than the alternative...
+			clock_gettime(CLOCK_MONOTONIC, &next_cycle);
+			next_cycle.tv_sec  += period_sec;
+			next_cycle.tv_nsec += period_nsec;
+			if (next_cycle.tv_nsec >= 1000000000) {
+				next_cycle.tv_sec  ++;
+				next_cycle.tv_nsec -= 1000000000;
+			}
+		}
+		*/
+		clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_cycle, NULL);
+	}
+
+	// humour the compiler.
+	return NULL;
+}
+
+
+
+int __init_%(locstr)s (int argc, char **argv){
+	int index;
+
+	for (index=0; index < NUMBER_OF_CLIENT_NODES;index++)
+		client_nodes[index].mb_nd = -1;
+	for (index=0; index < NUMBER_OF_SERVER_NODES;index++)
+		// mb_nd with negative numbers indicate how far it has been initialised (or not)
+		//   -2  --> no modbus node created;  no thread  created
+		//   -1  -->    modbus node created!; no thread  created
+		//  >=0  -->    modbus node created!;    thread  created!
+		server_nodes[index].mb_nd = -2; 
+
+	/* modbus library init */
+	/* Note that TOTAL_xxxNODE_COUNT are the nodes required by _ALL_ the instances of the modbus
+	 *  extension currently in the user's project. This file (MB_xx.c) is handling only one instance,
+	 *  but must initialize the library for all instances. Only the first call to mb_slave_and_master_init()
+	 *  will result in memory being allocated. All subsequent calls (by other MB_xx,c files) will be ignored
+	 *  by the mb_slave_and_master_init() funtion, as long as they are called with the same arguments.
+	 */
+	if (mb_slave_and_master_init(TOTAL_TCPNODE_COUNT, TOTAL_RTUNODE_COUNT, TOTAL_ASCNODE_COUNT) <0) {
+		fprintf(stderr, "Modbus plugin: Error starting modbus library\n");
+		// return imediately. Do NOT goto error_exit, as we did not get to
+		//  start the modbus library!
+		return -1;
+	}
+	
+	/* init the mutex for each client request */
+	/* Must be done _before_ launching the client threads!! */
+	for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){
+		if (pthread_mutex_init(&(client_requests[index].coms_buf_mutex), NULL)) {
+			fprintf(stderr, "Modbus plugin: Error initializing request for modbus client node %%s\n", client_nodes[client_requests[index].client_node_id].location);
+			goto error_exit;
+		}
+	}
+
+	/* init each client connection to remote modbus server, and launch thread */
+	/* NOTE: All client_nodes[].init_state are initialised to 0 in the code 
+	 *       generated by the modbus plugin 
+	 */
+	for (index=0; index < NUMBER_OF_CLIENT_NODES;index++){
+		/* establish client connection */
+		client_nodes[index].mb_nd = mb_master_connect (client_nodes[index].node_address);
+		if (client_nodes[index].mb_nd < 0){
+			fprintf(stderr, "Modbus plugin: Error creating modbus client node %%s\n", client_nodes[index].location);
+			goto error_exit;
+		}
+		client_nodes[index].init_state = 1; // we have created the node 
+		
+		/* launch a thread to handle this client node */
+		{
+			int res = 0;
+			pthread_attr_t attr;
+			res |= pthread_attr_init(&attr);
+			res |= pthread_create(&(client_nodes[index].thread_id), &attr, &__mb_client_thread, (void *)((char *)NULL + index));
+			if (res !=  0) {
+				fprintf(stderr, "Modbus plugin: Error starting modbus client thread for node %%s\n", client_nodes[index].location);
+				goto error_exit;
+			}
+		}
+		client_nodes[index].init_state = 2; // we have created the node and a thread
+	}
+
+	/* init each local server */
+	/* NOTE: All server_nodes[].init_state are initialised to 0 in the code 
+	 *       generated by the modbus plugin 
+	 */
+	for (index=0; index < NUMBER_OF_SERVER_NODES;index++){
+		/* create the modbus server */
+		server_nodes[index].mb_nd = mb_slave_new (server_nodes[index].node_address);
+		if (server_nodes[index].mb_nd < 0){
+			fprintf(stderr, "Modbus plugin: Error creating modbus server node %%s\n", server_nodes[index].location);
+			goto error_exit;
+		}
+		server_nodes[index].init_state = 1; // we have created the node
+		
+		/* launch a thread to handle this server node */
+		{
+			int res = 0;
+			pthread_attr_t attr;
+			res |= pthread_attr_init(&attr);
+			res |= pthread_create(&(server_nodes[index].thread_id), &attr, &__mb_server_thread, (void *)&(server_nodes[index]));
+			if (res !=  0) {
+				fprintf(stderr, "Modbus plugin: Error starting modbus server thread for node %%s\n", server_nodes[index].location);
+				goto error_exit;
+			}
+		}
+		server_nodes[index].init_state = 2; // we have created the node and thread
+	}
+
+	return 0;
+	
+error_exit:
+	__cleanup_%(locstr)s ();
+	return -1;
+}
+
+
+
+
+
+void __publish_%(locstr)s (){
+	int index;
+
+	for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){
+		/*just do the output requests */
+		if (client_requests[index].req_type == req_output){
+			pthread_mutex_lock(&(client_requests[index].coms_buf_mutex));
+			// copy from plcv_buffer to coms_buffer
+			memcpy((void *)client_requests[index].coms_buffer /* destination */,
+			       (void *)client_requests[index].plcv_buffer /* source */,
+			       REQ_BUF_SIZE * sizeof(u16) /* size in bytes */);
+			pthread_mutex_unlock(&(client_requests[index].coms_buf_mutex));
+		}
+	}
+}
+
+
+
+
+
+void __retrieve_%(locstr)s (){
+	int index;
+
+	for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){
+		/*just do the input requests */
+		if (client_requests[index].req_type == req_input){
+			pthread_mutex_lock(&(client_requests[index].coms_buf_mutex));
+			// copy from coms_buffer to plcv_buffer
+			memcpy((void *)client_requests[index].plcv_buffer /* destination */,
+			       (void *)client_requests[index].coms_buffer /* source */,
+			       REQ_BUF_SIZE * sizeof(u16) /* size in bytes */);
+			pthread_mutex_unlock(&(client_requests[index].coms_buf_mutex));
+		}
+	}
+}
+
+
+
+
+
+int __cleanup_%(locstr)s (){
+	int index, close;
+	int res = 0;
+
+	/* kill thread and close connections of each modbus client node */
+	for (index=0; index < NUMBER_OF_CLIENT_NODES; index++) {
+		close = 0;
+		if (client_nodes[index].init_state >= 2) {
+			// thread was launched, so we try to cancel it!
+			close  = pthread_cancel(client_nodes[index].thread_id);
+			close |= pthread_join  (client_nodes[index].thread_id, NULL);
+			if (close < 0)
+				fprintf(stderr, "Modbus plugin: Error closing thread for modbus client %%s\n", client_nodes[index].location);
+		}
+		res |= close;
+
+		close = 0;
+		if (client_nodes[index].init_state >= 1) {
+			// modbus client node was created, so we try to close it!
+			close = mb_master_close (client_nodes[index].mb_nd);
+			if (close < 0){
+				fprintf(stderr, "Modbus plugin: Error closing modbus client node %%s\n", client_nodes[index].location);
+				// We try to shut down as much as possible, so we do not return noW!
+			}
+			client_nodes[index].mb_nd = -1;
+		}
+		res |= close;
+		client_nodes[index].init_state = 0;
+	}
+	
+	/* kill thread and close connections of each modbus server node */
+	for (index=0; index < NUMBER_OF_SERVER_NODES; index++) {
+		close = 0;
+		if (server_nodes[index].init_state >= 2) {
+			// thread was launched, so we try to cancel it!
+			close  = pthread_cancel(server_nodes[index].thread_id);
+			close |= pthread_join  (server_nodes[index].thread_id, NULL);
+			if (close < 0)
+				fprintf(stderr, "Modbus plugin: Error closing thread for modbus server %%s\n", server_nodes[index].location);
+		}
+		res |= close;
+
+		close = 0;
+		if (server_nodes[index].init_state >= 1) {
+			// modbus server node was created, so we try to close it!
+			close = mb_slave_close (server_nodes[index].mb_nd);
+			if (close < 0) {
+				fprintf(stderr, "Modbus plugin: Error closing node for modbus server %%s (%%d)\n", server_nodes[index].location, server_nodes[index].mb_nd);
+				// We try to shut down as much as possible, so we do not return noW!
+			}
+			server_nodes[index].mb_nd = -1;
+		}
+		res |= close;
+		server_nodes[index].init_state = 0;
+	}
+
+	/* destroy the mutex of each client request */
+	for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++) {
+		if (pthread_mutex_destroy(&(client_requests[index].coms_buf_mutex))) {
+			fprintf(stderr, "Modbus plugin: Error destroying request for modbus client node %%s\n", client_nodes[client_requests[index].client_node_id].location);
+			// We try to shut down as much as possible, so we do not return noW!
+			res |= -1;
+		}
+	}
+
+	/* modbus library close */
+	//fprintf(stderr, "Shutting down modbus library...\n");
+	if (mb_slave_and_master_done()<0) {
+		fprintf(stderr, "Modbus plugin: Error shutting down modbus library\n");
+		res |= -1;
+	}
+
+	return res;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modbus/mb_runtime.h	Tue Jan 30 16:06:58 2018 +0100
@@ -0,0 +1,148 @@
+/* File generated by Beremiz (PlugGenerate_C method of modbus Plugin instance) */
+
+/*
+ * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
+ *
+ * This file is part of the Modbus library for Beremiz and matiec.
+ *
+ * This Modbus library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is made available on the understanding that it will not be
+ * used in safety-critical situations without a full and competent review.
+ */
+
+#include "mb_addr.h"
+#include "mb_tcp_private.h"
+#include "mb_master_private.h"
+
+
+
+#define DEF_REQ_SEND_RETRIES 0
+
+  // Used by the Modbus server node
+#define MEM_AREA_SIZE 65536
+typedef struct{
+	    u16		ro_bits [MEM_AREA_SIZE];
+	    u16		rw_bits [MEM_AREA_SIZE];
+	    u16		ro_words[MEM_AREA_SIZE];
+	    u16		rw_words[MEM_AREA_SIZE];
+	} server_mem_t;
+
+typedef struct{
+	    const char *location;
+	    u8		slave_id;
+	    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
+	    pthread_t	thread_id;  // thread handling this server
+	    server_mem_t	mem_area;
+	} server_node_t;
+
+
+  // Used by the Modbus client node
+typedef struct{
+	    const char *location;
+	    node_addr_t	node_address;
+	    int		mb_nd;
+	    int		init_state; // store how far along the client's initialization has progressed
+	    u64		comm_period;
+	    int		prev_error; // error code of the last printed error message (0 when no error) 
+	    pthread_t	thread_id;  // thread handling all communication with this client
+	} client_node_t;
+
+
+  // Used by the Modbus client plugin
+typedef enum {
+	    req_input,
+	    req_output,
+	    no_request		/* just for tests to quickly disable a request */
+	} iotype_t;
+
+#define REQ_BUF_SIZE 2000
+typedef struct{
+	    const char *location;
+	    int		client_node_id;
+	    u8		slave_id;
+	    iotype_t	req_type;
+	    u8		mb_function;
+	    u16		address;
+	    u16		count;
+	    int		retries;
+	    u8		error_code; // modbus error code (if any) of current request
+	    int		prev_error; // error code of the last printed error message (0 when no error) 
+	    struct timespec resp_timeout;
+	      // buffer used to store located PLC variables
+	    u16		plcv_buffer[REQ_BUF_SIZE];
+	      // buffer used to store data coming from / going to server
+	    u16		coms_buffer[REQ_BUF_SIZE]; 
+	    pthread_mutex_t coms_buf_mutex; // mutex to access coms_buffer[]
+	} client_request_t;
+
+
+/* The total number of nodes, needed to support _all_ instances of the modbus plugin */
+#define TOTAL_TCPNODE_COUNT       %(total_tcpnode_count)s
+#define TOTAL_RTUNODE_COUNT       %(total_rtunode_count)s
+#define TOTAL_ASCNODE_COUNT       %(total_ascnode_count)s
+
+/* Values for instance %(locstr)s of the modbus plugin */
+#define MAX_NUMBER_OF_TCPCLIENTS  %(max_remote_tcpclient)s
+
+#define NUMBER_OF_TCPSERVER_NODES %(tcpserver_node_count)s
+#define NUMBER_OF_TCPCLIENT_NODES %(tcpclient_node_count)s
+#define NUMBER_OF_TCPCLIENT_REQTS %(tcpclient_reqs_count)s
+
+#define NUMBER_OF_RTUSERVER_NODES %(rtuserver_node_count)s
+#define NUMBER_OF_RTUCLIENT_NODES %(rtuclient_node_count)s
+#define NUMBER_OF_RTUCLIENT_REQTS %(rtuclient_reqs_count)s
+
+#define NUMBER_OF_ASCIISERVER_NODES %(ascserver_node_count)s
+#define NUMBER_OF_ASCIICLIENT_NODES %(ascclient_node_count)s
+#define NUMBER_OF_ASCIICLIENT_REQTS %(ascclient_reqs_count)s
+
+#define NUMBER_OF_SERVER_NODES (NUMBER_OF_TCPSERVER_NODES + \
+                                NUMBER_OF_RTUSERVER_NODES + \
+                                NUMBER_OF_ASCIISERVER_NODES)
+
+#define NUMBER_OF_CLIENT_NODES (NUMBER_OF_TCPCLIENT_NODES + \
+                                NUMBER_OF_RTUCLIENT_NODES + \
+                                NUMBER_OF_ASCIICLIENT_NODES)
+
+#define NUMBER_OF_CLIENT_REQTS (NUMBER_OF_TCPCLIENT_REQTS + \
+                                NUMBER_OF_RTUCLIENT_REQTS + \
+                                NUMBER_OF_ASCIICLIENT_REQTS)
+
+
+/*initialization following all parameters given by user in application*/
+
+static client_node_t		client_nodes[NUMBER_OF_CLIENT_NODES] = {
+%(client_nodes_params)s
+};
+
+
+static client_request_t	client_requests[NUMBER_OF_CLIENT_REQTS] = {
+%(client_req_params)s
+};
+
+
+static server_node_t		server_nodes[NUMBER_OF_SERVER_NODES] = {
+%(server_nodes_params)s
+}
+;
+
+/*******************/
+/*located variables*/
+/*******************/
+
+%(loc_vars)s
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modbus/mb_utils.py	Tue Jan 30 16:06:58 2018 +0100
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz, a Integrated Development Environment for
+# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
+#
+# Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#    
+# This code is made available on the understanding that it will not be
+# used in safety-critical situations without a full and competent review.
+
+
+
+
+#dictionary implementing:
+#key   - string with the description we want in the request plugin GUI
+#tuple - (modbus function number, request type, max count value, data_type, bit_size)
+modbus_function_dict = {"01 - Read Coils" :               ( '1', 'req_input', 2000, "BOOL", 1 , "Q", "X", "Coil"),
+                        "02 - Read Input Discretes" :     ( '2', 'req_input', 2000, "BOOL", 1 , "I", "X", "Input Discrete"),
+                        "03 - Read Holding Registers" :   ( '3', 'req_input',  125, "WORD", 16, "Q", "W", "Holding Register"),
+                        "04 - Read Input Registers" :     ( '4', 'req_input',  125, "WORD", 16, "I", "W", "Input Register"),
+                        "05 - Write Single coil" :        ( '5','req_output',    1, "BOOL", 1 , "Q", "X", "Coil"),
+                        "06 - Write Single Register" :    ( '6','req_output',    1, "WORD", 16, "Q", "W", "Holding Register"),
+                        "15 - Write Multiple Coils" :     ('15','req_output', 1968, "BOOL", 1 , "Q", "X", "Coil"),
+                        "16 - Write Multiple Registers" : ('16','req_output',  123, "WORD", 16, "Q", "W", "Holding Register"),
+                       }
+
+
+
+def GetTCPServerNodePrinted(self, child):
+    """
+    Outputs a string to be used on C files
+    params: child - the correspondent subplugin in Beremiz
+    """
+    node_init_template = '''/*node %(locnodestr)s*/
+{"%(locnodestr)s", %(slaveid)s, {naf_tcp, {.tcp = {%(host)s, "%(port)s", DEF_CLOSE_ON_SILENCE}}}, -1 /* mb_nd */, 0 /* init_state */}'''
+
+    location = ".".join(map(str, child.GetCurrentLocation()))
+    host     = child.GetParamsAttributes()[0]["children"][0]["value"]
+    port     = child.GetParamsAttributes()[0]["children"][1]["value"]
+    slaveid  = child.GetParamsAttributes()[0]["children"][2]["value"]
+    if host=="#ANY#":
+      host='INADDR_ANY'
+    else:
+      host='"'+host+'"'
+    #slaveid = child.GetParamsAttributes()[0]["children"][2]["value"]
+    #if int(slaveid) not in xrange(256):
+        #self.GetCTRoot().logger.write_error("Error: Wrong slave ID in %s server node\nModbus Plugin C code returns empty\n"%location)
+        #return None
+
+    node_dict = {"locnodestr" : location,
+                 "host"       : host,
+                 "port"       : port,
+                 "slaveid"    : slaveid,
+                }
+    return node_init_template % node_dict
+
+
+
+
+def GetTCPServerMemAreaPrinted(self, child, nodeid):
+    """
+    Outputs a string to be used on C files
+    params: child - the correspondent subplugin in Beremiz
+            nodeid - on C code, each request has it's own parent node (sequential, 0..NUMBER_OF_NODES)
+                     It's this parameter.
+    return: None - if any definition error found
+            The string that should be added on C code - if everything goes allright
+    """
+    request_dict = {}
+
+    request_dict["locreqstr"] = "_".join(map(str, child.GetCurrentLocation()))
+    request_dict["nodeid"] = str(nodeid)
+    request_dict["address"] = child.GetParamsAttributes()[0]["children"][2]["value"]
+    if int(request_dict["address"]) not in xrange(65536):
+        self.GetCTRoot().logger.write_error("Modbus plugin: Invalid Start Address in server memory area node %(locreqstr)s (Must be in the range [0..65535])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
+        return None
+    request_dict["count"] = child.GetParamsAttributes()[0]["children"][1]["value"]
+    if int(request_dict["count"]) not in xrange(1, 65536):
+        self.GetCTRoot().logger.write_error("Modbus plugin: Invalid number of channels in server memory area node %(locreqstr)s (Must be in the range [1..65536-start_address])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
+        return None
+    if (int(request_dict["address"]) + int(request_dict["count"])) not in xrange(1,65537):
+        self.GetCTRoot().logger.write_error("Modbus plugin: Invalid number of channels in server memory area node %(locreqstr)s (Must be in the range [1..65536-start_address])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
+        return None
+    
+    return ""
+
+
+
+
+modbus_serial_baudrate_list = ["110", "300", "600", "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"]
+modbus_serial_stopbits_list = ["1", "2"]
+modbus_serial_parity_dict   = {"none": 0, "odd": 1, "even": 2}
+
+
+
+def GetRTUSlaveNodePrinted(self, child):
+    """
+    Outputs a string to be used on C files
+    params: child - the correspondent subplugin in Beremiz
+    """
+    node_init_template = '''/*node %(locnodestr)s*/
+{"%(locnodestr)s", %(slaveid)s, {naf_rtu, {.rtu = {"%(device)s", %(baud)s /*baud*/, %(parity)s /*parity*/, 8 /*data bits*/, %(stopbits)s, 0 /* ignore echo */}}}, -1 /* mb_nd */, 0 /* init_state */}'''
+
+    location = ".".join(map(str, child.GetCurrentLocation()))
+    device   = child.GetParamsAttributes()[0]["children"][0]["value"]
+    baud     = child.GetParamsAttributes()[0]["children"][1]["value"]
+    parity   = child.GetParamsAttributes()[0]["children"][2]["value"]
+    stopbits = child.GetParamsAttributes()[0]["children"][3]["value"]
+    slaveid  = child.GetParamsAttributes()[0]["children"][4]["value"]
+    
+    node_dict = {"locnodestr" : location,
+                 "device"     : device,
+                 "baud"       : baud,
+                 "parity"     : modbus_serial_parity_dict[parity],
+                 "stopbits"   : stopbits,
+                 "slaveid"    : slaveid
+                }
+    return node_init_template % node_dict
+
+
+
+ 
+
+def GetRTUClientNodePrinted(self, child):
+    """
+    Outputs a string to be used on C files
+    params: child - the correspondent subplugin in Beremiz
+    """
+    node_init_template = '''/*node %(locnodestr)s*/
+{"%(locnodestr)s", {naf_rtu, {.rtu = {"%(device)s", %(baud)s /*baud*/, %(parity)s /*parity*/, 8 /*data bits*/, %(stopbits)s, 0 /* ignore echo */}}}, -1 /* mb_nd */, 0 /* init_state */, %(coms_period)s /* communication period */}'''
+
+    location    = ".".join(map(str, child.GetCurrentLocation()))
+    device      = child.GetParamsAttributes()[0]["children"][0]["value"]
+    baud        = child.GetParamsAttributes()[0]["children"][1]["value"]
+    parity      = child.GetParamsAttributes()[0]["children"][2]["value"]
+    stopbits    = child.GetParamsAttributes()[0]["children"][3]["value"]
+    coms_period = child.GetParamsAttributes()[0]["children"][4]["value"]
+    
+    node_dict = {"locnodestr"  : location,
+                 "device"      : device,
+                 "baud"        : baud,
+                 "parity"      : modbus_serial_parity_dict[parity],
+                 "stopbits"    : stopbits,
+                 "coms_period" : coms_period
+                }
+    return node_init_template % node_dict
+
+
+
+
+def GetTCPClientNodePrinted(self, child):
+    """
+    Outputs a string to be used on C files
+    params: child - the correspondent subplugin in Beremiz
+    """
+    node_init_template = '''/*node %(locnodestr)s*/
+{"%(locnodestr)s", {naf_tcp, {.tcp = {"%(host)s", "%(port)s", DEF_CLOSE_ON_SILENCE}}}, -1 /* mb_nd */, 0 /* init_state */, %(coms_period)s /* communication period */, 0 /* prev_error */}'''
+
+    location = ".".join(map(str, child.GetCurrentLocation()))
+    host        = child.GetParamsAttributes()[0]["children"][0]["value"]
+    port        = child.GetParamsAttributes()[0]["children"][1]["value"]
+    coms_period = child.GetParamsAttributes()[0]["children"][2]["value"]
+
+    node_dict = {"locnodestr"  : location,
+                 "host"        : host,
+                 "port"        : port,
+                 "coms_period" : coms_period
+                }
+    return node_init_template % node_dict
+
+
+
+
+def GetClientRequestPrinted(self, child, nodeid):
+    """
+    Outputs a string to be used on C files
+    params: child - the correspondent subplugin in Beremiz
+            nodeid - on C code, each request has it's own parent node (sequential, 0..NUMBER_OF_NODES)
+                     It's this parameter.
+    return: None - if any definition error found
+            The string that should be added on C code - if everything goes allright
+    """
+
+    req_init_template = '''/*request %(locreqstr)s*/
+{"%(locreqstr)s", %(nodeid)s, %(slaveid)s, %(iotype)s, %(func_nr)s, %(address)s , %(count)s,
+DEF_REQ_SEND_RETRIES, 0 /* error_code */, 0 /* prev_code */, {%(timeout_s)d, %(timeout_ns)d} /* timeout */,
+{%(buffer)s}, {%(buffer)s}}'''
+    
+    timeout      = int(child.GetParamsAttributes()[0]["children"][4]["value"])
+    timeout_s    = int(timeout / 1000)
+    timeout_ms   = timeout - (timeout_s * 1000)
+    timeout_ns   = timeout_ms * 1000000
+    
+    request_dict = {}
+
+    request_dict["locreqstr" ] = "_".join(map(str, child.GetCurrentLocation()))
+    request_dict["nodeid"    ] = str(nodeid)
+    request_dict["slaveid"   ] =                      child.GetParamsAttributes()[0]["children"][1]["value"]
+    request_dict["address"   ] =                      child.GetParamsAttributes()[0]["children"][3]["value"]
+    request_dict["count"     ] =                      child.GetParamsAttributes()[0]["children"][2]["value"]
+    request_dict["timeout"   ] = timeout
+    request_dict["timeout_s" ] = timeout_s
+    request_dict["timeout_ns"] = timeout_ns
+    request_dict["buffer"    ] = ",".join(['0'] * int(child.GetParamsAttributes()[0]["children"][2]["value"]))
+    request_dict["func_nr"   ] = modbus_function_dict[child.GetParamsAttributes()[0]["children"][0]["value"]][0]
+    request_dict["iotype"    ] = modbus_function_dict[child.GetParamsAttributes()[0]["children"][0]["value"]][1]
+    request_dict["maxcount"  ] = modbus_function_dict[child.GetParamsAttributes()[0]["children"][0]["value"]][2]
+    
+    if int(request_dict["slaveid"]) not in xrange(256):
+        self.GetCTRoot().logger.write_error("Modbus plugin: Invalid slaveID in TCP client request node %(locreqstr)s (Must be in the range [0..255])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
+        return None
+    if int(request_dict["address"]) not in xrange(65536):
+        self.GetCTRoot().logger.write_error("Modbus plugin: Invalid Start Address in TCP client request node %(locreqstr)s (Must be in the range [0..65535])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
+        return None
+    if int(request_dict["count"]) not in xrange(1, 1+int(request_dict["maxcount"])):
+        self.GetCTRoot().logger.write_error("Modbus plugin: Invalid number of channels in TCP client request node %(locreqstr)s (Must be in the range [1..%(maxcount)s])\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
+        return None
+    if (int(request_dict["address"]) + int(request_dict["count"])) not in xrange(1,65537):
+        self.GetCTRoot().logger.write_error("Modbus plugin: Invalid number of channels in TCP client request node %(locreqstr)s (start_address + nr_channels must be less than 65536)\nModbus plugin: Aborting C code generation for this node\n"%request_dict)
+        return None
+    
+    return req_init_template % request_dict
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modbus/modbus.py	Tue Jan 30 16:06:58 2018 +0100
@@ -0,0 +1,785 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz, a Integrated Development Environment for
+# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
+#
+# Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#    
+# This code is made available on the understanding that it will not be
+# used in safety-critical situations without a full and competent review.
+
+
+
+
+import os, sys
+base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
+base_folder = os.path.join(base_folder, "..")
+ModbusPath  = os.path.join(base_folder, "Modbus")
+
+from mb_utils import *
+
+import wx
+from ConfigTreeNode import ConfigTreeNode
+from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
+
+
+
+###################################################
+###################################################
+#                                                 #
+#         C L I E N T    R E Q U E S T            # 
+#                                                 #
+###################################################
+###################################################
+
+
+class _RequestPlug:
+    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+      <xsd:element name="ModbusRequest">
+        <xsd:complexType>
+          <xsd:attribute name="Function" type="xsd:string" use="optional" default="01 - Read Coils"/>
+          <xsd:attribute name="SlaveID" use="optional" default="1">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="0"/>
+                    <xsd:maxInclusive value="255"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+          <xsd:attribute name="Nr_of_Channels" use="optional" default="1">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="1"/>
+                    <xsd:maxInclusive value="2000"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+          <xsd:attribute name="Start_Address" use="optional" default="0">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="0"/>
+                    <xsd:maxInclusive value="65535"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+          <xsd:attribute name="Timeout_in_ms" use="optional" default="10">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="1"/>
+                    <xsd:maxInclusive value="100000"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:schema>
+    """
+
+    def GetParamsAttributes(self, path = None):
+        infos = ConfigTreeNode.GetParamsAttributes(self, path = path)
+        for element in infos:
+            if element["name"] == "ModbusRequest":
+                for child in element["children"]:
+                    if child["name"] == "Function":
+                        list = modbus_function_dict.keys()
+                        list.sort()
+                        child["type"] = list
+        return infos
+      
+    def GetVariableLocationTree(self):
+        current_location = self.GetCurrentLocation()
+        name = self.BaseParams.getName()
+        address = self.GetParamsAttributes()[0]["children"][3]["value"]
+        count   = self.GetParamsAttributes()[0]["children"][2]["value"]
+        function= self.GetParamsAttributes()[0]["children"][0]["value"]
+        # 'BOOL' or 'WORD'
+        datatype= modbus_function_dict[function][3]
+        # 1 or 16
+        datasize= modbus_function_dict[function][4]
+        # 'Q' for coils and holding registers, 'I' for input discretes and input registers
+        datazone= modbus_function_dict[function][5] 
+        # 'X' for bits, 'W' for words
+        datatacc= modbus_function_dict[function][6] 
+        # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register'
+        dataname= modbus_function_dict[function][7] 
+        entries = []
+        for offset in range(address, address+count):
+            entries.append({
+                "name": dataname + " " + str(offset),
+                "type": LOCATION_VAR_MEMORY,
+                "size": datasize,
+                "IEC_type": datatype,
+                "var_name": "var_name",
+                "location": datatacc + ".".join([str(i) for i in current_location]) + "." + str(offset),
+                "description": "description",
+                "children": []})
+        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
+        @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
+        @param locations: List of complete variables locations \
+            [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+            "NAME" : name of the variable (generally "__IW0_1_2" style)
+            "DIR" : direction "Q","I" or "M"
+            "SIZE" : size "X", "B", "W", "D", "L"
+            "LOC" : tuple of interger for IEC location (0,1,2,...)
+            }, ...]
+        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+        """
+        return [], "", False
+        
+        
+###################################################
+###################################################
+#                                                 #
+#     S E R V E R    M E M O R Y    A R E A       # 
+#                                                 #
+###################################################
+###################################################
+
+#dictionary implementing:
+#key - string with the description we want in the request plugin GUI
+#list - (modbus function number, request type, max count value)
+modbus_memtype_dict = {"01 - Coils" :            ( '1', 'rw_bits',  65536, "BOOL", 1  , "Q", "X", "Coil"),
+                       "02 - Input Discretes" :  ( '2', 'ro_bits',  65536, "BOOL", 1  , "I", "X", "Input Discrete"),
+                       "03 - Holding Registers" :( '3', 'rw_words', 65536, "WORD", 16 , "Q", "W", "Holding Register"),
+                       "04 - Input Registers" :  ( '4', 'ro_words', 65536, "WORD", 16 , "I", "W", "Input Register"),
+                       }
+
+class _MemoryAreaPlug:
+    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+      <xsd:element name="MemoryArea">
+        <xsd:complexType>
+          <xsd:attribute name="MemoryAreaType" type="xsd:string" use="optional" default="01 - Coils"/>
+          <xsd:attribute name="Nr_of_Channels" use="optional" default="1">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="1"/>
+                    <xsd:maxInclusive value="65536"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+          <xsd:attribute name="Start_Address" use="optional" default="0">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="0"/>
+                    <xsd:maxInclusive value="65535"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:schema>
+    """
+
+    def GetParamsAttributes(self, path = None):
+        infos = ConfigTreeNode.GetParamsAttributes(self, path = path)
+        for element in infos:
+            if element["name"] == "MemoryArea":
+                for child in element["children"]:
+                    if child["name"] == "MemoryAreaType":
+                        list = modbus_memtype_dict.keys()
+                        list.sort()
+                        child["type"] = list
+        return infos
+
+    def GetVariableLocationTree(self):
+        current_location = self.GetCurrentLocation()
+        name = self.BaseParams.getName()
+        address = self.GetParamsAttributes()[0]["children"][2]["value"]
+        count   = self.GetParamsAttributes()[0]["children"][1]["value"]
+        function= self.GetParamsAttributes()[0]["children"][0]["value"]
+        # 'BOOL' or 'WORD'
+        datatype= modbus_memtype_dict[function][3]
+        # 1 or 16
+        datasize= modbus_memtype_dict[function][4]
+        # 'Q' for coils and holding registers, 'I' for input discretes and input registers
+        datazone= modbus_memtype_dict[function][5] 
+        # 'X' for bits, 'W' for words
+        datatacc= modbus_memtype_dict[function][6] 
+        # 'Coil', 'Holding Register', 'Input Discrete' or 'Input Register'
+        dataname= modbus_memtype_dict[function][7] 
+        entries = []
+        for offset in range(address, address+count):
+            entries.append({
+                "name": dataname + " " + str(offset),
+                "type": LOCATION_VAR_MEMORY,
+                "size": datasize,
+                "IEC_type": datatype,
+                "var_name": "var_name",
+                "location": datatacc + ".".join([str(i) for i in current_location]) + "." + str(offset),
+                "description": "description",
+                "children": []})
+        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
+        @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
+        @param locations: List of complete variables locations \
+            [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+            "NAME" : name of the variable (generally "__IW0_1_2" style)
+            "DIR" : direction "Q","I" or "M"
+            "SIZE" : size "X", "B", "W", "D", "L"
+            "LOC" : tuple of interger for IEC location (0,1,2,...)
+            }, ...]
+        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+        """
+        return [], "", False
+
+        
+###################################################
+###################################################
+#                                                 #
+#            T C P    C L I E N T                 # 
+#                                                 #
+###################################################
+###################################################
+
+class _ModbusTCPclientPlug:
+    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+      <xsd:element name="ModbusTCPclient">
+        <xsd:complexType>
+          <xsd:attribute name="Remote_IP_Address" type="xsd:string" use="optional" default="localhost"/>
+          <xsd:attribute name="Remote_Port_Number" type="xsd:string" use="optional" default="502"/>
+          <xsd:attribute name="Invocation_Rate_in_ms" use="optional" default="100">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:unsignedLong">
+                    <xsd:minInclusive value="1"/>
+                    <xsd:maxInclusive value="2147483647"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:schema>
+    """
+    # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms corresponds to aprox 25 days.
+    CTNChildrenTypes = [("ModbusRequest",_RequestPlug, "Request")]
+    # TODO: Replace with CTNType !!!
+    PlugType = "ModbusTCPclient"
+
+    # Return the number of (modbus library) nodes this specific TCP client will need
+    #   return type: (tcp nodes, rtu nodes, ascii nodes)
+    def GetNodeCount(self):
+        return (1, 0, 0)
+
+    def CTNGenerate_C(self, buildpath, locations):
+        """
+        Generate C code
+        @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
+        @param locations: List of complete variables locations \
+            [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+            "NAME" : name of the variable (generally "__IW0_1_2" style)
+            "DIR" : direction "Q","I" or "M"
+            "SIZE" : size "X", "B", "W", "D", "L"
+            "LOC" : tuple of interger for IEC location (0,1,2,...)
+            }, ...]
+        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+        """
+        return [], "", False
+
+
+
+        
+###################################################
+###################################################
+#                                                 #
+#            T C P    S E R V E R                 # 
+#                                                 #
+###################################################
+###################################################
+
+class _ModbusTCPserverPlug:
+    # NOTE: the Port number is a 'string' and not an 'integer'!
+    # This is because the underlying modbus library accepts strings 
+    # (e.g.: well known port names!)
+    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+      <xsd:element name="ModbusServerNode">
+        <xsd:complexType>
+          <xsd:attribute name="Local_IP_Address" type="xsd:string" use="optional"  default="#ANY#"/>
+          <xsd:attribute name="Local_Port_Number" type="xsd:string" use="optional" default="502"/>
+          <xsd:attribute name="SlaveID" use="optional" default="0">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="0"/>
+                    <xsd:maxInclusive value="255"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:schema>
+    """
+    CTNChildrenTypes = [("MemoryArea",_MemoryAreaPlug, "Memory Area")]
+    # TODO: Replace with CTNType !!!
+    PlugType = "ModbusTCPserver"
+
+    # Return the number of (modbus library) nodes this specific TCP server will need
+    #   return type: (tcp nodes, rtu nodes, ascii nodes)
+    def GetNodeCount(self):
+        return (1, 0, 0)
+
+    # Return a list with a single tuple conatining the (location, port number)
+    #     location: location of this node in the configuration tree
+    #     port number: IP port used by this Modbus/IP server
+    def GetIPServerPortNumbers(self):
+        port = self.GetParamsAttributes()[0]["children"][1]["value"]
+        return [(self.GetCurrentLocation() , port)]
+
+    def CTNGenerate_C(self, buildpath, locations):
+        """
+        Generate C code
+        @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
+        @param locations: List of complete variables locations \
+            [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+            "NAME" : name of the variable (generally "__IW0_1_2" style)
+            "DIR" : direction "Q","I" or "M"
+            "SIZE" : size "X", "B", "W", "D", "L"
+            "LOC" : tuple of interger for IEC location (0,1,2,...)
+            }, ...]
+        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+        """
+        return [], "", False
+
+
+
+
+###################################################
+###################################################
+#                                                 #
+#            R T U    C L I E N T                 # 
+#                                                 #
+###################################################
+###################################################
+
+class _ModbusRTUclientPlug:
+    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+      <xsd:element name="ModbusRTUclient">
+        <xsd:complexType>
+          <xsd:attribute name="Serial_Port" type="xsd:string"  use="optional" default="/dev/ttyS0"/>
+          <xsd:attribute name="Baud_Rate"   type="xsd:string"  use="optional" default="9600"/>
+          <xsd:attribute name="Parity"      type="xsd:string"  use="optional" default="even"/>
+          <xsd:attribute name="Stop_Bits"   type="xsd:string"  use="optional" default="1"/>
+          <xsd:attribute name="Invocation_Rate_in_ms" use="optional" default="100">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="1"/>
+                    <xsd:maxInclusive value="2147483647"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:schema>
+    """
+    # NOTE: Max value of 2147483647 (i32_max) for Invocation_Rate_in_ms corresponds to aprox 25 days.
+    CTNChildrenTypes = [("ModbusRequest",_RequestPlug, "Request")]
+    # TODO: Replace with CTNType !!!
+    PlugType = "ModbusRTUclient"
+
+    def GetParamsAttributes(self, path = None):
+        infos = ConfigTreeNode.GetParamsAttributes(self, path = path)
+        for element in infos:
+            if element["name"] == "ModbusRTUclient":
+                for child in element["children"]:
+                    if child["name"] == "Baud_Rate":
+                        child["type"] = modbus_serial_baudrate_list
+                    if child["name"] == "Stop_Bits":
+                        child["type"] = modbus_serial_stopbits_list
+                    if child["name"] == "Parity":
+                        child["type"] = modbus_serial_parity_dict.keys()
+        return infos
+      
+    # Return the number of (modbus library) nodes this specific RTU client will need
+    #   return type: (tcp nodes, rtu nodes, ascii nodes)
+    def GetNodeCount(self):
+        return (0, 1, 0)
+
+    def CTNGenerate_C(self, buildpath, locations):
+        """
+        Generate C code
+        @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
+        @param locations: List of complete variables locations \
+            [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+            "NAME" : name of the variable (generally "__IW0_1_2" style)
+            "DIR" : direction "Q","I" or "M"
+            "SIZE" : size "X", "B", "W", "D", "L"
+            "LOC" : tuple of interger for IEC location (0,1,2,...)
+            }, ...]
+        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+        """
+        return [], "", False
+
+
+
+###################################################
+###################################################
+#                                                 #
+#            R T U    S L A V E                   # 
+#                                                 #
+###################################################
+###################################################
+
+
+class _ModbusRTUslavePlug:
+    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+      <xsd:element name="ModbusRTUslave">
+        <xsd:complexType>
+          <xsd:attribute name="Serial_Port" type="xsd:string"  use="optional" default="/dev/ttyS0"/>
+          <xsd:attribute name="Baud_Rate"   type="xsd:string"  use="optional" default="9600"/>
+          <xsd:attribute name="Parity"      type="xsd:string"  use="optional" default="even"/>
+          <xsd:attribute name="Stop_Bits"   type="xsd:string"  use="optional" default="1"/>
+          <xsd:attribute name="SlaveID" use="optional" default="1">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="1"/>
+                    <xsd:maxInclusive value="255"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:schema>
+    """
+    CTNChildrenTypes = [("MemoryArea",_MemoryAreaPlug, "Memory Area")]
+    # TODO: Replace with CTNType !!!
+    PlugType = "ModbusRTUslave"
+
+    def GetParamsAttributes(self, path = None):
+        infos = ConfigTreeNode.GetParamsAttributes(self, path = path)
+        for element in infos:
+            if element["name"] == "ModbusRTUslave":
+                for child in element["children"]:
+                    if child["name"] == "Baud_Rate":
+                        child["type"] = modbus_serial_baudrate_list
+                    if child["name"] == "Stop_Bits":
+                        child["type"] = modbus_serial_stopbits_list
+                    if child["name"] == "Parity":
+                        child["type"] = modbus_serial_parity_dict.keys()
+        return infos
+      
+    # Return the number of (modbus library) nodes this specific RTU slave will need
+    #   return type: (tcp nodes, rtu nodes, ascii nodes)
+    def GetNodeCount(self):
+        return (0, 1, 0)
+
+    def CTNGenerate_C(self, buildpath, locations):
+        """
+        Generate C code
+        @param current_location: Tupple containing plugin IEC location : %I0.0.4.5 => (0,0,4,5)
+        @param locations: List of complete variables locations \
+            [{"IEC_TYPE" : the IEC type (i.e. "INT", "STRING", ...)
+            "NAME" : name of the variable (generally "__IW0_1_2" style)
+            "DIR" : direction "Q","I" or "M"
+            "SIZE" : size "X", "B", "W", "D", "L"
+            "LOC" : tuple of interger for IEC location (0,1,2,...)
+            }, ...]
+        @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+        """
+        return [], "", False
+
+    
+
+###################################################
+###################################################
+#                                                 #
+#             R O O T    C L A S S                # 
+#                                                 #
+###################################################
+###################################################
+class RootClass:
+    XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+      <xsd:element name="ModbusRoot">
+        <xsd:complexType>
+          <xsd:attribute name="MaxRemoteTCPclients" use="optional" default="10">
+            <xsd:simpleType>
+                <xsd:restriction base="xsd:integer">
+                    <xsd:minInclusive value="0"/>
+                    <xsd:maxInclusive value="65535"/>
+                </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:attribute>
+        </xsd:complexType>
+      </xsd:element>
+    </xsd:schema>
+    """
+    CTNChildrenTypes = [("ModbusTCPclient",_ModbusTCPclientPlug, "Modbus TCP Client")
+                       ,("ModbusTCPserver",_ModbusTCPserverPlug, "Modbus TCP Server")
+                       ,("ModbusRTUclient",_ModbusRTUclientPlug, "Modbus RTU Client")
+                       ,("ModbusRTUslave", _ModbusRTUslavePlug,  "Modbus RTU Slave")
+                       ]
+    
+    # Return the number of (modbus library) nodes this specific instance of the modbus plugin will need
+    #   return type: (tcp nodes, rtu nodes, ascii nodes)
+    def GetNodeCount(self):
+        max_remote_tcpclient = self.GetParamsAttributes()[0]["children"][0]["value"]
+        total_node_count = (max_remote_tcpclient, 0, 0)
+        for child in self.IECSortedChildren():
+            # ask each child how many nodes it needs, and add them all up.
+            total_node_count = tuple(x1 + x2 for x1, x2 in zip(total_node_count, child.GetNodeCount())) 
+        return total_node_count
+
+    # Return a list with tuples of the (location, port numbers) used by all the Modbus/IP servers
+    def GetIPServerPortNumbers(self):
+        IPServer_port_numbers = []
+        for child in self.IECSortedChildren():
+            if child.CTNType == "ModbusTCPserver":
+                IPServer_port_numbers.extend(child.GetIPServerPortNumbers())
+        return IPServer_port_numbers
+
+    def CTNGenerate_C(self, buildpath, locations):
+        #print "#############"
+        #print self.__class__
+        #print type(self)
+        #print "self.CTNType >>>"
+        #print self.CTNType
+        #print "type(self.CTNType) >>>"
+        #print type(self.CTNType)
+        #print "#############"
+        
+        loc_dict = {"locstr" : "_".join(map(str,self.GetCurrentLocation())),
+                   }
+        
+        # Determine the number of (modbus library) nodes ALL instances of the modbus plugin will need
+        #   total_node_count: (tcp nodes, rtu nodes, ascii nodes)
+        # Also get a list with tuples of (location, IP port numbers) used by all the Modbus/IP server nodes
+        #   This list is later used to search for duplicates in port numbers!
+        #   IPServer_port_numbers = [(location ,IPserver_port_number), ...]
+        #       location: tuple similar to (0, 3, 1) representing the location in the configuration tree "0.3.1.x"
+        #       IPserver_port_number: a number (i.e. port number used by the Modbus/IP server)
+        total_node_count = (0, 0, 0)
+        IPServer_port_numbers = []
+        for CTNInstance in self.GetCTRoot().IterChildren():
+            if CTNInstance.CTNType == "modbus":
+                # ask each modbus plugin instance how many nodes it needs, and add them all up.
+                total_node_count = tuple(x1 + x2 for x1, x2 in zip(total_node_count, CTNInstance.GetNodeCount())) 
+                IPServer_port_numbers.extend(CTNInstance.GetIPServerPortNumbers())
+
+        # Search for use of duplicate port numbers by Modbus/IP servers
+        #print IPServer_port_numbers 
+        # ..but first define a lambda function to convert a tuple with the config tree location to a nice looking string
+        #   for e.g., convert the tuple (0, 3, 4) to "0.3.4" 
+        lt_to_str = lambda loctuple: '.'.join(map(str, loctuple))
+        for i in range(0, len(IPServer_port_numbers)-1):
+            for j in range (i+1, len(IPServer_port_numbers)):
+                if IPServer_port_numbers[i][1] == IPServer_port_numbers[j][1]:
+                    self.GetCTRoot().logger.write_warning(_("Error: Modbus/IP Servers %s.x and %s.x use the same port number %s.\n")%(lt_to_str(IPServer_port_numbers[i][0]), lt_to_str(IPServer_port_numbers[j][0]), IPServer_port_numbers[j][1]))
+                    raise Exception, False
+                    # TODO: return an error code instead of raising an exception
+        
+        # Determine the current location in Beremiz's project configuration tree 
+        current_location = self.GetCurrentLocation()
+        
+        # define a unique name for the generated C and h files
+        prefix = "_".join(map(str, current_location))
+        Gen_MB_c_path = os.path.join(buildpath, "MB_%s.c"%prefix)
+        Gen_MB_h_path = os.path.join(buildpath, "MB_%s.h"%prefix) 
+        c_filename = os.path.join(os.path.split(__file__)[0],"mb_runtime.c")
+        h_filename = os.path.join(os.path.split(__file__)[0],"mb_runtime.h")
+
+        tcpclient_reqs_count = 0
+        rtuclient_reqs_count = 0
+        ascclient_reqs_count = 0
+        tcpclient_node_count = 0
+        rtuclient_node_count = 0
+        ascclient_node_count = 0
+        tcpserver_node_count = 0
+        rtuserver_node_count = 0
+        ascserver_node_count = 0
+        nodeid = 0
+        client_nodeid = 0
+        client_requestid = 0
+        server_id = 0
+        
+        server_node_list = []
+        client_node_list = []
+        client_request_list = []
+        server_memarea_list = []
+        loc_vars = []
+        loc_vars_list = [] # list of variables already declared in C code!
+        for child in self.IECSortedChildren():
+            #print "<<<<<<<<<<<<<"
+            #print "child (self.IECSortedChildren())----->"
+            #print child.__class__
+            #print ">>>>>>>>>>>>>"
+            ######################################
+            if child.PlugType == "ModbusTCPserver":
+                tcpserver_node_count += 1
+                new_node = GetTCPServerNodePrinted(self, child)
+                if new_node is None:
+                    return [],"",False
+                server_node_list.append(new_node)
+                ##############
+                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"]
+                    # '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(subchild.GetParamsAttributes()[0]["children"][2]["value"])
+                        relative_addr = absloute_address - start_address
+                        #test if relative address in request specified range
+                        if relative_addr in xrange(int(subchild.GetParamsAttributes()[0]["children"][1]["value"])):
+                            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":
+                rtuserver_node_count += 1
+                new_node = GetRTUSlaveNodePrinted(self, child)
+                if new_node is None:
+                    return [],"",False
+                server_node_list.append(new_node)
+                ##############
+                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"]
+                    # '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(subchild.GetParamsAttributes()[0]["children"][2]["value"])
+                        relative_addr = absloute_address - start_address
+                        #test if relative address in request specified range
+                        if relative_addr in xrange(int(subchild.GetParamsAttributes()[0]["children"][1]["value"])):
+                            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":
+                tcpclient_reqs_count += len(child.IECSortedChildren())
+                new_node = GetTCPClientNodePrinted(self, child)
+                if new_node is None:
+                    return [],"",False
+                client_node_list.append(new_node)
+                for subchild in child.IECSortedChildren():
+                    new_req = GetClientRequestPrinted(self, subchild, client_nodeid)
+                    if new_req is None:
+                        return [],"",False
+                    client_request_list.append(new_req)
+                    for iecvar in subchild.GetLocations():
+                        #absloute address - start address
+                        relative_addr = iecvar["LOC"][3] - int(subchild.GetParamsAttributes()[0]["children"][3]["value"])
+                        #test if relative address in request specified range
+                        if relative_addr in xrange(int(subchild.GetParamsAttributes()[0]["children"][2]["value"])):
+                            if str(iecvar["NAME"]) not in loc_vars_list:
+                                loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr))
+                                loc_vars_list.append(str(iecvar["NAME"]))
+                    client_requestid += 1
+                tcpclient_node_count += 1
+                client_nodeid += 1
+            ######################################
+            if child.PlugType == "ModbusRTUclient":
+                rtuclient_reqs_count += len(child.IECSortedChildren())
+                new_node = GetRTUClientNodePrinted(self, child)
+                if new_node is None:
+                    return [],"",False
+                client_node_list.append(new_node)
+                for subchild in child.IECSortedChildren():
+                    new_req = GetClientRequestPrinted(self, subchild, client_nodeid)
+                    if new_req is None:
+                        return [],"",False
+                    client_request_list.append(new_req)
+                    for iecvar in subchild.GetLocations():
+                        #absloute address - start address
+                        relative_addr = iecvar["LOC"][3] - int(subchild.GetParamsAttributes()[0]["children"][3]["value"])
+                        #test if relative address in request specified range
+                        if relative_addr in xrange(int(subchild.GetParamsAttributes()[0]["children"][2]["value"])):
+                            if str(iecvar["NAME"]) not in loc_vars_list:
+                                loc_vars.append("u16 *" + str(iecvar["NAME"]) + " = &client_requests[%d].plcv_buffer[%d];" % (client_requestid, relative_addr))
+                                loc_vars_list.append(str(iecvar["NAME"]))
+                    client_requestid += 1
+                rtuclient_node_count += 1
+                client_nodeid += 1
+            nodeid += 1
+        
+        loc_dict["loc_vars"]             = "\n".join(loc_vars)
+        loc_dict["server_nodes_params"]  = ",\n\n".join(server_node_list)
+        loc_dict["client_nodes_params"]  = ",\n\n".join(client_node_list)
+        loc_dict["client_req_params"]    = ",\n\n".join(client_request_list)
+        loc_dict["tcpclient_reqs_count"] = str(tcpclient_reqs_count)
+        loc_dict["tcpclient_node_count"] = str(tcpclient_node_count)
+        loc_dict["tcpserver_node_count"] = str(tcpserver_node_count)
+        loc_dict["rtuclient_reqs_count"] = str(rtuclient_reqs_count)
+        loc_dict["rtuclient_node_count"] = str(rtuclient_node_count)
+        loc_dict["rtuserver_node_count"] = str(rtuserver_node_count)
+        loc_dict["ascclient_reqs_count"] = str(ascclient_reqs_count)
+        loc_dict["ascclient_node_count"] = str(ascclient_node_count)
+        loc_dict["ascserver_node_count"] = str(ascserver_node_count)
+        loc_dict["total_tcpnode_count"]  = str(total_node_count[0])
+        loc_dict["total_rtunode_count"]  = str(total_node_count[1])
+        loc_dict["total_ascnode_count"]  = str(total_node_count[2])
+        loc_dict["max_remote_tcpclient"] = int(self.GetParamsAttributes()[0]["children"][0]["value"])
+
+        #get template file content into a string, format it with dict
+        #and write it to proper .h file
+        mb_main = open(h_filename).read() % loc_dict
+        f = open(Gen_MB_h_path,'w')
+        f.write(mb_main)
+        f.close()
+        #same thing as above, but now to .c file
+        mb_main = open(c_filename).read() % loc_dict
+        f = open(Gen_MB_c_path,'w')
+        f.write(mb_main)
+        f.close()
+
+        LDFLAGS = []
+        LDFLAGS.append(" \"-L" + ModbusPath + "\"")
+        LDFLAGS.append(" -lmb ")
+        LDFLAGS.append(" \"-Wl,-rpath," + ModbusPath + "\"")
+        #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_slave_and_master.o") + "\"")
+        #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_slave.o") + "\"")
+        #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_master.o") + "\"")
+        #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_tcp.o")    + "\"")
+        #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_rtu.o")    + "\"")
+        #LDFLAGS.append("\"" + os.path.join(ModbusPath, "mb_ascii.o")    + "\"")
+        #LDFLAGS.append("\"" + os.path.join(ModbusPath, "sin_util.o")  + "\"")
+        if os.name == 'nt':   # other possible values: 'posix' 'os2' 'ce' 'java' 'riscos'
+            LDFLAGS.append(" -lws2_32 ")  # on windows we need to load winsock library!
+
+        return [(Gen_MB_c_path, ' -I"'+ModbusPath+'"')], LDFLAGS, True