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 |