1 /*****************************************************************************
2    Major portions of this software are copyrighted by the Medical College
3    of Wisconsin, 1994-2000, and are released under the Gnu General Public
4    License, Version 2.  See the file README.Copyright for details.
5 ******************************************************************************/
6 
7 #include <math.h>
8 #include "thd_iochan.h"
9 #include "niml.h"
10 #include "afni_environ.h"
11 
12 /***** Global variable determining on which system AFNI runs.  *****/
13 /***** [default is the current system, can be changed by user] *****/
14 
15 static char afni_host[128] = "." ;
16 static char afni_name[128] = "\0" ;
17 static int  afni_port      = 0 ;  /* Init. before parsing command line
18                                     ZSS June 2011 */
19 
20 static int  afni_verbose = 0 ;  /* print out debug info? */
21 
22 static char * url = NULL ;
23 static char * uff = NULL ;
24 
25 /***** Prototype *****/
26 
27 int afni_io(void) ;
28 void handle_tta( float xx , float yy , float zz ) ;
29 
exit_me_baby(void)30 void exit_me_baby( void ){
31    if( uff != NULL ) unlink(uff) ;
32    return ;
33 }
34 
35 #include <signal.h>
sigfunc(int sig)36 void sigfunc(int sig)   /** signal handler for fatal errors **/
37 {
38    exit(1) ;
39 }
40 
41 /*===================================================================
42    Main program:
43      Read command line for options
44      Call afni_io routine forever
45 =====================================================================*/
46 
main(int argc,char * argv[])47 int main( int argc , char * argv[] )
48 {
49    int narg , ii ;
50 
51    (void)AFNI_prefilter_args(&argc,&argv);
52 
53    afni_port = get_port_named("PLUGOUT_TTA_PORT"); /* ZSS June 2011 */
54 
55    /***** See if the pitiful user wants help *****/
56 
57    if( argc == 2 && strncmp(argv[1],"-help",5) == 0 ){
58       printf("Usage: plugout_tta [options]\n"
59              "This program connects to AFNI and receives notification\n"
60              "whenever the user changes Talairach coordinates.\n"
61              "It then drives Netscape to display the closest figures\n"
62              "from the Talairach-Tournoux atlas.  Note that Netscape must\n"
63              "be running on the same computer as this plugout, since it\n"
64              "communicates with Netscape using a temporary URL file.\n"
65              "Options:\n"
66              "  -host name  Means to connect to AFNI running on the\n"
67              "                computer 'name' using TCP/IP.  The default is to\n"
68              "                connect on the current host using shared memory.\n"
69              "                To connect to the current host with TCP/IP, use\n"
70              "                '-host localhost', or use the '-port' option.\n"
71              "              For a list of currently used ports use afni -list_ports\n"
72              "  -v          Verbose mode: prints out progress reports.\n"
73              "  -port pp    Use TCP/IP port number 'pp'; default is %d.\n"
74              "The environment variable AFNI_TTAHOME controls where the atlas\n"
75              "images are loaded from.  If not given, a default value is used.\n"
76              "\n"
77              "To have different plugout_* programs talking to different\n"
78              "AFNI, use the -np* options below\n"
79              "%s\n"
80             , afni_port, get_np_help()) ;
81       exit(0) ;
82    }
83 
84    /* set up to delete the temporary URL file when the program ends */
85 
86    atexit(exit_me_baby) ;
87    signal(SIGINT ,sigfunc) ;
88    signal(SIGBUS ,sigfunc) ;
89    signal(SIGSEGV,sigfunc) ;
90    signal(SIGTERM,sigfunc) ;
91 
92    /***** Process command line options *****/
93 
94    narg = 1 ;
95    while( narg < argc ){
96 
97       /** -host name **/
98 
99       if( strncmp(argv[narg],"-host",5) == 0 ){
100          narg++ ;
101          if( narg >= argc ){
102             fprintf(stderr,"-host needs a following name!\a\n"); exit(1);
103          }
104          strcpy( afni_host , argv[narg] ) ;
105          narg++ ; continue ;
106       }
107 
108       /** -name sss **/
109 
110       if( strncmp(argv[narg],"-name",5) == 0 ){
111          narg++ ;
112          if( narg >= argc ){
113             fprintf(stderr,"-name needs a following string!\a\n"); exit(1);
114          }
115          strcpy( afni_name , argv[narg] ) ;
116          narg++ ; continue ;
117       }
118 
119       /** -v **/
120 
121       if( strncmp(argv[narg],"-v",2) == 0 ){
122          afni_verbose = 1 ;
123          narg++ ; continue ;
124       }
125 
126       /** -port pp **/
127 
128       if( strncmp(argv[narg],"-port",4) == 0 ){
129          narg++ ;
130          if( narg >= argc ){
131             fprintf(stderr,"-port needs a following argument!\a\n"); exit(1);
132          }
133          afni_port = strtol( argv[narg] , NULL , 10 ) ;
134          if( afni_port <= 0 ){
135             fprintf(stderr,"-port needs a positive argument!\a\n"); exit(1);
136          }
137          if( strcmp(afni_host,".") == 0 ) strcpy(afni_host,"localhost") ;
138          narg++ ; continue ;
139       }
140 
141       /** Je ne sais pas **/
142 
143       fprintf(stderr,"Unrecognized option: %s\a\n",argv[narg]) ;
144       exit(1) ;
145    }
146 
147    /***** Loop and check in with AFNI every 100 msec *****/
148 
149    while( 1 ){
150       ii = afni_io() ;        /* commune with AFNI  */
151       if( ii < 0 ) exit(0) ;  /* bad trip? then die */
152       iochan_sleep(100) ;     /* perchance to dream */
153    }
154 
155 }
156 
157 /*===================================================================
158   This routine handles all communications with AFNI.
159   The only input is the global variable afni_host, which determines
160   on which system AFNI is running.
161   The output is -1 if an error occured, 0 if everything is OK.
162 =====================================================================*/
163 
164 /***** Mode flags for determining what afni_io does.
165        The routine progress through 5 stages:
166 
167        1) Open a control connection to AFNI;
168      then
169        2) Wait until AFNI also opens the control connection;
170      then
171        3) Send a control string to AFNI saying what kind of
172             information we want, wait for an acknowledgment,
173             close the control connection, and open a data
174             connection to AFNI;
175      then
176        4) Wait for AFNI to also open the data connection;
177      then
178        5) See if AFNI sends any data, and if so, process it!  *****/
179 
180 #define AFNI_OPEN_CONTROL_MODE  1  /* 1st time thru: open control channel */
181 #define AFNI_WAIT_CONTROL_MODE  2  /* wait for AFNI to open control chan  */
182 #define AFNI_OPEN_DATA_MODE     3  /* now can open data channel to AFNI   */
183 #define AFNI_WAIT_DATA_MODE     4  /* waiting for AFNI to open data chan  */
184 #define AFNI_CONTINUE_MODE      5  /* at last! data channel is ready!     */
185 
186 /***** macros to send acknowledgement strings to AFNI *****/
187 
188 #define POACKSIZE       4  /* length of acknowledgement strings */
189 
190 #define PO_ACK_BAD(ic)  iochan_sendall( (ic) , "BAD" , POACKSIZE )
191 #define PO_ACK_OK(ic)   iochan_sendall( (ic) , "OK!" , POACKSIZE )
192 #define PO_SEND(ic,str) iochan_sendall( (ic) , (str) , strlen((str))+1 )
193 
afni_io(void)194 int afni_io(void)
195 {
196    static int afni_mode = AFNI_OPEN_CONTROL_MODE ;  /* status variable */
197    static IOCHAN * afni_ioc = NULL ;                /* connection to AFNI */
198    int ii ;
199 
200    /***************************************************************/
201    /***** Check to see if status is OK before we proceed.     *****/
202    /***** (if an error occurs below, afni_mode gets set to 0) *****/
203 
204    if( afni_mode <= 0 ) return -1 ;
205 
206    /***********************************************************************/
207    /***** First time into this routine?  Open control channel to AFNI *****/
208 
209    if( afni_mode == AFNI_OPEN_CONTROL_MODE ){
210       char afni_iocname[128] ;           /* will hold name of I/O channel */
211 
212       /** Note that the control channel is always a
213           TCP/IP channel to port #get_port_named("AFNI_PLUGOUT_TCP_0")
214           (used to be #7955) on the AFNI host system **/
215 
216       if( strcmp(afni_host,".") == 0 )
217          sprintf( afni_iocname , "tcp:%s:%d" ,
218             "localhost", get_port_named("AFNI_PLUGOUT_TCP_0") ); /* make name */
219       else
220          sprintf( afni_iocname , "tcp:%s:%d" ,
221             afni_host, get_port_named("AFNI_PLUGOUT_TCP_0") ) ;  /* make name */
222       afni_ioc = iochan_init( afni_iocname , "create" ) ;    /* create it */
223       if( afni_ioc == NULL ){
224          fprintf(stderr,
225                  "Can't create control channel %s to AFNI!\n",afni_iocname) ;
226          afni_mode = 0 ;
227          return -1 ;
228       }
229       afni_mode = AFNI_WAIT_CONTROL_MODE ; /* waiting for AFNI connection */
230       if( afni_verbose )
231          fprintf(stderr,"AFNI control channel created\n") ;
232    }
233 
234    /****************************************************/
235    /**** Check if AFNI control channel is connected ****/
236 
237    if( afni_mode == AFNI_WAIT_CONTROL_MODE ){
238       ii = iochan_writecheck( afni_ioc , 5 ) ;     /* wait at most 5 msec */
239 
240       /** the iochan_*check() routines return
241              -1 for an error,
242               0 if not ready,
243              >0 if the I/O channel is ready. **/
244 
245       if( ii < 0 ){
246          fprintf(stderr,"Control channel to AFNI failed!\a\n") ;
247          IOCHAN_CLOSE(afni_ioc) ;
248          afni_mode = 0 ;
249          return -1 ;
250       } else if( ii > 0 ){
251          afni_mode = AFNI_OPEN_DATA_MODE ;        /* prepare to send data */
252          if( afni_verbose )
253             fprintf(stderr,"AFNI control channel connected\n");
254       } else {
255          return 0 ;                                /* try again next time */
256       }
257    }
258 
259    /**********************************************************/
260    /**** Send control data to AFNI, and open data channel ****/
261 
262    if( afni_mode == AFNI_OPEN_DATA_MODE ){
263       char afni_iocname[128] ;
264       char afni_buf[256] ;
265 
266       /** decide name of data channel:
267             use shared memory (shm:) on ".",
268             use TCP/IP (tcp:) on other computer systems;
269         * Note that the TCP/IP port number can be
270            anything that isn't already in use;
271         * Note that the shm control name (here "test_plugout")
272            is a string that will be converted to an IPC
273            key (in function string_to_key in iochan.c).       **/
274 
275       if( strcmp(afni_host,".") == 0 )
276          strcpy( afni_iocname , "shm:test_plugout:1K+1K" ) ;
277       else
278          sprintf( afni_iocname , "tcp:%s:%d" , afni_host , afni_port ) ;
279 
280       /** write the command to AFNI into the buffer:
281             * each command ends with a newline character,
282                 except (possibly) the last command;
283             * the command buffer is a C string, which ends
284                 with an ASCII NUL character;
285             * TT_XYZ_DELTA means 'send me T-T coordinates when they change';
286             * DSET_IJK_DELTA means 'send me IJK voxel indices when they change';
287             * PONAME means 'use this string for informative messages';
288             * IOCHAN means 'use this I/O channel from now on'. **/
289 
290       if( afni_name[0] == '\0' ) strcpy(afni_name,"T-T-A") ;
291 
292       sprintf( afni_buf , "TT_XYZ_DELTA\n"
293                           "PONAME %s\n"
294                           "IOCHAN %s" ,
295                afni_name , afni_iocname ) ;
296 
297       if( afni_verbose )
298          fprintf(stderr,"Sending control information to AFNI\n") ;
299 
300       /** note that the ASCII NUL at the end of the buffer is sent **/
301 
302       ii = iochan_sendall( afni_ioc , afni_buf , strlen(afni_buf)+1 ) ;
303 
304       /** the return value is the number of bytes sent,
305           or -1 indicating a fatal error transpired.    **/
306 
307       if( ii < 0 ){
308          fprintf(stderr,"Transmission of control data to AFNI failed!\a\n") ;
309          IOCHAN_CLOSE(afni_ioc) ;
310          afni_mode = 0 ;
311          return -1 ;
312 
313       } else {
314 
315          /** wait for the acknowledgment from AFNI, then close channel **/
316 
317          ii = iochan_recvall( afni_ioc , afni_buf , POACKSIZE ) ;
318          IOCHAN_CLOSE(afni_ioc) ;
319 
320          if( ii < 0 || strncmp(afni_buf,"OK!",3) != 0 ){
321             fprintf(stderr,"AFNI didn't like control information!\a\n") ;
322             afni_mode = 0 ;
323             return -1 ;
324          }
325 
326          /** now open data channel to AFNI **/
327 
328          afni_ioc = iochan_init( afni_iocname , "create" ) ;
329          if( afni_ioc == NULL ){
330             fprintf(stderr,
331                     "Can't open data channel %s to AFNI!\a\n",afni_iocname) ;
332             afni_mode = 0 ;
333             return -1 ;
334          } else {
335             afni_mode = AFNI_WAIT_DATA_MODE ;
336             if( afni_verbose ) fprintf(stderr,"AFNI data channel created\n") ;
337          }
338       }
339    }
340 
341    /****************************************************/
342    /***** See if data channel is connected to AFNI *****/
343 
344    if( afni_mode == AFNI_WAIT_DATA_MODE ){
345 
346       ii = iochan_goodcheck( afni_ioc , 5 ) ;  /* wait at most 5 msec */
347       if( ii < 0 ){
348          fprintf(stderr,
349                  "AFNI data channel aborted before any data was sent!\a\n") ;
350          IOCHAN_CLOSE( afni_ioc ) ;
351          afni_mode = 0 ;
352          return -1 ;
353       } else if( ii > 0 ){                     /* ready to go! */
354          afni_mode = AFNI_CONTINUE_MODE ;
355          if( afni_verbose ) fprintf(stderr,"AFNI data channel is open\n") ;
356       } else {
357          return 0 ;                            /* try again next time */
358       }
359    }
360 
361    /************************************************************/
362    /***** The "normal" state of affairs:                   *****/
363    /***** AFNI is connected.  See if any data is arriving. *****/
364 
365    if( afni_mode == AFNI_CONTINUE_MODE ){
366       char afni_buf[256] ;
367       float xx , yy , zz ;
368       int   ix , jy , kz ;
369 
370       ii = iochan_readcheck( afni_ioc , 0 ) ;  /* don't wait */
371 
372       /** ii <  0  ==>  a fatal error has happened
373           ii == 0  ==>  no data is ready
374           ii >  0  ==>  data is ready to read from the channel **/
375 
376       if( ii < 0 ){
377          fprintf(stderr,"AFNI data channel aborted!\a\n") ;
378          IOCHAN_CLOSE(afni_ioc) ;
379          afni_mode = 0 ;
380          return -1 ;
381       } else if( ii == 0 ){
382          return 0 ;       /* no data ==> try again next time */
383       }
384 
385       /** at this point, data is incoming from AFNI **/
386 
387       ii = iochan_recv( afni_ioc , afni_buf , 256 ) ;
388 
389       if( ii <= 0 ){
390          fprintf(stderr,"AFNI data channel recv failed!\a\n") ;
391          IOCHAN_CLOSE(afni_ioc) ;
392          afni_mode = 0 ;
393          return -1 ;
394       }
395 
396       /** at last! "process" the data from AFNI
397                    (in this case, just print it out) **/
398 
399       ii = sscanf( afni_buf , "TT_XYZ %f %f %f"   , &xx,&yy,&zz ) ;
400 
401       /** also, AFNI will wait until we send an acknowledgment;
402           acknowledgment messages are always 4 (POACKSIZE) bytes long **/
403 
404       if( ii < 3 ){
405          fprintf(stderr,"AFNI sent bad data: %s\a\n",afni_buf) ;
406          PO_ACK_BAD(afni_ioc) ;
407       } else {
408          PO_ACK_OK(afni_ioc) ;
409          if( afni_verbose )
410             fprintf(stderr,"AFNI sent TT coords %9.3f %9.3f %9.3f\n",xx,yy,zz) ;
411          handle_tta(xx,yy,zz) ;
412       }
413    }
414 
415    return 0 ;
416 }
417 
418 /*------------------------------------------------------------------*/
419 
420 #define CORONAL_NUM 38
421 static float coronal_yy[] = {
422   -100, -95, -90, -85, -80, -75, -70, -65,
423    -60, -55, -50, -45, -40, -35, -32, -28,
424    -24, -20, -16, -12, -8,  -4,   0,   4,
425      8,  12,  16,  20, 24,  28,  32,  35,
426     40,  45,  50,  55, 60,  65
427 } ;
428 static char * coronal_ff[] = {
429    "tt_corm99.gif" , "tt_corm95.gif" , "tt_corm90.gif" , "tt_corm85.gif" ,
430    "tt_corm80.gif" , "tt_corm75.gif" , "tt_corm70.gif" , "tt_corm65.gif" ,
431    "tt_corm60.gif" , "tt_corm55.gif" , "tt_corm50.gif" , "tt_corm45.gif" ,
432    "tt_corm40.gif" , "tt_corm35.gif" , "tt_corm32.gif" , "tt_corm28.gif" ,
433    "tt_corm24.gif" , "tt_corm20.gif" , "tt_corm16.gif" , "tt_corm12.gif" ,
434    "tt_corm08.gif" , "tt_corm04.gif" , "tt_corp00.gif" , "tt_corp04.gif" ,
435    "tt_corp08.gif" , "tt_corp12.gif" , "tt_corp16.gif" , "tt_corp20.gif" ,
436    "tt_corp24.gif" , "tt_corp28.gif" , "tt_corp32.gif" , "tt_corp35.gif" ,
437    "tt_corp40.gif" , "tt_corp45.gif" , "tt_corp50.gif" , "tt_corp55.gif" ,
438    "tt_corp60.gif" , "tt_corp65.gif"
439 } ;
440 
441 #define SAGITTAL_NUM 18
442 static float sagittal_xx[] = {
443    0, 3, 5, 9, 13, 17, 21, 25, 29, 33 ,
444    37, 41, 43, 47, 51, 55, 59, 61
445 } ;
446 static char * sagittal_ff[] = {
447    "tt_sag00g.gif", "tt_sag03.gif" , "tt_sag05.gif" , "tt_sag09.gif" ,
448    "tt_sag13.gif" , "tt_sag17.gif" , "tt_sag21.gif" , "tt_sag25.gif" ,
449    "tt_sag29.gif" , "tt_sag33.gif" , "tt_sag37.gif" , "tt_sag41.gif" ,
450    "tt_sag43.gif" , "tt_sag47.gif" , "tt_sag51.gif" , "tt_sag55.gif" ,
451    "tt_sag59.gif" , "tt_sag61.gif"
452 } ;
453 
454 #define AXIAL_NUM 27
455 static float axial_zz[] = {
456    -40 , -36 , -32 , -28 , -24 , -20 , -16 , -12 , -8 , -4 ,
457    -1 , 1 , 4 , 8 , 12 , 16 , 20 , 24 , 28 , 32 , 35 ,
458    40 , 45 , 50 , 55 , 60 , 65
459 } ;
460 static char * axial_ff[] = {
461    "tt_horm40.gif" , "tt_horm36.gif" , "tt_horm32.gif" , "tt_horm28.gif" ,
462    "tt_horm24.gif" , "tt_horm20.gif" , "tt_horm16.gif" , "tt_horm12.gif" ,
463    "tt_horm08.gif" , "tt_horm04.gif" , "tt_horm01.gif" , "tt_horp01.gif" ,
464    "tt_horp04.gif" , "tt_horp08.gif" , "tt_horp12.gif" , "tt_horp16.gif" ,
465    "tt_horp20.gif" , "tt_horp24.gif" , "tt_horp28.gif" , "tt_horp32.gif" ,
466    "tt_horp35.gif" , "tt_horp40.gif" , "tt_horp45.gif" , "tt_horp50.gif" ,
467    "tt_horp55.gif" , "tt_horp60.gif" , "tt_horp65.gif"
468 } ;
469 
470 #define TTAHOME "http://varda.biophysics.mcw.edu/~cox/TTA/"
471 
handle_tta(float xx,float yy,float zz)472 void handle_tta( float xx , float yy , float zz )
473 {
474    char * tname ;
475    int ii,jj,kk , qqq ;
476    static int ii_old=-1 , jj_old=-1 , kk_old=-1 ;
477    FILE * fp ;
478    static char nbuf[444] ;
479    static char * ttahome ;
480 
481    /* create temporary URL */
482 
483    if( url == NULL ){
484       tname = tempnam(NULL,"tta") ;
485       url   = (char *) malloc(strlen(tname)+16) ;
486       uff   = url + 5 ;
487       strcpy(url,"file:") ; strcat(url,tname) ; strcat(url,".html") ;
488       free(tname) ;
489       sprintf(nbuf,"netscape -remote 'openURL(%s)'" , url ) ;
490       fprintf(stderr,"Temporary URL file is %s\n",uff) ;
491 
492       ttahome = getenv( "AFNI_TTAPATH" ) ;
493       if( ttahome == NULL ) ttahome = TTAHOME ;
494    }
495 
496    /* find sagittal image */
497 
498    xx = fabs(xx) ;
499    if( xx <= sagittal_xx[0] ){
500       ii = 0 ;
501    } else if( xx >= sagittal_xx[SAGITTAL_NUM-1] ){
502       ii = SAGITTAL_NUM - 1 ;
503    } else {
504       for( ii=1 ; ii < SAGITTAL_NUM && xx > sagittal_xx[ii] ; ii++ ) ; /* nada */
505       if( fabs(xx-sagittal_xx[ii-1]) < fabs(xx-sagittal_xx[ii]) ) ii-- ;
506    }
507 
508    /* find coronal image */
509 
510    if( yy <= coronal_yy[0] ){
511       jj = 0 ;
512    } else if( yy >= coronal_yy[CORONAL_NUM-1] ){
513       jj = CORONAL_NUM - 1 ;
514    } else {
515       for( jj=1 ; jj < CORONAL_NUM && yy > coronal_yy[jj] ; jj++ ) ; /* nada */
516       if( fabs(yy-coronal_yy[jj-1]) < fabs(yy-coronal_yy[jj]) ) jj-- ;
517    }
518 
519    /* find axial image */
520 
521    if( zz <= axial_zz[0] ){
522       kk = 0 ;
523    } else if( zz >= axial_zz[AXIAL_NUM-1] ){
524       kk = AXIAL_NUM - 1 ;
525    } else {
526       for( kk=1 ; kk < AXIAL_NUM && zz > axial_zz[kk] ; kk++ ) ; /* nada */
527       if( fabs(zz-axial_zz[kk-1]) < fabs(zz-axial_zz[kk]) ) kk-- ;
528    }
529 
530    if( ii == ii_old && jj == jj_old && kk == kk_old ) return ;
531 
532    /* write out url file */
533 
534    fp = fopen( uff , "w" ) ;
535    if( fp == NULL ){ fprintf(stderr,"Can't write URL file\n") ; return ; }
536 
537    fprintf(fp , "<HEAD>\n"
538                 "<title>T-T Atlas Pages</title>\n"
539                 "</HEAD>\n"
540                 "<BODY>\n"
541                 "<img align=middle src=\"%s%s\"><p>\n"
542                 "<img align=middle src=\"%s%s\"><p>\n"
543                 "<img align=middle src=\"%s%s\"><p>\n"
544                 "</BODY>\n" ,
545            ttahome , axial_ff[kk] ,
546            ttahome , coronal_ff[jj] ,
547            ttahome , sagittal_ff[ii] ) ;
548 
549    fclose(fp) ;
550 
551    if( afni_verbose )
552       fprintf(stderr,"Axial=%s  Coronal=%s  Sagittal=%s\n",
553               axial_ff[kk] , coronal_ff[jj] , sagittal_ff[ii] ) ;
554 
555    /* send message to Netscape */
556 
557    qqq = system(nbuf) ;
558    if( qqq != 0 )
559       fprintf(stderr,"Can't send command to Netscape - is it running?\n") ;
560 
561    ii_old = ii ; jj_old = jj ; kk_old = kk ;
562    return ;
563 }
564