BlinkenArea - GitList
Repositories
Blog
Wiki
bluebox
Code
Commits
Branches
Tags
Search
Tree:
fd252ce
Branches
Tags
master
bluebox
blue_dist
main.c
initial commit of files from bluebox project
Stefan Schuermans
commited
fd252ce
at 2015-12-19 20:16:38
main.c
Blame
History
Raw
/* bluebox distributor * version 0.3.2 date 2007-07-18 * Copyright (C) 2006-2007 Stefan Schuermans <stefan@blinkenarea.org> * Copyleft: GNU public license V2.0 - http://www.gnu.org/copyleft/gpl.html * a BlinkenArea project - http://www.blinkenarea.org/ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <errno.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/ioctl.h> #include <netinet/in.h> #include "protocols.h" #include "bd_config.h" #include "bd_fmt.h" //global variables int end = 0; //set to 1 by signal handler to indicate end of program //signal handler to end program static void end_signal( int sig_no ) { //end program end = 1; //keep compiler happy sig_no = 0; } //get current number of milliseconds (warps around) static unsigned int get_ms( ) { struct timeval tv; gettimeofday( &tv, NULL ); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } //main program int main( int arg_cnt, char * * args ) { char * p_config_file; unsigned int in_buffer_len, out_buffer_len, turn_off_buffer_len; unsigned char * p_in_buffer, * * p_out_buffers, * * p_turn_off_buffers; char * p_device_usage; st_bd_fmt * p_bd_fmt; int in_sock_fd, out_sock_fd; struct sockaddr_in addr; fd_set fdset; unsigned int last_recv, cur; struct timeval timeout; unsigned short dev_no, ser_no, pix_no, suffix_no; int cnt, len, i, y, x; unsigned int addr_len; unsigned char * ptr; in_addr_t in_addr; int timeout_detected; //print message printf( "\n" "bluebox distributor\n" "version 0.3.2 date 2007-07-18\n" "Copyright (C) 2006-2007 Stefan Schuermans <stefan@blinkenarea.org>\n" "Copyleft: GNU public license V2.0 - http://www.gnu.org/copyleft/gpl.html\n" "a BlinkenArea project - http://www.blinkenarea.org/\n" "\n" ); //no config file supplied if( arg_cnt < 2 ) { fprintf( stderr, "no config file supplied (syntax: \"%s <config-file>\"), using \"blue_dist.conf\"\n", args[0] ); p_config_file = "blue_dist.conf"; } //config file supplied else p_config_file = args[1]; //read config file and get settings bd_config_get( p_config_file ); printf( "configuration read: %d devices with %d serial ports with %d pixels\n", bd_out_dev_cnt, bd_out_ser_cnt, bd_out_pix_cnt ); //allocate input buffer in_buffer_len = 65536; //maximum size of a datagram p_in_buffer = (unsigned char *)malloc( in_buffer_len ); if( p_in_buffer == NULL ) { fprintf( stderr, "could not allocate buffer (input) of size %d\n\n", in_buffer_len ); return -1; } //allocate output buffers out_buffer_len = (1 + bd_out_pix_cnt + bd_out_suffix_cnt) * bd_out_ser_cnt; //length of output buffer for 1 device len = bd_out_dev_cnt * sizeof( unsigned char * ) //dev_cnt pointers to 1D arrays + bd_out_dev_cnt * out_buffer_len; //dev_cnt output buffers p_out_buffers = (unsigned char * *)malloc( len ); if( p_out_buffers == NULL ) { free( p_in_buffer ); fprintf( stderr, "could not allocate buffer (output) of size %d\n\n", len ); return -1; } //initialize output buffer array structure ptr = (unsigned char *)p_out_buffers + bd_out_dev_cnt * sizeof( unsigned char * ); //array of pointers to 1D arrays for( dev_no = 0; dev_no < bd_out_dev_cnt; dev_no++ ) //output buffers { p_out_buffers[dev_no] = ptr; ptr += out_buffer_len; } //initialize data in output buffers for( dev_no = 0; dev_no < bd_out_dev_cnt; dev_no++ ) { i = 0; for( ser_no = 0; ser_no < bd_out_ser_cnt; ser_no++ ) //command byte p_out_buffers[dev_no][i++] = bd_out_command; for( pix_no = 0; pix_no < bd_out_pix_cnt; pix_no++ ) //pixel values for( ser_no = 0; ser_no < bd_out_ser_cnt; ser_no++ ) p_out_buffers[dev_no][i++] = 0x00; for( suffix_no = 0; suffix_no < bd_out_suffix_cnt; suffix_no++ ) //suffix bytes for( ser_no = 0; ser_no < bd_out_ser_cnt; ser_no++ ) p_out_buffers[dev_no][i++] = bd_out_suffix; } //allocate turn off buffers turn_off_buffer_len = (1 + bd_out_suffix_cnt) * bd_out_ser_cnt; // length of turn off buffer for 1 device len = bd_out_dev_cnt * sizeof( unsigned char * ) //dev_cnt pointers to 1D arrays + bd_out_dev_cnt * turn_off_buffer_len; //dev_cnt turn off buffers p_turn_off_buffers = (unsigned char * *)malloc( len ); if( p_turn_off_buffers == NULL ) { free( p_out_buffers ); free( p_in_buffer ); fprintf( stderr, "could not allocate buffer (turn off) of size %d\n\n", len ); return -1; } //initialize turn off buffer array structure ptr = (unsigned char *)p_turn_off_buffers + bd_out_dev_cnt * sizeof( unsigned char * ); //array of pointers to 1D arrays for( dev_no = 0; dev_no < bd_out_dev_cnt; dev_no++ ) //turn off buffers { p_turn_off_buffers[dev_no] = ptr; ptr += turn_off_buffer_len; } //initialize data in turn off buffers for( dev_no = 0; dev_no < bd_out_dev_cnt; dev_no++ ) { i = 0; for( ser_no = 0; ser_no < bd_out_ser_cnt; ser_no++ ) p_turn_off_buffers[dev_no][i++] = bd_out_turn_off; //turn off byte for( suffix_no = 0; suffix_no < bd_out_suffix_cnt; suffix_no++ ) //suffix bytes for( ser_no = 0; ser_no < bd_out_ser_cnt; ser_no++ ) p_turn_off_buffers[dev_no][i++] = bd_out_suffix; } //generate format from format file p_bd_fmt = bd_fmt_load( bd_fmt_file ); if( p_bd_fmt == NULL ) { free( p_turn_off_buffers ); free( p_out_buffers ); free( p_in_buffer ); fprintf( stderr, "could not create format from \"%s\"\n\n", bd_fmt_file ); return -1; } printf( "format file read: %dx%d pixels\n", p_bd_fmt->width, p_bd_fmt->height ); //get used devices from format len = bd_out_dev_cnt * sizeof( char ); p_device_usage = (char *)malloc( len ); if( p_device_usage == NULL ) { bd_fmt_free( p_bd_fmt ); free( p_turn_off_buffers ); free( p_out_buffers ); free( p_in_buffer ); fprintf( stderr, "could not allocate buffer (used output devices) of size %d\n\n", len ); return -1; } for( dev_no = 0; dev_no < bd_out_dev_cnt; dev_no++ ) //default: device not used p_device_usage[dev_no] = 0; for( y = 0; y < p_bd_fmt->height; y++ ) //get used devices for( x = 0; x < p_bd_fmt->width; x++ ) p_device_usage[p_bd_fmt->pixels[y][x].dev_no] = 1; //show used devices printf( "used devices:" ); x = -1; y = -1; for( dev_no = 0; dev_no < bd_out_dev_cnt; dev_no++ ) { if( p_device_usage[dev_no] ) { if( y < 0 ) { printf( "%s %d", x >= 0 ? "," : "", dev_no ); x = dev_no; } y = dev_no; } else { if( y >= 0 && y != x ) printf( "..%d", y ); y = -1; } } if( y >= 0 && y != x ) printf( "..%d", y ); printf( "\n" ); //create input socket in_sock_fd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if( in_sock_fd == -1 ) { free( p_device_usage ); bd_fmt_free( p_bd_fmt ); free( p_turn_off_buffers ); free( p_out_buffers ); free( p_in_buffer ); fprintf( stderr, "could not create input socket: error: %s\n\n", strerror( errno ) ); return -1; } //bind input socket addr.sin_family = AF_INET; addr.sin_port = htons( bd_in_listen_port ); addr.sin_addr.s_addr = bd_in_listen_addr; if( bind( in_sock_fd, (struct sockaddr *)&addr, sizeof( addr ) ) == -1 ) { close( in_sock_fd ); free( p_device_usage ); bd_fmt_free( p_bd_fmt ); free( p_turn_off_buffers ); free( p_out_buffers ); free( p_in_buffer ); fprintf( stderr, "could not bind input socket to \"%d.%d.%d.%d:%d\" (udp): error: %s\n\n", ((unsigned char *)&addr.sin_addr.s_addr)[0], ((unsigned char *)&addr.sin_addr.s_addr)[1], ((unsigned char *)&addr.sin_addr.s_addr)[2], ((unsigned char *)&addr.sin_addr.s_addr)[3], ntohs( addr.sin_port ), strerror( errno ) ); return -1; } printf( "input socket listening on \"%d.%d.%d.%d:%d\" (udp)\n", ((unsigned char *)&addr.sin_addr.s_addr)[0], ((unsigned char *)&addr.sin_addr.s_addr)[1], ((unsigned char *)&addr.sin_addr.s_addr)[2], ((unsigned char *)&addr.sin_addr.s_addr)[3], ntohs( addr.sin_port ) ); //create output socket out_sock_fd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if( out_sock_fd == -1 ) { close( in_sock_fd ); free( p_device_usage ); bd_fmt_free( p_bd_fmt ); free( p_turn_off_buffers ); free( p_out_buffers ); free( p_in_buffer ); fprintf( stderr, "could not create output socket: error: %s\n\n", strerror( errno ) ); return -1; } //unblock output socket i = 1; if( ioctl( out_sock_fd, FIONBIO, &i ) == -1 ) { close( out_sock_fd ); close( in_sock_fd ); free( p_device_usage ); bd_fmt_free( p_bd_fmt ); free( p_turn_off_buffers ); free( p_out_buffers ); free( p_in_buffer ); fprintf( stderr, "could not unblock output socket: error: %s\n\n", strerror( errno ) ); return -1; } //bind output socket addr.sin_family = AF_INET; addr.sin_port = htons( bd_out_bind_port ); addr.sin_addr.s_addr = bd_out_bind_addr; if( bind( out_sock_fd, (struct sockaddr *)&addr, sizeof( addr ) ) == -1 ) { close( out_sock_fd ); close( in_sock_fd ); free( p_device_usage ); bd_fmt_free( p_bd_fmt ); free( p_turn_off_buffers ); free( p_out_buffers ); free( p_in_buffer ); fprintf( stderr, "could not bind output socket to \"%d.%d.%d.%d:%d\" (udp): error: %s\n\n", ((unsigned char *)&addr.sin_addr.s_addr)[0], ((unsigned char *)&addr.sin_addr.s_addr)[1], ((unsigned char *)&addr.sin_addr.s_addr)[2], ((unsigned char *)&addr.sin_addr.s_addr)[3], ntohs( addr.sin_port ), strerror( errno ) ); return -1; } printf( "output socket bound to \"%d.%d.%d.%d:%d\" (udp)\n", ((unsigned char *)&addr.sin_addr.s_addr)[0], ((unsigned char *)&addr.sin_addr.s_addr)[1], ((unsigned char *)&addr.sin_addr.s_addr)[2], ((unsigned char *)&addr.sin_addr.s_addr)[3], ntohs( addr.sin_port ) ); //initialize mapping from gayscale values to PWM values printf( "grayscale value to PWM value mapping:\n" " base=%f\n" " spread=%f\n" " gamma=%f\n", bd_map_base, bd_map_spread, bd_map_gamma ); gray2pwm_init( bd_map_base, bd_map_spread, bd_map_gamma ); //install signal handler to end program signal( SIGTERM, end_signal ); signal( SIGINT, end_signal ); signal( SIGHUP, end_signal ); //main loop last_recv = get_ms( ) - bd_in_timeout * 1000; timeout_detected = 1; while( ! end ) { cur = get_ms( ); unsigned int wait_time = cur - last_recv; //detect timeout if( wait_time >= bd_in_timeout * 1000 ) timeout_detected = 1; //detect elapsed timeout interval if( timeout_detected && wait_time >= bd_in_timeout_interval * 1000 ) { //remember this time as time of last reception last_recv = cur; //send turn off commands using output socket if( bd_verbose ) printf( "outputting turn off data to %d devices (length=%d)\n", bd_out_dev_cnt, turn_off_buffer_len ); in_addr = bd_out_ip_base; for( dev_no = 0; dev_no < bd_out_dev_cnt; dev_no++ ) { //send only to used devices if( p_device_usage[dev_no] ) { //assemble destination address addr.sin_family = AF_INET; addr.sin_port = htons( bd_out_dest_port ); addr.sin_addr.s_addr = in_addr; //send if( sendto( out_sock_fd, p_turn_off_buffers[dev_no], turn_off_buffer_len, 0, (struct sockaddr *)&addr, sizeof( addr ) ) != (int)turn_off_buffer_len ) { if( bd_verbose ) fprintf( stderr, "could not send to \"%d.%d.%d.%d:%d\" (udp) (length=%d): error: %s\n", ((unsigned char *)&addr.sin_addr.s_addr)[0], ((unsigned char *)&addr.sin_addr.s_addr)[1], ((unsigned char *)&addr.sin_addr.s_addr)[2], ((unsigned char *)&addr.sin_addr.s_addr)[3], ntohs( addr.sin_port ), turn_off_buffer_len, strerror( errno ) ); } } //if( p_device_usage[dev_no] ) //calculate IP address of next device in_addr += bd_out_ip_step; } //for( dev_no ... } //if( timeout_detected && wait_time >= bd_in_timeout_interval * 1000 ) //wait for reception of a datagram FD_ZERO( &fdset ); FD_SET( in_sock_fd, &fdset ); timeout.tv_sec = 0; unsigned int wait_time_x = cur - last_recv; if( wait_time_x >= bd_in_timeout * 1000 || (timeout_detected && wait_time_x >= bd_in_timeout_interval * 1000) ) timeout.tv_usec = 0; else { unsigned int timeout_ms_to = bd_in_timeout * 1000 - wait_time_x; unsigned int timeout_ms_to_i = bd_in_timeout_interval * 1000 - wait_time_x; unsigned int timeout_ms = timeout_ms_to < timeout_ms_to_i ? timeout_ms_to : timeout_ms_to_i; timeout.tv_usec = timeout_ms < 100 ? timeout_ms * 1000 : 100000; } cnt = select( in_sock_fd + 1, &fdset, NULL, NULL, &timeout ); if( cnt == -1 && errno != EINTR ) { fprintf( stderr, "select returned error: %s\n\n", strerror( errno ) ); break; } //reception of a datagram if( cnt > 0 && FD_ISSET( in_sock_fd, &fdset ) ) { //get datagram addr_len = sizeof( addr ); len = recvfrom( in_sock_fd, p_in_buffer, in_buffer_len, 0, (struct sockaddr *)&addr, &addr_len ); if( len >= 0 ) { if( bd_verbose ) printf( "reception from \"%d.%d.%d.%d:%d\" (udp) (length=%d)\n", ((unsigned char *)&addr.sin_addr.s_addr)[0], ((unsigned char *)&addr.sin_addr.s_addr)[1], ((unsigned char *)&addr.sin_addr.s_addr)[2], ((unsigned char *)&addr.sin_addr.s_addr)[3], ntohs( addr.sin_port ), len ); //remember time of last reception last_recv = get_ms( ); // no timeout timeout_detected = 0; //processs datagram if( proto_datagram( p_in_buffer, (unsigned int)len, p_bd_fmt, p_out_buffers ) ) { //send output data using output socket if( bd_verbose ) printf( "outputting pixel data to %d devices (length=%d)\n", bd_out_dev_cnt, out_buffer_len ); in_addr = bd_out_ip_base; for( dev_no = 0; dev_no < bd_out_dev_cnt; dev_no++ ) { //send only to used devices if( p_device_usage[dev_no] ) { //assemble destination address addr.sin_family = AF_INET; addr.sin_port = htons( bd_out_dest_port ); addr.sin_addr.s_addr = in_addr; //send if( sendto( out_sock_fd, p_out_buffers[dev_no], out_buffer_len, 0, (struct sockaddr *)&addr, sizeof( addr ) ) != (int)out_buffer_len ) { if( bd_verbose ) fprintf( stderr, "could not send to \"%d.%d.%d.%d:%d\" (udp) (length=%d): error: %s\n", ((unsigned char *)&addr.sin_addr.s_addr)[0], ((unsigned char *)&addr.sin_addr.s_addr)[1], ((unsigned char *)&addr.sin_addr.s_addr)[2], ((unsigned char *)&addr.sin_addr.s_addr)[3], ntohs( addr.sin_port ), out_buffer_len, strerror( errno ) ); } } //if( p_device_usage[dev_no] ) //calculate IP address of next device in_addr += bd_out_ip_step; } //for( dev_no ... } //if( proto_datagram( ... } //if( len ... } // if( cnt > 0 ... } //while( ! end ) printf( "terminated\n\n" ); //clean up close( out_sock_fd ); close( in_sock_fd ); free( p_device_usage ); bd_fmt_free( p_bd_fmt ); free( p_turn_off_buffers ); free( p_out_buffers ); free( p_in_buffer ); return 0; }