/* program pdprp.c - the PDP replacement project software
   started March, 1997 by Scot J. Kleinman 
   version 1.00 placed in operation 11June97
   on a Linux PC (i486; kernel 2.0.27; Redhat 4.1)
   with a Computer Boards Inc. CIO-DIO24 digital IO board.

   it starts pdprp.tcl and receives calls from it

   Program must be compiled  with a -O (or -O2 or higher) 
   for the outb and inb commands to work.
   It needs the tk and tcl libraries for Tcl/Tk stuff.
   Here is the command line for compiling:
   cc pdprp.c -o pdprp -O2 -I/usr/X11R6/include/ -L/usr/X11R6/lib/ -ltk -ltcl -ldl -lX11 -lm -lc

   ioperm needs to be run as root.  so the executable should
   be owned by root and chmod'ed to 4711.

   All global variables, structures, etc. start with a capital
   letter. 

   Variables which are #defined are in all caps.

   Note current limits: bench name 9 chars; camera name 31 chars
                        host computer name 9 chars;
   and see the #defines below

   HISTORY
   -------
   27Jun97  SJK v1.01 - placed in operation at BBSO
    3Jul97  SJK v1.10 - Added timeout check for Light Release signal (DWL)
                        and Ready for Light signal (RFL)
                        and updated the way errors are handled in general
                        incl. making pdprp suspend cycle until user acknowledges
                        the error requestor (except for LBA case so far)
                        (needs pdprp.tcl v1.10)
                        DOESN'T WORK - FIXES ADDED IN V1.11
   21Aug97 SJK v1.11 -  Added fixes for the non-working v1.10.
                        needs pdprp.tcl v1.11 (which has been modified to
                        also handle ghosting of dwell time when appropriate)
   12Oct   SJK v1.20 -  Added support for OSL and VMG Vaxes.  Fixed a bug so
                        we do not continue cycling if there is an RFL Timeout
                        error box open.  Fixed some <= loops that should
                        have been just <. Fixed a bug in start_cycling
                        where it went to the first bench without checking
                        to see if the host was ready or not. Fixed serial port
                        parameters (was not in canonical mode, for example).
                        Flushed all serial ports on startup- this means - start
                        pdprp first before starting up host computer cycling.
                        Fixed start-up race condition. The only requirement is
                        that pdprp is started BEFORE the host computer programs
                        are started- after that, you can start cycling in any
                        order.  (Eg., mac start sequence then pdprp start26 or
                        pdprp start26 then mac start sequence.) Fixed type of
                        Io_s.port from int to unsigned long int. Fixed nib1 and
                        nib2 to return unsgined char not int.
                        Needs pdprp.tcl version 1.20
   14Oct   SJK v1.21    Fixed one-bench cycles and holds so they work-
                        previously they would not communicate with the
                        host.  Now, a one-bench cycle tells the host to
                        take a picture every cycle time, whereas a hold
                        tells the host to take a picture as soon as the
                        host says it's ready (actually, I cheat and use a
                        one second cycle time).
                        Changed timeouts to be TWICE the cycle time unless
                        cycle time is < 5s in which case you are on your own -
                        no timeouts will be reported.
                        Needs pdprp.tcl version 1.21
   16Oct   SJK v1.30    Put serial check routines in the main event loop
                        so we always respond to serial commands whether or
                        not we are cycling.   This elimates many problems
                        in synching with vaxes. Fixed bug in hold where
                        cycle time got left at 1 on de-hold. Added a
                        bell on Error messages: 3 bells for an error
                        box and one for an error message update although
                        you can't always tell the difference between the
                        one and three bells.  needs pdprp.tcl version 1.30
   21Oct   SJK v1.31    changed logic installed in v1.21 so timeouts
                        are reported for all benches with cycle times of
                        <5 s EXCEPT for the VMGVax, since I have no idea
                        of how long a VMG will take.  uses pdprp.tcl
                        version 1.31 just to set version right in
                        title bar
   24Oct   SJK v1.32    changed to only allocate serial ports which we
                        are using instead of all of them as
                        before. Prompted by telcon.c needing to use
                        ttyC7 to talk to PC GUider.  made change to
                        pdprp.tcl to fix bug where it was saving a one
                        second cycle time if you save settings while a
                        bench is on hold. 
                        needs pdprp.tcl v1.32
   24Oct   SJK v1.33    pdprp.tcl only release (only pdprp.tcl changed)
    6Nov   SJK v.135    fixed a VMGVax communication bug (an if = should be ==)
    4Nov   SJK v1.40    (1.34 and 1.35 were pdprp.tcl only changes)
                        this version starts the communication with the telescope
                        control. It sets longobs mode, picstatus and 
                        lightbench as appropriate in hq.tcl via pdprp.tcl
                        and won't take a picture if any telescope is moving.
                        makes calls to gettm and gettp in pdprp.tcl 
                        tells host computers real telescope positions now
                        needs pdprp.tcl v1.40
 */

#include<stdio.h>
#include<stdlib.h> /*atoi()*/
#include<string.h> /*string functions*/
#include<time.h>  /*for the time_t (long) and time() defs*/
#define time() time(NULL)
#include<asm/io.h>  /*needed for outb()*/
#include<unistd.h>  /*needed for ioperm() & g/setuid*/
#include<tcl.h>
#include<tk.h>      /*for Tcl/Tk GUI*/
/* these 4 are for serial port control */
#include <unistd.h>  /* UNIX standard function defs */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */

#define MAXBENCH 16  /*max # of total benches*/
#define MAX26BEN 4   /*max # of benches allowed in 26" cycle: 26E1/2,26C,26W,SF
                       change if ADD 26E1 AND 26E2 or the SJ/SF synch stuff*/
#define MAX10BEN 3   /*max # of benches allowed in 10" cycle:10E,10C,10W*/
#define BASE 0x310UL /*base address of DIO board*/
#define PORTS 24     /*Number of i/o ports (each one with 8 bits)*/

                     /*The next group are all error codes*/
#define ELBA 0       /*light not available error*/
#define EDWL 1       /*Done With Light signal not received from host*/
#define ERFL 2       /*host not Ready For Light*/
#define MAXEC 3      /*total # of error codes listed above*/

                      /*The next group are for Serial port assignments*/
#define MACSPORT 0    /*Sfd index for Macintosh serial communication*/
#define OSLSPORT 1    /*Sfd index for OSLVax serial communication*/
#define VMGSPORT 2    /*Sfd index for VMGVax serial communiation*/
/*ports 6 and 7 are off limits - they are Tel guider and PC Guider*/
#define NUMSPORT 3    /*total # of serial ports listed above*/

                      /*The next group are input serial command codes*/
#define RFL 1         /*Ready for light*/
#define DWL 2         /*Done With Light -used to be lightbeam released*/
#define END 3         /*End  - host program sequence terminated VAX only*/
#define IFR 4         /*Information request - currently VAX only*/

#define BELL '\007'   /*the ASCII code for the bell*/

struct Bench_s 
    {
    int bnum;         /*bench code, ex. 7*/
    int lnum;         /*LBC number for bench- this is what is sent by LBC*/
    char bench[9];    /*name of Bench, ex. 26W*/
    char camera[31];  /*camera name, ex. OSL*/
    int ct;           /*cycle time*/
    int dt;           /*dwell time*/
    char host[9];     /*host computer*/
    int lr;           /*light released: 1=yes; 0=no*/
    };
typedef struct Bench_s Bstruct;

struct Io_s
    {
    unsigned long port;                       /*port #:PortA1,PortB1,...*/
    unsigned char bit;                        /*bit for reading/writing*/
    };
typedef struct Io_s Iostruct;

  /*set up ports to be accessed*/
int initialize_io(void);                  

  /*set up and start Tcl/Tk GUI*/
int tcl_initialize(Tcl_Interp *interp);

 /*open a serial port*/
int open_sport(char *terminal);

  /*set up i/o port/pin assignments*/
void p_initialize(void);                

  /*read bench.params bench assignment file*/
int read_bdata(void);      

  /*build bench database*/
void assign_dbase(int bench_num, char *name); 

  /*start bench cycling - argv[1] contains scope*/
int start_cycling(ClientData cd, Tcl_Interp *interp, int argc, char **argv);

  /*stop bench cycling - argv[1] contains scope*/
int stop_cycling(ClientData cd, Tcl_Interp *interp, int argc, char **argv);

  /*go to specified bench*/
