modbus/mb_runtime.c
changeset 2687 c79c5d49ba34
parent 2685 f71c22b2ca25
child 2688 4dd67aa45855
equal deleted inserted replaced
2686:703ebf57508a 2687:c79c5d49ba34
    26 #include <stdio.h>
    26 #include <stdio.h>
    27 #include <string.h>  /* required for memcpy() */
    27 #include <string.h>  /* required for memcpy() */
    28 #include <errno.h>
    28 #include <errno.h>
    29 #include <time.h>
    29 #include <time.h>
    30 #include <signal.h>
    30 #include <signal.h>
       
    31 #include <unistd.h>  /* required for pause() */
    31 #include "mb_slave_and_master.h"
    32 #include "mb_slave_and_master.h"
    32 #include "MB_%(locstr)s.h"
    33 #include "MB_%(locstr)s.h"
    33 
    34 
    34 
    35 
    35 #define MAX_MODBUS_ERROR_CODE 11
    36 #define MAX_MODBUS_ERROR_CODE 11
   326 
   327 
   327 	while (1) {
   328 	while (1) {
   328 		/*
   329 		/*
   329 		struct timespec cur_time;
   330 		struct timespec cur_time;
   330 		clock_gettime(CLOCK_MONOTONIC, &cur_time);
   331 		clock_gettime(CLOCK_MONOTONIC, &cur_time);
   331 		fprintf(stderr, "Modbus client thread - new cycle (%%ld:%%ld)!\n", cur_time.tv_sec, cur_time.tv_nsec);
   332 		fprintf(stderr, "Modbus client thread (%%d) - new cycle (%%ld:%%ld)!\n", client_node_id, cur_time.tv_sec, cur_time.tv_nsec);
   332 		*/
   333 		*/
   333 		int req;
   334 		int req;
   334 		for (req=0; req < NUMBER_OF_CLIENT_REQTS; req ++){
   335 		for (req=0; req < NUMBER_OF_CLIENT_REQTS; req ++){
   335 			/* just do the requests belonging to the client */
   336 			/* just do the requests belonging to the client */
   336 			if (client_requests[req].client_node_id != client_node_id)
   337 			if (client_requests[req].client_node_id != client_node_id)
   343              *     (in which case we execute all the requests belonging to the client node)
   344              *     (in which case we execute all the requests belonging to the client node)
   344              */
   345              */
   345             if ((client_requests[req].flag_exec_req == 0) && (client_nodes[client_requests[req].client_node_id].periodic_act == 0))
   346             if ((client_requests[req].flag_exec_req == 0) && (client_nodes[client_requests[req].client_node_id].periodic_act == 0))
   346                 continue;
   347                 continue;
   347             
   348             
   348             //fprintf(stderr, "Modbus plugin: RUNNING<###> of Modbus request %%d  (periodic = %%d  flag_exec_req = %%d)\n", 
   349             /*
   349             //        req, client_nodes[client_requests[req].client_node_id].periodic_act, client_requests[req].flag_exec_req );
   350             fprintf(stderr, "Modbus client thread (%%d): RUNNING Modbus request %%d  (periodic = %%d  flag_exec_req = %%d)\n", 
       
   351                     client_node_id, req, client_nodes[client_requests[req].client_node_id].periodic_act, client_requests[req].flag_exec_req );
       
   352             */
   350             
   353             
   351 			int res_tmp = __execute_mb_request(req);
   354 			int res_tmp = __execute_mb_request(req);
   352 			switch (res_tmp) {
   355 			switch (res_tmp) {
   353 			  case PORT_FAILURE: {
   356 			  case PORT_FAILURE: {
   354 				if (res_tmp != client_nodes[client_node_id].prev_error)
   357 				if (res_tmp != client_nodes[client_node_id].prev_error)
   473     pthread_mutex_unlock(&(client_nodes[client_node_id].mutex));
   476     pthread_mutex_unlock(&(client_nodes[client_node_id].mutex));
   474 }
   477 }
   475 
   478 
   476 
   479 
   477 
   480 
   478 static int stop_mb_client_timer_thread;
   481 /* Thread that simply implements a periodic 'timer',
       
   482  *  i.e. periodically sends signal to the  thread running __mb_client_thread()
       
   483  * 
       
   484  * Note that we do not use a posix timer (timer_create() ) because there doesn't seem to be a way
       
   485  * of having the timer notify the thread that is portable across Xenomai and POSIX.
       
   486  * - SIGEV_THREAD    : not supported by Xenomai
       
   487  * - SIGEV_THREAD_ID : Linux specific (i.e. non POSIX)
       
   488  *                     Even so, I did not get it to work under Linux (issues with the header files)
       
   489  * - SIGEV_SIGNAL    : Will not work, as signal is sent to random thread in process!
       
   490  */
   479 static void *__mb_client_timer_thread(void *_index) {
   491 static void *__mb_client_timer_thread(void *_index) {
   480     sigset_t set;
       
   481     int signum;
       
   482     sigemptyset(&set);
       
   483     sigaddset(&set, SIGALRM);
       
   484 
       
   485 	int client_node_id = (char *)_index - (char *)NULL; // Use pointer arithmetic (more portable than cast)
   492 	int client_node_id = (char *)_index - (char *)NULL; // Use pointer arithmetic (more portable than cast)
   486     /* initialize the timer that will be used to periodically activate the client node */
   493 	struct timespec next_cycle;
   487     {
       
   488         // start off by reseting the flag that will be set whenever the timer expires
       
   489         client_nodes[client_node_id].periodic_act = 0;
       
   490 
       
   491         if (timer_create(CLOCK_REALTIME, NULL, &(client_nodes[client_node_id].timer_id)) < 0) {
       
   492             fprintf(stderr, "Modbus plugin: Error (%%s) creating timer for modbus client node %%s\n", strerror(errno), client_nodes[client_node_id].location);
       
   493             return NULL;
       
   494         }
       
   495     }
       
   496 
   494 
   497 	int period_sec  =  client_nodes[client_node_id].comm_period / 1000;          /* comm_period is in ms */
   495 	int period_sec  =  client_nodes[client_node_id].comm_period / 1000;          /* comm_period is in ms */
   498 	int period_nsec = (client_nodes[client_node_id].comm_period %%1000)*1000000; /* comm_period is in ms */
   496 	int period_nsec = (client_nodes[client_node_id].comm_period %%1000)*1000000; /* comm_period is in ms */
   499 
   497 
   500 	// configure the timer for periodic activation
   498 	// Enable thread cancelation. Enabled is default, but set it anyway to be safe.
   501     {
   499 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
   502       struct itimerspec timerspec;
   500     
   503       timerspec.it_interval.tv_sec  = period_sec;
   501     if (client_nodes[client_node_id].comm_period <= 0) {
   504       timerspec.it_interval.tv_nsec = period_nsec;
   502         // No periodic activation required => nothing to do! 
   505       timerspec.it_value            = timerspec.it_interval;
   503         while (1) pause(); // wait to be canceled when program terminates (shutdown() is called)
   506       
   504         return NULL;  // not really necessary, just makes it easier to understand the code.
   507       if (timer_settime(client_nodes[client_node_id].timer_id, 0 /* flags */, &timerspec, NULL) < 0)
       
   508         fprintf(stderr, "Modbus plugin: Error configuring periodic activation timer for Modbus client %%s.\n", client_nodes[client_node_id].location);          
       
   509     }
   505     }
   510 
   506 
   511     stop_mb_client_timer_thread = 0;
   507 	// get the current time
   512     while(!stop_mb_client_timer_thread) {
   508 	clock_gettime(CLOCK_MONOTONIC, &next_cycle);
   513         if(sigwait (&set, &signum) == -1)
   509 
   514             perror ("sigwait");
   510     while(1) {
   515 
   511         // Determine absolute time instant for starting the next cycle
   516         if(stop_mb_client_timer_thread)
   512         struct timespec prev_cycle, now;
   517             break;
   513         prev_cycle = next_cycle;
   518 
   514         timespec_add(next_cycle, period_sec, period_nsec);
   519         if(signum == SIGALRM)
   515                 
   520             __client_node_timer_callback_function(client_node_id);
   516         /* NOTE:
   521         else
   517          * It is probably un-necessary to check for overflow of timer!
   522             fprintf(stderr, "Modbus plugin: spurious wakeup of timer thread for Modbus client %%s.\n", client_nodes[client_node_id].location);          
   518          * Even in 32 bit systems this will take at least 68 years since the computer booted
   523 
   519          * (remember, we are using CLOCK_MONOTONIC, which should start counting from 0
       
   520          * every time the system boots). On 64 bit systems, it will take over 
       
   521          * 10^11 years to overflow.
       
   522          */
       
   523         clock_gettime(CLOCK_MONOTONIC, &now);
       
   524         if (next_cycle.tv_sec < prev_cycle.tv_sec) {
       
   525            /* Timer overflow. See NOTE B above */
       
   526             next_cycle = now;
       
   527             timespec_add(next_cycle, period_sec, period_nsec);
       
   528         }
       
   529         
       
   530         while (0 != clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_cycle, NULL));
       
   531         __client_node_timer_callback_function(client_node_id);
   524     }
   532     }
   525 
       
   526     // timer was created, so we try to destroy it!
       
   527     int res = timer_delete(client_nodes[client_node_id].timer_id);
       
   528     if (res < 0)
       
   529         fprintf(stderr, "Modbus plugin: Error destroying timer for modbus client node %%s\n", client_nodes[client_node_id].location);
       
   530 
   533 
   531     return NULL;
   534     return NULL;
   532 }
   535 }
   533 
   536 
   534 
   537 
   792 		}
   795 		}
   793 		res |= close;
   796 		res |= close;
   794 
   797 
   795 		close = 0;
   798 		close = 0;
   796 		if (client_nodes[index].init_state >= 4) {
   799 		if (client_nodes[index].init_state >= 4) {
   797             stop_mb_client_timer_thread = 1;
   800 			// timer thread was launched, so we try to cancel it!
   798             pthread_kill(client_nodes[index].timer_thread_id, SIGALRM);
   801 			close  = pthread_cancel(client_nodes[index].timer_thread_id);
   799 			close |= pthread_join  (client_nodes[index].timer_thread_id, NULL);
   802 			close |= pthread_join  (client_nodes[index].timer_thread_id, NULL);
   800 			if (close < 0)
   803 			if (close < 0)
   801 				fprintf(stderr, "Modbus plugin: Error closing timer thread for modbus client node %%s\n", client_nodes[index].location);
   804 				fprintf(stderr, "Modbus plugin: Error closing timer thread for modbus client node %%s\n", client_nodes[index].location);
   802 
   805 
   803 		}
   806 		}
   833 		}
   836 		}
   834 		res |= close;
   837 		res |= close;
   835 		client_nodes[index].init_state = 0;
   838 		client_nodes[index].init_state = 0;
   836 	}
   839 	}
   837 	
   840 	
       
   841 //fprintf(stderr, "Modbus plugin: __cleanup_%%s()  5  close=%%d   res=%%d\n", client_nodes[index].location, close, res);
   838 	/* kill thread and close connections of each modbus server node */
   842 	/* kill thread and close connections of each modbus server node */
   839 	for (index=0; index < NUMBER_OF_SERVER_NODES; index++) {
   843 	for (index=0; index < NUMBER_OF_SERVER_NODES; index++) {
   840 		close = 0;
   844 		close = 0;
   841 		if (server_nodes[index].init_state >= 2) {
   845 		if (server_nodes[index].init_state >= 2) {
   842 			// thread was launched, so we try to cancel it!
   846 			// thread was launched, so we try to cancel it!