1 /***
2   This sample program shows how to send volumes of data to AFNI
3   through a TCP/IP socket, using the NIML interface.  RWCox - Jul 2009
4 ***/
5 
6 #include "mrilib.h"
7 
8 #if 0       /* ZSS June 2011, replaced by get_port_named() */
9    #define AFNI_NIML_PORT 53212            /* TCP/IP port that AFNI uses */
10 #endif
11 NI_stream NF_stream = (NI_stream)NULL ;
12 
13 /*=============================================================================*/
14 
NF_exit(void)15 void NF_exit(void)                   /* Function to be called to make sure */
16 {                                    /* the AFNI data channel gets closed. */
17    fprintf(stderr,"*** niml_feedme exits: closing socket to AFNI\n") ;
18    NI_stream_close(NF_stream) ;
19    return ;
20 }
21 
22 /*-----------------------------------------------------------------------------*/
23 
24 #include <signal.h>
25 
NF_sigfunc(int sig)26 void NF_sigfunc(int sig)   /** signal handler for fatal errors **/
27 {
28    char *sname ;
29    static volatile int fff=0 ;
30    if( fff ) _exit(1) ; else fff = 1 ;
31    switch(sig){
32       default:      sname = "unknown" ; break ;
33       case SIGINT:  sname = "SIGINT"  ; break ;
34       case SIGPIPE: sname = "SIGPIPE" ; break ;
35       case SIGSEGV: sname = "SIGSEGV" ; break ;
36       case SIGBUS:  sname = "SIGBUS"  ; break ;
37       case SIGTERM: sname = "SIGTERM" ; break ;
38    }
39    fprintf(stderr,"\n*** Fatal Signal %d (%s) received\n",sig,sname) ;
40    exit(1) ;
41 }
42 
43 /*-----------------------------------------------------------------------------*/
44 
main(int argc,char * argv[])45 int main( int argc , char *argv[] )
46 {
47    char *drive_afni[128] ;
48    int   ndrive=0 , iarg=1 ;
49 
50    char host[1024]="localhost", nsname[2048], *geomstr, *cpt, temp[32] ;
51    int dt=1000 , ctold,ctnew , ctzero ;
52    int verbose=0 , kk,nn , nvox,nval , do_accum=0 ;
53    THD_3dim_dataset *dset ;
54    NI_element *nel ;
55    MRI_IMAGE *fim ; float *far ;
56    char *targname="niml_feedme" ;
57 
58    /*-- help the ignorant user --*/
59 
60    if( argc < 2 || strcmp(argv[1],"-help") == 0 ){
61       printf(
62         "Usage: niml_feedme [options] dataset\n"
63         "\n"
64         "* Sends volumes from the dataset to AFNI via the NIML socket interface.\n"
65         "* You must run AFNI with the command 'afni -niml' so that the program\n"
66         "  will be listening for the socket connection.\n"
67         "* Inside AFNI, the transmitted dataset will be named 'niml_feedme'.\n"
68         "* For another way to send image data to AFNI, see progam rtfeedme.\n"
69         "* At present, there is no way to attach statistical parameters to\n"
70         "  a transmitted volume.\n"
71         "* This program sends all volumes in float format, simply because\n"
72         "  that's easy for me.  But you can also send byte, short, and\n"
73         "  complex valued volumes.\n"
74         "* This program is really just a demo; it has little practical use.\n"
75         "\n"
76         "OPTIONS:\n"
77         "  -host sname =  Send data, via TCP/IP, to AFNI running on the\n"
78         "                 computer system 'sname'.  By default, uses the\n"
79         "                 current system (localhost), if you don't use this\n"
80         "                 option.\n"
81         "\n"
82         "  -dt ms      =  Tries to maintain an inter-transmit interval of 'ms'\n"
83         "                 milliseconds.  The default is 1000 msec per volume.\n"
84         "\n"
85         "  -verb       =  Be (very) talkative about actions.\n"
86         "\n"
87         "  -accum      =  Send sub-bricks so that they accumulate in AFNI.\n"
88         "                 The default is to create only a 1 volume dataset\n"
89         "                 inside AFNI, and each sub-brick just replaces\n"
90         "                 that one volume when it is received.\n"
91         "\n"
92         "  -target nam =  Change the dataset name transmitted to AFNI from\n"
93         "                 'niml_feedme' to 'nam'.\n"
94         "\n"
95         "  -drive cmd  =  Send 'cmd' as a DRIVE_AFNI command.\n"
96         "                * If cmd contains blanks, it must be in 'quotes'.\n"
97         "                * Multiple -drive options may be used.\n"
98         "                * These commands will be sent to AFNI just after\n"
99         "                  the first volume is transmitted.\n"
100         "                * See file README.driver for a list of commands.\n"
101         "\n"
102         "EXAMPLE: Send volumes from a 3D+time dataset to AFNI:\n"
103         "\n"
104         "  niml_feedme -dt 1000 -verb -accum -target Elvis \\\n"
105         "              -drive 'OPEN_WINDOW axialimage'     \\\n"
106         "              -drive 'OPEN_WINDOW axialgraph'     \\\n"
107         "              -drive 'SWITCH_UNDERLAY Elvis'      \\\n"
108         "              timeseries+orig\n"
109         "\n"
110         "Author: RW Cox -- July 2009\n"
111       ) ;
112       PRINT_COMPILE_DATE ;
113       exit(0) ;
114    }
115 
116    mainENTRY("niml_feedme") ;
117 
118    /*-- scan arguments --*/
119 
120    while( iarg < argc && argv[iarg][0] == '-' ){
121 
122       if( strncmp(argv[iarg],"-target",5) == 0 ){
123         targname = strdup(argv[++iarg]) ; iarg++ ; continue ;
124       }
125 
126       if( strcmp(argv[iarg],"-drive") == 0 ){
127         drive_afni[ndrive++] = argv[++iarg] ; iarg++ ; continue ;
128       }
129 
130       if( strcmp(argv[iarg],"-host") == 0 ){
131         strcpy( host , argv[++iarg] ) ; iarg++ ; continue ;
132       }
133 
134       if( strcmp(argv[iarg],"-dt") == 0 ){
135         dt = (int)strtod(argv[++iarg],NULL) ; if( dt < 9 ) dt = 9 ;
136         iarg++ ; continue ;
137       }
138 
139       if( strncmp(argv[iarg],"-verbose",4) == 0 ){
140         verbose = 1 ; iarg++ ; continue ;
141       }
142 
143       if( strncmp(argv[iarg],"-accum",4) == 0 ){
144         do_accum = 1 ; iarg++ ; continue ;
145       }
146 
147       ERROR_exit("Unrecognized option: %s",argv[iarg]) ;
148    }
149 
150    if( iarg >= argc ) ERROR_exit("No dataset on command line?!") ;
151 
152    /*-- read in the dataset --*/
153 
154    dset = THD_open_dataset( argv[iarg] ) ;
155    if( dset == NULL ) ERROR_exit("Can't open dataset '%s'",argv[iarg]) ;
156    DSET_load(dset) ;
157    if( !DSET_LOADED(dset) ) ERROR_exit("Can't load dataset '%s'",argv[iarg]) ;
158 
159    cpt     = EDIT_get_geometry_string(dset) ;
160    geomstr = strdup(cpt) ;  /* describes geometry of dataset grid */
161 
162    if( verbose ) INFO_message("geometry string = '%s'",geomstr) ;
163 
164    nvox = DSET_NVOX(dset);  /* number of voxels in dataset */
165    nval = DSET_NVALS(dset); /* number of sub-bricks in dataset */
166 
167    /*-- this stuff is one-time-only setup of the I/O to AFNI --*/
168 
169    atexit(NF_exit) ;             /* call this when program ends */
170 
171    signal(SIGINT ,NF_sigfunc) ;  /* setup signal handler */
172    signal(SIGBUS ,NF_sigfunc) ;  /* for fatal errors */
173    signal(SIGSEGV,NF_sigfunc) ;
174    signal(SIGTERM,NF_sigfunc) ;
175 
176    /* name of NIML stream (socket) to open */
177 
178    sprintf( nsname , "tcp:%s:%d" , host , get_port_named("AFNI_DEFAULT_LISTEN_NIML"));
179 
180    /* open the socket (i.e., dial the telephone call) */
181 
182    fprintf(stderr,"opening NIML stream '%s' ",nsname) ;
183    NF_stream = NI_stream_open( nsname , "w" ) ;
184 
185    /* loop until AFNI connects (answers the call),
186       printing a '.' every 1/2 second to keep the user happy */
187 
188    while(1){
189      kk = NI_stream_writecheck( NF_stream , 500 ) ;
190      if( kk == 1 ){ fprintf(stderr," connected!\n") ; break ; }
191      if( kk <  0 ){ fprintf(stderr," ** connection fails **\n") ; exit(1) ; }
192      fprintf(stderr,".") ;
193    }
194 
195    /*-- Create VOLUME_DATA NIML element to hold the brick data --*/
196 
197    nel = NI_new_data_element( "VOLUME_DATA" , nvox ) ;
198 
199    /* add attributes to the element to help AFNI construct the dataset */
200 
201      /* define the grid of the dataset */
202    NI_set_attribute( nel , "geometry_string" , geomstr ) ;
203 
204      /* define the name of the dataset */
205    NI_set_attribute( nel , "target_name"     , targname ) ;
206 
207      /* all sub-bricks in the input dataset will be sent to be
208         sub-brick #0 in the dataset inside AFNI
209         -- if you don't want this behavior, and want the dataset
210            inside AFNI to keep growing, then don't set this attribute! */
211 
212    if( !do_accum ) NI_set_attribute( nel , "index" , "0" ) ;
213 
214      /* +tlrc view?  [default in AFNI is +orig view] */
215    if( dset->view_type == VIEW_TALAIRACH_TYPE )
216      NI_set_attribute( nel , "view" , "tlrc" ) ;
217 
218    /**-- loop over sub-bricks and send them to AFNI --*/
219 
220    ctzero = NI_clock_time() ;  /* for later reference */
221 
222    if( verbose ) INFO_message("Starting sub-brick loop") ;
223 
224    for( kk=0 ; kk < nval ; kk++ ){
225 
226      ctold = NI_clock_time() ;   /* clock time at start of work (ms) */
227 
228      /* get a float copy of the kk-th sub-brick */
229 
230      fim = THD_extract_float_brick( kk , dset ) ;
231 
232      DSET_unload_one(dset,kk) ;  /* unload this sub-brick now */
233 
234      if( fim == NULL ){  /* should never happen */
235        ERROR_message("Can't get sub-brick #%d?? -- skipping",kk) ;
236        NI_sleep(dt) ; continue ;
237      }
238 
239      /* copy the float data into the NIML element for transmission */
240 
241      far = MRI_FLOAT_PTR(fim) ;
242      if( kk == 0 )               /* first time: create data column in element */
243        NI_add_column( nel , NI_FLOAT , far ) ;
244      else                        /* later times: overwrite nel data column */
245        memcpy( nel->vec[0] , far , sizeof(float)*nvox ) ;
246 
247      mri_free(fim) ;  /* done with this now [data all copied to nel] */
248 
249      /* set sub-brick index in AFNI if doing accumulation */
250 
251      if( do_accum ){
252        sprintf(temp,"%d",kk) ; NI_set_attribute( nel , "index" , temp ) ;
253      }
254 
255      /*** send the data element to AFNI ***/
256 
257      nn = NI_write_element( NF_stream , nel , NI_BINARY_MODE ) ;
258 
259      /* if something bad happened in the transmission, report it */
260 
261      if( nn <= 0 ){
262        ERROR_message("Can't write sub-brick #%d to AFNI!",kk) ; break ;
263      }
264 
265      /*** first time through ==>
266           do the '-drive' commands now by sending processing instructions ***/
267 
268      if( kk == 0 && ndrive > 0 ){
269        int ii ; NI_procins *npi ;
270        if( verbose )
271          ININFO_message("Sending %d 'drive_afni' elements now",ndrive) ;
272        npi = NI_new_processing_instruction( "DRIVE_AFNI" ) ;
273        NI_sleep(1) ;    /* give AFNI a msec to digest the data */
274        for( ii=0 ; ii < ndrive ; ii++ ){
275          NI_set_attribute( npi , "cmd" , drive_afni[ii] ) ;
276          (void)NI_write_element( NF_stream , npi , NI_TEXT_MODE ) ;
277        }
278        NI_free_element(npi) ; /* delete this struct from the world! */
279      }
280 
281      ctnew = NI_clock_time() ;  /* clock time now */
282 
283      if( verbose ) ININFO_message("Sent %d bytes for sub-brick #%d in %d ms",
284                                   nn , kk , ctnew-ctold ) ;
285 
286      NI_sleep( dt - (ctnew-ctold) ) ;  /* sleep so that time delay is right */
287 
288    } /* end of loop over sub-bricks */
289 
290    /** summarize, do some cleanup, and exit stage left **/
291 
292    if( verbose && kk > 0 ){
293      float dtav = (NI_clock_time()-ctzero) / (float)kk ;
294      INFO_message("Transmission finished: %.1f ms = average time per volume",dtav) ;
295    }
296 
297    NI_free_element(nel) ;  /* destroy the data element */
298    DSET_delete(dset) ;     /* destroy the dataset */
299 
300    exit(0) ;
301 }
302