int goto_bench(Bstruct *telescope[], time_t time[], int scope, int bench);

  /*deal with cycling*/
void check_cycle(void); 

 /*check serial ports for input and respond to simple requests*/
void check_serial(void);

 /*find the telescope of the bench controlled by serial port sport*/
int find_sport_tel(int sport);

  /*ready to switch?*/
int check_tel(Bstruct *telescope[], time_t time[], int current_bench,
              int NumXXben, int scope);     

  /*check for data computer release of light at current bench*/
int light_released(Bstruct *telescope[], int bench, int scope);

  /*check host computer for ready for light signal*/
int ready_for_light(Bstruct *telescope[], int bench, int scope);

  /*see if the wait for an RFL (Ready For Light) signal has timed out  yet*/
int rfl_timeout_check(Bstruct *telescope[], int bench, int scope, time_t now);

  /*set first nibble of byte*/
unsigned char nib1(unsigned char *byte, unsigned char value);

  /*set 2nd nibble of byte*/
unsigned char nib2(unsigned char *byte, unsigned char value);

  /*return values of a given bit from a given port*/
int bitcheck(unsigned long int port, unsigned char bit);

  /*pop up an error box in Tcl/Tk GUI with a 3-line message*/
void error_popup(int numstr, int scope);

  /*update the last error box only in the GUI (for ignored errors)*/
void error_update(int numstr);

  /*build up error Ignore array called by pdprp.tcl*/
int error_ignore (ClientData cd, Tcl_Interp *interp, int argc, char **argv);

  /*reset Enotify array - called by pdprp.tcl*/
int error_denotify (ClientData cd, Tcl_Interp *interp, int argc, char **argv);

  /*send a string out a serial port*/
int serial_out (int sfd, char *string);

  /*read a string from a serial port*/
int serial_in (int sfd, char *string);

  /*get and parse a command from the serial port*/
int get_serial(int sport);

  /*form the serial output string saying light is there*/
void form_sout_string(int bench, int scope, char *string);

  /*form the serial output info string for the vaxen*/
void form_vaxsout_string(int scope, char *string);

Bstruct Bench[MAXBENCH];    /*bench name and timing information*/
Bstruct *Bench26[MAX26BEN]; /*active 26" benches*/
Bstruct *Bench10[MAX10BEN]; /*active 10" benches*/

Iostruct Lbcsig[MAXBENCH];  /*port/pin assignments for LBC OK signal*/

time_t Time26[MAX26BEN];           /*timers for each 26" bench*/
time_t Time10[MAX10BEN];           /*timers for each 10" bench*/

int B26e1 = 0;  /*bench # assignments*/
int B26e2 = 0;  /*assigned in assign_dbase()*/
int B26c  = 0;  /*which is called by read_bdata()*/
int B26w  = 0;
int Bsj    = 0;
int Bsf    = 0;
int B10e   = 0;
int B10w   = 0;
int B10c   = 0;

int Num26ben;       /*Number of active 26" benches*/
int Num10ben;       /*Number of active 10" benches*/
int Cycling26=0;    /*1=Cycling 26" benches; 0=not*/
int Cycling10=0;    /*1=Cycling 10" benches; 0=not*/
int On26=0;         /*Current 26" bench*/
int On10=0;         /*Current 10" bench*/
int Long10=0;       /*flag for long 10" observation*/
int Long26=0;       /*flag for long 26" observation*/
int Enotify[MAXEC][MAXBENCH]; /*Flags for user already notified of error; 1=yes*/
int Erfltime[MAXBENCH]; /*holds timers for time-out error on RFL signal*/

unsigned char Obyte[PORTS];    /*array of output bytes 0->portA, 1->portB,...*/

char Tclerrs[10][68];          /*array of error strings for GUI Error box*/
int Ignore[MAXEC][MAXBENCH];   /*holds error codes and benches to ignore*/
int Rflto=0;                   /*Boolean for is there an RFL TimeOut error*/

unsigned long Base = BASE;       /*base address of the DIO board*/
unsigned long PortA1 = BASE+0;   /*address of port A1 on DIO board*/
unsigned long PortB1 = BASE+1;   /*address of port B1 on DIO board*/ 
unsigned long PortC1 = BASE+2;   /*address of port C1 on DIO board*/
unsigned long Control1 = BASE+3; /*control address for ports 1 on DIO board*/

int Sfd[NUMSPORT];                      /*serial port file descriptors*/
int Srfl[NUMSPORT];                     /*incoming ready for light from serial ports*/
int Sdwl[NUMSPORT];                     /*incoming done with light from serial ports*/

Tcl_Interp *Interp;     /*the Tcl/Tk GUI interface*/

time_t Tzero;  /*temporary*/

/*MAIN*/
main()
    {
    int err;        /*prcodedure return value*/
    int now; 
    int i,j;

    /*Initialize arrays*/
    for (i=0; i<MAXEC;i++)
        for (j=0;j<MAXBENCH;j++)
            Enotify[i][j]=Ignore[i][j]=0;
    for (i=0;i<MAXBENCH;i++)
        Erfltime[i]=0;
    for (i=0;i<NUMSPORT;i++)
        Srfl[i]=Sdwl[i]=0;
 
    now=time();

    /* set up IO ports */
    if ((err=initialize_io())!=0)
        return(err);
    /* read in data now, even though start will read it in later.
       this is just in case user quits now without starting*/ 
    if ((err=read_bdata())!=0)
        return(err);

    /*set up Tcl/Tk GUI*/
    Interp=Tcl_CreateInterp();
    if ((err=tcl_initialize(Interp)))
        return(err);

    Tzero=time();
  
    /*Begin main event loop*/
      while(1)
        {
        check_serial();
        if (Cycling26 || Cycling10)
            check_cycle();
        Tcl_GlobalEval(Interp,"update\n");
        }

    return(0);

    }

/*INITIALIZE_IO*/
/* function intialize_io sets up the ioports for access 
  called by main()
*/
int initialize_io()

    {
    int err;
    int i;
    uid_t user; /*current user id*/
    struct termios sopts; /*serial port options*/
    char term[11];
    char buffer[80]; /*used to flush serial buffers*/

    /*give permission to acess IO ports
      0x300 is base port address
      +0 = Port A1
      +1 = Port B1
      +2 = Port C1
      +3 = Control1
      +4 = Port A2
      ...
    */

    /* ioperm must have root permission to run sucessfully*/
    /*first store current user id - then su to root*/
    user=getuid();
    setuid(0);

    err=ioperm(Base,4,1);

    /*go back to original user*/
    setuid(user);

    if (err!=0)     /*error in ioperm call*/
        {
        printf("ioperm returned %d.\n",err);
        printf("The program probably does not have root permission,\n");
        printf("required by ioperm.\n");
        return(err);
        }

    /* now set up control for I/O modes:Port A1&B1 are output; C1 is input*/
    outb(137,Control1);

    /* initialize global array of output bytes */
    for (i=0;i<24;i++)
        Obyte[i]=0;

    /* Now set up serial ports */

    for (i=0;i<NUMSPORT;i++)
        {
        sprintf(term,"/dev/ttyC%d",i);
        if ((Sfd[i] = open_sport(term))==-1)
            return(-1);

        /*make read return 0 bytes if none available*/
        fcntl(Sfd[i], F_SETFL, FNDELAY);

        /* set some port parameters */
        /*First, get current parameters*/
        tcgetattr(Sfd[i], &sopts);

        /*turn off echo*/
        sopts.c_lflag &= ~(ECHO);

        /*set canonical (line at a time mode)*/
        sopts.c_lflag |= ICANON;

        /*maps input CR to NL*/
        sopts.c_iflag |= ICRNL;
        sopts.c_iflag &= ~(IGNCR); 
        sopts.c_iflag &= ~(INLCR); 

        /*accept \r as EOL in addition to \n*/
        sopts.c_cc[VEOL]='\r';

        /*turn on output processing so other o_flags have an effect*/
        sopts.c_oflag |= (OPOST);
        /*allow me to send a CR instead of NL if I want*/
        sopts.c_oflag &= ~ONLRET;
        sopts.c_oflag &= ~ONLCR;
        sopts.c_oflag &= ~OCRNL;
 
        /*now baud rates*/
        cfsetispeed(&sopts,B9600);
        cfsetospeed(&sopts,B9600);
        /* now set to 8N1 */
        sopts.c_cflag &= ~CSIZE;
        sopts.c_cflag |= CS8;
        sopts.c_cflag &= ~PARENB;
        sopts.c_cflag &= ~CSTOPB;

        /*turn off hardware flow control*/
        sopts.c_cflag &= ~CRTSCTS;
        /*turn off software flow control on input and output*/
        sopts.c_iflag &= ~IXON;
        sopts.c_iflag &= ~IXOFF; 

        /*make these settings active now*/
        tcsetattr(Sfd[i], TCSANOW, &sopts);
        
        /*Clear/flush the input buffer*/
        while(serial_in(Sfd[i],buffer)>0);
        }

    return (0);
    }

