Edouard@1912: /* File generated by Beremiz (PlugGenerate_C method of Modbus plugin) */
Edouard@1912: 
Edouard@1912: /*
Edouard@1912:  * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt)
Edouard@1912:  *
Edouard@1912:  * This file is part of the Modbus library for Beremiz and matiec.
Edouard@1912:  *
Edouard@1912:  * This Modbus library is free software: you can redistribute it and/or modify
Edouard@1912:  * it under the terms of the GNU Lesser General Public License as published by
Edouard@2019:  * the Free Software Foundation, either version 2 of the License, or
Edouard@1912:  * (at your option) any later version.
Edouard@1912:  *
Edouard@1912:  * This program is distributed in the hope that it will be useful, but
Edouard@1912:  * WITHOUT ANY WARRANTY; without even the implied warranty of
Edouard@1912:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
Edouard@1912:  * General Public License for more details.
Edouard@1912:  *
Edouard@1912:  * You should have received a copy of the GNU Lesser General Public License
Edouard@1912:  * along with this Modbus library.  If not, see <http://www.gnu.org/licenses/>.
Edouard@1912:  *
Edouard@1912:  * This code is made available on the understanding that it will not be
Edouard@1912:  * used in safety-critical situations without a full and competent review.
Edouard@1912:  */
Edouard@1912: 
Edouard@1912: 
Edouard@1912: #include <stdio.h>
Edouard@1912: #include <string.h>  /* required for memcpy() */
Edouard@2683: #include <errno.h>
msousa@2647: #include <time.h>
msousa@2647: #include <signal.h>
msousa@2687: #include <unistd.h>  /* required for pause() */
Edouard@1912: #include "mb_slave_and_master.h"
Edouard@1912: #include "MB_%(locstr)s.h"
Edouard@1912: 
Edouard@1912: 
Edouard@1912: #define MAX_MODBUS_ERROR_CODE 11
Edouard@1912: static const char *modbus_error_messages[MAX_MODBUS_ERROR_CODE+1] = {
Edouard@1912:     /* 0 */ "",                             /* un-used -> no error! */
Edouard@1912:     /* 1 */ "illegal/unsuported function",
Edouard@1912:     /* 2 */ "illegal data address",
Edouard@1912:     /* 3 */ "illegal data value",
Edouard@1912:     /* 4 */ "slave device failure",
Edouard@1912:     /* 5 */ "acknowledge -> slave intends to reply later",
Edouard@1912:     /* 6 */ "slave device busy",
Edouard@1912:     /* 7 */ "negative acknowledge",
Edouard@1912:     /* 8 */ "memory parity error",
Edouard@1912:     /* 9 */ "",                             /* undefined by Modbus */
Edouard@1912:     /* 10*/ "gateway path unavalilable",
Edouard@1912:     /* 11*/ "gateway target device failed to respond"
Edouard@1912: };    
Edouard@1912: 
Edouard@1912: 
Edouard@1912: /* Execute a modbus client transaction/request */
Edouard@1912: static int __execute_mb_request(int request_id){
Edouard@1912: 	switch (client_requests[request_id].mb_function){
Edouard@1912: 	
Edouard@1912: 	case  1: /* read coils */
Edouard@1912: 		return read_output_bits(client_requests[request_id].slave_id,
Edouard@1912: 					client_requests[request_id].address,
Edouard@1912: 					client_requests[request_id].count,
Edouard@1912: 					client_requests[request_id].coms_buffer,
Edouard@1912: 					(int) client_requests[request_id].count,
Edouard@1912: 					client_nodes[client_requests[request_id].client_node_id].mb_nd,
Edouard@1912: 					client_requests[request_id].retries,
msousa@2713: 					&(client_requests[request_id].mb_error_code),
Edouard@1912: 					&(client_requests[request_id].resp_timeout),
Edouard@1912: 					&(client_requests[request_id].coms_buf_mutex));
Edouard@1912: 
Edouard@1912: 	case  2: /* read discrete inputs */
Edouard@1912: 		return read_input_bits( client_requests[request_id].slave_id,
Edouard@1912: 					client_requests[request_id].address,
Edouard@1912: 					client_requests[request_id].count,
Edouard@1912: 					client_requests[request_id].coms_buffer,
Edouard@1912: 					(int) client_requests[request_id].count,
Edouard@1912: 					client_nodes[client_requests[request_id].client_node_id].mb_nd,
Edouard@1912: 					client_requests[request_id].retries,
msousa@2713: 					&(client_requests[request_id].mb_error_code),
Edouard@1912: 					&(client_requests[request_id].resp_timeout),
Edouard@1912: 					&(client_requests[request_id].coms_buf_mutex));
Edouard@1912: 
Edouard@1912: 	case  3: /* read holding registers */
Edouard@1912: 		return read_output_words(client_requests[request_id].slave_id,
Edouard@1912: 					client_requests[request_id].address,
Edouard@1912: 					client_requests[request_id].count,
Edouard@1912: 					client_requests[request_id].coms_buffer,
Edouard@1912: 					(int) client_requests[request_id].count,
Edouard@1912: 					client_nodes[client_requests[request_id].client_node_id].mb_nd,
Edouard@1912: 					client_requests[request_id].retries,
msousa@2713: 					&(client_requests[request_id].mb_error_code),
Edouard@1912: 					&(client_requests[request_id].resp_timeout),
Edouard@1912: 					&(client_requests[request_id].coms_buf_mutex));
Edouard@1912: 	
Edouard@1912: 	case  4: /* read input registers */
Edouard@1912: 		return read_input_words(client_requests[request_id].slave_id,
Edouard@1912: 					client_requests[request_id].address,
Edouard@1912: 					client_requests[request_id].count,
Edouard@1912: 					client_requests[request_id].coms_buffer,
Edouard@1912: 					(int) client_requests[request_id].count,
Edouard@1912: 					client_nodes[client_requests[request_id].client_node_id].mb_nd,
Edouard@1912: 					client_requests[request_id].retries,
msousa@2713: 					&(client_requests[request_id].mb_error_code),
Edouard@1912: 					&(client_requests[request_id].resp_timeout),
Edouard@1912: 					&(client_requests[request_id].coms_buf_mutex));
Edouard@1912: 
Edouard@1912: 	case  5: /* write single coil */
Edouard@1912: 		return write_output_bit(client_requests[request_id].slave_id,
Edouard@1912: 					client_requests[request_id].address,
Edouard@1912: 					client_requests[request_id].coms_buffer[0],
Edouard@1912: 					client_nodes[client_requests[request_id].client_node_id].mb_nd,
Edouard@1912: 					client_requests[request_id].retries,
msousa@2713: 					&(client_requests[request_id].mb_error_code),
Edouard@1912: 					&(client_requests[request_id].resp_timeout),
Edouard@1912: 					&(client_requests[request_id].coms_buf_mutex));
Edouard@1912: 
Edouard@1912: 	case  6: /* write single register */
Edouard@1912: 		return write_output_word(client_requests[request_id].slave_id,
Edouard@1912: 					client_requests[request_id].address,
Edouard@1912: 					client_requests[request_id].coms_buffer[0],
Edouard@1912: 					client_nodes[client_requests[request_id].client_node_id].mb_nd,
Edouard@1912: 					client_requests[request_id].retries,
msousa@2713: 					&(client_requests[request_id].mb_error_code),
Edouard@1912: 					&(client_requests[request_id].resp_timeout),
Edouard@1912: 					&(client_requests[request_id].coms_buf_mutex));
Edouard@1912: 
Edouard@1912: 	case  7: break; /* function not yet supported */
Edouard@1912: 	case  8: break; /* function not yet supported */
Edouard@1912: 	case  9: break; /* function not yet supported */
Edouard@1912: 	case 10: break; /* function not yet supported */
Edouard@1912: 	case 11: break; /* function not yet supported */
Edouard@1912: 	case 12: break; /* function not yet supported */
Edouard@1912: 	case 13: break; /* function not yet supported */
Edouard@1912: 	case 14: break; /* function not yet supported */
Edouard@1912: 	
Edouard@1912: 	case 15: /* write multiple coils */
Edouard@1912: 		return write_output_bits(client_requests[request_id].slave_id,
Edouard@1912: 					 client_requests[request_id].address,
Edouard@1912: 					 client_requests[request_id].count,
Edouard@1912: 					 client_requests[request_id].coms_buffer,
Edouard@1912: 					 client_nodes[client_requests[request_id].client_node_id].mb_nd,
Edouard@1912: 					 client_requests[request_id].retries,
msousa@2713: 					 &(client_requests[request_id].mb_error_code),
Edouard@1912: 					 &(client_requests[request_id].resp_timeout),
Edouard@1912: 					 &(client_requests[request_id].coms_buf_mutex));
Edouard@1912: 
Edouard@1912: 	case 16: /* write multiple registers */
Edouard@1912: 		return write_output_words(client_requests[request_id].slave_id,
Edouard@1912: 					client_requests[request_id].address,
Edouard@1912: 					client_requests[request_id].count,
Edouard@1912: 					client_requests[request_id].coms_buffer,
Edouard@1912: 					client_nodes[client_requests[request_id].client_node_id].mb_nd,
Edouard@1912: 					client_requests[request_id].retries,
msousa@2713: 					&(client_requests[request_id].mb_error_code),
Edouard@1912: 					&(client_requests[request_id].resp_timeout),
Edouard@1912: 					&(client_requests[request_id].coms_buf_mutex));
Edouard@1912: 	
Edouard@1912: 	default: break;  /* should never occur, if file generation is correct */
Edouard@1912: 	}
Edouard@1912: 
Edouard@1912: 	fprintf(stderr, "Modbus plugin: Modbus function %%d not supported\n", request_id); /* should never occur, if file generation is correct */
Edouard@1912: 	return -1;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: /* pack bits from unpacked_data to packed_data */
Edouard@1912: static inline int __pack_bits(u16 *unpacked_data, u16 start_addr, u16 bit_count,  u8  *packed_data) {
Edouard@1912:   u8 bit;
Edouard@1912:   u16 byte, coils_processed;
Edouard@1912: 
Edouard@1912:   if ((0 == bit_count) || (65535-start_addr < bit_count-1))
Edouard@1912:     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
Edouard@1912:   
Edouard@1912:   for( byte = 0, coils_processed = 0; coils_processed < bit_count; byte++) {
Edouard@1912:     packed_data[byte] = 0;
Edouard@1912:     for( bit = 0x01; (bit & 0xFF) && (coils_processed < bit_count); bit <<= 1, coils_processed++ ) {
Edouard@1912:       if(unpacked_data[start_addr + coils_processed])
Edouard@1912:             packed_data[byte] |=  bit; /*   set bit */
Edouard@1912:       else  packed_data[byte] &= ~bit; /* reset bit */
Edouard@1912:     }
Edouard@1912:   }
Edouard@1912:   return 0;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@1912: /* unpack bits from packed_data to unpacked_data */
Edouard@1912: static inline int __unpack_bits(u16 *unpacked_data, u16 start_addr, u16 bit_count,  u8  *packed_data) {
Edouard@1912:   u8  temp, bit;
Edouard@1912:   u16 byte, coils_processed;
Edouard@1912: 
Edouard@1912:   if ((0 == bit_count) || (65535-start_addr < bit_count-1))
Edouard@1912:     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
Edouard@1912:   
Edouard@1912:   for(byte = 0, coils_processed = 0; coils_processed < bit_count; byte++) {
Edouard@1912:     temp = packed_data[byte] ;
Edouard@1912:     for(bit = 0x01; (bit & 0xff) && (coils_processed < bit_count); bit <<= 1, coils_processed++) {
Edouard@1912:       unpacked_data[start_addr + coils_processed] = (temp & bit)?1:0;
Edouard@1912:     }
Edouard@1912:   }
Edouard@1912:   return 0;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
msousa@2721: static int __read_inbits   (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes) {
msousa@2721:   int res = __pack_bits(((server_mem_t *)mem_map)->ro_bits, start_addr, bit_count, data_bytes);
msousa@2721:   
msousa@2722:   if (res >= 0) {
msousa@2722:     /* update the flag and counter of Modbus requests we have processed. */
msousa@2721:     ((server_mem_t *)mem_map)->flag_read_req_counter++;
msousa@2722:     ((server_mem_t *)mem_map)->flag_read_req_flag = 1;
msousa@2722:   }
msousa@2721: 
msousa@2721:   return res;
msousa@2721: }
msousa@2721: 
msousa@2721: static int __read_outbits  (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes) {
msousa@2721:   int res = __pack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes);
msousa@2721:   
msousa@2722:   if (res >= 0) {
msousa@2722:     /* update the flag and counter of Modbus requests we have processed. */
msousa@2721:     ((server_mem_t *)mem_map)->flag_read_req_counter++;
msousa@2722:     ((server_mem_t *)mem_map)->flag_read_req_flag = 1;
msousa@2722:   }
msousa@2721: 
msousa@2721:   return res;
msousa@2721: }
msousa@2721: 
msousa@2721: static int __write_outbits (void *mem_map, u16 start_addr, u16 bit_count, u8  *data_bytes) {
msousa@2721:   int res = __unpack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes);
msousa@2721:   
msousa@2722:   if (res >= 0) {
msousa@2722:     /* update the flag and counter of Modbus requests we have processed. */
msousa@2721:     ((server_mem_t *)mem_map)->flag_write_req_counter++;
msousa@2722:     ((server_mem_t *)mem_map)->flag_write_req_flag = 1;
msousa@2722:   }
msousa@2721: 
msousa@2721:   return res;
msousa@2721: }
msousa@2721: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: static int __read_inwords  (void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) {
Edouard@1913: 
Edouard@1912:   if ((start_addr + word_count) > MEM_AREA_SIZE)
Edouard@1912:     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
Edouard@1913: 
msousa@2722:   /* update the flag and counter of Modbus requests we have processed. */
msousa@2721:   ((server_mem_t *)mem_map)->flag_read_req_counter++;
msousa@2722:   ((server_mem_t *)mem_map)->flag_read_req_flag = 1;
msousa@2721: 
Edouard@1913:   /* use memcpy() because loop with pointers (u16 *) caused alignment problems */
Edouard@1912:   memcpy(/* dest */ (void *)data_words,
Edouard@1912:          /* src  */ (void *)&(((server_mem_t *)mem_map)->ro_words[start_addr]),
Edouard@1912:          /* size */ word_count * 2);
Edouard@1912:   return 0;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: static int __read_outwords (void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) {
Edouard@1913: 
Edouard@1912:   if ((start_addr + word_count) > MEM_AREA_SIZE)
Edouard@1912:     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
Edouard@1913: 
msousa@2722:   /* update the flag and counter of Modbus requests we have processed. */
msousa@2721:   ((server_mem_t *)mem_map)->flag_read_req_counter++;
msousa@2722:   ((server_mem_t *)mem_map)->flag_read_req_flag = 1;
msousa@2721: 
Edouard@1913:   /* use memcpy() because loop with pointers (u16 *) caused alignment problems */
Edouard@1912:   memcpy(/* dest */ (void *)data_words,
Edouard@1912:          /* src  */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]),
Edouard@1912:          /* size */ word_count * 2);
Edouard@1912:   return 0;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: static int __write_outwords(void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) {
Edouard@1913: 
Edouard@1912:   if ((start_addr + word_count) > MEM_AREA_SIZE)
Edouard@1912:     return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */
Edouard@1912: 
msousa@2722:   /* update the flag and counter of Modbus requests we have processed. */
msousa@2721:   ((server_mem_t *)mem_map)->flag_write_req_counter++;
msousa@2722:   ((server_mem_t *)mem_map)->flag_write_req_flag = 1;
msousa@2721: 
Edouard@1912:   /* WARNING: The data returned in the data_words[] array is not guaranteed to be 16 bit aligned.
Edouard@1912:    *           It is not therefore safe to cast it to an u16 data type.
Edouard@1912:    *           The following code cannot be used. memcpy() is used instead.
Edouard@1912:    */
Edouard@1912:   /*
Edouard@1912:   for (count = 0; count < word_count ; count++)
Edouard@1912:     ((server_mem_t *)mem_map)->rw_words[count + start_addr] = data_words[count];
Edouard@1912:   */
Edouard@1912:   memcpy(/* dest */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]),
Edouard@1912:          /* src  */ (void *)data_words,
Edouard@1912:          /* size */ word_count * 2);
Edouard@1912:   return 0;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: #include <pthread.h>
Edouard@1912: 
Edouard@1912: static void *__mb_server_thread(void *_server_node)  {
Edouard@1912: 	server_node_t *server_node = _server_node;
Edouard@1912: 	mb_slave_callback_t callbacks = { 
Edouard@1912: 			&__read_inbits,
Edouard@1912: 			&__read_outbits,
Edouard@1912: 			&__write_outbits,
Edouard@1912: 			&__read_inwords,
Edouard@1912: 			&__read_outwords,
Edouard@1912: 			&__write_outwords,
Edouard@1912: 			(void *)&(server_node->mem_area)
Edouard@1912: 			};  
Edouard@1912: 	
Edouard@1912: 	// Enable thread cancelation. Enabled is default, but set it anyway to be safe.
Edouard@1912: 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
Edouard@1912: 
Edouard@1912: 	// mb_slave_run() should never return!
Edouard@1912: 	mb_slave_run(server_node->mb_nd /* nd */, callbacks, server_node->slave_id);
Edouard@1912: 	fprintf(stderr, "Modbus plugin: Modbus server for node %%s died unexpectedly!\n", server_node->location); /* should never occur */
Edouard@1912: 	return NULL;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@2480: #define timespec_add(ts, sec, nsec) {		\
Edouard@2480: 	ts.tv_sec  +=  sec;			\
Edouard@2480: 	ts.tv_nsec += nsec;			\
Edouard@2480: 	if (ts.tv_nsec >= 1000000000) {		\
Edouard@2480: 		ts.tv_sec  ++;			\
Edouard@2480: 		ts.tv_nsec -= 1000000000;	\
Edouard@2480: 	}					\
Edouard@2480: }
Edouard@2480: 
Edouard@1912: 
Edouard@1912: static void *__mb_client_thread(void *_index)  {
Edouard@1912: 	int client_node_id = (char *)_index - (char *)NULL; // Use pointer arithmetic (more portable than cast)
Edouard@1912: 
Edouard@1912: 	// Enable thread cancelation. Enabled is default, but set it anyway to be safe.
Edouard@1912: 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
Edouard@1912: 	
msousa@2647:     /* loop the communication with the client
msousa@2647:      * 
msousa@2647:          * When the client thread has difficulty communicating with remote client and/or server (network issues, for example),
msousa@2647:          * then the communications get delayed and we will fall behind in the period. 
msousa@2647:          * 
msousa@2647:          * This is OK. Note that if the condition variable were to be signaled multiple times while the client thread is inside the same
msousa@2647:          * Modbus transaction, then all those signals would be ignored.
msousa@2647:          * However, and since we keep the mutex locked during the communication cycle, it is not possible to signal the condition variable
msousa@2647:          * during that time (it is only possible while the thread is blocked during the call to pthread_cond_wait().
msousa@2647:          * 
msousa@2647:          * This means that when network issues eventually get resolved, we will NOT have a bunch of delayed activations to handle
msousa@2647:          * in quick succession (which would goble up CPU time). 
msousa@2647:          * 
msousa@2647:          * Notice that the above property is valid whether the communication cycle is run with the mutex locked, or unlocked.
msousa@2647:          * Since it makes it easier to implement the correct semantics for the other activation methods if the communication cycle
msousa@2647:          * is run with the mutex locked, then that is what we do.
msousa@2647:          * 
msousa@2647:      * Note that during all the communication cycle we will keep locked the mutex 
msousa@2647:      * (i.e. the mutex used together with the condition variable that will activate a new communication cycle)
msousa@2647:      * 
msousa@2647:      * Note that we never get to explicitly unlock this mutex. It will only be unlocked by the pthread_cond_wait()
msousa@2647:      * call at the end of the cycle.
msousa@2647:      */
msousa@2647:     pthread_mutex_lock(&(client_nodes[client_node_id].mutex));
msousa@2647: 
Edouard@1912: 	while (1) {
Edouard@1912: 		/*
Edouard@1912: 		struct timespec cur_time;
Edouard@1912: 		clock_gettime(CLOCK_MONOTONIC, &cur_time);
msousa@2687: 		fprintf(stderr, "Modbus client thread (%%d) - new cycle (%%ld:%%ld)!\n", client_node_id, cur_time.tv_sec, cur_time.tv_nsec);
Edouard@1912: 		*/
Edouard@1912: 		int req;
Edouard@1912: 		for (req=0; req < NUMBER_OF_CLIENT_REQTS; req ++){
msousa@2647: 			/* just do the requests belonging to the client */
Edouard@1912: 			if (client_requests[req].client_node_id != client_node_id)
Edouard@1912: 				continue;
msousa@2647:             
msousa@2647:             /* only do the request if:
msousa@2647:              *   - this request was explictly asked to be executed by the client program
msousa@2647:              *  OR
msousa@2647:              *   - the client thread was activated periodically
msousa@2647:              *     (in which case we execute all the requests belonging to the client node)
msousa@2647:              */
msousa@2647:             if ((client_requests[req].flag_exec_req == 0) && (client_nodes[client_requests[req].client_node_id].periodic_act == 0))
msousa@2647:                 continue;
msousa@2647:             
msousa@2687:             /*
msousa@2687:             fprintf(stderr, "Modbus client thread (%%d): RUNNING Modbus request %%d  (periodic = %%d  flag_exec_req = %%d)\n", 
msousa@2687:                     client_node_id, req, client_nodes[client_requests[req].client_node_id].periodic_act, client_requests[req].flag_exec_req );
msousa@2687:             */
msousa@2647:             
Edouard@1912: 			int res_tmp = __execute_mb_request(req);
msousa@2713: 			client_requests[req].tn_error_code = 0; // assume success
Edouard@1912: 			switch (res_tmp) {
Edouard@1912: 			  case PORT_FAILURE: {
Edouard@1912: 				if (res_tmp != client_nodes[client_node_id].prev_error)
Edouard@1912: 					fprintf(stderr, "Modbus plugin: Error connecting Modbus client %%s to remote server.\n", client_nodes[client_node_id].location);
Edouard@1912: 				client_nodes[client_node_id].prev_error = res_tmp;
msousa@2713: 				client_requests[req].tn_error_code = 1; // error accessing IP network, or serial interface
Edouard@1912: 				break;
Edouard@1912: 			  }
Edouard@1912: 			  case INVALID_FRAME: {
Edouard@1912: 				if ((res_tmp != client_requests[req].prev_error) && (0 == client_nodes[client_node_id].prev_error))
Edouard@1912: 					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);
Edouard@1912: 				client_requests[req].prev_error = res_tmp;
msousa@2713: 				client_requests[req].tn_error_code = 2; // reply received from server was an invalid frame
Edouard@1912: 				break;
Edouard@1912: 			  }
Edouard@1912: 			  case TIMEOUT: {
Edouard@1912: 				if ((res_tmp != client_requests[req].prev_error) && (0 == client_nodes[client_node_id].prev_error))
Edouard@1912: 					fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s timed out waiting for reply from server.\n", client_requests[req].location);
Edouard@1912: 				client_requests[req].prev_error = res_tmp;
msousa@2713: 				client_requests[req].tn_error_code = 3; // server did not reply before timeout expired
Edouard@1912: 				break;
Edouard@1912: 			  }
Edouard@1912: 			  case MODBUS_ERROR: {
msousa@2713: 				if (client_requests[req].prev_error != client_requests[req].mb_error_code) {
msousa@2713: 					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].mb_error_code);
msousa@2713: 					if (client_requests[req].mb_error_code <= MAX_MODBUS_ERROR_CODE ) {
msousa@2713: 						fprintf(stderr, "(%%s)", modbus_error_messages[client_requests[req].mb_error_code]);
Edouard@1912: 						fprintf(stderr, ".\n");
Edouard@1912: 					}
Edouard@1912: 				}
msousa@2713: 				client_requests[req].prev_error = client_requests[req].mb_error_code;
msousa@2713: 				client_requests[req].tn_error_code = 4; // server returned a valid Modbus error frame
Edouard@1912: 				break;
Edouard@1912: 			  }
Edouard@1912: 			  default: {
Edouard@1912: 				if ((res_tmp >= 0) && (client_nodes[client_node_id].prev_error != 0)) {
Edouard@1912: 					fprintf(stderr, "Modbus plugin: Modbus client %%s has reconnected to server/slave.\n", client_nodes[client_node_id].location);
Edouard@1912: 				}
Edouard@1912: 				if ((res_tmp >= 0) && (client_requests[req]        .prev_error != 0)) {
Edouard@1912: 					fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s has succesfully resumed comunication.\n", client_requests[req].location);
Edouard@1912: 				}
Edouard@1912: 				client_nodes[client_node_id].prev_error = 0;
Edouard@1912: 				client_requests[req]        .prev_error = 0;
Edouard@1912: 				break;
Edouard@1912: 			  }
Edouard@1912: 			}
msousa@2713: 
msousa@2714: 			/* Set the flag_tn_error_code and flag_mb_error_code that are mapped onto
msousa@2714:              * located BYTE variables, so the user program
msousa@2713:              * knows how the communication is going.
msousa@2713:              */
msousa@2714:             client_requests[req].flag_mb_error_code = client_requests[req].mb_error_code;
msousa@2714:             client_requests[req].flag_tn_error_code = client_requests[req].tn_error_code;
msousa@2713:             
msousa@2647:             /* We have just finished excuting a client transcation request.
msousa@2647:              * If the current cycle was activated by user request we reset the flag used to ask to run it
msousa@2647:              */
msousa@2647:             if (0 != client_requests[req].flag_exec_req) {
msousa@2647:                 client_requests[req].flag_exec_req     = 0;
msousa@2647:                 client_requests[req].flag_exec_started = 0;   
msousa@2647:             }
msousa@2647:             
msousa@2647:             //fprintf(stderr, "Modbus plugin: RUNNING<---> of Modbus request %%d  (periodic = %%d  flag_exec_req = %%d)\n", 
msousa@2647:             //        req, client_nodes[client_requests[req].client_node_id].periodic_act, client_requests[req].flag_exec_req );
msousa@2647:         }
msousa@2647: 
msousa@2647:         // Wait for signal (from timer or explicit request from user program) before starting the next cycle
msousa@2647:         {
msousa@2647:             // No need to lock the mutex. Is is already locked just before the while(1) loop.
msousa@2647:             // Read the comment there to understand why.
msousa@2647:             // pthread_mutex_lock(&(client_nodes[client_node_id].mutex));
msousa@2647:             
msousa@2647:             /* the client thread has just finished a cycle, so all the flags used to signal an activation
msousa@2647:              * and specify the activation source (periodic, user request, ...)
msousa@2647:              * get reset here, before waiting for a new activation.
msousa@2647:              */
msousa@2647:             client_nodes[client_node_id].periodic_act = 0;            
msousa@2647:             client_nodes[client_node_id].execute_req  = 0;
msousa@2647:             
msousa@2647:             while (client_nodes[client_node_id].execute_req == 0)
msousa@2647:                 pthread_cond_wait(&(client_nodes[client_node_id].condv),
msousa@2647:                                 &(client_nodes[client_node_id].mutex)); 
msousa@2647:                             
msousa@2647:             // We run the communication cycle with the mutex locked.
msousa@2647:             // Read the comment just above the while(1) to understand why.
msousa@2647:             // pthread_mutex_unlock(&(client_nodes[client_node_id].mutex));
msousa@2647:         }
Edouard@1912: 	}
Edouard@1912: 
Edouard@1912: 	// humour the compiler.
Edouard@1912: 	return NULL;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
msousa@2647: 
Edouard@2683: 
Edouard@2683: 
msousa@2687: /* Thread that simply implements a periodic 'timer',
msousa@2687:  *  i.e. periodically sends signal to the  thread running __mb_client_thread()
msousa@2647:  * 
msousa@2687:  * Note that we do not use a posix timer (timer_create() ) because there doesn't seem to be a way
msousa@2687:  * of having the timer notify the thread that is portable across Xenomai and POSIX.
msousa@2687:  * - SIGEV_THREAD    : not supported by Xenomai
msousa@2687:  * - SIGEV_THREAD_ID : Linux specific (i.e. non POSIX)
msousa@2687:  *                     Even so, I did not get it to work under Linux (issues with the header files)
msousa@2687:  * - SIGEV_SIGNAL    : Will not work, as signal is sent to random thread in process!
msousa@2647:  */
Edouard@2683: static void *__mb_client_timer_thread(void *_index) {
Edouard@2683: 	int client_node_id = (char *)_index - (char *)NULL; // Use pointer arithmetic (more portable than cast)
msousa@2687: 	struct timespec next_cycle;
Edouard@2683: 
Edouard@2684: 	int period_sec  =  client_nodes[client_node_id].comm_period / 1000;          /* comm_period is in ms */
Edouard@2684: 	int period_nsec = (client_nodes[client_node_id].comm_period %%1000)*1000000; /* comm_period is in ms */
Edouard@2684: 
msousa@2687: 	// Enable thread cancelation. Enabled is default, but set it anyway to be safe.
msousa@2687: 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
msousa@2687:     
msousa@2687:     if (client_nodes[client_node_id].comm_period <= 0) {
msousa@2687:         // No periodic activation required => nothing to do! 
msousa@2687:         while (1) pause(); // wait to be canceled when program terminates (shutdown() is called)
msousa@2687:         return NULL;  // not really necessary, just makes it easier to understand the code.
Edouard@2684:     }
Edouard@2684: 
msousa@2687: 	// get the current time
msousa@2687: 	clock_gettime(CLOCK_MONOTONIC, &next_cycle);
msousa@2687: 
msousa@2687:     while(1) {
msousa@2687:         // Determine absolute time instant for starting the next cycle
msousa@2687:         struct timespec prev_cycle, now;
msousa@2687:         prev_cycle = next_cycle;
msousa@2687:         timespec_add(next_cycle, period_sec, period_nsec);
msousa@2687:                 
msousa@2687:         /* NOTE:
msousa@2687:          * It is probably un-necessary to check for overflow of timer!
msousa@2687:          * Even in 32 bit systems this will take at least 68 years since the computer booted
msousa@2687:          * (remember, we are using CLOCK_MONOTONIC, which should start counting from 0
msousa@2687:          * every time the system boots). On 64 bit systems, it will take over 
msousa@2687:          * 10^11 years to overflow.
msousa@2647:          */
msousa@2687:         clock_gettime(CLOCK_MONOTONIC, &now);
msousa@2687:         if (next_cycle.tv_sec < prev_cycle.tv_sec) {
msousa@2687:            /* Timer overflow. See NOTE B above */
msousa@2687:             next_cycle = now;
msousa@2687:             timespec_add(next_cycle, period_sec, period_nsec);
msousa@2687:         }
msousa@2687:         
msousa@2687:         while (0 != clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_cycle, NULL));
msousa@2688:         
msousa@2688:         /* signal the client node's condition variable on which the client node's thread should be waiting... */
msousa@2688:         /* Since the communication cycle is run with the mutex locked, we use trylock() instead of lock() */
msousa@2688:         if (pthread_mutex_trylock (&(client_nodes[client_node_id].mutex)) == 0) {
msousa@2688:             client_nodes[client_node_id].execute_req  = 1; // tell the thread to execute
msousa@2688:             client_nodes[client_node_id].periodic_act = 1; // tell the thread the activation was done by periodic timer   
msousa@2688:             pthread_cond_signal (&(client_nodes[client_node_id].condv));
msousa@2688:             pthread_mutex_unlock(&(client_nodes[client_node_id].mutex));
msousa@2688:         } else {
msousa@2688:             /* We never get to signal the thread for activation. But that is OK.
msousa@2688:              * If it still in the communication cycle (during which the mutex is kept locked)
msousa@2688:              * then that means that the communication cycle is falling behing in the periodic 
msousa@2688:              * communication cycle, and we therefore need to skip a period.
msousa@2688:              */
msousa@2688:         }
Edouard@2683:     }
Edouard@2683: 
msousa@2688:     return NULL; // humour the compiler -> will never be executed!
Edouard@2683: }
msousa@2647: 
msousa@2647: 
Edouard@1913: int __cleanup_%(locstr)s ();
Edouard@1912: int __init_%(locstr)s (int argc, char **argv){
Edouard@1912: 	int index;
Edouard@1912: 
msousa@2654: 	for (index=0; index < NUMBER_OF_CLIENT_NODES;index++) {
Edouard@1912: 		client_nodes[index].mb_nd = -1;
msousa@2654:         /* see comment in mb_runtime.h to understad why we need to initialize these entries */
msousa@2654:         switch (client_nodes[index].node_address.naf) {
msousa@2654:             case naf_tcp:
msousa@2654:                 client_nodes[index].node_address.addr.tcp.host    = client_nodes[index].str1;
msousa@2654:                 client_nodes[index].node_address.addr.tcp.service = client_nodes[index].str2;
msousa@2654:                 break;
msousa@2654:             case naf_rtu:
msousa@2654:                 client_nodes[index].node_address.addr.rtu.device  = client_nodes[index].str1;
msousa@2654:                 break;
msousa@2654:         }
msousa@2654:     }
msousa@2654: 
msousa@2654: 	for (index=0; index < NUMBER_OF_SERVER_NODES;index++) {
Edouard@1912: 		// mb_nd with negative numbers indicate how far it has been initialised (or not)
Edouard@1912: 		//   -2  --> no modbus node created;  no thread  created
Edouard@1912: 		//   -1  -->    modbus node created!; no thread  created
Edouard@1912: 		//  >=0  -->    modbus node created!;    thread  created!
Edouard@1912: 		server_nodes[index].mb_nd = -2; 
msousa@2722: 		server_nodes[index].mem_area.flag_write_req_flag    = 0; 
msousa@2721: 		server_nodes[index].mem_area.flag_write_req_counter = 0; 
msousa@2721: 		server_nodes[index].mem_area.flag_read_req_counter  = 0; 
msousa@2722: 		server_nodes[index].mem_area.flag_read_req_flag     = 0; 
msousa@2655:         /* see comment in mb_runtime.h to understad why we need to initialize these entries */
msousa@2655:         switch (server_nodes[index].node_address.naf) {
msousa@2655:             case naf_tcp:
msousa@2655:                 server_nodes[index].node_address.addr.tcp.host    = server_nodes[index].str1;
msousa@2655:                 server_nodes[index].node_address.addr.tcp.service = server_nodes[index].str2;
msousa@2655:                 break;
msousa@2655:             case naf_rtu:
msousa@2655:                 server_nodes[index].node_address.addr.rtu.device  = server_nodes[index].str1;
msousa@2655:                 break;
msousa@2655:         }
msousa@2654: 	}
Edouard@1912: 
Edouard@1912: 	/* modbus library init */
Edouard@1912: 	/* Note that TOTAL_xxxNODE_COUNT are the nodes required by _ALL_ the instances of the modbus
Edouard@1912: 	 *  extension currently in the user's project. This file (MB_xx.c) is handling only one instance,
Edouard@1912: 	 *  but must initialize the library for all instances. Only the first call to mb_slave_and_master_init()
Edouard@1912: 	 *  will result in memory being allocated. All subsequent calls (by other MB_xx,c files) will be ignored
Edouard@1912: 	 *  by the mb_slave_and_master_init() funtion, as long as they are called with the same arguments.
Edouard@1912: 	 */
Edouard@1912: 	if (mb_slave_and_master_init(TOTAL_TCPNODE_COUNT, TOTAL_RTUNODE_COUNT, TOTAL_ASCNODE_COUNT) <0) {
Edouard@1912: 		fprintf(stderr, "Modbus plugin: Error starting modbus library\n");
Edouard@1912: 		// return imediately. Do NOT goto error_exit, as we did not get to
Edouard@1912: 		//  start the modbus library!
Edouard@1912: 		return -1;
Edouard@1912: 	}
Edouard@1912: 	
msousa@2647: 	/* init each client request */
Edouard@1912: 	/* Must be done _before_ launching the client threads!! */
Edouard@1912: 	for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){
msousa@2647:         /* make sure flags connected to user program MB transaction start request are all reset */
msousa@2647:         client_requests[index].flag_exec_req     = 0;
msousa@2647:         client_requests[index].flag_exec_started = 0;        
msousa@2647:         /* init the mutex for each client request */
msousa@2647:         /* Must be done _before_ launching the client threads!! */
Edouard@1912: 		if (pthread_mutex_init(&(client_requests[index].coms_buf_mutex), NULL)) {
Edouard@1912: 			fprintf(stderr, "Modbus plugin: Error initializing request for modbus client node %%s\n", client_nodes[client_requests[index].client_node_id].location);
Edouard@1912: 			goto error_exit;
Edouard@1912: 		}
Edouard@1912: 	}
Edouard@1912: 
Edouard@1912: 	/* init each client connection to remote modbus server, and launch thread */
Edouard@1912: 	/* NOTE: All client_nodes[].init_state are initialised to 0 in the code 
Edouard@1912: 	 *       generated by the modbus plugin 
Edouard@1912: 	 */
Edouard@1912: 	for (index=0; index < NUMBER_OF_CLIENT_NODES;index++){
Edouard@1912: 		/* establish client connection */
Edouard@1912: 		client_nodes[index].mb_nd = mb_master_connect (client_nodes[index].node_address);
Edouard@1912: 		if (client_nodes[index].mb_nd < 0){
Edouard@1912: 			fprintf(stderr, "Modbus plugin: Error creating modbus client node %%s\n", client_nodes[index].location);
Edouard@1912: 			goto error_exit;
Edouard@1912: 		}
Edouard@1912: 		client_nodes[index].init_state = 1; // we have created the node 
Edouard@1912: 		
msousa@2647: 		/* initialize the mutex variable that will be used by the thread handling the client node */
Edouard@2685:         bzero(&(client_nodes[index].mutex), sizeof(pthread_mutex_t));
msousa@2647:         if (pthread_mutex_init(&(client_nodes[index].mutex), NULL) < 0) {
msousa@2647: 			fprintf(stderr, "Modbus plugin: Error creating mutex for modbus client node %%s\n", client_nodes[index].location);
msousa@2647: 			goto error_exit;                
msousa@2647:         }
msousa@2647: 		client_nodes[index].init_state = 2; // we have created the mutex
msousa@2647: 		
msousa@2647: 		/* initialize the condition variable that will be used by the thread handling the client node */
Edouard@2685:         bzero(&(client_nodes[index].condv), sizeof(pthread_cond_t));
msousa@2647:         if (pthread_cond_init(&(client_nodes[index].condv), NULL) < 0) {
msousa@2647: 			fprintf(stderr, "Modbus plugin: Error creating condition variable for modbus client node %%s\n", client_nodes[index].location);
msousa@2647: 			goto error_exit;                
msousa@2647:         }
msousa@2647:         client_nodes[index].execute_req = 0; //variable associated with condition variable
msousa@2647: 		client_nodes[index].init_state = 3; // we have created the condition variable
msousa@2647: 		
Edouard@2683: 		/* launch a thread to handle this client node timer */
Edouard@2683: 		{
Edouard@2683: 			int res = 0;
Edouard@2683: 			pthread_attr_t attr;
Edouard@2683: 			res |= pthread_attr_init(&attr);
Edouard@2683: 			res |= pthread_create(&(client_nodes[index].timer_thread_id), &attr, &__mb_client_timer_thread, (void *)((char *)NULL + index));
Edouard@2683: 			if (res !=  0) {
Edouard@2683: 				fprintf(stderr, "Modbus plugin: Error starting timer thread for modbus client node %%s\n", client_nodes[index].location);
Edouard@2683: 				goto error_exit;
Edouard@2683: 			}
Edouard@2683: 		}
msousa@2647:         client_nodes[index].init_state = 4; // we have created the timer
msousa@2647: 
Edouard@1912: 		/* launch a thread to handle this client node */
Edouard@1912: 		{
Edouard@1912: 			int res = 0;
Edouard@1912: 			pthread_attr_t attr;
Edouard@1912: 			res |= pthread_attr_init(&attr);
Edouard@1912: 			res |= pthread_create(&(client_nodes[index].thread_id), &attr, &__mb_client_thread, (void *)((char *)NULL + index));
Edouard@1912: 			if (res !=  0) {
msousa@2647: 				fprintf(stderr, "Modbus plugin: Error starting thread for modbus client node %%s\n", client_nodes[index].location);
Edouard@1912: 				goto error_exit;
Edouard@1912: 			}
Edouard@1912: 		}
msousa@2647: 		client_nodes[index].init_state = 5; // we have created the thread
Edouard@1912: 	}
Edouard@1912: 
Edouard@1912: 	/* init each local server */
Edouard@1912: 	/* NOTE: All server_nodes[].init_state are initialised to 0 in the code 
Edouard@1912: 	 *       generated by the modbus plugin 
Edouard@1912: 	 */
Edouard@1912: 	for (index=0; index < NUMBER_OF_SERVER_NODES;index++){
Edouard@1912: 		/* create the modbus server */
Edouard@1912: 		server_nodes[index].mb_nd = mb_slave_new (server_nodes[index].node_address);
Edouard@1912: 		if (server_nodes[index].mb_nd < 0){
Edouard@1912: 			fprintf(stderr, "Modbus plugin: Error creating modbus server node %%s\n", server_nodes[index].location);
Edouard@1912: 			goto error_exit;
Edouard@1912: 		}
Edouard@1912: 		server_nodes[index].init_state = 1; // we have created the node
Edouard@1912: 		
Edouard@1912: 		/* launch a thread to handle this server node */
Edouard@1912: 		{
Edouard@1912: 			int res = 0;
Edouard@1912: 			pthread_attr_t attr;
Edouard@1912: 			res |= pthread_attr_init(&attr);
Edouard@1912: 			res |= pthread_create(&(server_nodes[index].thread_id), &attr, &__mb_server_thread, (void *)&(server_nodes[index]));
Edouard@1912: 			if (res !=  0) {
Edouard@1912: 				fprintf(stderr, "Modbus plugin: Error starting modbus server thread for node %%s\n", server_nodes[index].location);
Edouard@1912: 				goto error_exit;
Edouard@1912: 			}
Edouard@1912: 		}
Edouard@1912: 		server_nodes[index].init_state = 2; // we have created the node and thread
Edouard@1912: 	}
Edouard@1912: 
Edouard@1912: 	return 0;
Edouard@1912: 	
Edouard@1912: error_exit:
Edouard@1912: 	__cleanup_%(locstr)s ();
Edouard@1912: 	return -1;
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: void __publish_%(locstr)s (){
Edouard@1912: 	int index;
Edouard@1912: 
Edouard@1912: 	for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){
msousa@2647: 		/* synchronize the PLC and MB buffers only for the output requests */
Edouard@1912: 		if (client_requests[index].req_type == req_output){
msousa@2647:             
msousa@2647:             // lock the mutex brefore copying the data
Edouard@2011: 			if(pthread_mutex_trylock(&(client_requests[index].coms_buf_mutex)) == 0){
msousa@2647:                 
msousa@2647:                 // Check if user configured this MB request to be activated whenever the data to be written changes
msousa@2647:                 if (client_requests[index].write_on_change) {
msousa@2647:                     // Let's check if the data did change...
msousa@2647:                     // compare the data in plcv_buffer to coms_buffer
msousa@2647:                     int res;
msousa@2647:                     res = memcmp((void *)client_requests[index].coms_buffer /* buf 1 */,
msousa@2647:                                  (void *)client_requests[index].plcv_buffer /* buf 2*/,
msousa@2647:                                  REQ_BUF_SIZE * sizeof(u16) /* size in bytes */);
msousa@2647:                     
msousa@2647:                     // if data changed, activate execution request 
msousa@2647:                     if (0 != res)
msousa@2647:                         client_requests[index].flag_exec_req = 1;
msousa@2647:                 }
msousa@2647:                 
Edouard@2011:                 // copy from plcv_buffer to coms_buffer
Edouard@2011:                 memcpy((void *)client_requests[index].coms_buffer /* destination */,
Edouard@2011:                        (void *)client_requests[index].plcv_buffer /* source */,
Edouard@2011:                        REQ_BUF_SIZE * sizeof(u16) /* size in bytes */);
Edouard@2011:                 pthread_mutex_unlock(&(client_requests[index].coms_buf_mutex));
Edouard@2011:             }
Edouard@1912: 		}
msousa@2647:         /* if the user program set the execution request flag, then activate the thread
msousa@2647:          *  that handles this Modbus client transaction so it gets a chance to be executed
msousa@2647:          *  (but don't activate the thread if it has already been activated!)
msousa@2647:          * 
msousa@2647:          * NOTE that we do this, for both the IN and OUT mapped location, under this
msousa@2647:          *  __publish_() function. The scan cycle of the PLC works as follows:
msousa@2647:          *   - call __retrieve_()
msousa@2647:          *   - execute user programs
msousa@2647:          *   - call __publish_()
msousa@2647:          *   - insert <delay> until time to start next periodic/cyclic scan cycle
msousa@2647:          * 
msousa@2647:          *  In an attempt to be able to run the MB transactions during the <delay>
msousa@2647:          *  interval in which not much is going on, we handle the user program
msousa@2647:          *  requests to execute a specific MB transaction in this __publish_()
msousa@2647:          *  function.
msousa@2647:          */
msousa@2647:         if ((client_requests[index].flag_exec_req != 0) && (0 == client_requests[index].flag_exec_started)) {
msousa@2647:             int client_node_id = client_requests[index].client_node_id;
msousa@2688:             
msousa@2688:              /* We TRY to signal the client thread.
msousa@2688:               * We do this because this function can be called at the end of the PLC scan cycle
msousa@2688:               * and we don't want it to block at that time.
msousa@2688:               */
msousa@2688:              if (pthread_mutex_trylock(&(client_nodes[client_node_id].mutex)) == 0) {
msousa@2688:                  client_nodes[client_node_id].execute_req = 1; // tell the thread to execute
msousa@2688:                  pthread_cond_signal (&(client_nodes[client_node_id].condv));
msousa@2688:                  pthread_mutex_unlock(&(client_nodes[client_node_id].mutex));
msousa@2688:                  /* - upon success, set flag_exec_started
msousa@2688:                   * - both flags (flag_exec_req and flag_exec_started) will be reset
msousa@2688:                   *   once the transaction has completed.
msousa@2688:                   */
msousa@2688:                  client_requests[index].flag_exec_started = 1;    
msousa@2688:              } else {
msousa@2688:                  /* The mutex is locked => the client thread is currently executing MB transactions.
msousa@2688:                   * We will try to activate it in the next PLC cycle...
msousa@2688:                   * For now, do nothing.
msousa@2688:                   */
msousa@2688:              }
msousa@2647:         }                    
msousa@2647:     }
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: void __retrieve_%(locstr)s (){
Edouard@1912: 	int index;
Edouard@1912: 
Edouard@1912: 	for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){
Edouard@1912: 		/*just do the input requests */
Edouard@1912: 		if (client_requests[index].req_type == req_input){
Edouard@2011: 			if(pthread_mutex_trylock(&(client_requests[index].coms_buf_mutex)) == 0){
Edouard@2011:                 // copy from coms_buffer to plcv_buffer
Edouard@2011:                 memcpy((void *)client_requests[index].plcv_buffer /* destination */,
Edouard@2011:                        (void *)client_requests[index].coms_buffer /* source */,
Edouard@2011:                        REQ_BUF_SIZE * sizeof(u16) /* size in bytes */);
Edouard@2011:                 pthread_mutex_unlock(&(client_requests[index].coms_buf_mutex));
Edouard@2011:             }
Edouard@1912: 		}
Edouard@1912: 	}
Edouard@1912: }
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: 
Edouard@1912: int __cleanup_%(locstr)s (){
Edouard@1912: 	int index, close;
Edouard@1912: 	int res = 0;
Edouard@1912: 
Edouard@1912: 	/* kill thread and close connections of each modbus client node */
Edouard@1912: 	for (index=0; index < NUMBER_OF_CLIENT_NODES; index++) {
Edouard@1912: 		close = 0;
msousa@2647: 		if (client_nodes[index].init_state >= 5) {
Edouard@1912: 			// thread was launched, so we try to cancel it!
Edouard@1912: 			close  = pthread_cancel(client_nodes[index].thread_id);
Edouard@1912: 			close |= pthread_join  (client_nodes[index].thread_id, NULL);
Edouard@1912: 			if (close < 0)
msousa@2647: 				fprintf(stderr, "Modbus plugin: Error closing thread for modbus client node %%s\n", client_nodes[index].location);
msousa@2647: 		}
msousa@2647: 		res |= close;
msousa@2647: 
msousa@2647: 		close = 0;
msousa@2647: 		if (client_nodes[index].init_state >= 4) {
msousa@2687: 			// timer thread was launched, so we try to cancel it!
msousa@2687: 			close  = pthread_cancel(client_nodes[index].timer_thread_id);
Edouard@2683: 			close |= pthread_join  (client_nodes[index].timer_thread_id, NULL);
msousa@2647: 			if (close < 0)
Edouard@2683: 				fprintf(stderr, "Modbus plugin: Error closing timer thread for modbus client node %%s\n", client_nodes[index].location);
Edouard@2683: 
msousa@2647: 		}
msousa@2647: 		res |= close;
msousa@2647: 
msousa@2647: 		close = 0;
msousa@2647: 		if (client_nodes[index].init_state >= 3) {
msousa@2647: 			// condition variable was created, so we try to destroy it!
msousa@2647: 			close  = pthread_cond_destroy(&(client_nodes[index].condv));
msousa@2647: 			if (close < 0)
msousa@2647: 				fprintf(stderr, "Modbus plugin: Error destroying condition variable for modbus client node %%s\n", client_nodes[index].location);
msousa@2647: 		}
msousa@2647: 		res |= close;
msousa@2647: 
msousa@2647: 		close = 0;
msousa@2647: 		if (client_nodes[index].init_state >= 2) {
msousa@2647: 			// mutex was created, so we try to destroy it!
msousa@2647: 			close  = pthread_mutex_destroy(&(client_nodes[index].mutex));
msousa@2647: 			if (close < 0)
msousa@2647: 				fprintf(stderr, "Modbus plugin: Error destroying mutex for modbus client node %%s\n", client_nodes[index].location);
Edouard@1912: 		}
Edouard@1912: 		res |= close;
Edouard@1912: 
Edouard@1912: 		close = 0;
Edouard@1912: 		if (client_nodes[index].init_state >= 1) {
Edouard@1912: 			// modbus client node was created, so we try to close it!
Edouard@1912: 			close = mb_master_close (client_nodes[index].mb_nd);
Edouard@1912: 			if (close < 0){
Edouard@1912: 				fprintf(stderr, "Modbus plugin: Error closing modbus client node %%s\n", client_nodes[index].location);
Edouard@1912: 				// We try to shut down as much as possible, so we do not return noW!
Edouard@1912: 			}
Edouard@1912: 			client_nodes[index].mb_nd = -1;
Edouard@1912: 		}
Edouard@1912: 		res |= close;
Edouard@1912: 		client_nodes[index].init_state = 0;
Edouard@1912: 	}
Edouard@1912: 	
msousa@2687: //fprintf(stderr, "Modbus plugin: __cleanup_%%s()  5  close=%%d   res=%%d\n", client_nodes[index].location, close, res);
Edouard@1912: 	/* kill thread and close connections of each modbus server node */
Edouard@1912: 	for (index=0; index < NUMBER_OF_SERVER_NODES; index++) {
Edouard@1912: 		close = 0;
Edouard@1912: 		if (server_nodes[index].init_state >= 2) {
Edouard@1912: 			// thread was launched, so we try to cancel it!
Edouard@1912: 			close  = pthread_cancel(server_nodes[index].thread_id);
Edouard@1912: 			close |= pthread_join  (server_nodes[index].thread_id, NULL);
Edouard@1912: 			if (close < 0)
Edouard@1912: 				fprintf(stderr, "Modbus plugin: Error closing thread for modbus server %%s\n", server_nodes[index].location);
Edouard@1912: 		}
Edouard@1912: 		res |= close;
Edouard@1912: 
Edouard@1912: 		close = 0;
Edouard@1912: 		if (server_nodes[index].init_state >= 1) {
Edouard@1912: 			// modbus server node was created, so we try to close it!
Edouard@1912: 			close = mb_slave_close (server_nodes[index].mb_nd);
Edouard@1912: 			if (close < 0) {
Edouard@1912: 				fprintf(stderr, "Modbus plugin: Error closing node for modbus server %%s (%%d)\n", server_nodes[index].location, server_nodes[index].mb_nd);
Edouard@1912: 				// We try to shut down as much as possible, so we do not return noW!
Edouard@1912: 			}
Edouard@1912: 			server_nodes[index].mb_nd = -1;
Edouard@1912: 		}
Edouard@1912: 		res |= close;
Edouard@1912: 		server_nodes[index].init_state = 0;
Edouard@1912: 	}
Edouard@1912: 
Edouard@1912: 	/* destroy the mutex of each client request */
Edouard@1912: 	for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++) {
Edouard@1912: 		if (pthread_mutex_destroy(&(client_requests[index].coms_buf_mutex))) {
Edouard@1912: 			fprintf(stderr, "Modbus plugin: Error destroying request for modbus client node %%s\n", client_nodes[client_requests[index].client_node_id].location);
Edouard@1912: 			// We try to shut down as much as possible, so we do not return noW!
Edouard@1912: 			res |= -1;
Edouard@1912: 		}
Edouard@1912: 	}
Edouard@1912: 
Edouard@1912: 	/* modbus library close */
Edouard@1912: 	//fprintf(stderr, "Shutting down modbus library...\n");
Edouard@1912: 	if (mb_slave_and_master_done()<0) {
Edouard@1912: 		fprintf(stderr, "Modbus plugin: Error shutting down modbus library\n");
Edouard@1912: 		res |= -1;
Edouard@1912: 	}
Edouard@1912: 
Edouard@1912: 	return res;
Edouard@1912: }
Edouard@1912: 
msousa@2654: 
msousa@2654: 
msousa@2654: 
msousa@2654: 
msousa@2654: /**********************************************/
msousa@2654: /** Functions for Beremiz web interface.     **/
msousa@2654: /**********************************************/
msousa@2654: 
msousa@2654: /*
msousa@2654:  * Beremiz has a program to run on the PLC (Beremiz_service.py)
msousa@2654:  * to handle downloading of compiled programs, start/stop of PLC, etc.
msousa@2654:  * (see runtime/PLCObject.py for start/stop, loading, ...)
msousa@2654:  * 
msousa@2654:  * This service also includes a web server to access PLC state (start/stop)
msousa@2654:  * and to change some basic confiuration parameters.
msousa@2654:  * (see runtime/NevowServer.py for the web server)
msousa@2654:  * 
msousa@2654:  * The web server allows for extensions, where additional configuration
msousa@2654:  * parameters may be changed on the running/downloaded PLC.
msousa@2654:  * Modbus plugin also comes with an extension to the web server, through
msousa@2654:  * which the basic Modbus plugin configuration parameters may be changed
msousa@2654:  * 
msousa@2654:  * These parameters are changed _after_ the code (.so file) is loaded into 
msousa@2654:  * memmory. These changes may be applied before (or after) the code starts
msousa@2654:  * running (i.e. before or after __init_() ets called)! 
msousa@2654:  * 
msousa@2654:  * The following functions are never called from other C code. They are 
msousa@2654:  * called instead from the python code in runtime/Modbus_config.py, that
msousa@2654:  * implements the web server extension for configuring Modbus parameters.
msousa@2654:  */
msousa@2654: 
msousa@2654: 
msousa@2654: /* The number of Cient nodes (i.e. the number of entries in the client_nodes array)
msousa@2654:  * The number of Server nodes (i.e. the numb. of entries in the server_nodes array)
msousa@2654:  * 
msousa@2654:  * These variables are also used by the Modbus web config code to determine 
msousa@2654:  * whether the current loaded PLC includes the Modbus plugin
msousa@2654:  * (so it should make the Modbus parameter web interface visible to the user).
msousa@2654:  */
msousa@2654: const int __modbus_plugin_client_node_count = NUMBER_OF_CLIENT_NODES;
msousa@2654: const int __modbus_plugin_server_node_count = NUMBER_OF_SERVER_NODES;
msousa@2654: const int __modbus_plugin_param_string_size = MODBUS_PARAM_STRING_SIZE;
msousa@2654: 
msousa@2654: 
msousa@2654: 
msousa@2654: /* NOTE: We could have the python code in runtime/Modbus_config.py
msousa@2654:  *       directly access the server_node_t and client_node_t structures,
msousa@2654:  *       however this would create a tight coupling between these two
msousa@2654:  *       disjoint pieces of code.
msousa@2654:  *       Any change to the server_node_t or client_node_t structures would
msousa@2654:  *       require the python code to be changed accordingly. I have therefore 
msousa@2654:  *       opted to create get/set functions, one for each parameter.
msousa@2654:  * 
msousa@2654:  *       We also convert the enumerated constants naf_ascii, etc...
msousa@2654:  *       (from node_addr_family_t in modbus/mb_addr.h)
msousa@2654:  *       into strings so as to decouple the python code that will be calling
msousa@2654:  *       these functions from the Modbus library code definitions.
msousa@2654:  */
msousa@2654: const char *addr_type_str[] = {
msousa@2654:         [naf_ascii] = "ascii",
msousa@2654:         [naf_rtu  ] = "rtu",
msousa@2654:         [naf_tcp  ] = "tcp"    
msousa@2654: };
msousa@2654: 
msousa@2654: 
msousa@2654: #define __safe_strcnpy(str_dest, str_orig, max_size) {  \
msousa@2654:     strncpy(str_dest, str_orig, max_size);              \
msousa@2654:     str_dest[max_size - 1] = '\0';                      \
msousa@2654: }
msousa@2654: 
msousa@2654: 
msousa@2654: /* NOTE: The host, port and device parameters are strings that may be changed 
msousa@2654:  *       (by calling the following functions) after loading the compiled code 
msousa@2654:  *       (.so file) into memory, but before the code starts running
msousa@2654:  *       (i.e. before __init_() gets called).
msousa@2654:  *       This means that the host, port and device parameters may be changed
msousa@2654:  *       _before_ they get mapped onto the str1 and str2 variables by __init_(),
msousa@2654:  *       which is why the following functions must access the str1 and str2 
msousa@2654:  *       parameters directly.
msousa@2654:  */
msousa@2654: const char *       __modbus_get_ClientNode_config_name(int nodeid)  {return client_nodes[nodeid].config_name;                    }
msousa@2654: const char *       __modbus_get_ClientNode_host       (int nodeid)  {return client_nodes[nodeid].str1;                           }
msousa@2654: const char *       __modbus_get_ClientNode_port       (int nodeid)  {return client_nodes[nodeid].str2;                           }
msousa@2654: const char *       __modbus_get_ClientNode_device     (int nodeid)  {return client_nodes[nodeid].str1;                           }
msousa@2654: int                __modbus_get_ClientNode_baud       (int nodeid)  {return client_nodes[nodeid].node_address.addr.rtu.baud;     }
msousa@2654: int                __modbus_get_ClientNode_parity     (int nodeid)  {return client_nodes[nodeid].node_address.addr.rtu.parity;   }
msousa@2654: int                __modbus_get_ClientNode_stop_bits  (int nodeid)  {return client_nodes[nodeid].node_address.addr.rtu.stop_bits;}
msousa@2654: u64                __modbus_get_ClientNode_comm_period(int nodeid)  {return client_nodes[nodeid].comm_period;                    }
msousa@2654: const char *       __modbus_get_ClientNode_addr_type  (int nodeid)  {return addr_type_str[client_nodes[nodeid].node_address.naf];}
msousa@2654:                                                                                                                         
msousa@2654: const char *       __modbus_get_ServerNode_config_name(int nodeid)  {return server_nodes[nodeid].config_name;                    }
msousa@2665: const char *       __modbus_get_ServerNode_host       (int nodeid)  {char*x=server_nodes[nodeid].str1; return (x[0]=='\0'?"#ANY#":x); }
msousa@2655: const char *       __modbus_get_ServerNode_port       (int nodeid)  {return server_nodes[nodeid].str2;                           }
msousa@2655: const char *       __modbus_get_ServerNode_device     (int nodeid)  {return server_nodes[nodeid].str1;                           }
msousa@2654: int                __modbus_get_ServerNode_baud       (int nodeid)  {return server_nodes[nodeid].node_address.addr.rtu.baud;     }
msousa@2654: int                __modbus_get_ServerNode_parity     (int nodeid)  {return server_nodes[nodeid].node_address.addr.rtu.parity;   }
msousa@2654: int                __modbus_get_ServerNode_stop_bits  (int nodeid)  {return server_nodes[nodeid].node_address.addr.rtu.stop_bits;}
msousa@2655: u8                 __modbus_get_ServerNode_slave_id   (int nodeid)  {return server_nodes[nodeid].slave_id;                       }
msousa@2654: const char *       __modbus_get_ServerNode_addr_type  (int nodeid)  {return addr_type_str[server_nodes[nodeid].node_address.naf];}
msousa@2654: 
msousa@2654: 
msousa@2654: void __modbus_set_ClientNode_host       (int nodeid, const char * value)  {__safe_strcnpy(client_nodes[nodeid].str1, value, MODBUS_PARAM_STRING_SIZE);}
msousa@2654: void __modbus_set_ClientNode_port       (int nodeid, const char * value)  {__safe_strcnpy(client_nodes[nodeid].str2, value, MODBUS_PARAM_STRING_SIZE);}
msousa@2654: void __modbus_set_ClientNode_device     (int nodeid, const char * value)  {__safe_strcnpy(client_nodes[nodeid].str1, value, MODBUS_PARAM_STRING_SIZE);}
msousa@2654: void __modbus_set_ClientNode_baud       (int nodeid, int          value)  {client_nodes[nodeid].node_address.addr.rtu.baud      = value;}
msousa@2654: void __modbus_set_ClientNode_parity     (int nodeid, int          value)  {client_nodes[nodeid].node_address.addr.rtu.parity    = value;}
msousa@2654: void __modbus_set_ClientNode_stop_bits  (int nodeid, int          value)  {client_nodes[nodeid].node_address.addr.rtu.stop_bits = value;}
msousa@2654: void __modbus_set_ClientNode_comm_period(int nodeid, u64          value)  {client_nodes[nodeid].comm_period                     = value;}
msousa@2654:                                                                                                                         
msousa@2654: 
msousa@2665: void __modbus_set_ServerNode_host       (int nodeid, const char * value)  {if (strcmp(value,"#ANY#")==0) value = "";
msousa@2665:                                                                            __safe_strcnpy(server_nodes[nodeid].str1, value, MODBUS_PARAM_STRING_SIZE);}
msousa@2655: void __modbus_set_ServerNode_port       (int nodeid, const char * value)  {__safe_strcnpy(server_nodes[nodeid].str2, value, MODBUS_PARAM_STRING_SIZE);}
msousa@2655: void __modbus_set_ServerNode_device     (int nodeid, const char * value)  {__safe_strcnpy(server_nodes[nodeid].str1, value, MODBUS_PARAM_STRING_SIZE);}
msousa@2655: void __modbus_set_ServerNode_baud       (int nodeid, int          value)  {server_nodes[nodeid].node_address.addr.rtu.baud      = value;}
msousa@2655: void __modbus_set_ServerNode_parity     (int nodeid, int          value)  {server_nodes[nodeid].node_address.addr.rtu.parity    = value;}
msousa@2655: void __modbus_set_ServerNode_stop_bits  (int nodeid, int          value)  {server_nodes[nodeid].node_address.addr.rtu.stop_bits = value;}
msousa@2655: void __modbus_set_ServerNode_slave_id   (int nodeid, u8           value)  {server_nodes[nodeid].slave_id                        = value;}
msousa@2655: