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 /***************************************************************
8   Sample plugout program to send the dataset (i,j,k)
9   indices for viewing.
10 ****************************************************************/
11 
12 /***** Header file for communication routines *****/
13 
14 #include "thd_iochan.h"
15 #include "niml.h"
16 #include "cs.h"
17 #include "afni_environ.h"
18 
19 /***** Global variable determining on which system AFNI runs.  *****/
20 /***** [default is the current system, can be changed by user] *****/
21 
22 static char afni_host[128] = "." ;
23 static char afni_name[128] = "\0" ;
24 static int  afni_port      = 0 ;  /* Init. before parsing command line
25                                     ZSS June 2011 */
26 static int  afni_verbose   = 0 ;  /* print out debug info? */
27 
28 /***** Prototype *****/
29 
30 int afni_io(void) ;
31 
32 /*===================================================================
33    Main program:
34      Read command line for options
35      Call afni_io routine forever
36 =====================================================================*/
37 
main(int argc,char * argv[])38 int main( int argc , char * argv[] )
39 {
40    int narg , ii ;
41 
42    (void)AFNI_prefilter_args(&argc,&argv);
43 
44    afni_port = get_port_named("PLUGOUT_IJK_PORT"); /* ZSS June 2011 */
45 
46    /***** See if the pitiful user wants help *****/
47 
48    if( argc == 2 && strncmp(argv[1],"-help",5) == 0 ){
49       printf("Usage: plugout_ijk [-host name] [-v]\n"
50              "This program connects to AFNI and send (i,j,k)\n"
51              "dataset indices to control the viewpoint.\n\n"
52              "Options:\n"
53              "  -host name  Means to connect to AFNI running on the\n"
54              "                computer 'name' using TCP/IP.  The default is to\n"
55              "                connect on the current host using shared memory.\n"
56              "  -v          Verbose mode.\n"
57              "  -port pp    Use TCP/IP port number 'pp'.  The default is\n"
58              "                %d, but if two plugouts are running on the\n"
59              "                same computer, they must use different ports.\n"
60              "                For a list of currently used ports use afni -list_ports\n"
61              "  -name sss   Use the string 'sss' for the name that AFNI assigns\n"
62              "                to this plugout.  The default is something stupid.\n"
63              "\n"
64              "To have different plugout_* programs talking to different\n"
65              "AFNI, use the -np* options below\n"
66              "%s\n"
67             , afni_port, get_np_help()) ;
68       exit(0) ;
69    }
70 
71    /***** Process command line options *****/
72 
73    narg = 1 ;
74    while( narg < argc ){
75 
76       /** -host name **/
77 
78       if( strncmp(argv[narg],"-host",5) == 0 ){
79          narg++ ;
80          if( narg >= argc ){
81             fprintf(stderr,"-host needs a following name!\a\n"); exit(1);
82          }
83          strcpy( afni_host , argv[narg] ) ;
84          narg++ ; continue ;
85       }
86 
87       /** -name sss **/
88 
89       if( strncmp(argv[narg],"-name",5) == 0 ){
90          narg++ ;
91          if( narg >= argc ){
92             fprintf(stderr,"-name needs a following string!\a\n"); exit(1);
93          }
94          strcpy( afni_name , argv[narg] ) ;
95          narg++ ; continue ;
96       }
97 
98       /** -v **/
99 
100       if( strncmp(argv[narg],"-v",2) == 0 ){
101          afni_verbose = 1 ;
102          narg++ ; continue ;
103       }
104 
105       /** -port pp **/
106 
107       if( strncmp(argv[narg],"-port",4) == 0 ){
108          narg++ ;
109          if( narg >= argc ){
110             fprintf(stderr,"-port needs a following argument!\a\n"); exit(1);
111          }
112          afni_port = strtol( argv[narg] , NULL , 10 ) ;
113          if( afni_port <= 0 ){
114             fprintf(stderr,"-port needs a positive argument!\a\n"); exit(1);
115          }
116          if( strcmp(afni_host,".") == 0 ) strcpy(afni_host,"localhost") ;
117          narg++ ; continue ;
118       }
119 
120       /** Je ne sais pas **/
121 
122       fprintf(stderr,"Unrecognized option: %s\a\n",argv[narg]) ;
123       exit(1) ;
124    }
125 
126    /***** Loop and check in with AFNI every 100 msec *****/
127 
128    while( 1 ){
129       ii = afni_io() ;        /* commune with AFNI  */
130       if( ii < 0 ) exit(0) ;  /* bad trip? then die */
131       iochan_sleep(100) ;     /* perchance to dream */
132    }
133 
134 }
135 
136 /*===================================================================
137   This routine handles all communications with AFNI.
138   The only input is the global variable afni_host, which determines
139   on which system AFNI is running.
140   The output is -1 if an error occured, 0 if everything is OK.
141 =====================================================================*/
142 
143 /***** Mode flags for determining what afni_io does.
144        The routine progress through 5 stages:
145 
146        1) Open a control connection to AFNI;
147      then
148        2) Wait until AFNI also opens the control connection;
149      then
150        3) Send a control string to AFNI saying what kind of
151             information we want, wait for an acknowledgment,
152             close the control connection, and open a data
153             connection to AFNI;
154      then
155        4) Wait for AFNI to also open the data connection;
156      then
157        5) Send data to AFNI!                                *****/
158 
159 #define AFNI_OPEN_CONTROL_MODE  1  /* 1st time thru: open control channel */
160 #define AFNI_WAIT_CONTROL_MODE  2  /* wait for AFNI to open control chan  */
161 #define AFNI_OPEN_DATA_MODE     3  /* now can open data channel to AFNI   */
162 #define AFNI_WAIT_DATA_MODE     4  /* waiting for AFNI to open data chan  */
163 #define AFNI_CONTINUE_MODE      5  /* at last! data channel is ready!     */
164 
165 /***** macros to send acknowledgement strings to AFNI *****/
166 
167 #define POACKSIZE       4  /* length of acknowledgement strings */
168 
169 #define PO_ACK_BAD(ic)  iochan_sendall( (ic) , "BAD" , POACKSIZE )
170 #define PO_ACK_OK(ic)   iochan_sendall( (ic) , "OK!" , POACKSIZE )
171 #define PO_SEND(ic,str) iochan_sendall( (ic) , (str) , strlen((str))+1 )
172 
afni_io(void)173 int afni_io(void)
174 {
175    static int afni_mode = AFNI_OPEN_CONTROL_MODE ;  /* status variable */
176    static IOCHAN * afni_ioc = NULL ;                /* connection to AFNI */
177    int ii ;
178 
179    /***************************************************************/
180    /***** Check to see if status is OK before we proceed.     *****/
181    /***** (if an error occurs below, afni_mode gets set to 0) *****/
182 
183    if( afni_mode <= 0 ) return -1 ;
184 
185    /***********************************************************************/
186    /***** First time into this routine?  Open control channel to AFNI *****/
187 
188    if( afni_mode == AFNI_OPEN_CONTROL_MODE ){
189       char afni_iocname[128] ;           /* will hold name of I/O channel */
190 
191       /** Note that the control channel is always a
192           TCP/IP channel to port #get_port_named("AFNI_PLUGOUT_TCP_0")
193           (used to be #7955) on the AFNI host system **/
194 
195       if( strcmp(afni_host,".") == 0 )
196          sprintf( afni_iocname , "tcp:%s:%d" ,
197             "localhost", get_port_named("AFNI_PLUGOUT_TCP_0") ); /* make name */
198       else
199          sprintf( afni_iocname , "tcp:%s:%d" ,
200             afni_host, get_port_named("AFNI_PLUGOUT_TCP_0") ) ;  /* make name */
201 
202       afni_ioc = iochan_init( afni_iocname , "create" ) ;    /* create it */
203       if( afni_ioc == NULL ){
204          fprintf(stderr,
205                  "Can't create control channel %s to AFNI!\n",afni_iocname) ;
206          afni_mode = 0 ;
207          return -1 ;
208       }
209       afni_mode = AFNI_WAIT_CONTROL_MODE ; /* waiting for AFNI connection */
210       if( afni_verbose )
211          fprintf(stderr,"AFNI control channel created\n") ;
212    }
213 
214    /****************************************************/
215    /**** Check if AFNI control channel is connected ****/
216 
217    if( afni_mode == AFNI_WAIT_CONTROL_MODE ){
218       ii = iochan_writecheck( afni_ioc , 5 ) ;     /* wait at most 5 msec */
219 
220       /** the iochan_*check() routines return
221              -1 for an error,
222               0 if not ready,
223              >0 if the I/O channel is ready. **/
224 
225       if( ii < 0 ){
226          fprintf(stderr,"Control channel to AFNI failed!\a\n") ;
227          IOCHAN_CLOSE(afni_ioc) ;
228          afni_mode = 0 ;
229          return -1 ;
230       } else if( ii > 0 ){
231          afni_mode = AFNI_OPEN_DATA_MODE ;        /* prepare to send data */
232          if( afni_verbose )
233             fprintf(stderr,"AFNI control channel connected\n");
234       } else {
235          return 0 ;                                /* try again next time */
236       }
237    }
238 
239    /**********************************************************/
240    /**** Send control data to AFNI, and open data channel ****/
241 
242    if( afni_mode == AFNI_OPEN_DATA_MODE ){
243       char afni_iocname[128] ;
244       char afni_buf[256] ;
245 
246       /** decide name of data channel:
247             use shared memory (shm:) on ".",
248             use TCP/IP (tcp:) on other computer systems;
249         * Note that the TCP/IP port number can be
250            anything that isn't already in use;
251         * Note that the shm control name (here "test_plugout")
252            is a string that will be converted to an IPC
253            key (in function string_to_key in iochan.c).       **/
254 
255       if( strcmp(afni_host,".") == 0 )
256          strcpy( afni_iocname , "shm:test_plugout:1K+1K" ) ;
257       else
258          sprintf( afni_iocname , "tcp:%s:%d" , afni_host , afni_port ) ;
259 
260       /** write the command to AFNI into the buffer:
261             * each command ends with a newline character,
262                 except (possibly) the last command;
263             * the command buffer is a C string, which ends
264                 with an ASCII NUL character;
265             * PONAME means 'use this string for informative messages';
266             * IOCHAN means 'use this I/O channel from now on'. **/
267 
268       if( afni_name[0] == '\0' ) strcpy(afni_name,"aHorseCalledMan") ;
269 
270       sprintf( afni_buf , "PONAME %s\n"
271                           "IOCHAN %s" ,
272                afni_name , afni_iocname ) ;
273 
274       if( afni_verbose )
275          fprintf(stderr,"Sending control information to AFNI\n") ;
276 
277       /** note that the ASCII NUL at the end of the buffer is sent **/
278 
279       ii = iochan_sendall( afni_ioc , afni_buf , strlen(afni_buf)+1 ) ;
280 
281       /** the return value is the number of bytes sent,
282           or -1 indicating a fatal error transpired.    **/
283 
284       if( ii < 0 ){
285          fprintf(stderr,"Transmission of control data to AFNI failed!\a\n") ;
286          IOCHAN_CLOSE(afni_ioc) ;
287          afni_mode = 0 ;
288          return -1 ;
289 
290       } else {
291 
292          /** wait for the acknowledgment from AFNI, then close channel **/
293 
294          ii = iochan_recvall( afni_ioc , afni_buf , POACKSIZE ) ;
295          IOCHAN_CLOSE(afni_ioc) ;
296 
297          if( ii < 0 || strncmp(afni_buf,"OK!",3) != 0 ){
298             fprintf(stderr,"AFNI didn't like control information!\a\n") ;
299             afni_mode = 0 ;
300             return -1 ;
301          }
302 
303          /** now open data channel to AFNI **/
304 
305          afni_ioc = iochan_init( afni_iocname , "create" ) ;
306          if( afni_ioc == NULL ){
307             fprintf(stderr,
308                     "Can't open data channel %s to AFNI!\a\n",afni_iocname) ;
309             afni_mode = 0 ;
310             return -1 ;
311          } else {
312             afni_mode = AFNI_WAIT_DATA_MODE ;
313             if( afni_verbose ) fprintf(stderr,"AFNI data channel created\n") ;
314          }
315       }
316    }
317 
318    /****************************************************/
319    /***** See if data channel is connected to AFNI *****/
320 
321    if( afni_mode == AFNI_WAIT_DATA_MODE ){
322 
323       ii = iochan_goodcheck( afni_ioc , 5 ) ;  /* wait at most 5 msec */
324       if( ii < 0 ){
325          fprintf(stderr,
326                  "AFNI data channel aborted before any data was sent!\a\n") ;
327          IOCHAN_CLOSE( afni_ioc ) ;
328          afni_mode = 0 ;
329          return -1 ;
330       } else if( ii > 0 ){                     /* ready to go! */
331          afni_mode = AFNI_CONTINUE_MODE ;
332          if( afni_verbose ) fprintf(stderr,"AFNI data channel is open\n") ;
333       } else {
334          return 0 ;                            /* try again next time */
335       }
336    }
337 
338    /**************************************************************/
339    /***** The "normal" state of affairs:  AFNI is connected. *****/
340    /***** See if the user wants to send i,j,k to AFNI.       *****/
341 
342    if( afni_mode == AFNI_CONTINUE_MODE ){
343       char afni_buf[256] ;
344       int   ix , jy , kz ;
345 
346       /* get user input */
347 
348       printf("Enter i j k: ") ; fflush(stdout) ; afni_fgets(afni_buf,256,stdin) ;
349       ii = sscanf(afni_buf,"%d %d %d",&ix,&jy,&kz) ;
350       if( ii < 3 ){
351          printf("** Warning -- planetary meltdown will occur in 10 seconds!\a\n") ;
352          iochan_sleep(1000) ;
353          ii = iochan_writecheck(afni_ioc,5) ; if( ii < 0 ) return -1 ;
354          return 0 ;
355       }
356 
357       /* send input to AFNI (essentially unedited) */
358 
359       sprintf(afni_buf,"DSET_IJK_SET %d %d %d",ix,jy,kz) ;
360       ii = iochan_sendall( afni_ioc , afni_buf , strlen(afni_buf)+1 ) ;
361 
362       if( ii > 0 ){  /* send was OK; wait for acknowledgment */
363          ii = iochan_recvall( afni_ioc , afni_buf , POACKSIZE ) ;
364       }
365 
366       if( ii < 0 ){   /* send or acknowledgment failed */
367          fprintf(stderr,"AFNI data channel aborted!\a\n") ;
368          IOCHAN_CLOSE(afni_ioc) ;
369          afni_mode = 0 ;
370          return -1 ;
371       }
372       return 0 ;
373    }
374 
375    return 0 ;
376 }
377