/*OPEN_SPORT*/
/* function open_sport opens the given serial port and
   returns the file descriptor to it
   called by initialize_io()
*/
int open_sport(char *term)

    {
    int fd; /*file desriptpr*/

    fd = open (term,O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd==-1)
        fprintf(stderr,"open_sport: Unable to open %s - %s\n",
                        term,strerror(errno));
    return (fd);
    }

/*TCL_INITIALIZE*/
/* procedure tcl_initialize starts up the Tcl/Tk GUI. The
   code for which is in ./pdprp.tcl
   called by main()
*/
int tcl_initialize(Tcl_Interp *interp)

    {

    if (Tcl_Init(interp) == TCL_ERROR)
         {
         fprintf(stderr, "Tcl_Init failed: %s\n",interp->result);
         return(1);
         }

    if (Tk_Init(interp) == TCL_ERROR)
         {
         fprintf(stderr, "Tk_Init failed: %s\n",interp->result);
         return(1);
         }
    
    /*create new commands which Tcl/Tk GUI will call*/
    /*This one starts the cycling*/
    Tcl_CreateCommand (interp, "Cstart", (Tcl_CmdProc *) start_cycling,
                       (ClientData *) NULL, (Tcl_CmdDeleteProc *) NULL);
    /*This one stops the cycling*/
    Tcl_CreateCommand (interp, "Cstop", (Tcl_CmdProc *) stop_cycling,
                       (ClientData *) NULL, (Tcl_CmdDeleteProc *) NULL);
    /*This one builds up the error Ignore array*/
    Tcl_CreateCommand (interp, "Cignore", (Tcl_CmdProc *) error_ignore,
                       (ClientData *) NULL, (Tcl_CmdDeleteProc *) NULL);
    /*Reset the Enotify array*/
    Tcl_CreateCommand (interp, "Cdenotify", (Tcl_CmdProc *) error_denotify,
                       (ClientData *) NULL, (Tcl_CmdDeleteProc *) NULL);

    /*tell Tcl/Tk which file to execute*/
    Tcl_EvalFile(interp,"./pdprp.tcl");

    /*update the GUI display which in this case shows the interface*/
    Tcl_GlobalEval(interp,"update\n");

    return(0);
    }

/*P_INITIALIZE*/
/* procedure p_initialize sets fills the global arrays with port and
  pin assignments for I/O
  called by read_bdata();
*/
void p_initialize()

    {
    /*Lbcsig[] holds what port and bit to use to look for the LBC
      ok when it is done moving to a bench*/
    Lbcsig[B26e1].port = PortC1;
    Lbcsig[B26e1].bit = 0;
    Lbcsig[B26e2].port = PortC1;
    Lbcsig[B26e2].bit = 1;
    Lbcsig[B26c].port = PortC1;
    Lbcsig[B26c].bit = 2;
    Lbcsig[B26w].port = PortC1;
    Lbcsig[B26w].bit = 3;
    Lbcsig[Bsf].port = PortC1;
    Lbcsig[Bsf].bit = 4;
    Lbcsig[B10e].port = PortC1;
    Lbcsig[B10e].bit = 5;
    Lbcsig[B10c].port = PortC1;
    Lbcsig[B10c].bit = 6;
    Lbcsig[B10w].port = PortC1;
    Lbcsig[B10w].bit = 7;
    }

/*READ_BDATA*/
/* procedure read_bdata reads the bench assignment and timing information
   from the bench.params file.
   Returns values are:
   0  OK
   -1 can't open bench.params
   -2 bench.params values are inconsistent
   -3 too many 26" bench assignments
   -4 too many 10" bench assignments

   called by start_cycling()
   callas assign_dbase()
   calls p_initialize()
*/
int read_bdata()
 
    {
    FILE *ifp;      /*file pointer with bench info*/
    char s[81];     /*input string*/
    int bnum;       /*bench code*/
    int lnum;       /*LBC number*/
    char bname[9];  /*bench name*/
    char bcam[31];  /*camera name*/
    char bhc[9];    /*bench host computer*/
    int bct, bdt;   /*bench cycle and dwell times*/
    int i;          /*counter*/
    int i26,i10;    /*26" and 10" bench assignment counters*/
    int on;         /*bench on flag: 1=yes; 0=no*/

    /*initialize bench array*/
    for (i=0;i<MAXBENCH;i++)
        Bench[i].ct=Bench[i].dt=0;

    /* read in bench information form bench.params*/
    ifp=fopen("bench.params","r");
    if (ifp==NULL)
       {
       printf("cannot open file bench.params.\n");
       return(-1);
       }

    while (fgets(s,81,ifp)!=NULL)
       if (s[0]!='#')
           {
           sscanf(s,"%d %d %d %s %s %d %d %s",&bnum,&lnum,&on,bname,bcam,&bct,&bdt,bhc);
           assign_dbase(bnum,bname);
           Bench[bnum].bnum=bnum;
           Bench[bnum].lnum=lnum;
           strcpy(Bench[bnum].bench,bname);
           strcpy(Bench[bnum].camera,bcam);
           Bench[bnum].ct=bct;
           Bench[bnum].dt=bdt;
           strcpy(Bench[bnum].host,bhc);
           if (!on) /*if bench not on set ct and dt to 0*/
               {
               Bench[bnum].ct=0;
               Bench[bnum].dt=0;
               }
           }

    /*check bench.params assignments for inconsitencies*/
    if ((Bench[B26e1].ct!=0)&&(Bench[B26e2].ct!=0))
        {
        printf("Can't have both 26E benches active at once.\n");
        return(-2);
        }
    for (i=0;i<MAXBENCH;i++)
        if (Bench[i].dt>Bench[i].ct)
            {
            printf("Bench %s has dwell time %d > cycle time %d.\n",
                    Bench[i].bench,Bench[i].dt,Bench[i].ct);
            return(-2);
            }
    /*build up Bench26 and Bench10 structures -modify this if change B26e1/e2
      and sj/sf*/

    i26=i10=0;
    if (Bench[B26e1].ct)
        Bench26[i26++]=&Bench[B26e1];
    else if (Bench[B26e2].ct)
        Bench26[i26++]=&Bench[B26e2];
    if (Bench[B26c].ct)
        Bench26[i26++]=&Bench[B26c];
    if (Bench[B26w].ct)
        Bench26[i26++]=&Bench[B26w];
    if (Bench[Bsf].ct)
        Bench26[i26++]=&Bench[Bsf];
    if (i26>MAX26BEN)
        {
        printf("Too many 26\" bench assignments.\n");
        return(-3);
        }
    Num26ben = i26;

    if (Bench[B10e].ct)
        Bench10[i10++]=&Bench[B10e];
    if (Bench[B10c].ct)
        Bench10[i10++]=&Bench[B10c];
    if (Bench[B10w].ct)
        Bench10[i10++]=&Bench[B10w];
    if (i10>MAX10BEN)
        {
        printf("Too many 10\" bench assignments.\n");
        return(-4);
        }
    Num10ben = i10;

    /*setup pin assignments in global arrays*/
    p_initialize();

    for (i=0;i<Num26ben;i++)
       printf("%d %d %s\n",i,Bench26[i]->bnum,Bench26[i]->bench);
    for (i=0;i<Num10ben;i++)
       printf("%d %d %s\n",i,Bench10[i]->bnum,Bench10[i]->bench);
    return(0);
    }

