diff --git a/src/util/lopi.c b/src/util/lopi.c new file mode 100644 index 000000000..b689be7c7 --- /dev/null +++ b/src/util/lopi.c @@ -0,0 +1,2215 @@ +/* $Id$ + * Author: Betty Ann Gunther + * Date: 02-15-91 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 02-26-91 bg Changed cursor to move only to controls. + * .02 07-03-91 rac changed to use "lopi" rather than "bw" + */ + + +#include "lopi_def.h" + +struct except_node{ + long status; /* Channel access standard status code. */ + char msg[MAX_LIN_LEN]; /* Character string containing context information. */ + short flg; /* Flag to indicate the arrival of an exception. */ + }; + +struct ev_node{ + short data_flg; /* Flag to indicate that new data has been added. */ + short status ; /* Status returned from function calls. */ + short severity; + char str[MAX_STRING_SIZE]; + short prev_size; /* Size of string. */ + }; + + +struct mon_node{ /* Structure to hold update information. */ + short l_crn_row; /* Row location of upper left hand corner. */ + short l_crn_col; /* Col location of upper left hand corner. */ + short r_crn_row; /* Row location of lower right hand corner. */ + short r_crn_col; /* Col location of lower right hand corner. */ + char chan[CHAN_NM_SIZE]; /* Channel name. */ + chid chan_id; /* Channel id for channel access. */ + short chan_type; + short prev_size; + struct ev_node *ev_ptr; /* Pointer to event handler info. */ + short conn_flg; /* Pointer to connection handler info. */ + struct mon_node *next; /* Pointer to next node in the linked list. */ + struct mon_node *prev; /* Pointer to previous node in the linked list. */ + }; + +struct txt_node{ /* Structure to hold text label information. */ + short l_crn_row; /* Row location of upper left hand corner. */ + short l_crn_col; /* Col location of upper left hand corner. */ + char txt_str[MAX_LIN_LEN]; /* Text string to be displayed. */ + struct txt_node *next; /* Pointer to next node. */ + }; + +struct window_node{ /* Structure which will hold all window information. There + will be a linked list of each kind of structure possible. + This structure will contain a pointer to the first node on + that list. */ + struct mon_node *c_head; + struct mon_node *m_head; + struct txt_node *t_head; + }; + +static SEM_ID mon_sem,key_sem; /* Semaphores for protection of monitor and control + linked lists, the key board and screen, and the + exception handler's structure */ + +static struct window_node *wind_array[MAX_DISP_NUM]; /* Array of windows available. */ + +static char disp_lst[FN_LEN] = "lopi.dat"; /* Array for name of file containing list of display files. */ +static char *d_menu[MXMENU]; +static short nxt_ln = 0; /* Counter. */ +static short data_flg; /* Global Flag to indicate a monitor has received data. Set in event handler. + Reset in display_monitors when data has been displayed. */ +static short m_row; /* Current row number . */ +static short m_col; /* Current column number. */ +static int n_lines = 0; /* Number of Menu lines read in. */ + +static int tid1,tid2; /* Task id's */ +static short screen = OFF; +static short init = TRUE; /* Flag which indicates first pass through the menu. */ +static char key_buff[BUFF_SIZE],*rd_ptr,*wr_ptr; /* Buffer which will accumulate keyboard input while + waiting for a chance to update the screen. Pointers showing where + next available spot is for reading and for writing. */ +static char val_in[MAX_STRING_SIZE]; /* Global buffer for user input value to be written to data base. */ +static short q_num; /* Number of items waiting to be output from input queue. */ +static short cur_mv; /* Flag to indicate that arrow keys are being struck */ +static int status; + +static int err_fd; /* Fd to which logMsg will write during the course of this + subroutine. */ +static int lopi_fd; /* File descriptor for serial port. */ +struct except_node *except_ptr; + +VOID lopi() + +{ + + /* Create and initialize semaphores protecting the keyboard and the + monitor linked list. */ + mon_sem = semCreate(); + key_sem = semCreate(); + semGive(mon_sem); + semGive(key_sem); + except_ptr = except_alloc(); + + /* Initialize buffer for keyboard input. */ + + blank_fill(key_buff,BUFF_SIZE); + + /* Open file in which errrors will be recorded. */ + SEVCHK(ca_task_initialize(),"Unable to initialize."); + + q_num = 0; + + tid1 = taskSpawn("lopi_init",210,VX_STDIO||VX_FP_TASK,20000,lopi_init,&screen,&q_num,&data_flg, + key_buff,val_in); + tid2 = taskSpawn("get_key",208,VX_STDIO||VX_FP_TASK,5000,get_key,&q_num, + key_buff,&screen,val_in); + + /* Suspend shell to allow lopi the use of the keyboard. */ + taskSuspend(SHELL_ID); + } + +/**********************************************************************************/ +/* Entry point for local terminal. Reads a file called lopi.dat which contains a */ +/* list of display files. Reads in the display names and the display files and */ +/* makes a menu from which the user can select the display he wants to see. */ +/* Display information is kept in an array of window structures. Displays the */ +/* selected display. +/**********************************************************************************/ + +static VOID lopi_init(screen,que_num,pdata_flg,k_buff,val_in) + short *screen; + short *que_num; /* Number of items in keyboard buffer waiting to be read. */ + short *pdata_flg; /* Flag to indicate that monitor had received new data. */ + char k_buff[BUFF_SIZE]; /* Buffer for key board entries and pointer + to the next empty space in the buffer */ + char val_in[MAX_STRING_SIZE]; + { + char *read_ptr, *write_ptr; /* Pointers to next available places for reading and + writing in the keyboard input buffer. */ + short selected; + char prefix[MAX_LIN_LEN]; + char txt_str[MAX_LIN_LEN]; + register short display = ON; + int disp_nm[FN_LEN]; + short t_row, t_col = 0; + int new_fd; /* Fd to which standard error is now writing. This is the fd + to which logMsg will be reset as this subroutine terminates. */ + + read_ptr = k_buff; + write_ptr = k_buff; + err_fd = creat("lopi_err",0666); + err_fd = open("lopi_err",UPDATE,0666); + + + + /* Build menu a from file called lopi.dat. Count the lines read in. This + is the number of files to lopi read in. */ + create_menu(d_menu,&n_lines); + get_displays(d_menu,n_lines,wind_array); + + /* Gives user a chance to read error messages generated by get_displays. */ + + taskDelay(100); + + /* Open a file descriptor for the serial terminal. + Set it to raw mode. */ + + logFdSet(err_fd); + lopi_fd = open("/tyCo/0",UPDATE,0); + status = ioctl(lopi_fd,FIOSETOPTIONS,OPT_RAW); + + CLEAR_SCREEN + INIT_CURSOR + selected = NOTHING_SELECTED; + + while (display == ON) + { + if (n_lines == 0) + printf("No displays to open"); + else if (n_lines == 1) + { + selected = 0; + init = FALSE; + *screen = ON; + CLEAR_SCREEN + display_file(wind_array,selected,screen,que_num,pdata_flg,k_buff,val_in); + selected = NOTHING_SELECTED; + } + + else if (((*screen) == OFF) && (n_lines >1)) + { + + /* Task delay allows display_monitors to finish writing + to screen before clearing the screen and printing the + menu. */ + + if (!init) + taskDelay(100); + CLEAR_SCREEN + print_menu(d_menu,n_lines); + m_row = STRT_MENU_ROW; + m_col = STRT_MENU_COL-1; + *que_num = 0; + write_ptr = k_buff; + + /* Move cursor upper left hand corner of menu. */ + mv_cursor(&m_row,&m_col,NO_CMD); + + } + + /* Get cursor movement from the screen. */ + + /* Move cursor only along edge of menu while menu is + being displayed. Semaphores are not necessary + no other task is competing for screen or linked lists at + this time. */ + + while ((selected) == NOTHING_SELECTED && (n_lines > 1) ) + { + + while ((*que_num) > 0) + { + switch (*write_ptr) + { + case U_ARROW: + m_row++; + if (m_row > n_lines + STRT_MENU_ROW ) + m_row = STRT_MENU_ROW; + mv_cursor(&m_row,&m_col,NO_CMD); + break; + case D_ARROW: + m_row--; + if ( m_row < STRT_MENU_ROW) + m_row = n_lines + STRT_MENU_ROW; + mv_cursor(&m_row,&m_col,NO_CMD); + break; + case RETURN: + selected = m_row - STRT_MENU_ROW; + t_row++; + break; + + default: + break; + + } /* End switch. */ + + /* If a legal restart occurs reset buffer pointer + back to the first space in the buffer. and data to be + processed back to 0.*/ + + if ((*write_ptr == F6) || (*write_ptr == RETURN) || (*write_ptr == F7)) + { + semTake(key_sem); + (*que_num) = 0; + write_ptr = k_buff; + semGive(key_sem); + } + else + { + semTake(key_sem); + write_ptr++; + (*que_num)--; + if(*write_ptr == NULL) + write_ptr = k_buff; + semGive(key_sem); + } + } /*end while *que_num > 0 */ + + + + } /* End while selected == NOTHING_SELECTED . */ + + + /* User has chosen to exit the menu and the program. */ + + if (( selected == n_lines) || (n_lines == 1) ) + { + display = OFF; + CLEAR_SCREEN + status = ioctl(lopi_fd,FIOSETOPTIONS,OPT_TERMINAL); + taskResume(SHELL_ID); + } + + else if ((selected >= 0) && (selected < n_lines)) + { + init = FALSE; + *screen = ON; + CLEAR_SCREEN + display_file(wind_array,selected,screen,que_num,pdata_flg,k_buff,val_in); + selected = NOTHING_SELECTED; + *screen = OFF; + } + + } /* End while display. */ + + /* Reset terminal to terminal mode. */ + + close(err_fd); + new_fd = ioGlobalStdGet(0,STD_ERR); + logFdSet(new_fd); + semTake(mon_sem); + taskDelete(tid2); + taskDelete(tid1); + ca_task_exit(); + semGive(mon_sem); + taskDelay(100); + free_mem(wind_array,d_menu,n_lines); + close(lopi_fd); + + } /* End lopi. */ + +/******************************************************************************************/ +/* Uses NFS to read in a list of displays kept in a file called lopi.dat in the application */ +/* directory. */ +/******************************************************************************************/ + +static VOID create_menu(menu,nlines) + char *menu[MXMENU]; + int *nlines; + { + FILE *fp; + int ltr; + char item[40],*m_ptr; + short chr_ctr = 0; /* Counter. */ + + /* Open file. Return error message if that is impossible. */ + + if ((fp = fopen(disp_lst, "r")) == NULL) + { + printf("Can't open %s \n", disp_lst); + exit(1); + } + + /* Otherwise create a menu of displays for this application. */ + + else + { + + *nlines = 0; + + /* Read in characters from display file one by one.*/ + while ((ltr = getc(fp)) != EOF) + { + item[chr_ctr] = ltr; + chr_ctr++; + + /* At the end of each line, enter the display title into the display + memory array. */ + + if ((ltr == NEWLINE) && (chr_ctr != 0)) + { + item[chr_ctr-1] = NULL; + if (( m_ptr = (char *) malloc(chr_ctr + 1)) == NULL) + { + printf("Not enough memory in create_menu\n"); + exit(1); + } + + if (*nlines <= MXMENU) + { + strcpy(m_ptr,item); + d_menu[nxt_ln++] = m_ptr; + (*nlines)++; + chr_ctr = 0; /* Reset the character counter. */ + } + else /* Error message for too many display names. */ + printf("Maximum number of displays exceeded."); + } + } /* End while. */ + } /* End else. */ + + fclose(fp); /* Close the file. */ + } /* End create_menu. */ + + + /************************************************************************/ + /* Prints to the screen the menu passed to it in the array menu as */ + /* well as the instructions. Positions cursor in front of the first */ + /* menu item. */ + /************************************************************************/ + + static VOID print_menu(menu,nlines) + char *menu[MXMENU]; /* Array of menu items. */ + int nlines; /* Number of items in the array. */ + { + short mrow; + short mcol; + short b_ctr; + mrow = STRT_TTLE_ROW; + mcol = STRT_TTLE_COL; + mv_cursor(&mrow,&mcol,"DISPLAY MENU"); + mrow++; + mcol -= 3; + mv_cursor(&mrow,&mcol,NO_CMD); + mrow++; + mv_cursor(&mrow,&mcol,"Move cursor with arrow keys."); + mrow++; + mv_cursor(&mrow,&mcol,"Select with carriage return."); + mrow = STRT_MENU_ROW; + mcol = STRT_MENU_COL; + for (b_ctr = 0; b_ctr < nlines; b_ctr++) + { + mv_cursor(&mrow,&mcol,menu[b_ctr]); + mrow++; + } + mv_cursor (&mrow,&mcol,"EXIT"); + mrow++; + mrow = STRT_MENU_ROW; + mcol = STRT_MENU_COL - 1; + } + + +/*********************************************************************************/ +/* Gets keystrokes from the screen,translates them to lopi opi commands and */ +/* puts them in a buffer which is read by lopi, display_file and display_monitors. */ +/* Reads up arrow,down arrow,right arrow, left arrow, F6, F7 and carriage */ +/* return. When it reads an escape it calls get_esc_seq to complete the job of */ +/* getting the escape sequence from the screen and translating it into a lopi opi */ +/* command. */ +/*********************************************************************************/ + +static VOID get_key(cue_num,k_buff,pScreen,val_in) + + short *cue_num; /* Contains the number of letters in the queue ends the message + to getkey that the buffer has been read. */ + char k_buff[BUFF_SIZE]; + short *pScreen; + char val_in[MAX_STRING_SIZE]; + { + char *in_ptr; /* Pointer to next place for in put and next place + for output. */ + short ctr; /* Counter. */ + char ltr; + int nmbr; + + int mon_row = 0; /* For debug only. */ + int mon_col = 0; + int t_row = 0; /* For debug only. */ + int t_col = 0; + char txt_str[MAX_LIN_LEN]; + char prefix[MAX_LIN_LEN]; + + semTake(key_sem); + *pScreen = OFF; + in_ptr = k_buff; + *cue_num = 0; + semGive(key_sem); + + FOREVER + { + + /* Read characters from keyboard and put them in a buffer until + the buffer has been read.If an escape is reached continue to read + input until one of the supported escape sequences has been completed. + Convert those keystrokes to a code and return it. */ + + nmbr = read(lopi_fd,<r,1); + + do + { + /* If the letter is an escape sequence, do more reads to get + the rest of the sequence. Then convert it to a single character + and put it in the buffer. */ + + if(ltr == ESC) + { + semTake(key_sem); + ltr = get_esc_seq(); + semGive(key_sem); + + switch(ltr) + { + case U_ARROW: + semTake(key_sem); + (*in_ptr) = U_ARROW; + (*cue_num)++; + semGive(key_sem); + in_ptr++; + break; + + case D_ARROW: + semTake(key_sem); + (*in_ptr) = D_ARROW; + (*cue_num)++; + semGive(key_sem); + in_ptr++; + break; + + case F6: + semTake(key_sem); + (*in_ptr) = F6; + *pScreen = OFF; + in_ptr = k_buff; + semGive(key_sem); + break; + + case F7: + if (*pScreen == ON) + { + make_box(); + get_value(val_in); + taskDelay(30); + } + semTake(key_sem); + (*in_ptr) = F7; + (*cue_num)++; + in_ptr = k_buff; + semGive(key_sem); + ltr = NULL; + break; + default: + break; + + } /* End switch. */ + } /* End if ltr == ESC. */ + if ((ltr == RETURN) || (ltr == CR)) + { + semTake(key_sem); + (*in_ptr) = RETURN; + (*cue_num)++; + semGive(key_sem); + in_ptr = k_buff; + } + + if ((*in_ptr) == NULL) + in_ptr = k_buff; + nmbr = read(lopi_fd,<r,1); + } /* End do while. */ + while ((*cue_num) <= (BUFF_SIZE -1) ); /* Exit this loop if buffer is filled. */ + + + } /* End FOREVER */ + + } /* End get_key. */ + + +/********************************************************************************/ +/* Gets escape sequences from screen and returns their meaning to get key. */ +/* Returns up arrow, down arrow, right arrow, left arrow, F6 and F7. For */ +/* all other letters returns ILL_CHR . */ +/********************************************************************************/ + +static char get_esc_seq() + { + char lttr; + int nbr; + + nbr = read(lopi_fd,<tr,1); + if (lttr ==L_BRACKET) + { + nbr = read(lopi_fd,<tr,1); + switch(lttr) + { + case 'A': + return D_ARROW; + case 'B': + return U_ARROW; + case '1': + nbr = read(lopi_fd,<tr,1); + if (lttr == '7') + { + nbr = read(lopi_fd,<tr,1); + if (lttr == TILDE) + return F6; + } + if (lttr == '8') + { + nbr = read(lopi_fd,<tr,1); + if (lttr == TILDE) + return F7; + } + default: + /* If an illegal escape sequence has been read + then the legal character flag will not be set + and buffer write pointer will not be advanced. */ + + return ILL_CHR; + } /* End switch */ + + } /* End if ltter == L_BRACKET. */ + else + return ILL_CHR; + } /* End get_esc_seq. */ + +/*****************************************************************************/ +/* Draws a box on the screen for user to enter the value he wishes to write. */ +/*****************************************************************************/ +static VOID make_box() + { + char str[MAX_LIN_LEN]; + short row = BEG_BOX; + short col = BEG_BOX; + + strcpy(str, "************************************************************************"); + mv_cursor(&row,&col,str); + row+=2; + strcpy(str, "************************************************************************"); + row = END_BOX; + mv_cursor(&row,&col,str); + row--; + col = BOX_IN_COL; + mv_cursor(&row,&col,NO_CMD); + } + +/************************************************************************/ +/* Gets value from user input from screen. Echos user's input back. */ +/* A null in the first location in the array indicates no value was */ +/* entered. */ +/************************************************************************/ + +static VOID get_value(val_in) + char val_in[MAX_STRING_SIZE]; /* Array into which letters will be placed as they + are read in from screen. */ + { + char numb, *chr_ptr; /* Variable into which ltrs are read before putting + them into field_array. */ + VOID mv_cursor(); + + short row = BOX_IN_ROW; + short col = BEG_BOX; + int nmbr; + short ctr; + char prefix[40]; + char txt_str[MAX_STRING_SIZE]; + + blank_fill(val_in,MAX_STRING_SIZE); + strcpy(val_in, "* Enter value: *"); + mv_cursor(&row,&col,val_in); + col = 16; + blank_fill(val_in,MAX_STRING_SIZE); + mv_cursor(&row,&col,NO_CMD); + + /* Initialize pointer to first location in array. */ + + chr_ptr = val_in; + + /* Read input from screen until a return is entered. */ + nmbr = read(lopi_fd,&numb,1); + + while ((numb != RETURN) && (numb != CR) && (*chr_ptr != NULL )) + { + /* If value is legal enter it in value array. */ + + if (( isalpha(numb)) || (isdigit(numb)) || (numb == DOT)) + { + *chr_ptr = numb; + chr_ptr++; + mv_cursor(&row,&col,val_in); + nmbr = read(lopi_fd,&numb,1); + } /* End if isalpha */ + else if ((numb == DELETE) && (chr_ptr != val_in)) + { + chr_ptr--; + *chr_ptr = BLANK; + mv_cursor(&row,&col,val_in); + nmbr = read(lopi_fd,&numb,1); + } + else if ((numb != RETURN) || (numb != CR)) + { + nmbr = read(lopi_fd,&numb,1); + } + + } /* End while numb != return. */ + + *chr_ptr = NULL; + col = BEG_BOX; + + /* Erase the box and data. */ + + for (ctr = BEG_BOX; ctr <= END_BOX; ctr++) + { + row = ctr; + mv_cursor(&row,&col,NO_CMD); + ERASE_LINE; + } + } + +/**************************************************************************************/ +/* Takes the character array passed to it and fills it with blanks and terminates it */ +/* with a NULL. */ +/**************************************************************************************/ + +static VOID blank_fill(chr_array,chr_size) + char chr_array[MAX_LIN_LEN]; + short chr_size; + { + short c_ctr = 0; /* Index to array being filled. */ + + while(c_ctr < (chr_size - 1)) + chr_array[c_ctr++] = BLANK; + chr_array[chr_size -1] = NULL; + + } +/**************************************************************************************/ +/* Takes the character array passed to it and fills it with blanks and terminates it */ +/* with a NULL. If chr_size is 0 the function does nothing. If chr_size is 1, the */ +/* NULL is placed in the first position in the array. If chr_size is greater than the */ +/* array size, the array will be filled with blanks and a NULL will be placed in the */ +/* last space of the array. */ +/**************************************************************************************/ + +/* static VOID blank_fill(chr_array,chr_size) + char chr_array[MAX_LIN_LEN]; + short chr_size; + { + short row= 0,col = 0; + short c_ctr = 0; Index to array being filled. + + if (strlen(chr_array) < chr_size) + chr_size = strlen(chr_array); + if (chr_size <= 0) + return; + else if (chr_size == 1) + { + chr_array[0] = NULL; + return; + } + else + { + while(c_ctr < (chr_size - 1)) + chr_array[c_ctr++] = BLANK; + chr_array[chr_size -1] = NULL; + return; + } + + } */ + +/****************************************************************************/ +/* Moves cursor to row and column passed to it. Prints a string after the */ +/* cursor if one is sent to it. */ +/****************************************************************************/ + + +static VOID mv_cursor(mrow,mcol,str) + short *mrow; + short *mcol; + char str[100]; /* String to be printed after cursor has a been + moved. */ + { + char command[140]; + + /* Create a string containing the proper VT220 commands + to move the cursor to the location desired. */ + + bzero(command,sizeof(command)); + sprintf(command,"[%d;%dH %s",*mrow,*mcol,str); + + /* Send command to the terminal. */ + + semTake(key_sem); + + fdprintf(lopi_fd,"%c",ESC); + fdprintf(lopi_fd,"%s",command); + + semGive(key_sem); + } + + + /****************************************************************************/ + /* Strips away blanks from the string to which it */ + /* points. Returns a pointer to the first non-blank character. */ + /****************************************************************************/ + + +static char *skipblanks(str) + char *str; + { + + while ( (*str == BLANK)) + str++; + + return (str); + } + + +/****************************************************************************/ +/* Reads in a line from a file and puts it in an array. */ +/* Strips off newline character. Terminates string with a NULL. */ +/* Returns the number of characters read in or EOF. */ +/****************************************************************************/ + + +static int fgetline(disp_ln,d_fp) + + FILE *d_fp; /* File pointer to the file being read in. */ + char disp_ln[MAX_LIN_LEN]; + { + int c; + char *lnptr; + + for (lnptr = disp_ln; lnptr - disp_ln < MAX_LIN_LEN - 1 && + (c = getc(d_fp)) != EOF && c != NEWLINE; lnptr++) + *lnptr = (char) c; + + *lnptr = NULL; + + if ( c == EOF) + return EOF; + else + return lnptr - disp_ln; + } + + +/****************************************************************************/ +/* Reads in the files listed in disp_files one by one. It builds a */ +/* structure called a window for each display file it reads in. It builds */ +/* linked lists of controls, monitors and texts. These lists will later be */ +/* used to display the dynamic information as it comes in. */ +/****************************************************************************/ + + + static VOID get_displays(disp_files,nfiles,disp_array) + + char *disp_files[FN_LEN]; /* Array for list of filenames to be selected by the user. */ + int nfiles; /* Number of files read in. */ + struct window_node *disp_array[MAX_DISP_NUM]; /* Array of windows available. */ + + { + FILE *disp_fp; + char *lptr,dline[MAX_LIN_LEN]; /* Array to hold the contents of a line from the + display file as it is read in. */ + struct mon_node *ctl_ptr; + int flg; /* Flag for EOF. */ + int disp_ctr; /* Counters. */ + for (disp_ctr = 0; disp_ctr < nfiles; disp_ctr++) + { + if ((disp_fp = fopen(disp_files[disp_ctr], "r")) == NULL) + printf("Can't open %s \n", disp_files[disp_ctr]); + else + { + disp_array[disp_ctr] = win_alloc(); + disp_array[disp_ctr]->c_head = NULL; + disp_array[disp_ctr]->m_head = NULL; + disp_array[disp_ctr]->t_head = NULL; + flg = 0; + do + { + flg = fgetline(dline,disp_fp); + lptr = skipblanks(dline); /* Skip any leading blanks if any. */ + switch (*lptr) + { + case 'c': + add_ctl(disp_array,disp_ctr,lptr); + break; + + case 'm': + add_mon(disp_array,disp_ctr,lptr); + break; + + case 't': + add_txt(disp_array,disp_ctr,lptr); + break; + + default: + break; + } /* End switch. */ + bzero(dline,sizeof(dline)); + + } while(flg != EOF); + + status = fclose(disp_fp); + + } /* End else. */ + + } /* End for.*/ + /* if (DEBUG) + read_disp_lst(wind_array,n_lines); */ + } /* End get_displays */ + + + +/****************************************************************************/ +/* Allocates memory for a monitor. */ +/****************************************************************************/ + +static struct mon_node *mon_alloc() + { + struct mon_node *monptr; + + if (( monptr = (struct mon_node *)malloc(sizeof(struct mon_node))) == NULL) + { + logMsg("Insufficient memory in mon_alloc.\n "); + close(err_fd); + abort(); + } + else + return monptr; + } + +/****************************************************************************/ +/* Allocates memory for a text label. */ +/****************************************************************************/ + +static struct txt_node *txt_alloc() + { + struct txt_node *txtptr; + + if (( txtptr = (struct txt_node *)malloc(sizeof(struct txt_node))) == NULL) + { + logMsg("Insufficient memory in tx_alloc.\n "); + close(err_fd); + abort(); + } + else + return txtptr; + } + +/****************************************************************************/ +/* Allocates memory for an event node. */ +/****************************************************************************/ + +static struct ev_node *ev_alloc() + { + struct ev_node *evptr; + + if (( evptr = (struct ev_node *)malloc(sizeof(struct ev_node))) == NULL) + { + logMsg("Insufficient memory in ev_alloc.\n "); + close(err_fd); + abort(); + } + else + return evptr; + } + +/****************************************************************************/ +/* Allocates memory for an exception node. */ +/****************************************************************************/ + +static struct except_node *except_alloc() + { + struct except_node *ex_ptr; + + if (( ex_ptr = (struct except_node *)malloc(sizeof(struct except_node))) == NULL) + { + logMsg("Insufficient memory in except_alloc.\n "); + close(err_fd); + abort(); + } + else + return ex_ptr; + } + +/****************************************************************************/ +/* Allocates memory for a window structure. */ +/****************************************************************************/ + +static struct window_node *win_alloc() + { + struct window_node *winptr; + + if (( winptr = (struct window_node *)malloc(sizeof(struct window_node))) == NULL) + { + logMsg("Insufficient memory in win_alloc.\n "); + close(err_fd); + abort(); + } + else + return winptr; + } + +/*****************************************************************************/ +/* This function attaches a control node to the linked list headed by */ +/* win_array->ctl. It returns the pointer to the head of the list which now */ +/* points to the new node. */ +/*****************************************************************************/ + + static add_ctl(win_array,win_ctr,c_line) + struct window_node *win_array[MAX_DISP_NUM]; + short win_ctr; /* Counters. */ + char *c_line; + { + struct mon_node *c_ptr,*new_ptr; + char chan_nm[CHAN_NM_SIZE],*chan_ptr; /* Array to hold channel name as it is parsed + from line and a pointer to it. */ + register short l_crn_row = 0,l_crn_col = 0; + register short r_crn_row = 0,r_crn_col = 0; + short r_loc_flg = OFF; /* Flag which is set when location is found in element file. */ + short c_loc_flg = OFF; /* Flag which is set when location is found in element file. */ + + chan_ptr = chan_nm; + bzero(chan_nm,sizeof(chan_nm)); + + *chan_ptr = NULL; + + /* Move past any characters at beginning of display element. */ + + while (isalpha(*c_line)) + c_line++; + c_line = skip_to_digit(c_line); + + /* If there is no channel location, omit the channel from the linked list. */ + + if ((*c_line) && (isdigit(*c_line))) + { + l_crn_row = to_short(c_line); + while(isdigit(*c_line)) + c_line++; + r_loc_flg = ON; + c_line = skip_to_digit(c_line); + + if (*c_line) + { + l_crn_col = to_short(c_line); + c_loc_flg = ON; + while (isdigit(*c_line) ) + c_line++; + } + } + + /* Advance pointer past the left corner location in element array. */ + + while ((*c_line)&& (isdigit(*c_line))) + c_line++; + + /* Advance pointer to next digit or alpha character. */ + + while ((*c_line) && (!isdigit(*c_line)) && (!isalpha(*c_line))) + c_line ++; + + /* Return a pointer to the next non-blank character in the element array. */ + + if ((*c_line) && (isdigit(*c_line))) + { + r_crn_row = to_short(c_line); + while(isdigit(*c_line)) + c_line++; + c_line = skip_to_digit(c_line); + r_crn_col = to_short(c_line); + while ((!isalpha(*c_line)) && (*c_line)) + c_line++; + } + + /* If there is no channel name give appropriate error message. */ + + + if (!(*c_line)) + printf("No channel name for control. Channel omitted.\n"); + + /* Otherwise copy channel name to the channel name array. */ + if (isalpha(*c_line)) + { + while( (isalpha(*c_line)) || (isdigit(*c_line)) || (*c_line == UNDERSC) ||(*c_line == COLON) + || (*c_line == DOT)) + *chan_ptr++ = *c_line++; + *chan_ptr = NULL; + } + + /* Allocate and initialize header for doubly linked list. Initialize header's + l_crn_row field to -1. */ + + if (win_array[win_ctr]->c_head == NULL) + { + win_array[win_ctr]->c_head = mon_alloc(); + win_array[win_ctr]->c_head->next = win_array[win_ctr]->c_head; + win_array[win_ctr]->c_head->prev = win_array[win_ctr]->c_head; + strcpy(win_array[win_ctr]->c_head->chan,"head"); + win_array[win_ctr]->c_head->l_crn_row = -1; + c_ptr = win_array[win_ctr]->c_head; + } + + + /* If the channel has a name and locations for the left corner and the right + corner, add the node immediately after the head of the list. */ + + c_ptr = win_array[win_ctr]->c_head->next; + + if ((*chan_nm) && (r_loc_flg) && (c_loc_flg)) + { + while((c_ptr->next->l_crn_row != -1) && (c_ptr->l_crn_row < l_crn_row)) + { + c_ptr = c_ptr->next; + } + c_ptr = win_array[win_ctr]->c_head->next; + while((c_ptr->next->l_crn_row != -1) && (c_ptr->l_crn_row == l_crn_row) && + (c_ptr->l_crn_col < l_crn_col)) + { + c_ptr = c_ptr->next; + } + new_ptr = mon_alloc(); + new_ptr->next= c_ptr->next; /* Set pointer to the head to point at the new node. */ + new_ptr->prev = c_ptr; + c_ptr->next = new_ptr; + new_ptr->next->prev = new_ptr; + strcpy(new_ptr->chan,chan_nm); + new_ptr->l_crn_row = l_crn_row; + new_ptr->l_crn_col = l_crn_col; + new_ptr->r_crn_row = r_crn_row; + new_ptr->r_crn_col = r_crn_col; + } + + + } + +/************************************************************************************/ +/* This function attaches a monitor node to the linked list headed by win_array-> */ +/* mon. It returns the pointer to the head of the list which now points to the new */ +/* node. */ +/************************************************************************************/ + + static add_mon(win_array,win_ctr,m_line) + struct window_node *win_array[MAX_DISP_NUM]; + short win_ctr; /* Counters. */ + char *m_line; + { + struct mon_node *node_ptr; + char chan_nm[CHAN_NM_SIZE],*chan_ptr; /* Array to hold channel name as it is parsed + from line and a pointer to it. */ + register short l_crn_row = 0,l_crn_col = 0; + short r_loc_flg = OFF; /* Flag which is set when location is found in element file. */ + short c_loc_flg = OFF; /* Flag which is set when location is found in element file. */ + + chan_ptr = chan_nm; + bzero(chan_nm,sizeof(chan_nm)); + + *chan_ptr = NULL; + + /* Move past any characters at beginning of display element. */ + + while (isalpha(*m_line)) + m_line++; + + m_line = skip_to_digit(m_line); + + /* If there is no channel location, omit the channel from the linked list. */ + + + if ((*m_line) && (isdigit(*m_line))) + { + l_crn_row = to_short(m_line); + r_loc_flg = ON; + /* Move past left row number. */ + + while(isdigit(*m_line)) + m_line++; + + /* Move to column location. */ + + m_line = skip_to_digit(m_line); + + if ((*m_line) && (isdigit(*m_line))) + { + l_crn_col = to_short(m_line); + c_loc_flg = ON; + while (isdigit (*m_line)) + m_line++; + } + + } + + while ( (*m_line) && (!isalpha(*m_line))) + m_line++; + + if (isalpha(*m_line)) + { + while( (isalpha(*m_line)) || (isdigit(*m_line)) || (*m_line == UNDERSC) || (*m_line == COLON)) + *chan_ptr++ = *m_line++; + *chan_ptr = NULL; + } + chan_ptr = chan_nm; + + /* Enter channel name and location for monitor in monitor linked list. */ + + if (win_array[win_ctr]->m_head == NULL) + { + if ((*chan_nm) && (c_loc_flg) && (r_loc_flg)) + { + win_array[win_ctr]->m_head = mon_alloc(); + win_array[win_ctr]->m_head->next = NULL; + if (*chan_nm) + strcpy(win_array[win_ctr]->m_head->chan,chan_nm); + win_array[win_ctr]->m_head->l_crn_row = l_crn_row; + win_array[win_ctr]->m_head->l_crn_col = l_crn_col; + } + else if (!(*chan_nm)) + printf("No channel name for monitor. Channel omitted.\n"); + else if ((! (c_loc_flg)) || (! (r_loc_flg)) ) + printf("Incomplete location information for display.\n"); + } + + + /* Otherwise, add the node immediately after the head of the list. */ + + else + { + if ((*chan_nm) && (c_loc_flg) && (r_loc_flg)) + { + node_ptr = mon_alloc(); /* Set the new node's next pointer to point at + the head of the list. */ + node_ptr->next= win_array[win_ctr]->m_head; /* Set pointer to the head to point at the new node. */ + win_array[win_ctr]->m_head = node_ptr; + if (*chan_nm) + strcpy(win_array[win_ctr]->m_head->chan,chan_nm); + win_array[win_ctr]->m_head->l_crn_row = l_crn_row; + win_array[win_ctr]->m_head->l_crn_col = l_crn_col; + } + else if (!(*chan_nm)) + printf("No channel name for monitor. Channel omitted.\n"); + else if ((! (c_loc_flg)) || (! (r_loc_flg)) ) + printf("Incomplete location information for display. Channel omitted.\n"); + } + + } + +/************************************************************************************/ +/* This function attaches a text node to the linked list headed by win_array-> */ +/* txt. It returns the pointer to the head of the list which now points to the new */ +/* node. */ +/************************************************************************************/ + + static add_txt(win_array,win_ctr,t_line) + struct window_node *win_array[MAX_DISP_NUM]; + short win_ctr; /* Counters. */ + char *t_line; + { + struct txt_node *node_ptr; + char txt_str[MAX_LIN_LEN],*txt_ptr; /* Array to hold text string as it is parsed + from line and a pointer to it. */ + + register short l_crn_row = 0,l_crn_col = 0; + short r_loc_flg = OFF; /* Flag which is set when location is found in element file. */ + short c_loc_flg = OFF; /* Flag which is set when location is found in element file. */ + + txt_ptr = txt_str; + bzero(txt_str,sizeof(txt_str)); + + *txt_ptr = NULL; + + /* Move past any characters at beginning of display element. */ + + while (isalpha(*t_line)) + t_line++; + + /* Move to next digit in display element. */ + + t_line = skip_to_digit(t_line); + + + /* If there is no text location, omit the string from the linked list. */ + + if (*t_line) + { + l_crn_row = to_short(t_line); + r_loc_flg = ON; + while (isdigit(*t_line)) + t_line++; + t_line = skip_to_digit(t_line); + + if ((*t_line) && (isdigit (*t_line))) + { + l_crn_col = to_short(t_line); + c_loc_flg = ON; + + /* Skip past digit. */ + + while (isdigit(*t_line)) + t_line++; + + } + + } + + + /* Skip digits and blanks and illegal characters then read in text string. */ + + while ( (*t_line) && (!isalpha(*t_line)) && (!isdigit(*t_line)) && (*t_line != UNDERSC) + && (*t_line != COLON) && (*t_line != ASTER)) + t_line++; + + /* Text string must begin with an alphanumeric character, and underscore,a colon, or an + asterisk. After the first character blanks will be legal for text strings. */ + + if ((isalpha(*t_line)) || (isdigit(*t_line)) || (*t_line == UNDERSC) || (*t_line == COLON) || + (*t_line == ASTER)) + { + while( (isalpha(*t_line)) || (isdigit(*t_line)) || (*t_line == UNDERSC) || (*t_line == BLANK) + || (*t_line == COLON) || (*t_line == ASTER) ||(*t_line == DOT)) + *txt_ptr++ = *t_line++; + *txt_ptr = NULL; + } + + if (win_array[win_ctr]->t_head == NULL) + { + if ((*txt_str) && (r_loc_flg) && (c_loc_flg)) + { + win_array[win_ctr]->t_head = txt_alloc(); + win_array[win_ctr]->t_head->next = NULL; + if (*txt_str) + strcpy(win_array[win_ctr]->t_head->txt_str,txt_str); + win_array[win_ctr]->t_head->l_crn_row = l_crn_row; + win_array[win_ctr]->t_head->l_crn_col = l_crn_col; + } + else if (! (txt_str)) + printf("No text string. Text node omitted.\n"); + else if ((!(r_loc_flg)) || (! (c_loc_flg))) + printf("Incomplete location information. Text node omitted.\n"); + } + + + /* Otherwise, add the node immediately after the head of the list. */ + + else + { + if ((*txt_str) && (r_loc_flg) && (c_loc_flg)) + { + node_ptr = txt_alloc(); /* Set the new node's next pointer to point at + the head of the list. */ + node_ptr->next= win_array[win_ctr]->t_head; /* Set pointer to the head to point at the new node. */ + win_array[win_ctr]->t_head = node_ptr; + if (*txt_str) + strcpy(win_array[win_ctr]->t_head->txt_str,txt_str); + win_array[win_ctr]->t_head->l_crn_row = l_crn_row; + win_array[win_ctr]->t_head->l_crn_col = l_crn_col; + } + else if (! (txt_str)) + printf("No text string. Text node omitted.\n"); + else if ((!(r_loc_flg)) || (! (c_loc_flg))) + printf("Incomplete location information. Text node omitted.\n"); + } + + } + +/************************************************************************************/ +/* This function throws away all characters and returns a pointer */ +/* to the first digit. */ +/************************************************************************************/ + +static char *skip_to_digit(pstr) + char *pstr; + { + + pstr; + while (*pstr) + { + if( (isdigit(*pstr)) ) + return (pstr); + else if (pstr == NULL) + { + return (NULL); + } + else + pstr++; + if (DEBUG) + printf("In skip_to_digit string %s \n",pstr); + + } + + return (NULL); + } + +/************************************************************************************/ +/* Get a number from display element line, convert it to register short. */ +/* This function was stolen from Bob Dalesio with a slight modification. */ +/************************************************************************************/ + +static int to_short(pstr) + char *pstr; + { + short num; + + num = 0; + while(isdigit(*pstr)) + { + num = (num * 10) + (*pstr -'0'); + pstr++; + } + return num; + } + + +/*************************************************************************************/ +/* This subroutine will be used for debugging only. It reads out the linked list of */ +/* control nodes. It is best to comment out print_menu when using this. */ +/*************************************************************************************/ + +static read_disp_lst(wnd_array,nfiles) +struct window_node *wnd_array[MAX_DISP_NUM]; +int nfiles; +{ + struct mon_node *ctl_ptr; + struct mon_node *mon_ptr; + struct txt_node *txt_ptr; + short lopi_i,lopi_j; /* Counters. */ + + lopi_j = 1; + for (lopi_i = 0; lopi_i < nfiles; lopi_i++) + { + ctl_ptr = wnd_array[lopi_i]->c_head; + if (ctl_ptr->next->l_crn_row == LIST_END ) + printf("Ctl list for display %d is empty.\n",lopi_i); + else + { + ctl_ptr = ctl_ptr->next; + while (ctl_ptr->next->l_crn_row != LIST_END) + { + printf("Display %d: Node %d\n",lopi_i,lopi_j); + printf("wnd_array[%d]->c_head->chan = %s \n",lopi_i,ctl_ptr->chan); + printf("wnd_array[%d]->c_head->l_crn_row = %d \n",lopi_i,ctl_ptr->l_crn_row); + printf("wnd_array[%d]->c_head->l_crn_col = %d \n",lopi_i,ctl_ptr->l_crn_col); + lopi_j++; + ctl_ptr = ctl_ptr->next; + } + } + lopi_j = 1; + mon_ptr = wnd_array[lopi_i]->m_head; + if (mon_ptr == NULL) + printf("Mon list for display %d is empty.\n",lopi_i); + else + { + while (mon_ptr != NULL) + { + printf("Display %d: Node %d\n",lopi_i,lopi_j); + printf("wnd_array[%d]->m_head->chan = %s \n",lopi_i,mon_ptr->chan); + printf("wnd_array[%d]->m_head->l_crn_row = %d \n",lopi_i,mon_ptr->l_crn_row); + printf("wnd_array[%d]->m_head->l_crn_col = %d \n",lopi_i,mon_ptr->l_crn_col); + lopi_j++; + mon_ptr = mon_ptr->next; + } + } + lopi_j = 1; + txt_ptr = wnd_array[lopi_i]->t_head; + if (txt_ptr == NULL) + printf("Txt list for display %d is empty.\n",lopi_i); + else + { + while (txt_ptr != NULL) + { + printf("Display %d: Node %d\n",lopi_i,lopi_j); + printf("wnd_array[%d]->t_head->txt_str = %s \n",lopi_i,txt_ptr->txt_str); + printf("wnd_array[%d]->t_head->l_crn_row = %d \n",lopi_i,txt_ptr->l_crn_row); + printf("wnd_array[%d]->t_head->l_crn_col = %d \n",lopi_i,txt_ptr->l_crn_col); + lopi_j++; + txt_ptr = txt_ptr->next; + } + } + lopi_j = 1; + } +} + + +/***************************************************************************************/ +/* This function will spawn two child processes. One will asynchronously get data */ +/* from the field and put it on the screen. The other will asynchronously get input */ +/* from the keyboard and react to it . The second will run at a slightly higher */ +/* priority than the first. */ +/***************************************************************************************/ + +static display_text(disp_array,selected) + struct window_node *disp_array[MAX_DISP_NUM]; + short selected; + { + short mrow,mcol; + struct txt_node *txt_ptr; + char txt_str[MAX_LIN_LEN]; + mrow = 0; + mcol = 0; + mv_cursor(&mrow,&mcol,NO_CMD); + txt_ptr = disp_array[selected]->t_head; + + while (txt_ptr != NULL) + { + mrow = txt_ptr->l_crn_row; + mcol = txt_ptr->l_crn_col; + strcpy(txt_str,txt_ptr->txt_str); + mv_cursor(&mrow,&mcol,txt_str); + txt_ptr = txt_ptr->next; + } + + } + +/***************************************************************************************/ +/* */ +/* */ +/* */ +/***************************************************************************************/ + +static display_file(disp_array,selected,screen_up,data_num,pdata_flg,k_buff,val_in,cur_mv) + struct window_node *disp_array[MAX_DISP_NUM]; + short selected; + short *screen_up; + short *data_num; /* Flag indicating that there is data waiting to + be read from the input buffer. */ + short *pdata_flg; /* Flag indicating that a monitor has brought in new data. */ + char k_buff[BUFF_SIZE]; /* Buffer for characters entered. */ + char val_in[MAX_STRING_SIZE]; + short cur_mv; /* Flag to indicate that arrow keys are being struck */ + { + char *w_ptr; /* Pointer to next available space for writing. */ + struct mon_node *c_ptr; + struct mon_node *m_ptr; + struct txt_node *t_ptr; + char txt_str[MAX_LIN_LEN]; + char prefix[MAX_LIN_LEN]; + int nmbr; + short krow,kcol; /* Variables keeping track of where cursor is at all times. */ + short trow,tcol; /* Variables keeping track of where cursor is at all times. */ + short init; /* Flag to indicate display monitors whether to initialize monitors or + not. */ + + + /* Initialize buffers into which channel names and values are + written for a put. */ + + k_buff[BUFF_SIZE - 1] = NULL; + w_ptr = k_buff; + *data_num = 0; + + krow = 0; /*Krow and kcol store cursor position. */ + kcol = 0; + + trow = 0; /* trow and tcol store cursor position for diagnostic messages only. */ + trow = 0; + + /* If a screen with no data has been selected, exit. */ + + m_ptr = disp_array[selected]->m_head; + c_ptr = disp_array[selected]->c_head; + t_ptr = disp_array[selected]->t_head; + + if ((c_ptr == NULL) && (m_ptr == NULL) + && ( t_ptr == NULL) ) + { + selected = NOTHING_SELECTED; + screen_up = OFF; + w_ptr = k_buff; + (*data_num) = 0; + } + /* Otherwise put up text and initialize the monitors. */ + + else + { + display_text(disp_array,selected); + init = ON; + display_monitors(disp_array,selected,screen_up,pdata_flg,&init,&cur_mv,except_ptr); + init = OFF; + } + + /* Place cursor on upper first control screen. */ + + if (c_ptr->next->l_crn_row != LIST_END) + c_ptr = c_ptr->next; + krow = c_ptr->l_crn_row; + kcol = c_ptr->l_crn_col; + mv_cursor(&krow,&kcol,NO_CMD); + + while (*screen_up == ON) + { + + /* If there is something in the buffer it will be consumed + until it is used up. */ + + + while (*data_num > 0) + { + switch (*w_ptr) + { + case F6: /* F6 will cause an exit. Everything else in buffer + will be lost. */ + semTake(key_sem); + w_ptr = k_buff; + *data_num = 0; + *screen_up = OFF; + semGive(key_sem); + break; + case F7: + display_text(disp_array,selected); + semTake(key_sem); + w_ptr = k_buff; + *data_num = 0; + semGive(key_sem); + trow = BEG_BOX; + tcol = BEG_BOX; + put_value(c_ptr,val_in); + break; + case U_ARROW: + if (c_ptr->prev->l_crn_row != LIST_END) + { + c_ptr = c_ptr->prev; + krow = c_ptr->l_crn_row; + kcol = c_ptr->l_crn_col; + mv_cursor(&krow,&kcol,NO_CMD); + } + else + { + c_ptr = c_ptr->prev->prev; + krow = c_ptr->l_crn_row; + kcol = c_ptr->l_crn_col; + mv_cursor(&krow,&kcol,NO_CMD); + } + w_ptr++; + semTake(key_sem); + (*data_num)--; + if ((*w_ptr) == NULL) + w_ptr = k_buff; + semGive(key_sem); + break; + case D_ARROW: + if (c_ptr->next->l_crn_row != LIST_END) + { + c_ptr = c_ptr->next; + krow = c_ptr->l_crn_row; + kcol = c_ptr->l_crn_col; + mv_cursor(&krow,&kcol,NO_CMD); + } + else + { + c_ptr = c_ptr->next->next; + krow = c_ptr->l_crn_row; + kcol = c_ptr->l_crn_col; + mv_cursor(&krow,&kcol,NO_CMD); + } + mv_cursor(&krow,&kcol,NO_CMD); + semTake(key_sem); + w_ptr++; + (*data_num)--; + if ((*w_ptr) == NULL) + w_ptr = k_buff; + semGive(key_sem); + break; + case RETURN: + semTake(key_sem); + (*data_num) = 0; + w_ptr = k_buff; + semGive(key_sem); + break; + default: + w_ptr++; + semTake(key_sem); + if ((*w_ptr) == NULL) + w_ptr = k_buff; + semGive(key_sem); + break; + } /* End switch. */ + + + } /* End while data_num > 0 */ + + if (*pdata_flg) + { + display_monitors(disp_array,selected,screen_up,pdata_flg,&init,&cur_mv,except_ptr); + mv_cursor(&krow,&kcol,NO_CMD); + } + + } /* end while screen up. */ + + stop_monitors(disp_array,selected); +} + +static VOID display_monitors(disp_array,m_select,screen,pdata_flg,init,cur_mv,except_ptr) + struct window_node *disp_array[MAX_DISP_NUM]; + short m_select; /* Pointer to number of screen selected. */ + short *screen; /* Pointer to variable which tells when to begin desplay. */ + short *pdata_flg; /* Flag to indicate that new data had been received. */ + short *init; /* Flag to indicate that monitors need to be initialized. */ + short *cur_mv; /*Flag to indicate movement of the cursor. */ + struct except_node *except_ptr; + { + short mon_row,mon_col; /* Local variable used to move cursor to position where update is + required. After update, cursor will always be returned to the + position where keyboard entry cursor is located. */ + short trow = 1;short tcol = 0; + int status; + chtype type = TYPENOTCONN; + char txt_str[120]; + char prefix[80]; + struct mon_node *mon_ptr, *ctl_ptr; + + mon_row = 0; + mon_col = 0; + mon_ptr = disp_array[m_select]->m_head; + ctl_ptr = disp_array[m_select]->c_head; + if (ctl_ptr->next->l_crn_row != LIST_END) + ctl_ptr = ctl_ptr->next; + + if (*init) + { + SEVCHK(ca_task_initialize(),"Unable to initialize."); + + status = ca_add_exception_event(lopi_exception_handler,except_ptr); + if (status != ECA_NORMAL) + logMsg(" Exception handler error message:%d\n",ca_message_text[CA_EXTRACT_MSG_NO(status)]); + except_ptr->flg = NO_NEW_DATA; + bzero(except_ptr->msg,(sizeof(except_ptr->msg))); + + + while (ctl_ptr->l_crn_row != LIST_END) + { + ctl_ptr->ev_ptr = ev_alloc(); + ctl_ptr->ev_ptr->data_flg = NO_NEW_DATA; + ctl_ptr->conn_flg = CA_OP_CONN_DOWN; + mon_row = ctl_ptr->l_crn_row; + mon_col = ctl_ptr->l_crn_col; + ctl_ptr->prev_size = 4; + + /* Put up a row of x's where monitor is to be displayed. */ + + ESCAPE + REVERSE_VIDEO + strcpy(ctl_ptr->ev_ptr->str," "); + mv_cursor(&mon_row,&mon_col,ctl_ptr->ev_ptr->str); + ESCAPE + ATR_OFF + status = ca_build_and_connect(ctl_ptr->chan,TYPENOTCONN,0,&ctl_ptr->chan_id, + NULL,lopi_conn_handler,ctl_ptr); + SEVCHK(status,NULL); + if (DEBUG) + logMsg("build_and_connect for %s, status = %d\n",ctl_ptr->chan,status); + ctl_ptr = ctl_ptr->next; + } + + while (mon_ptr != NULL) + { + mon_ptr->ev_ptr = ev_alloc(); + mon_ptr->ev_ptr->data_flg = NO_NEW_DATA; + mon_ptr->conn_flg = CA_OP_CONN_DOWN; + mon_row = mon_ptr->l_crn_row; + mon_col = mon_ptr->l_crn_col; + mon_ptr->prev_size = 4; + + /* Put up a row of x's where monitor is to be displayed. */ + + ESCAPE + REVERSE_VIDEO + strcpy(mon_ptr->ev_ptr->str," "); + mv_cursor(&mon_row,&mon_col,mon_ptr->ev_ptr->str); + ESCAPE + ATR_OFF + status = ca_build_and_connect(mon_ptr->chan,TYPENOTCONN,0,&mon_ptr->chan_id, + NULL,lopi_conn_handler,mon_ptr); + if(DEBUG) + logMsg("build_and_connect for %s, status = %d\n",mon_ptr->chan,status); + mon_ptr = mon_ptr->next; + } + + + mon_ptr = disp_array[m_select]->m_head; + + while (mon_ptr != NULL) + { + status = ca_add_event(DBR_STS_STRING,mon_ptr->chan_id,lopi_ev_handler,mon_ptr->ev_ptr,NULL); + SEVCHK(status,NULL); + if (status != ECA_NORMAL) + logMsg("Message:%d\n",ca_message_text[CA_EXTRACT_MSG_NO(status)]); + type = ca_field_type(mon_ptr->chan_id); + if (DEBUG) + logMsg("Added event for %s,status = %d,type = %d\n",mon_ptr->chan,status,type); + + /* If channel is found display data if channel is up. Display error messae if channel + is down. */ + + + if ((type = (ca_field_type(mon_ptr->chan_id))) != TYPENOTCONN) + + /* If channel is connected display error message if it is down. + Print data if it is up. */ + { + if (mon_ptr->conn_flg == CA_OP_CONN_UP) + { + mon_row = mon_ptr->l_crn_row; + mon_col = mon_ptr->l_crn_col; + if (mon_ptr->prev_size > (strlen(mon_ptr->ev_ptr->str))) + { + blank_fill(txt_str,mon_ptr->prev_size); + mv_cursor(&mon_row,&mon_col,txt_str); + } + mon_ptr->prev_size = strlen(mon_ptr->ev_ptr->str); + mv_cursor(&mon_row,&mon_col,mon_ptr->ev_ptr->str); + } + else if (mon_ptr->conn_flg == CA_OP_CONN_DOWN) + { + ESCAPE + REVERSE_VIDEO + strcpy(txt_str,"CHAN DOWN"); + mv_cursor(&mon_row,&mon_col,txt_str); + ESCAPE + ATR_OFF + } + else + { + ESCAPE + REVERSE_VIDEO + strcpy(txt_str,"UNDEF"); + mv_cursor(&mon_row,&mon_col,txt_str); + ESCAPE + ATR_OFF + } + } + + mon_ptr = mon_ptr->next; + } + + if (disp_array[m_select]->c_head->next->l_crn_row != LIST_END) + ctl_ptr = disp_array[m_select]->c_head->next; + + while (ctl_ptr->l_crn_row != LIST_END) + { + status = ca_add_event(DBR_STS_STRING,ctl_ptr->chan_id,lopi_ev_handler, ctl_ptr->ev_ptr,NULL); + if (status != ECA_NORMAL) + logMsg("Message:%s\n",ca_message_text[CA_EXTRACT_MSG_NO(status)]); + type = ca_field_type(ctl_ptr->chan_id); + logMsg("Added event for %s,status = %d,type = %d\n",ctl_ptr->chan,status,type); + + /* If channel is connected, display data if channel is up. Display error message if + channel is down. */ + + + if ((type = (ca_field_type(ctl_ptr->chan_id))) != TYPENOTCONN) + { + if (ctl_ptr->conn_flg == CA_OP_CONN_UP) + { + mon_row = ctl_ptr->l_crn_row; + mon_col = ctl_ptr->l_crn_col; + if (ctl_ptr->prev_size > strlen(ctl_ptr->ev_ptr->str)) + { + blank_fill(txt_str,ctl_ptr->prev_size); + mv_cursor(&mon_row,&mon_col,txt_str); + } + mv_cursor(&mon_row,&mon_col,ctl_ptr->ev_ptr->str); + ctl_ptr->prev_size = strlen(ctl_ptr->ev_ptr->str); + } + + else if (ctl_ptr->conn_flg == CA_OP_CONN_DOWN) + { + ESCAPE + REVERSE_VIDEO + strcpy(txt_str,"CHAN DOWN"); + mv_cursor(&mon_row,&mon_col,txt_str); + ESCAPE + ATR_OFF + } + else + { + ESCAPE + REVERSE_VIDEO + strcpy(txt_str,"UNDEF"); + mv_cursor(&mon_row,&mon_col,txt_str); + ESCAPE + ATR_OFF + } + } + + /* If channel was never connected, display appropriate message. */ + + + ctl_ptr = ctl_ptr->next; + } /* End if ctl_ptr not equal to LIST END*/ + + status = ca_flush_io(); + SEVCHK(status,NULL); + + if (except_ptr->flg) + { + lopi_signal(except_ptr->status,except_ptr->msg); + if (status != ECA_NORMAL) + logMsg("Status abnormal: %d\n",ca_message_text[CA_EXTRACT_MSG_NO(status)]); + else + logMsg("Normal status:%d\n",ca_message_text[CA_EXTRACT_MSG_NO(status)]); + except_ptr->flg = NO_NEW_DATA; + } + + } /* End if init */ + else + { + mon_ptr = disp_array[m_select]->m_head; + + semTake(mon_sem); + while (mon_ptr != NULL) + { + if (*cur_mv == ON) + { + mon_row = mon_ptr->l_crn_row; + mon_col = mon_ptr->l_crn_col; + if (mon_ptr->prev_size > (strlen(mon_ptr->ev_ptr->str))) + { + blank_fill(txt_str,mon_ptr->prev_size); + mv_cursor(&mon_row,&mon_col,txt_str); + } + mon_ptr->prev_size = strlen(mon_ptr->ev_ptr->str); + mv_cursor(&mon_row,&mon_col,mon_ptr->ev_ptr->str); + mon_ptr = mon_ptr->next; + } + else if ((mon_ptr->ev_ptr->data_flg) && (mon_ptr->conn_flg == CA_OP_CONN_UP)) + { + mon_row = mon_ptr->l_crn_row; + mon_col = mon_ptr->l_crn_col; + mon_ptr->ev_ptr->data_flg = NO_NEW_DATA; + if (mon_ptr->prev_size > strlen(mon_ptr->ev_ptr->str)) + { + blank_fill(txt_str,mon_ptr->prev_size); + mv_cursor(&mon_row,&mon_col,txt_str); + } + mon_ptr->prev_size = strlen(mon_ptr->ev_ptr->str + 2); + mv_cursor(&mon_row,&mon_col,mon_ptr->ev_ptr->str); + mon_ptr = mon_ptr->next; + } + else if (mon_ptr->conn_flg == CA_OP_CONN_DOWN) + { + mon_row = mon_ptr->l_crn_row; + mon_col = mon_ptr->l_crn_col; + ESCAPE + REVERSE_VIDEO + strcpy(txt_str,"CHAN DOWN"); + mv_cursor(&mon_row,&mon_col,txt_str); + ESCAPE + ATR_OFF + mon_ptr->prev_size = 10; + mon_ptr = mon_ptr->next; + } + else + mon_ptr = mon_ptr->next; + + } + + if (disp_array[m_select]->c_head->next->l_crn_row != LIST_END) + ctl_ptr = disp_array[m_select]->c_head->next; + + while (ctl_ptr->l_crn_row != LIST_END) + { + if (*cur_mv == ON) + { + mon_row = ctl_ptr->l_crn_row; + mon_col = ctl_ptr->l_crn_col; + if (ctl_ptr->prev_size > (strlen(ctl_ptr->ev_ptr->str))) + { + blank_fill(txt_str,ctl_ptr->prev_size); + mv_cursor(&mon_row,&mon_col,txt_str); + } + mon_ptr->prev_size = strlen(ctl_ptr->ev_ptr->str); + mv_cursor(&mon_row,&mon_col,ctl_ptr->ev_ptr->str); + ctl_ptr = ctl_ptr->next; + } + else if ((ctl_ptr->ev_ptr->data_flg) && (ctl_ptr->conn_flg == CA_OP_CONN_UP)) + { + mon_row = ctl_ptr->l_crn_row; + mon_col = ctl_ptr->l_crn_col; + ctl_ptr->ev_ptr->data_flg = NO_NEW_DATA; + if (ctl_ptr->prev_size > strlen(ctl_ptr->ev_ptr->str)) + { + blank_fill(txt_str,ctl_ptr->prev_size ); + mv_cursor(&mon_row,&mon_col,txt_str); + } + ctl_ptr->prev_size =(strlen(ctl_ptr->ev_ptr->str)) + 2; + mv_cursor(&mon_row,&mon_col,ctl_ptr->ev_ptr->str); + ctl_ptr = ctl_ptr->next; + } + else if (ctl_ptr->conn_flg == CA_OP_CONN_DOWN) + { + mon_row = ctl_ptr->l_crn_row; + mon_col = ctl_ptr->l_crn_col; + ESCAPE + REVERSE_VIDEO + strcpy(txt_str,"CHAN DOWN"); + mv_cursor(&mon_row,&mon_col,txt_str); + ESCAPE + ATR_OFF + ctl_ptr->prev_size = 10; + ctl_ptr = ctl_ptr->next; + } + else + ctl_ptr = ctl_ptr->next; + } + + + *cur_mv = OFF; + *pdata_flg = NO_NEW_DATA ; + + semGive(mon_sem); + if (except_ptr->flg) + { + lopi_signal(except_ptr->status,except_ptr->msg); + mon_col++; + if (status != ECA_NORMAL) + logMsg("Status abnormal: %d\n",ca_message_text[CA_EXTRACT_MSG_NO(status)]); + else + logMsg("Normal status:%d\n",ca_message_text[CA_EXTRACT_MSG_NO(status)]); + except_ptr->flg = NO_NEW_DATA; + } + + } /* End else */ + } + +/*************************************************************************************/ +/* VOID stop_monitors */ +/*************************************************************************************/ +static VOID stop_monitors(disp_array,m_select) + struct window_node *disp_array[MAX_DISP_NUM]; + short m_select; /* Pointer to number of screen selected. */ + { + struct mon_node *mon_ptr, *ctl_ptr; + + mon_ptr = disp_array[m_select]->m_head; + if (disp_array[m_select]->c_head->next->l_crn_row != LIST_END) + ctl_ptr = disp_array[m_select]->c_head->next; + + /* Free channel access resources. */ + + semTake(mon_sem); + + while (mon_ptr != NULL) + { + ca_clear_channel(mon_ptr->chan_id); + mon_ptr = mon_ptr->next; + } + + while (ctl_ptr->l_crn_row != LIST_END) + { + ca_clear_channel(ctl_ptr->chan_id); + ctl_ptr = ctl_ptr->next; + } + + /* Exit channel access. */ + + ca_flush_io(); + + semGive(mon_sem); + + /* Initialize monitor pointer to the head of the monitor linked list + in order to move through linked list freeing memory. */ + + + semTake(mon_sem); + mon_ptr = disp_array[m_select]->m_head; + while (mon_ptr != NULL) + { + free(mon_ptr->ev_ptr); + mon_ptr->ev_ptr = NULL; + mon_ptr = mon_ptr->next; + } + + /* Initialize control pointer to the head of the control linked list + in order to move through the list freeing memory. */ + + + if (disp_array[m_select]->c_head->next->l_crn_row != LIST_END) + ctl_ptr = disp_array[m_select]->c_head->next; + + while (ctl_ptr->l_crn_row != LIST_END) + { + free(ctl_ptr->ev_ptr); + mon_ptr->ev_ptr = NULL; + ctl_ptr = ctl_ptr->next; + } + semGive(mon_sem); + + } + +/*************************************************************************************/ +/* Monitor event handler. Sets the global data_flg when an event is received */ +/* asynchronously. The flag is reset in display_monitors. */ +/*************************************************************************************/ + + + +static VOID lopi_ev_handler(lopi_arg) + struct event_handler_args lopi_arg; + { + struct dbr_sts_string *sts_str_ptr; + struct ev_node *ev_ptr; + char txt_str[40]; + static short mon_row = 0; + static short mon_col = 0; + char name[15],*nm_ptr; + struct mon_node *mptr; + + sts_str_ptr = (struct dbr_sts_string *)lopi_arg.dbr; + ev_ptr = ((struct ev_node *)lopi_arg.usr); + + /* If new data has arrived, copy it to the data_string and + set a flag indicating new data. */ + + mptr = (struct mon_node *)ca_puser(lopi_arg.chid); + nm_ptr =(char *)(ca_name(lopi_arg.chid)); + semTake(mon_sem); + if(DEBUG) + logMsg("Ev hand for CA:%s, BW:%s\n",nm_ptr,mptr->chan); + if ((data_flg = strncmp(ev_ptr->str,sts_str_ptr->value,12) != 0)) + { + strncpy(ev_ptr->str, sts_str_ptr->value,12); + ev_ptr->str[12] = NULL; + ev_ptr->data_flg = NEW_DATA; + } + semGive(mon_sem); + } + +/*************************************************************************************/ +/* Exception handler. This routine is called by channel access for asychronous */ +/* events in the server. */ +/*************************************************************************************/ + + static VOID lopi_exception_handler(lopi_except_arg) + struct exception_handler_args lopi_except_arg; + { + struct except_node *except_ptr; + except_ptr = (struct except_node *)lopi_except_arg.usr; + if (DEBUG) + logMsg("Exception handler got exception. \n"); + strncpy(except_ptr->msg,lopi_except_arg.ctx, MAX_LIN_LEN - 2); + except_ptr->msg[MAX_LIN_LEN - 1] = NULL; + except_ptr->status = lopi_except_arg.stat; + except_ptr->flg = NEW_DATA; + } + +/*************************************************************************************/ +/* Monitor connection handler. This routine is called by channel access to report */ +/* changes in the connection status channels. */ +/*************************************************************************************/ + +static VOID lopi_conn_handler(lopi_connect_arg) + struct connection_handler_args lopi_connect_arg; + { + struct mon_node *pmon; + int status; + char txt_str[40]; + static short mon_row = 0; + static short mon_col = 0; + char name[15],*nm_ptr; + + pmon = (struct mon_node *)ca_puser(lopi_connect_arg.chid); + nm_ptr = (char *)ca_name(lopi_connect_arg.chid); + if (lopi_connect_arg.op == CA_OP_CONN_DOWN) + { + pmon->conn_flg = CA_OP_CONN_DOWN; + } + else if (lopi_connect_arg.op == CA_OP_CONN_UP) + { + pmon->conn_flg = CA_OP_CONN_UP; + } + else + pmon->conn_flg = UNDEF; + + if (DEBUG) + logMsg("Conn hand for CA:%s, BW:%s\n",nm_ptr,pmon->chan); + + } + +/*************************************************************************************/ +/* Frees memory allocated by lopi.c */ +/*************************************************************************************/ + +static free_mem(disp_array,dmenu,nlines,except_ptr) + struct window_node *disp_array[MAX_DISP_NUM]; + int nlines; + char *dmenu[MXMENU]; + { + struct txt_node *txt_ptr; + struct mon_node *ctl_ptr; + struct mon_node *mon_ptr; + char *mptr; + register short lopi_i; + for (lopi_i = 0; lopi_i < nlines; lopi_i++) + { + while (disp_array[lopi_i]->t_head != NULL) + { + txt_ptr = disp_array[lopi_i]->t_head; + disp_array[lopi_i]->t_head = disp_array[lopi_i]->t_head->next; + free(txt_ptr); + } + + while(disp_array[lopi_i]->c_head != NULL) + { + ctl_ptr = disp_array[lopi_i]->c_head; + disp_array[lopi_i]->c_head = disp_array[lopi_i]->c_head->next; + free(ctl_ptr); + } + + while(disp_array[lopi_i]->m_head != NULL) + { + mon_ptr = disp_array[lopi_i]->m_head; + disp_array[lopi_i]->m_head = disp_array[lopi_i]->m_head->next; + free(mon_ptr); + } + + mptr = dmenu[lopi_i]; + free(mptr); + dmenu[lopi_i] = NULL; + + } + free(except_ptr); + } + +/********************************************************************************/ +/* Puts the value passed to it as a string to the data base channel which cptr */ +/* points to. */ +/********************************************************************************/ + +static put_value(c_ptr,val_in) + char val_in[MAX_STRING_SIZE]; /* Array containing value to be put. */ + struct mon_node *c_ptr; /* Pointer to record of channel to which value is to + be passes. */ + { + status = ca_put(DBR_STRING,c_ptr->chan_id,((void *)val_in)); + status = ca_flush_io(); + if (status != ECA_NORMAL) + logMsg("Message:%s\n",ca_message_text[CA_EXTRACT_MSG_NO(status)]); + } diff --git a/src/util/lopi_def.h b/src/util/lopi_def.h new file mode 100644 index 000000000..61cea1ff2 --- /dev/null +++ b/src/util/lopi_def.h @@ -0,0 +1,222 @@ + /* $Id$ + * Author: Betty Ann Gunther + * Date: 02-15-91 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * .01 07-03-91 rac changed "bw" to "lopi" + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +VOID lopi_init(); +VOID create_menu(); +VOID get_displays(); +struct mon_node *mon_alloc(); /* Allocates memory for an update.*/ +struct txt_node *txt_alloc(); /* Allocates memory for a test label. */ +struct ev_node *ev_alloc(); /* Allocates memory for an event node. */ +struct except_node *except_alloc(); /* Allocates memory for an exception node. */ +struct window_node *win_alloc(); /* Allocates memory for a window structure. */ +VOID init_monitors (); +VOID print_menu(); +VOID mv_cursor(); +VOID get_key(); +char get_esc_seq(); +VOID make_box(); +VOID get_value(); +VOID blank_fill(); +VOID mv_cursor(); +char *skipblanks(); +int fgetline(); +VOID lopi_conn_handler(); +VOID lopi_exception_handler(); +VOID add_ctl(); +VOID add_mon(); +VOID add_txt(); +char *skip_to_digit(); +int int_to_short(); +VOID read_disp_lst(); +VOID display_text(); +VOID display_file(); +VOID display_monitors(); +VOID stop_monitors(); +VOID lopi_ev_handler(); +VOID lopi_exception_handler(); +VOID lopi_conn_handler(); +VOID free_mem(); +struct mon_node *find_channel(); +VOID put_value(); + +#define FN_LEN 24 +#define LIST_END -1 +#define NEWLINE '\n' +#define MXMENU 34 +#define ESC '\33' +#define L_BRACKET '[' +#define RETURN '\15' +#define CR '\13' +#define DELETE '\177' +#define BLANK ' ' +#define UNDERSC '_' +#define COLON ':' +#define TILDE '~' +#define ASTER '*' /* Legal text character. */ +#define L_ARROW '$' +#define R_ARROW '%' +#define U_ARROW '!' +#define D_ARROW '@' +#define ILL_CHR '#' +#define F6 '^' /* Permits exit back to main menu. */ +#define F7 '&' +#define DOT '.' + +/* VT220 Escape secquences */ + +#define CLEAR_SCREEN printf("%c", '\33'); \ + printf("%s","[2J"); +#define INIT_CURSOR printf("%c", '\33'); \ + printf("%s", "[?25h"); +#define ERASE_LINE printf("%c",ESC); \ + printf("%s","[2K"); +#define ESCAPE printf("%c",'\33'); +#define REVERSE_VIDEO printf("%s","[7;5m"); +#define ATR_OFF printf("%s","[0m"); + +#define CTL 1 +#define MON 0 +#define DEBUG 0 +#define OFF 0 +#define ON 1 +#define NO_CMD "\0" +#define NEW_DATA 1 +#define NO_NEW_DATA 0 +#define NOTHING_SELECTED -1 /* Flag to indicate that lopi is waiting + for the user to make a selection. */ +#define STRT_TTLE_ROW 1 /* Title row position. */ +#define STRT_TTLE_COL 30 /* Title column position. */ +#define STRT_MENU_ROW 7 /* Row position of first menu item. */ +#define STRT_MENU_COL 20 /* Column position of first menu item. */ +#define MAX_DISP_NUM 10 /* Maximum number of displays. */ +#define MAX_LIN_LEN 80 /* Maximum number of characters in a line of a + display file. */ +#define BUFF_SIZE 10 +#define SHELL_ID 7 +#define MAX_SCREEN_HEIGHT 24 +#define MAX_SCREEN_WIDTH 78 +#define CHAN_NM_SIZE 40 +#define MAX_FIELD_SIZE 8 + +#define ERR_MSG_ROW 20 +#define ERR_MSG_COL 0 +#define BOX_IN_ROW 2 +#define BOX_IN_COL 43 +#define BEG_BOX 1 +#define END_BOX 3 +#define UNDEF 8 +#define FOUND 1 +#define NOT_FOUND 0 + +shell_init() +{ + ioctl(1,FIOSETOPTIONS,OPT_RAW); +} + +/* + * CA_SIGNAL() + * + * + */ + +lopi_signal(ca_status,message) +int ca_status; +char *message; +{ + short row = ERR_MSG_ROW; + short col = ERR_MSG_COL; + char prefix[MAX_LIN_LEN]; + char txt_str[MAX_LIN_LEN]; + + static char *severity[] = + { + "Warning", + "Success", + "Error", + "Info", + "Fatal", + "Fatal", + "Fatal", + "Fatal" + }; + +/* + if( CA_EXTRACT_MSG_NO(ca_status) >= NELEMENTS(ca_message_text) ){ + message = "corrupt status"; + ca_status = ECA_INTERNAL; + } +*/ + strcpy(txt_str, "CA.Diagnostic.........."); + mv_cursor(&row,&col,txt_str); + + + if(message){ + strcpy(prefix,"Severity:[%s] Error:[%s]Context: [%s]"); + sprintf(txt_str,prefix,severity[CA_EXTRACT_SEVERITY(ca_status)], + ca_message_text[CA_EXTRACT_MSG_NO(ca_status)],message); + mv_cursor(&row,&col,txt_str); + } + else{ + strcpy(prefix,"Severity:[%s],Error:[%s]"); + sprintf(txt_str,prefix,severity[CA_EXTRACT_SEVERITY(ca_status)], + ca_message_text[CA_EXTRACT_MSG_NO(ca_status)]); + mv_cursor(&row,&col,txt_str); + } + /* + * + * + * Terminate execution if unsuccessful + * + * + */ + if( !(ca_status & CA_M_SUCCESS) ){ +# ifdef VMS + lib$signal(0); +# endif +# ifdef vxWorks + ti(VXTHISTASKID); + tt(VXTHISTASKID); +# endif + /* abort(ca_status); */ + } + +} +