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