/*ASSIGN_DBASE*/
/* procedure assign_dbase builds up the global data base of bench assignments
   called by read_bdata()
*/
void assign_dbase(int num, char *name)

    {
    if (!strncmp(name,"26e1",4))  /*if name=="26E1"*/
        B26e1=num;
    else if  (!strncmp(name,"26e2",4))
        B26e2=num;
    else if  (!strncmp(name,"26c",3))
        B26c=num;
    else if  (!strncmp(name,"26w",3))
        B26w=num;
    else if  (!strncmp(name,"sj",2))
        Bsj=num;
    else if  (!strncmp(name,"sf",2))
        Bsf=num;
    else if  (!strncmp(name,"10e",3))
        B10e=num;
    else if  (!strncmp(name,"10c",3))
        B10c=num;
    else if  (!strncmp(name,"10w",3))
        B10w=num;

    return;
    }

/*START_CYCLING*/
/* procedure start_cycling starts the bench cycling
   called by the pdprp.tcl Tcl/Tk GUI start command
   calls read_bdata(), goto_bench(), error_popup(), error_ignore()
         ready_for_light, rfl_timeout_check(), and check_serial
*/
int start_cycling(ClientData cd, Tcl_Interp *interp, int argc, char **argv)
    /*this is how Tcl/Tk passes parameters. I only expect one arg: scope*/
   
    {
    int err;  
    int i;
    int scope;   /*10 or 26*/
    time_t now;  /*current time*/
    int numxxben; /*either Num26ben or Num10ben*/
    Bstruct **bench; /*either points to Bench26 or Bench10*/
    time_t *ttime; /*either points to Time26 or Time10*/
    int gberr;    /*return value from goto_bench*/
    int initial_bench = 0; /*bench to go to initially*/

    /*read bench information and initialize*/
    if ((err=read_bdata())!=0)
        return(err);

    scope = atoi(argv[1]);

    if (scope==26)
        {
        /*if (Num26ben>1)*/
            /*Cycling26 = 1;*/
        numxxben = Num26ben;
        bench = Bench26;
        ttime = Time26;     
        On26=0;
        }
    else
        {
        /*if (Num10ben>1)*/
            /*Cycling10 = 1;*/
        numxxben = Num10ben;
        bench = Bench10;
        ttime = Time10;     
        On10=0;
        }

    /*clear timer for RFL timeout check*/
    Erfltime[bench[initial_bench]->bnum]=0;

    /*Check to make sure bench is ready*/
    while (!ready_for_light(bench,initial_bench,scope))
        {
        /*not ready for light- check for timeout*/
        if (rfl_timeout_check(bench,initial_bench,scope,time()))
            break; /*timed out and ignored so proceed*/
        else
            if (Rflto) /*timed out and not ignored*/
                {
                fprintf(stderr, "start_cycle: Bench %s not returning RFL\n",
                                 bench[initial_bench]->bench);
                /*XXXpopup box will say check_tel error not start_cycle error*/
                return(TCL_OK);
                }
        check_serial(); /*check serial ports for new commands*/
        }

    now=time()+1;    /*Assume all this stuff takes ~1sec*/
    /*start timers*/
    for(i=0;i<numxxben;i++)
        ttime[i]=now+i*2;

    /*reset light release signal*/
    bench[0]->lr=0;

    /*go to initial bench (make this selectable later by changing initial_bench)*/
    if ((gberr=goto_bench(bench, ttime, scope, initial_bench))!=0)
        {
        printf("start_cycling: scope=%d, gberr=%d\n",scope,gberr);
        sprintf(Tclerrs[0],"start_cycling: Error");
        sprintf(Tclerrs[1],"bench: %s - goto_bench error:", bench[0]->bench);
        switch (gberr)
            {
            case -1: strcpy(Tclerrs[2], "Light Beam Available not returned");
                     sprintf(Tclerrs[3],"%d %d",bench[0]->bnum,ELBA);
                     if (Ignore[ELBA][bench[0]->bnum])
                         error_update(3);
                     else
                         error_popup(3, scope);
                     break;
            }
        }
    
    /*set cycling flags*/
    if (scope==26)
        Cycling26=1;
    else
        Cycling10=1;

    return (TCL_OK);
    }

/*STOP_CYCLING*/
/* procedure stop_cycling stops the bench cycling
   and send light to "home" bench (same as start_cycling starts with)
   called by the pdprp.tcl Tcl/Tk GUI stop command
*/ 
int stop_cycling(ClientData cd, Tcl_Interp *interp, int argc, char **argv)
    /*this is how Tcl/Tk passes parameters. I only expect one arg: scope*/

    {
    int scope;  /*10 or 26*/
    char s[80]; /*command to send to Tcl/Tk GUI*/
    int gberr;  /*return value from goto_bench*/

    scope = atoi(argv[1]);
  
    /*in each section below, I set set tel[bench]->dt to non 0 to fool
      goto_bench into not notifying host that light is now there.
      This is ok, since on re-start, the real values get read back in*/

    if ((scope==26)&&(Cycling26))
        {
        Cycling26=0;
        sprintf(s,"bcolor .f26.f%s.fcb.cb1 normal\n",Bench26[On26]->bench);
        Tcl_GlobalEval(Interp,s); 
        /*go to initial bench (make this selectable later)*/
        Bench26[0]->dt=1;
        if ((gberr=goto_bench(Bench26, Time26, 26, 0))!=0)
            {
            printf("stop_cycling: scope=26, gberr=%d\n",gberr);
            sprintf(Tclerrs[0],"stop_cycling: Error");
            sprintf(Tclerrs[1],"bench: %s - goto_bench error:", Bench26[0]->bench);
            switch (gberr)
                {
                case -1: strcpy(Tclerrs[2], "Light Beam Available not returned");
                         sprintf(Tclerrs[3],"%d %d",Bench26[0]->bnum,ELBA);
                         if (Ignore[ELBA][Bench26[0]->bnum])
                             error_update(3);
                         else
                             error_popup(3,scope);
                         break;
                }
            }
        sprintf(s,"bcolor .f26.f%s.fcb.cb1 green\n",Bench26[0]->bench);
        Tcl_GlobalEval(Interp,s); 
        On26=0;
        /*tell hq we are not taking a picture*/
        sprintf(s,"send hq setpicstatus 26 0\n");
        Tcl_GlobalEval(Interp,s);
        }
    else if (Cycling10)
        {
        Cycling10=0;
        sprintf(s,"bcolor .f10.f%s.fcb.cb1 normal\n",Bench10[On10]->bench);
        Tcl_GlobalEval(Interp,s); 
        /*go to initial bench (make this selectable later)*/
        Bench10[0]->dt=1;
        if ((gberr=goto_bench(Bench10, Time10, 10, 0))!=0)
            {
            printf("stop_cycling: scope=10, gberr=%d\n",gberr);
            sprintf(Tclerrs[0],"stop_cycling: Error");
            sprintf(Tclerrs[1],"bench: %s - goto_bench error:", Bench10[0]->bench);
            switch (gberr)
                {
                case -1: strcpy(Tclerrs[2], "Light Beam Available not returned");
                         sprintf(Tclerrs[3],"%d %d",Bench10[0]->bnum,ELBA);
                         if (Ignore[ELBA][Bench10[0]->bnum])
                             error_update(3);
                         else
                             error_popup(3, scope);
                         break;
                }
            }
        sprintf(s,"bcolor .f10.f%s.fcb.cb1 green\n",Bench10[0]->bench);
        Tcl_GlobalEval(Interp,s); 
        On10=0;
        /*tell hq we are not taking a picture*/
        sprintf(s,"send hq setpicstatus 10 0\n");
        Tcl_GlobalEval(Interp,s);
        }
    return (TCL_OK);
    }

