/*** This sample program shows how to send volumes of data to AFNI through a TCP/IP socket, using the NIML interface. RWCox - Jul 2009 ***/ #include "mrilib.h" #if 0 /* ZSS June 2011, replaced by get_port_named() */ #define AFNI_NIML_PORT 53212 /* TCP/IP port that AFNI uses */ #endif NI_stream NF_stream = (NI_stream)NULL ; /*=============================================================================*/ void NF_exit(void) /* Function to be called to make sure */ { /* the AFNI data channel gets closed. */ fprintf(stderr,"*** niml_feedme exits: closing socket to AFNI\n") ; NI_stream_close(NF_stream) ; return ; } /*-----------------------------------------------------------------------------*/ #include void NF_sigfunc(int sig) /** signal handler for fatal errors **/ { char *sname ; static volatile int fff=0 ; if( fff ) _exit(1) ; else fff = 1 ; switch(sig){ default: sname = "unknown" ; break ; case SIGINT: sname = "SIGINT" ; break ; case SIGPIPE: sname = "SIGPIPE" ; break ; case SIGSEGV: sname = "SIGSEGV" ; break ; case SIGBUS: sname = "SIGBUS" ; break ; case SIGTERM: sname = "SIGTERM" ; break ; } fprintf(stderr,"\n*** Fatal Signal %d (%s) received\n",sig,sname) ; exit(1) ; } /*-----------------------------------------------------------------------------*/ int main( int argc , char *argv[] ) { char *drive_afni[128] ; int ndrive=0 , iarg=1 ; char host[1024]="localhost", nsname[2048], *geomstr, *cpt, temp[32] ; int dt=1000 , ctold,ctnew , ctzero ; int verbose=0 , kk,nn , nvox,nval , do_accum=0 ; THD_3dim_dataset *dset ; NI_element *nel ; MRI_IMAGE *fim ; float *far ; char *targname="niml_feedme" ; /*-- help the ignorant user --*/ if( argc < 2 || strcmp(argv[1],"-help") == 0 ){ printf( "Usage: niml_feedme [options] dataset\n" "\n" "* Sends volumes from the dataset to AFNI via the NIML socket interface.\n" "* You must run AFNI with the command 'afni -niml' so that the program\n" " will be listening for the socket connection.\n" "* Inside AFNI, the transmitted dataset will be named 'niml_feedme'.\n" "* For another way to send image data to AFNI, see program rtfeedme.\n" "* At present, there is no way to attach statistical parameters to\n" " a transmitted volume.\n" "* This program sends all volumes in float format, simply because\n" " that's easy for me. But you can also send byte, short, and\n" " complex valued volumes.\n" "* This program is really just a demo; it has little practical use.\n" "\n" "OPTIONS:\n" " -host sname = Send data, via TCP/IP, to AFNI running on the\n" " computer system 'sname'. By default, uses the\n" " current system (localhost), if you don't use this\n" " option.\n" "\n" " -dt ms = Tries to maintain an inter-transmit interval of 'ms'\n" " milliseconds. The default is 1000 msec per volume.\n" "\n" " -verb = Be (very) talkative about actions.\n" "\n" " -accum = Send sub-bricks so that they accumulate in AFNI.\n" " The default is to create only a 1 volume dataset\n" " inside AFNI, and each sub-brick just replaces\n" " that one volume when it is received.\n" "\n" " -target nam = Change the dataset name transmitted to AFNI from\n" " 'niml_feedme' to 'nam'.\n" "\n" " -drive cmd = Send 'cmd' as a DRIVE_AFNI command.\n" " * If cmd contains blanks, it must be in 'quotes'.\n" " * Multiple -drive options may be used.\n" " * These commands will be sent to AFNI just after\n" " the first volume is transmitted.\n" " * See file README.driver for a list of commands.\n" "\n" "EXAMPLE: Send volumes from a 3D+time dataset to AFNI:\n" "\n" " niml_feedme -dt 1000 -verb -accum -target Elvis \\\n" " -drive 'OPEN_WINDOW axialimage' \\\n" " -drive 'OPEN_WINDOW axialgraph' \\\n" " -drive 'SWITCH_UNDERLAY Elvis' \\\n" " timeseries+orig\n" "\n" "Author: RW Cox -- July 2009\n" ) ; PRINT_COMPILE_DATE ; exit(0) ; } mainENTRY("niml_feedme") ; /*-- scan arguments --*/ while( iarg < argc && argv[iarg][0] == '-' ){ if( strncmp(argv[iarg],"-target",5) == 0 ){ targname = strdup(argv[++iarg]) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-drive") == 0 ){ drive_afni[ndrive++] = argv[++iarg] ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-host") == 0 ){ strcpy( host , argv[++iarg] ) ; iarg++ ; continue ; } if( strcmp(argv[iarg],"-dt") == 0 ){ dt = (int)strtod(argv[++iarg],NULL) ; if( dt < 9 ) dt = 9 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-verbose",4) == 0 ){ verbose = 1 ; iarg++ ; continue ; } if( strncmp(argv[iarg],"-accum",4) == 0 ){ do_accum = 1 ; iarg++ ; continue ; } ERROR_exit("Unrecognized option: %s",argv[iarg]) ; } if( iarg >= argc ) ERROR_exit("No dataset on command line?!") ; /*-- read in the dataset --*/ dset = THD_open_dataset( argv[iarg] ) ; if( dset == NULL ) ERROR_exit("Can't open dataset '%s'",argv[iarg]) ; DSET_load(dset) ; if( !DSET_LOADED(dset) ) ERROR_exit("Can't load dataset '%s'",argv[iarg]) ; cpt = EDIT_get_geometry_string(dset) ; geomstr = strdup(cpt) ; /* describes geometry of dataset grid */ if( verbose ) INFO_message("geometry string = '%s'",geomstr) ; nvox = DSET_NVOX(dset); /* number of voxels in dataset */ nval = DSET_NVALS(dset); /* number of sub-bricks in dataset */ /*-- this stuff is one-time-only setup of the I/O to AFNI --*/ atexit(NF_exit) ; /* call this when program ends */ signal(SIGINT ,NF_sigfunc) ; /* setup signal handler */ signal(SIGBUS ,NF_sigfunc) ; /* for fatal errors */ signal(SIGSEGV,NF_sigfunc) ; signal(SIGTERM,NF_sigfunc) ; /* name of NIML stream (socket) to open */ sprintf( nsname , "tcp:%s:%d" , host , get_port_named("AFNI_DEFAULT_LISTEN_NIML")); /* open the socket (i.e., dial the telephone call) */ fprintf(stderr,"opening NIML stream '%s' ",nsname) ; NF_stream = NI_stream_open( nsname , "w" ) ; /* loop until AFNI connects (answers the call), printing a '.' every 1/2 second to keep the user happy */ while(1){ kk = NI_stream_writecheck( NF_stream , 500 ) ; if( kk == 1 ){ fprintf(stderr," connected!\n") ; break ; } if( kk < 0 ){ fprintf(stderr," ** connection fails **\n") ; exit(1) ; } fprintf(stderr,".") ; } /*-- Create VOLUME_DATA NIML element to hold the brick data --*/ nel = NI_new_data_element( "VOLUME_DATA" , nvox ) ; /* add attributes to the element to help AFNI construct the dataset */ /* define the grid of the dataset */ NI_set_attribute( nel , "geometry_string" , geomstr ) ; /* define the name of the dataset */ NI_set_attribute( nel , "target_name" , targname ) ; /* all sub-bricks in the input dataset will be sent to be sub-brick #0 in the dataset inside AFNI -- if you don't want this behavior, and want the dataset inside AFNI to keep growing, then don't set this attribute! */ if( !do_accum ) NI_set_attribute( nel , "index" , "0" ) ; /* +tlrc view? [default in AFNI is +orig view] */ if( dset->view_type == VIEW_TALAIRACH_TYPE ) NI_set_attribute( nel , "view" , "tlrc" ) ; /**-- loop over sub-bricks and send them to AFNI --*/ ctzero = NI_clock_time() ; /* for later reference */ if( verbose ) INFO_message("Starting sub-brick loop") ; for( kk=0 ; kk < nval ; kk++ ){ ctold = NI_clock_time() ; /* clock time at start of work (ms) */ /* get a float copy of the kk-th sub-brick */ fim = THD_extract_float_brick( kk , dset ) ; DSET_unload_one(dset,kk) ; /* unload this sub-brick now */ if( fim == NULL ){ /* should never happen */ ERROR_message("Can't get sub-brick #%d?? -- skipping",kk) ; NI_sleep(dt) ; continue ; } /* copy the float data into the NIML element for transmission */ far = MRI_FLOAT_PTR(fim) ; if( kk == 0 ) /* first time: create data column in element */ NI_add_column( nel , NI_FLOAT , far ) ; else /* later times: overwrite nel data column */ memcpy( nel->vec[0] , far , sizeof(float)*nvox ) ; mri_free(fim) ; /* done with this now [data all copied to nel] */ /* set sub-brick index in AFNI if doing accumulation */ if( do_accum ){ sprintf(temp,"%d",kk) ; NI_set_attribute( nel , "index" , temp ) ; } /*** send the data element to AFNI ***/ nn = NI_write_element( NF_stream , nel , NI_BINARY_MODE ) ; /* if something bad happened in the transmission, report it */ if( nn <= 0 ){ ERROR_message("Can't write sub-brick #%d to AFNI!",kk) ; break ; } /*** first time through ==> do the '-drive' commands now by sending processing instructions ***/ if( kk == 0 && ndrive > 0 ){ int ii ; NI_procins *npi ; if( verbose ) ININFO_message("Sending %d 'drive_afni' elements now",ndrive) ; npi = NI_new_processing_instruction( "DRIVE_AFNI" ) ; NI_sleep(1) ; /* give AFNI a msec to digest the data */ for( ii=0 ; ii < ndrive ; ii++ ){ NI_set_attribute( npi , "cmd" , drive_afni[ii] ) ; (void)NI_write_element( NF_stream , npi , NI_TEXT_MODE ) ; } NI_free_element(npi) ; /* delete this struct from the world! */ } ctnew = NI_clock_time() ; /* clock time now */ if( verbose ) ININFO_message("Sent %d bytes for sub-brick #%d in %d ms", nn , kk , ctnew-ctold ) ; NI_sleep( dt - (ctnew-ctold) ) ; /* sleep so that time delay is right */ } /* end of loop over sub-bricks */ /** summarize, do some cleanup, and exit stage left **/ if( verbose && kk > 0 ){ float dtav = (NI_clock_time()-ctzero) / (float)kk ; INFO_message("Transmission finished: %.1f ms = average time per volume",dtav) ; } NI_free_element(nel) ; /* destroy the data element */ DSET_delete(dset) ; /* destroy the dataset */ exit(0) ; }