/*GOTO_BENCH*/
/* procedure goto_bench tells the LBC to go to the bench
   specified by the index bench in the telescope structure tel
   returns:
   0 if ok
   -1 if LBA (lightbeam available) signal not received
   -2 if serial input from host not read correctly
   -3 if host computer not ready
   calls nib1(),nib2(),bitcheck(),get_serial(),form_sout_string(),
         serial_out(), and bcolor in pdprp.tcl 
   called by start_cycling() and check_cycle()
*/
int goto_bench(Bstruct *tel[], time_t timer[], int scope, int bench)

    { 
    /*LBC is controlled through PortB1 and hence Obyte[1]*/
    /*10" takes bits 0-3; 26" uses bits 4-7*/

    int dwell;   /*dwell time for bench*/
    int i;       /*is set to tel[bench]->bnum*/
    char s[80];  /*command string sent to Tcl/Tk*/
    time_t t10la, t26la; /*time when started to look for light available sig*/
    int rval=0;          /*return value*/
    char ostring[29];    /*serial output string sent to say light is there*/
    int sport = -1;      /*index in Sfd of host serial port in use*/

    /*reset light release signals*/
    tel[bench]->lr=0;

    printf("  Goto bench code %d:%s on %d Tel. LBC# %d\n",
           tel[bench]->bnum,tel[bench]->bench,scope,tel[bench]->lnum);
    printf("   time is %ld\n",time()-Tzero);

    if (scope==10)
        {
        outb(nib1(&Obyte[1],tel[bench]->lnum),PortB1);
        printf("XXX obyte=%u;readback=%u\n", Obyte[1],inb(PortB1));
        t10la=time();
        /* check for LBC done */
        i = tel[bench]->bnum;
        /*Signal is high when light is there*/
        while (!bitcheck(Lbcsig[i].port,Lbcsig[i].bit))
            {
            if (time()>(t10la+3))   /*wait up to 3 seconds*/
                {
                /*I've timed out waiting for light available signal
                  so tell calling routine about it, but carry on as if
                  everything is ok. (The calling routine will display
                  an error box, so the user can decide if things really
                  are screwy or not....)*/
                rval=-1;
                break;
                }
            /*continue to allow user interaction with GUI while waiting*/
            Tcl_GlobalEval(Interp,"update\n");
            }
        }
    else
        {
        outb(nib2(&Obyte[1],tel[bench]->lnum),PortB1);
        printf("XXX obyte=%u;readback=%u\n", Obyte[1],inb(PortB1));
        t26la=time();
        /* check for LBC done */
        i = tel[bench]->bnum;
        /*Signal is low when light is there*/
        while (bitcheck(Lbcsig[i].port,Lbcsig[i].bit))
            {
            if (time()>(t26la+3))   /*wait up to 3 seconds*/
                {
                /*see coments in this loop in the 10" section*/
                rval=-1;
                break;
                }
            /*continue to allow user interaction with GUI while waiting*/
            Tcl_GlobalEval(Interp,"update\n");
            }
        }

    /* ADD Synch mode? */
    /* ADD Check filter */

    /*turn active bench red*/
    sprintf(s,"bcolor .f%d.f%s.fcb.cb1 red\n",scope,tel[bench]->bench);
    Tcl_GlobalEval(Interp,s);
    /*tell hq which bench now has light*/
    sprintf(s,"send hq setlightbench %d %s\n",scope,tel[bench]->bench);
    Tcl_GlobalEval(Interp,s);

    /*before telling computer to take picture- make sure telescope is
      not moving*/
    /*this lets pdprp.tcl know if any telescope is moving or not*/
    sprintf(s,"gettm 0\n");
    Tcl_GlobalEval(Interp,s);
    /*now get that information ourselves from pdprp.tcl variable
      and loop until telescope is no longer moving - then tell host ok*/
    /*XXXdo I timeout here or have better logic other than if any telescope
         moving, wait until it stops?*/
    while (atoi(Tcl_GetVar(Interp,"Telmov",TCL_GLOBAL_ONLY)))
        {
        sprintf(s,"gettm 0\n");
        Tcl_GlobalEval(Interp,s);
        /*print the waiting message on the GUI*/
        Tcl_GlobalEval(Interp,"pmesg wait\n");
        Tcl_GlobalEval(Interp,"update\n");
        }
    /*I'm done- telescope is not moving - clear GUI message and proceed*/
    Tcl_GlobalEval(Interp,"pmesg clear\n");
   
    /* if not dwelling, communicate with host computer */
    if (tel[bench]->dt == 0)      /*i.e. not dwelling*/
        {
        if (strncmp ("CCDMac",tel[bench]->host,6)==0)
            sport=MACSPORT;
        else if (strncmp ("OSLVax",tel[bench]->host,6)==0)
            sport=OSLSPORT;
        else if (strncmp ("VMGVax",tel[bench]->host,6)==0)
            sport=VMGSPORT;

        if (sport==MACSPORT)
            {
            /*before telling computer light is there, clear the input
              serial buffer*/
            while(get_serial(sport)!=0);
            /*now form the output string and send to host computer*/
            form_sout_string(tel[bench]->bnum,scope,ostring);
            serial_out(Sfd[sport],ostring);
            }
        if ((sport==OSLSPORT)||(sport==VMGSPORT))
            serial_out(Sfd[sport],"LBA\r");
        }
    /*tell hq we are taking a picture*/
    sprintf(s,"send hq setpicstatus %d 1\n",scope);
    Tcl_GlobalEval(Interp,s);

    /*set timer*/
    if ((dwell=tel[bench]->dt))
        timer[bench]=time()+dwell;
    else
       timer[bench]=time()+tel[bench]->ct;

    return(rval);
    }

/*CHECK_SERIAL*/
/* procedure check_serial checks all the serial ports for incoming commands.
   It responds to IFR and END from Vaxes.
   It fills global arrays for each signal for program access later.
   called by main() 
             start_cycling()
   calls get_serial()
         find_sport_tel()
         serial_out()
         form_vaxsout_string()
*/
void check_serial()

    {
    int sport;  /*serial port to check*/
    int scom;   /*incoming serial command return code*/
    int tel;    /*telescope on which the camera controlled by the host
                  connected to the given serial port is located*/
    char vistring[80]; /*Vax Information response STRING*/
    char s[80];  /*command sent to Tcl/Tk interpreter*/

    for (sport=0;sport<NUMSPORT;sport++)
        {
        scom=get_serial(sport); /*get and parse command*/
        switch (scom)
            {
            case 0  : break; /*no command received*/
            case RFL: Srfl[sport]=1; /*Ready for Light*/
                      break;
            case DWL: Sdwl[sport]=1; /*Done with Light*/
                      break;
            case END: tel=find_sport_tel(sport); /*End - sequence terminated*/
                      if (tel<27) 
                          {
                          /*respond and stop cycling*/
                          serial_out(Sfd[sport],"RDY\r");
                          sprintf(s, "Cstop %d\n",tel);
                          Tcl_GlobalEval(Interp,s);
                          /*XXXI should pop up some dialog box, but for now just stop*/
                          }
                       else
                          {
                          /*tel >27 so it must be 36 which means this serial port
                            talks to a host controlling benches on both telescopes
                            so respond and stop cycling on both telescopes.
                            currently this will never happen since only Vaxes send
                            END, but maybe we will modify the Mac or new computers
                            to do so someday - so here is the logic in case.*/
                          serial_out(Sfd[sport],"RDY\r");
                          sprintf(s, "Cstop 10\n");
                          Tcl_GlobalEval(Interp,s);
                          sprintf(s,"Cstop 26\n");
                          Tcl_GlobalEval(Interp,s);
                          /*XXXI should pop up some dialog box, but for now just stop*/
                          }
                      break;
            case IFR: tel=find_sport_tel(sport); /*Information request*/
                      /*This is only VAX - so tel can only be 10 or 26*/
                      form_vaxsout_string(tel,vistring);
                      serial_out(Sfd[sport],vistring);
                      break;
            }
        }
    }

/*FIND_SPORT_TEL*/
/* function find_sport_tel returns the telescope of the bench
   controlled by the given serial port
   returns 36 if both telescopes contain a bench controlled by sport
   called by check_serial()
             get_serial()
*/
int find_sport_tel(int sport)

    {
    char host[9]; /*host computer name*/
    int i;
    int tel=0;    /*the telescope we are after*/

    /*determine host name given serial port*/
    if (sport==MACSPORT)
        sprintf(host,"CCDMac");
    else if (sport==OSLSPORT)
        sprintf(host,"OSLVax");
    else if (sport==VMGSPORT)
        sprintf(host,"VMGVax");
    else
        fprintf(stderr,"ERROR: find_sport -unknown serial port %d\n",sport);
 
    for (i=0;i<Num26ben;i++)
        if (strncmp(host,Bench26[i]->host,6)==0)
           tel=26;
    for (i=0;i<Num10ben;i++)
        if (strncmp(host,Bench10[i]->host,6)==0)
            if (tel==26) /*already found this host on 26"*/
                tel=36;
            else 
                tel=10;

    if (tel==0)
        fprintf(stderr,"ERROR:find_sport- can't find bench controlled by %s\n",host);
        /*this is a major problem- the rest of the program won't work*/

    return(tel);
    }

/*CHECK_CYCLE*/
/* procedure check_cycle does all the cycle checking and updating stuff
   called by main()
   calls check_tel()
         goto_bench()
         bcolor in pdprp.tcl
*/
void check_cycle()

    {
    int next_ben;  /*next bench to go to*/
    char s[80];    /*command to send to the Tcl/Tk GUI*/
    int gberr;     /*return value from goto_bench*/

    /*have to add synch stuff here*/

    /*check 10" bench to see if it is ready to switch*/
    if (Cycling10) 
        if ((next_ben=check_tel(Bench10,Time10,On10,Num10ben,10))>=0)
            {
            /*telescope is ready to switch*/
            /*turn off red light on bench in GUI*/
            sprintf(s,"bcolor .f10.f%s.fcb.cb1 normal\n",Bench10[On10]->bench);
            Tcl_GlobalEval(Interp,s); 
            if ((gberr=goto_bench(Bench10,Time10,10,next_ben))!=0)
                {
				printf("check_cycle: scope=10, gberr=%d\n",gberr);
                sprintf(Tclerrs[0],"check_cycle: Error");
                sprintf(Tclerrs[1],"bench: %s - goto_bench error:",
                        Bench10[next_ben]->bench);
                switch (gberr)
                    {
                    case -1: strcpy(Tclerrs[2],
                                    "Light Beam Available not returned");
                             sprintf(Tclerrs[3],"%d %d",Bench10[next_ben]->bnum,
                                     ELBA);
                             if (Ignore[ELBA][Bench10[next_ben]->bnum])
                                 error_update(3);
                             else
                                 error_popup(3, 10);
                             break;
                    }
                }
            On10 = next_ben;
            }

    /*check 26" bench to see if it is ready to switch*/
    if (Cycling26)
        if ((next_ben=check_tel(Bench26,Time26,On26,Num26ben,26))>=0)
            {
            /*telescope is ready to switch*/
            /*turn off red light on bench in GUI*/
            sprintf(s,"bcolor .f26.f%s.fcb.cb1 normal\n",Bench26[On26]->bench);
            Tcl_GlobalEval(Interp,s); 
            if ((gberr=goto_bench(Bench26,Time26,26,next_ben))!=0)
                {
				printf("check_cycle: scope=26, gberr=%d\n",gberr);
                sprintf(Tclerrs[0],"check_cycle: Error");
                sprintf(Tclerrs[1],"bench: %s - goto_bench error:",
                        Bench26[next_ben]->bench);
                switch (gberr)
                    {
                    case -1: strcpy(Tclerrs[2],
                                    "Light Beam Available not returned");
                             sprintf(Tclerrs[3],"%d %d",Bench26[next_ben]->bnum,
                                     ELBA);
                             if (Ignore[ELBA][Bench26[next_ben]->bnum])
                                 error_update(3);
                             else
                                 error_popup(3, 26);
                             break;
                    }
                }
            On26 = next_ben;
            }
    return;
    }

/*CHECK_TEL*/
/* procedure check_tel checks to see if the given telescope is ready to
  change benches.  bench is the telescope index of the current bench.
  Returns: next bench number if ready
           -1 if not.
           -2 if timed out waiting for light release
           return value from light_released
   called by check_cycle() 
   calls light_released(), ready_for_light()
         error_popup(), error_update()
*/
int check_tel(Bstruct *tel[], time_t timer[], int bench, int maxben, int scope)
    /*tel is either Bench10 or Bench26, similar with timer = Time26 or Time10
      and maxben is either Num26ben or Num10ben*/
    {
    time_t now;  /*current time*/
    int i;
    int lerr; /*light_released() return value*/
    int noto=0; /*flag for no time out or not*/
    time_t totime=0; /*time out time- time to time out looking for RFL*/
    char s[80];      /*command sent to pdprp.tcl*/
 
    now=time();
    if ((tel[bench]->dt)&&(!tel[bench]->lr)) /*are we in dwell mode?*/
        if (timer[bench] >= now)  /*yes; are we done dwelling?*/
            return(-1);               /*nope - not done dwelling*/
        else                   
            {                         /*yes - done dwelling*/
            tel[bench]->lr = 1;   /*simulate the light release signal*/
            /*set time for next cycle*/
            timer[bench]=now+tel[bench]->ct - tel[bench]->dt;
            /*light is released so send message to hq saying so*/
            sprintf(s,"send hq setpicstatus %d 0\n",scope);
            Tcl_GlobalEval(Interp,s);
            }
    else  /*not in dwell mode or dwell over; check light release*/
         if (!tel[bench]->lr)     /*if light wasn't previously released*/
             {
             if ((lerr=light_released(tel,bench,scope))==0) /*light not now released?*/
                 {
                 /*no light not released*/
                 /*set timeout time= twice cycle time normally,unless
                   cycle time is <=5s,then it is 60 seconds from expected
                   time unless it is the VMGVax in which case I simply don't
                   check for timeout since I really have no idea how long this
                   particular VMG should take.*/
                 if (tel[bench]->ct>=5)
                     totime=timer[bench]+tel[bench]->ct;
                 else
                     if (strncmp(tel[bench]->host,"VMGVax",6)!=0)
                         totime=timer[bench]+60;
                     else
                          noto=1;
                         
                 if ((now>totime) && (!Enotify[EDWL][tel[bench]->bnum]) && (!noto))
                 /*no-wait for timeout period unless instructed otherwise via above logic*/
                     {
                     /*timeout period has passed and the bench has not released 
                       the light*/
                     fprintf(stderr, "check_tel: Bench %s not returning DWL\n",
                                      tel[bench]->bench);
                     sprintf(Tclerrs[0],"check_tel: Error");
                     if (tel[bench]->ct>=5)
                         sprintf(Tclerrs[1],"bench: %s - check_tel waited two cycle times",
                                             tel[bench]->bench);
                     else
                         sprintf(Tclerrs[1],"bench: %s - check_tel waited 60 seconds",
                                             tel[bench]->bench);
                     sprintf(Tclerrs[2], "Light not released by bench");
                     sprintf(Tclerrs[3], "%d %d",tel[bench]->bnum,EDWL);

                     Enotify[EDWL][tel[bench]->bnum]=1;
                     if (Ignore[EDWL][tel[bench]->bnum])
                         {
                         error_update(3);
                         Enotify[EDWL][tel[bench]->bnum]=0;
                         /*I'm ignoring - so continue as if it's ok*/
                         /*set things up and fall through without returning*/
                         tel[bench]->lr=1;
                         lerr=1;
                         }
                     else
                         {
                         error_popup(3, scope);
                         return(-2);
                         }
                     }
                 else 
                     return(-1);     /*light not released and not timed out*/
                 }
             else if (lerr==1)
                {
                /*light is now released*/
                /*so send message to hq saying so*/
                sprintf(s,"send hq setpicstatus %d 0\n",scope);
                Tcl_GlobalEval(Interp,s);
                }
             if (lerr<0) /*got some kind of error from light_released()*/
                 return(lerr);
             }

    /*I only get here if light is released or dwell time is over*/
    /*check if any cycle timers are ready*/
    /*Or I am simulating light release by Ignoring the error condition*/

    if (Rflto)   /*if I'm in an RFL timeout with active error box*/
        return(-1); /*don't do anything*/
    for (i=bench+1;i<maxben;i++)
        if (timer[i] <= now)
            if (ready_for_light(tel,i,scope))
                {
                Erfltime[tel[i]->bnum]=0;
                return(i);
                }
            else
                if (rfl_timeout_check(tel,i,scope,now)) /*timed out & ignored*/
                    return(i);
    if (Rflto)   /*I just got an RFL timeout box, so don't do anything*/
        return(-1);
    for (i=0;i<=bench;i++)
        if (timer[i] <= now)
            if (ready_for_light(tel,i,scope))
                {
                Erfltime[tel[i]->bnum]=0;
                return(i);
                }
            else
                if (rfl_timeout_check(tel,i,scope,now)) /*timed out & ignored*/
                    return(i);

    /*no cycle timers ready or benches not ready*/    
    return(-1);
    }

/*LIGHT_RELEASED*/
/* procedure light_released sees if the light from the specifed bench has been
  released by host computer
  returns 1 if light released
          0 if light not released
         (return values between -1 and -5 inclusive are reserved for check_tel())
  called by check_tel()
*/
int light_released(Bstruct *tel[], int bench, int scope)
    /*tel is the telescope structure to check (Bench10 or Bench26)*/
    /*bench is the bench # of the bench to check*/ 
    /*scope is the telescope - either 10 or 26*/

    {
    int sport = -1; /*index for serial port to check in Sfd*/

     /*if all hosts have same language, I can simplify this 
       be setting the Sfd[] index given Bench[bench]->host and making
       all benches go through all this shit below*/

    if (strncmp("CCDMac",tel[bench]->host,6)==0)
        sport=MACSPORT;
    else if (strncmp("OSLVax",tel[bench]->host,6)==0)
        sport=OSLSPORT;
    else if (strncmp("VMGVax",tel[bench]->host,6)==0)
        sport=VMGSPORT;

    if (Sdwl[sport]==1) /*got a DWL signal*/
        {
        Sdwl[sport]=0; /*reset it*/
        tel[bench]->lr=1;
        if ((sport==OSLSPORT)||(sport==VMGSPORT))
            serial_out(Sfd[sport],"RDY\r");
        return(1);
        }
    else
       return(0);
    }

/*READY_FOR_LIGHT*/
/* function ready_for_light checks to see if ready for light
   signal has been sent by host computer.
   returns 1 if yes (host is ready for light)
           0 if no  (host not ready for light)
   called by check_tel()
*/
int ready_for_light(Bstruct *tel[], int bench,int scope)
    /*tel is the telescope structure to check (Bench10 or Bench26)*/
    /*bench is the bench # of the bench to check*/ 
    /*scope is the telescope - either 10 or 26*/
    
    {
    int sport = -1; /*index for serial port to be used in Sfd[]*/
   

    if (strncmp("CCDMac",tel[bench]->host,6)==0)
        sport=MACSPORT;
    else if (strncmp("OSLVax",tel[bench]->host,6)==0)
        sport=OSLSPORT;
    else if (strncmp("VMGVax",tel[bench]->host,6)==0)
        sport=VMGSPORT;
    
    if (sport==-1) /*either None or unknown host*/
        return(1);

    if (Srfl[sport]==1)
        {
        Srfl[sport]=0;
        return(1);
        }
    else
        return(0);

    }

/*RFL_TIMEOUT_CHECK*/
/* I didn't get an RFL when the bench was ready- so see if it has timed
   out.
   returns 0 Not timed out or timed out and not ignored
           1 if timeout and ignore
   sets global variable Rflto to 1 if timed out and not ignored

   called by check_tel(), start_cycling
   calls error_update() and error_popup()
*/
int rfl_timeout_check(Bstruct *tel[], int bench, int scope, time_t now)
    /*tel[] is either Bench26[] or Bench10[]*/
    
    {
    int bn; /*short for tel[bench]->bnum*/
    int rval = 0; /*return value*/
    int noto=0; /*flag for no time out or not*/

    /*bench is not ready for light even though it's time has
      come - so check to see if it has timed out- which I 
      define here as two cycle times after its timer becomes ready*/
    bn=tel[bench]->bnum;

    /*Here is how I set the timeout time:
      if cycle time is >=5 seconds, set timeout = twice the cycle time
      if it's not, then set it to 60 seconds from now unless it's the
      VMG that is being controlled in which case don't ever time out- this
      is not perfect, but it's the best I could come up with.*/
    if (Erfltime[bn]==0)
        if (tel[bench]->ct>=5)
           Erfltime[bn]=now+2*tel[bench]->ct;  /*timeout = 2*cycle time*/
        else
            if (strncmp(tel[bench]->host,"VMGVax",6)!=0)   /*if not vmgvax*/
                Erfltime[bn]=now+60; /*time out after one minute*/
            else
                noto=1;
    else
        if ((now > Erfltime[bn]) && (!Enotify[ERFL][bn]) && (!noto))
            /*check for time-out unless noto flag has been set by above logic*/
            {
            /*Yup- I waited two cycle times and I stil do not get an RFL*/
            /*so do the error thing*/
            fprintf(stderr, "check_tel: Bench %s not returning RFL\n",
                             tel[bench]->bench);
            sprintf(Tclerrs[0],"check_tel: Error");
            if (tel[bench]->ct>=5)
                sprintf(Tclerrs[1],"bench: %s - check_tel waited two cycle times",
                        tel[bench]->bench);
            else
                sprintf(Tclerrs[2],"bench: %s - check_tel waited 60 seconds",
                        tel[bench]->bench);
            sprintf(Tclerrs[2], "Computer not reporting Ready For Light");
            sprintf(Tclerrs[3], "%d %d",bn,ERFL);
            Enotify[ERFL][bn]=1;
            if (Ignore[ERFL][bn])
                {
                error_update(3);
                Enotify[ERFL][bn]=0;
                Erfltime[bn]=0;
                rval=1;
                }
            else
                {
                Rflto=1;
                error_popup(3, scope);
                }
            }

    return (rval);
    }

/*NIB1*/
/* function nib1 sets the first nibble (4 bits) of the given byte to the
   supplied value
   called by goto_bench() 
*/
unsigned char nib1(unsigned char *byte, unsigned char value)

    {
    unsigned char mask = 0x0F;

    if (value > 15)
        {
        printf ("nib1: Cannot set a nibble to value %d which is >15!\n",value);
        exit (-1);
        }

    *byte &= ~mask;  /*strip low 4 bits*/
    *byte |= value;  /*set low 4 bits to value*/
    
    return (*byte);
    }

/*NIB2*/
/* function nib2 sets the second nibble (4 bits) of the given byte to the
   supplied value
   called by goto_bench() 
*/
unsigned char nib2(unsigned char *byte, unsigned char value)

    {
    unsigned char mask = 0xF0;


    if (value > 15)
        {
        printf ("nib2: Cannot set a nibble to value %d which is >15!\n",value);
        exit (-1);
        }

    *byte &= ~mask;         /*strip upper 4 bits*/
    *byte |= (value << 4);   /*set upper 4 bits to value*/

    return (*byte);
    }

/*BITCHECK*/
/* function bitcheck returns the value of the given bit from the 
   given input port.
   called by goto_bench()
*/
int bitcheck (unsigned long int port, unsigned char bit)
   
   {
   return( (inb(port)&(1<<bit))>>bit );
   }

/*ERROR_POPUP*/
/*procedure error_popup pops up an error box in the Tcl/Tk GUI
  with a three-line  message and a stop, continue,and ignore button
  called by start_cycling(), stop_cycling(), check_cycle()
  calls errstr and berror in pdprp.tcl
*/
void error_popup (int numstr, int scope)

    {
    char s[80];
    int i;
    
    printf("%c%c%c\n",BELL,BELL,BELL);
    for (i=0;i<=numstr;i++)
        {
        sprintf(s,"errstr %d {%s}\n",i,Tclerrs[i]);
        Tcl_GlobalEval(Interp,s);
        }

    sprintf(s,"berror %d %d\n",numstr, scope);
    Tcl_GlobalEval(Interp,s); 
    }

/*ERROR_UPDATE*/
/*procedure error_update just allows the Tcl/TK GUI
  to update the last error message without popping
  up an error box. Used for ignored messages
  called by start_cycling, stop_cycling, check_cycle
  calls errstr and lerrupdate in pdprp.tcl
*/
void error_update (int numstr)

    {
    char s[80];
    int i;

    printf("%c\n",BELL);
    for (i=0;i<=numstr;i++)
        {
        sprintf(s,"errstr %d {%s}\n",i,Tclerrs[i]);
        Tcl_GlobalEval(Interp,s);
        }
    
    strcpy(s, "lerrupdate ignore\n");
    Tcl_GlobalEval(Interp,s);
    }

/*ERROR_IGNORE*/
/*function error_ignore set up the global Ignore array with
  information on which errors to ignore- ie. not notify the
  observer about. Also clears any global ERROR flags
  called by the Tcl/Tk code routine errignore
*/
int error_ignore (ClientData cd, Tcl_Interp *interp, int argc, char **argv)
    /*This is what the Tcl/Tk interface needs to call a C routine*/
    /*I expect two int parameters as input: errbench and errcode*/
    {
    int errbench; /*bench which has the error*/
    int errcode;  /*error code*/

    errbench = atoi(argv[1]); 
    errcode = atoi(argv[2]);

    Ignore[errcode][errbench]=1;

    if (errcode==ERFL)
        Rflto=0;

    return(TCL_OK);
    }

/*ERROR_DENOTIFY*/
/*function error_denotify clears the Enotify array once an error
  has been acknowledged by the user- either through the Stop,
  Continue, or Ignore  command in pdprp.tcl.
  called by podprp.tcl
*/
int error_denotify (ClientData cd, Tcl_Interp *interp, int argc, char **argv)
    /*This is what the Tcl/Tk interface needs to call a C routine*/
    /*I expect two int parameters as input: errbench and errcode*/
    {
    int errbench; /*bench which has the error*/
    int errcode;  /*error code*/

    if (argc==3) /*$errstring(3) in pdprp.tcl was passed as two paramteters*/
        { 
        errbench = atoi(argv[1]); 
        errcode = atoi(argv[2]);
        }
    else /*$errstring(3) in pdprp.tcl was passed as one parameter*/
        sscanf(argv[1],"%d %d",&errbench,&errcode);

    Enotify[errcode][errbench]=0;

    if (errcode==ERFL)
        Rflto=0;

    return(TCL_OK);
    }
/*SERIAL_OUT*/
/* function serial_out sends the given string out the 
   given serial port (described by its file descriptor)
   returns the # of bytes written or the err value
   called by goto_bench()
             check_serial()
*/
int serial_out (int sfd, char *string)
   
    {
    int n;
    int err;

    n=strlen(string);
    if ((err=write(sfd,string,n))<0)
        fprintf(stderr,"serial port write failed: %s\n",string);

    return(err);
    }

/*SERIAL_IN*/
/* function serial_in reads a string from the given 
   serial port (described by its file descriptor).
   it will read a max of 80 bytes
   returns # bytes read or the error number
   called by get_serial()
             initialize_io()
*/
int serial_in (int sfd, char *string)
 
    {
    int n=0;

    if ((n=read(sfd,string,80))>0)
        string[n]='\0';
    return (n);
    }

/*GET_SERIAL*/
/* function get_serial gets a command from the serial port
   and parses it,
   returning the command code corresponding
             0 if no command
            -1 if unknown command
   called by goto_bench() (only to clear mac buffer)
             check_serial()
   calls serial_in()
         find_sport_tel()
*/
int get_serial(int sport)
    /*sport is the index in Sfd of the serial port to use*/ 
    {
    int serr;         /*serial_in return value*/
    char command[80]; /*the input serial command string*/
    char s[80];       /*command sent to pdprp.tcl*/
    int tel;          /*telescope on which osl or vmg is*/
    int *llong;       /*either Long10 or Long26*/
 
    serr=serial_in(Sfd[sport], command);
    if (serr<=0) /*no command*/
        return(0);
    printf("XXXget_serial got %s\n",command);

    if (sport==MACSPORT)   /*Talking to Mac*/
        {
        if (strncmp("L",command,1)==0) /*Ready For Light*/
            return(RFL); 
        if (strncmp("R",command,1)==0) /*Done With Light*/
            return(DWL);
        }

     else if ((sport==OSLSPORT)||(sport==VMGSPORT))
        {
        if (strncmp("WAI",command,3)==0) /*Ready for Light*/
            return(RFL);
        if (strncmp("SET",command,3)==0) /*Ready for Light*/
            return(RFL);
        if (strncmp("EVM",command,3)==0) /*Done with Light*/
            {
            /*check to see if I was in long obs previously*/
            tel=find_sport_tel(sport);
            /*Vaxes control only one instrument, so tel must be either
              10 or 26- it will never be 36 which means both telescopes
              are controlled trough given serial port*/
            if (tel==10)
                llong=&Long10; 
            else
                llong=&Long26;
            if (*llong)
                {
                /*was in long mode- so unset it*/
                printf("XXXunsetting longobs for %d tel\n",tel);
                sprintf(s,"send hq setlongobs %d\n",-1*tel);
                Tcl_GlobalEval(Interp,s);
                *llong=0;
                }
            return(DWL);
            }
        if (strncmp("LBR",command,3)==0) /*Done with Light*/
            {
            /*this means done with light, but a multi-cycle observation
              in in progress, so don't move the telescope...*/
            tel=find_sport_tel(sport);
            if (tel==10)
                llong=&Long10; 
            else
                llong=&Long26;
            if (!*llong)
                {
                /*have not previously set long observation mode- so set it now*/
                printf("XXXsetting longobs for %d tel\n",tel);
                sprintf(s,"send hq setlongobs %d\n",tel);
                Tcl_GlobalEval(Interp,s);
                *llong=1;
                }
            return(DWL);
            }
        if (strncmp("IFR",command,3)==0) /*Got IFR*/
            return(IFR);
        if (strncmp("END",command,3)==0) /*Got END*/
            return(END);
        }
     else  /*sport != MAC or OSL/VMG SPORT & I don't know how to handle it*/

        {
        fprintf(stderr,"get_serial: don't know serial port %d\n",sport);
        return(-1);
        }


    /*Must have gotten an unknown command, then*/
    fprintf(stderr,"get_serial: got unknown command %s\n",command);
    fprintf(stderr,"from port %d.\n",sport);
    return(-1);
    }

/*FORM_SOUT_STRING*/
/* procedure form_sout_string forms the output string 
   sent through the serial port to the Mac computer
   saying light is there. The string's format is:
   chars  contents
   -----  --------
   0-3    RA (" from disk center)
   4      E or W
   5-8    DEC (" from disk center)
   9      N or S
   10-11  Bench code where light is
   12-19  Filter name  string
   20-27  Wavlength name string
   28     \n (newline)
   29     \0

called by goto_bench()
*/
void form_sout_string(int bnum, int scope, char *string)

    {
    char s[80];   /*command string sent pdprp.tcl*/
    int x, y;     /*X and Y position of telescope*/
    char xd, yd;  /*X and Y direction*/

    /*XXXI don't yet know filter or wavelength*/

    /*get position from pdprp.tcl*/
    sprintf(s,"gettp %d\n",scope);
    Tcl_GlobalEval(Interp,s);
    if (scope==10)
        {
        x=atoi(Tcl_GetVar(Interp,"X10",TCL_GLOBAL_ONLY));
        y=atoi(Tcl_GetVar(Interp,"Y10",TCL_GLOBAL_ONLY));
        }
    else
        {
        x=atoi(Tcl_GetVar(Interp,"X26",TCL_GLOBAL_ONLY));
        y=atoi(Tcl_GetVar(Interp,"Y26",TCL_GLOBAL_ONLY));
        }

    if (x>0)
        xd='W';
    else
        {
        x*=-1;
        xd='E';
        }
    if (y>0)
        yd='N';
    else
        {
        y*=-1;
        yd='S';
        }
    
    sprintf(string,"%04d%c%04d%c%02dFILTERXXWAVELENX\n\0",x,xd,y,yd,bnum);
    printf("XXXsending %s to mac\n",string);
    return;
    }

/*FORM_VAXSOUT_STRING*/
/* procedure form_vaxsout_string forms the string to be sent out
   the serial port to the Vaxes when they request information.
   The format is
    chars  content
    -----  -------
    char   IFO
    char   space
    int    telescope # 26 or 10
    char   space
    int    RA (" from disk center)
    char   space
    int    DEC (" from disk center)
    char   space
    int    region number

   called by get_serial()
*/
void form_vaxsout_string(int scope, char *string)

    {
    char s[80];   /*command string sent pdprp.tcl*/
    int x, y;     /*X and Y position of telescope*/

    /*Currently, I know everything but region numberXXX*/

    /*get position from pdprp.tcl*/
    sprintf(s,"gettp %d\n",scope);
    Tcl_GlobalEval(Interp,s);
    if (scope==10)
        {
        x=atoi(Tcl_GetVar(Interp,"X10",TCL_GLOBAL_ONLY));
        y=atoi(Tcl_GetVar(Interp,"Y10",TCL_GLOBAL_ONLY));
        }
    else
        {
        x=atoi(Tcl_GetVar(Interp,"X26",TCL_GLOBAL_ONLY));
        y=atoi(Tcl_GetVar(Interp,"Y26",TCL_GLOBAL_ONLY));
        }

    sprintf(string,"IFO %2d %d %d 0\r\0",scope,x,y);
    printf("XXXsending %s to vax\n",string);
    return;
    }
