Beispiel #1
0
PyObject *pv_getter_pvhostname(pvobject * self, void *closure)
{
	if (self->chanId)
		return PyString_FromString(ca_host_name(self->chanId));
	else {
		PYCA_ERR("pvhostname: invalid chid");
	}
}
static void printChidInfo(chid chid, char *message)
{
	printf("\n%s\n",message);
	printf("pv: %s  type(%d) nelements(%ld) host(%s)",
			ca_name(chid),ca_field_type(chid),ca_element_count(chid),
			ca_host_name(chid));
	printf(" read(%d) write(%d) state(%d)\n",
			ca_read_access(chid),ca_write_access(chid),ca_state(chid));
}
static void exCB(struct exception_handler_args args)
{
#define MAX_EXCEPTIONS 25    
    static int nexceptions=0;
    static int ended=0;

    if(ended) return;
    if(nexceptions++ > MAX_EXCEPTIONS) {
        ended=1;
        fprintf(stderr,"exCB Exception: Channel Access Exception:\n"
          "Too many exceptions [%d]\n"
          "No more will be handled\n"
          "Please fix the problem and restart camonitorpv",
          MAX_EXCEPTIONS);
        ca_add_exception_event(NULL, NULL);
        return;
    }
    
    fprintf(stderr,"exCB Exception: Channel Access Exception:\n"
      "  Channel Name: %s\n"
      "  Native Type: %s\n"
      "  Native Count: %lu\n"
      "  Access: %s%s\n"
      "  IOC: %s\n"
      "  Message: %s\n"
      "  Context: %s\n"
      "  Requested Type: %s\n"
      "  Requested Count: %ld\n"
      "  Source File: %s\n"
      "  Line number: %u",
      args.chid?ca_name(args.chid):"Unavailable",
      args.chid?dbf_type_to_text(ca_field_type(args.chid)):"Unavailable",
      args.chid?ca_element_count(args.chid):0,
      args.chid?(ca_read_access(args.chid)?"R":""):"Unavailable",
      args.chid?(ca_write_access(args.chid)?"W":""):"",
      args.chid?ca_host_name(args.chid):"Unavailable",
      ca_message(args.stat)?ca_message(args.stat):"Unavailable",
      args.ctx?args.ctx:"Unavailable",
      dbf_type_to_text(args.type),
      args.count,
      args.pFile?args.pFile:"Unavailable",
      args.pFile?args.lineNo:0);
}
Beispiel #4
0
long dbcar(char *precordname, int level)
{
    DBENTRY             dbentry;
    DBENTRY             *pdbentry=&dbentry;
    long                status;
    dbCommon            *precord;
    dbRecordType        *pdbRecordType;
    dbFldDes            *pdbFldDes;
    DBLINK              *plink;
    int                 ncalinks=0;
    int                 nconnected=0;
    int                 noReadAccess=0;
    int                 noWriteAccess=0;
    unsigned long       nDisconnect=0;
    unsigned long       nNoWrite=0;
    caLink              *pca;
    int                 j;

    if (!precordname || precordname[0] == '\0' || !strcmp(precordname, "*")) {
        precordname = NULL;
        printf("CA links in all records\n\n");
    } else {
        printf("CA links in record named '%s'\n\n", precordname);
    }
    dbInitEntry(pdbbase,pdbentry);
    status = dbFirstRecordType(pdbentry);
    while (!status) {
        status = dbFirstRecord(pdbentry);
        while (!status) {
            if (precordname ?
                !strcmp(precordname, dbGetRecordName(pdbentry)) :
                !dbIsAlias(pdbentry)) {
                pdbRecordType = pdbentry->precordType;
                precord = (dbCommon *)pdbentry->precnode->precord;
                for (j=0; j<pdbRecordType->no_links; j++) {
                    pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
                    plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
                    dbLockSetGblLock();
                    if (plink->type == CA_LINK) {
                        ncalinks++;
                        pca = (caLink *)plink->value.pv_link.pvt;
                        if (pca
                        && pca->chid
                        && (ca_field_type(pca->chid) != TYPENOTCONN)) {
                            nconnected++;
                            nDisconnect += pca->nDisconnect;
                            nNoWrite += pca->nNoWrite;
                            if (!ca_read_access(pca->chid)) noReadAccess++;
                            if (!ca_write_access(pca->chid)) noWriteAccess++;
                            if (level>1) {
                                int rw = ca_read_access(pca->chid) |
                                         ca_write_access(pca->chid) << 1;
                                static const char *rights[4] = {
                                    "No Access", "Read Only",
                                    "Write Only", "Read/Write"
                                };
                                int mask = plink->value.pv_link.pvlMask;
                                printf("%28s.%-4s ==> %-28s  (%lu, %lu)\n",
                                    precord->name,
                                    pdbFldDes->name,
                                    plink->value.pv_link.pvname,
                                    pca->nDisconnect,
                                    pca->nNoWrite);
                                printf("%21s [%s%s%s%s] host %s, %s\n", "",
                                    mask & pvlOptInpNative ? "IN" : "  ",
                                    mask & pvlOptInpString ? "IS" : "  ",
                                    mask & pvlOptOutNative ? "ON" : "  ",
                                    mask & pvlOptOutString ? "OS" : "  ",
                                    ca_host_name(pca->chid),
                                    rights[rw]);
                            }
                        } else {
                            if (level>0) {
                                printf("%28s.%-4s --> %-28s  (%lu, %lu)\n",
                                    precord->name,
                                    pdbFldDes->name,
                                    plink->value.pv_link.pvname,
                                    pca->nDisconnect,
                                    pca->nNoWrite);
                            }
                        }
                    }
                    dbLockSetGblUnlock();
                }
                if (precordname) goto done;
            }
            status = dbNextRecord(pdbentry);
        }
        status = dbNextRecordType(pdbentry);
    }
done:
    if ((level > 1 && nconnected > 0) ||
        (level > 0 && ncalinks != nconnected)) printf("\n");
    printf("Total %d CA link%s; ",
           ncalinks, (ncalinks != 1) ? "s" : "");
    printf("%d connected, %d not connected.\n",
           nconnected, (ncalinks - nconnected));
    printf("    %d can't read, %d can't write.",
           noReadAccess, noWriteAccess);
    printf("  (%lu disconnects, %lu writes prohibited)\n\n",
           nDisconnect, nNoWrite);
    dbFinishEntry(pdbentry);
    
    if ( level > 2  && dbCaClientContext != 0 ) {
        ca_context_status ( dbCaClientContext, level - 2 );
    }

    return(0);
}
Beispiel #5
0
void mexFunction(	int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{   
    int i,j, k, status,buflen,Cnt, Hndl, L,M,N,  NumHandles, commandswitch;
    
    int *HndlArray;
    mxArray  *mymxArray;
    double *myDblPr;
    chtype RequestType;
    
    char PVName[PV_NAME_LENGTH_MAX+1];
    // char MCAMessageString[MCA_MESSAGE_STRING_LENGTH_MAX+1];
    
    
      

    dbr_string_t StrBuffer;
    
        
    
    const char *MCAInfoFields[]={"PVName","ElementCount","NativeType","State","MCAMessage","Host"};
    char *NativeTypeStrings[] = {"STRING","INT","FLOAT","ENUM","CHAR","LONG","DOUBLE"};
    


    
    if(!CA_INITIALIZED) // Initialize CA if not initialized (first call)
    {   mexPrintf("Initializing MATLAB Channel Access ... \n");
        status = ca_task_initialize();
        if(status!=ECA_NORMAL)
            mexErrMsgTxt("Unable to initialise Challel Access\n");
        CA_INITIALIZED = true;
        // Register a function to be called when a this mex-file is cleared from memory
        // with 'clear' or when exitting MATLAB
        mexAtExit(mca_cleanup);
        // Lock the mex-file so that it can not be cleared without explicitly
        // mexUnclock
        mexLock();
        
        //start periodic polling:
/*        PollTimerHandle = SetTimer(NULL,NULL,MCA_POLL_PERIOD,background_poll);
        if(PollTimerHandle)
            mexPrintf("Periodic CA polling started! System Timer ID: %u\n",PollTimerHandle);
        else
            mexWarnMsgTxt("Failed to start periodic CA polling\n");
 */       
        
    }

    commandswitch = (int)mxGetScalar(prhs[0]);
   
    switch(commandswitch)
    {  case 0: 
            mexUnlock();
            break;
    
        case 1: // MCAOPEN - add channel(s) by PV names, all arguments following prhs[0]
               // must be strings - names of PV's
            for(i=1;i<nrhs;i++)
            {   mxGetString(prhs[i],PVName,PV_NAME_LENGTH_MAX+1);
                status = ca_search(PVName,&(CHNLS[HandlesUsed].CHID));
                if(status == ECA_NORMAL) // if not - go on to the next PV name
                {   status = ca_pend_io(MCA_SEARCH_TIMEOUT);
                    if (status == ECA_NORMAL)
                    {   // Allocate persistent memory for the DataBuffer on this channel
                        // to hold all elements of the DBR_XXX type
                        // nearest to the native type
                        // RequestType = dbf_type_to_DBR(ca_field_type(CHNLS[HandlesUsed].CHID));
                        // Cnt=ca_element_count(CHNLS[HandlesUsed].CHID);
                        
                        
                        CHNLS[HandlesUsed].NumElements = ca_element_count(CHNLS[HandlesUsed].CHID);
                        CHNLS[HandlesUsed].NativeType2DBR = dbf_type_to_DBR(ca_field_type(CHNLS[HandlesUsed].CHID));
                        CHNLS[HandlesUsed].MonitorEventCount = 0;
                        
                        switch(CHNLS[HandlesUsed].NativeType2DBR)
                        {   case DBR_STRING:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_string_t));
                                
                            break;
                        
                            case DBR_INT: // As defined in db_access.h DBR_INT = DBR_SHORT = 1
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_short_t)); 
                            break;
                        
                            case DBR_FLOAT:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_float_t)); 
                            break;
                
                            case DBR_ENUM:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_enum_t)); 
                            break;
                
                            case DBR_CHAR:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_char_t)); 
                            break;
                    
                            case DBR_LONG:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_short_t)); 
                            break;
                    
                            case DBR_DOUBLE:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_double_t)); 
                            break;
                        }   
                        mexMakeMemoryPersistent(CHNLS[HandlesUsed].DataBuffer);
                        
                                 
                        if(CHNLS[HandlesUsed].NativeType2DBR==DBR_STRING) // CACHE
                        {   if(CHNLS[HandlesUsed].NumElements==1) // Create MATLAB string - originally empty
                                CHNLS[HandlesUsed].CACHE = mxCreateString("");
                            else // Create MATLAB cell array of strings
                            {   CHNLS[HandlesUsed].CACHE = mxCreateCellMatrix(1,CHNLS[HandlesUsed].NumElements);
                                for(k=0;k<CHNLS[HandlesUsed].NumElements;k++)
                                {   mymxArray = mxCreateString("");
                                    mexMakeArrayPersistent(mymxArray);
                                    mxSetCell(CHNLS[HandlesUsed].CACHE, k, mymxArray);
                                }
                            }
                        }
                        else // Make CACHE a numeric mxArray 
                        {    CHNLS[HandlesUsed].CACHE = mxCreateDoubleMatrix(1,CHNLS[HandlesUsed].NumElements,mxREAL);  
                        }
                        
                        mexMakeArrayPersistent(CHNLS[HandlesUsed].CACHE);
                        
                        plhs[i-1]=mxCreateScalarDouble(++HandlesUsed);                        
                    
                    }
                    else
                        plhs[i-1]=mxCreateScalarDouble(0);

                }
                else
                    plhs[i-1]=mxCreateScalarDouble(0);
            } break;
            
        
        case 2:// MCAOPEN - add channel(s) by PV names. The arguments following prhs[0]
               // argument must be a cell array of strings - PV names
            
            L = mxGetM(prhs[1])*mxGetN(prhs[1]);
            plhs[0] = mxCreateDoubleMatrix(1,L,mxREAL);
            myDblPr = mxGetPr(plhs[0]);
            
            for(i=0;i<L;i++)
            {   mymxArray = mxGetCell(prhs[1],i);
                mxGetString(mymxArray,PVName,PV_NAME_LENGTH_MAX+1);
                status = ca_search(PVName,&(CHNLS[HandlesUsed].CHID));
                if(status == ECA_NORMAL) // if not - go on to the next PV name
                {   status = ca_pend_io(MCA_IO_TIMEOUT);
                    if (status == ECA_NORMAL)
                    {   // Allcate persistent memory for the DataBuffer on this channel
                        //RequestType = dbf_type_to_DBR(ca_field_type(CHNLS[HandlesUsed].CHID));
                        CHNLS[HandlesUsed].NativeType2DBR = dbf_type_to_DBR(ca_field_type(CHNLS[HandlesUsed].CHID));
                        CHNLS[HandlesUsed].NumElements = ca_element_count(CHNLS[HandlesUsed].CHID);
                        CHNLS[HandlesUsed].MonitorEventCount = 0;
                        //Cnt=ca_element_count(CHNLS[HandlesUsed].CHID);
                        switch(CHNLS[HandlesUsed].NativeType2DBR)
                        {   case DBR_STRING:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_string_t)); 
                            break;
                        
                            case DBR_INT: // As defined in db_access.h DBR_INT = DBR_SHORT = 1
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_short_t)); 
                            break;
                        
                            case DBR_FLOAT:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_float_t)); 
                            break;
                
                        
                            case DBR_ENUM:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_enum_t)); 
                            break;
                
                            case DBR_CHAR:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_char_t)); 
                            break;
                    
                            case DBR_LONG:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_short_t)); 
                            break;
                    
                            case DBR_DOUBLE:
                                CHNLS[HandlesUsed].DataBuffer = mxCalloc(CHNLS[HandlesUsed].NumElements,sizeof(dbr_double_t)); 
                            break;
                        }   
                        mexMakeMemoryPersistent(CHNLS[HandlesUsed].DataBuffer);   
                        
                        if(CHNLS[HandlesUsed].NativeType2DBR == DBR_STRING) // CACHE
                        {   CHNLS[HandlesUsed].CACHE = mxCreateCellMatrix(1,CHNLS[HandlesUsed].NumElements);
                            for(k=0;k<CHNLS[HandlesUsed].NumElements;k++)
                            {   mymxArray = mxCreateString(StrBuffer);
                                mexMakeArrayPersistent(mymxArray);
                                mxSetCell(CHNLS[HandlesUsed].CACHE, k, mymxArray);
                            }
                        }
                        else
                        {    CHNLS[HandlesUsed].CACHE = mxCreateDoubleMatrix(1,CHNLS[HandlesUsed].NumElements,mxREAL);  
                        }
                        
                        mexMakeArrayPersistent(CHNLS[HandlesUsed].CACHE);
                        
                        
                        myDblPr[i] = ++HandlesUsed;
                    }
                    else
                        myDblPr[i] = 0;
                }
                else
                    myDblPr[i] = 0;
            } break;
            
       case 3: // MCAOPEN Return names of connected channels as cell array of strings
            plhs[0] = mxCreateCellArray(1, &HandlesUsed);
            for(i=0;i<HandlesUsed;i++)
            {   if(CHNLS[i].CHID!=NULL)
                    {   mymxArray = mxCreateString(ca_name(CHNLS[i].CHID));
                        mxSetCell(plhs[0], i, mymxArray);
                    }
                else
                    {   mymxArray = mxCreateString("");
                        //mexPrintf("Handle: %d PV: %s\n",i+1, "Cleared Channel");
                        mxSetCell(plhs[0], i, mymxArray);
                    }
              } break;
        
        
         
        case 5: // MCACLOSE permanently clear channel
            Hndl = (int)mxGetScalar(prhs[1]);
            if(Hndl<1 || Hndl>HandlesUsed)
                mexErrMsgTxt("Handle out of range");  

            // If a monitor is installed, set the EVID pointer to NULL 
            // ca_clear_event dos not do it by itself

            if(CHNLS[Hndl-1].EVID) 
                CHNLS[Hndl-1].EVID = NULL;
                
            // If there is Callback String - destroy it
            if(CHNLS[Hndl-1].MonitorCBString)
            {   mxFree(CHNLS[Hndl-1].MonitorCBString); 
                CHNLS[Hndl-1].MonitorCBString =NULL;
            }    
            
            if(ca_state(CHNLS[Hndl-1].CHID)==3)
                mexWarnMsgTxt("Channel previously cleared");
            else
                if(ca_clear_channel(CHNLS[Hndl-1].CHID)!=ECA_NORMAL)
                    mexErrMsgTxt("ca_clear_channel failed");

            break;
            
        case 10: // MCAINFO return channels info as MATLAB structure array
            if(HandlesUsed>0)
            {   plhs[0] = mxCreateStructMatrix(1,HandlesUsed,6,MCAInfoFields);
                
                for(i=0;i<HandlesUsed;i++)
                {   mxSetFieldByNumber(plhs[0],i,0,mxCreateString(ca_name(CHNLS[i].CHID)));
                    mxSetFieldByNumber(plhs[0],i,1,mxCreateScalarDouble(ca_element_count(CHNLS[i].CHID)));
                    mxSetFieldByNumber(plhs[0],i,5,mxCreateString(ca_host_name(CHNLS[i].CHID)));
                    
                    switch(ca_state(CHNLS[i].CHID))
                    {   case 1: // Disconnected due to Server or Network - may reconnect 
                            mxSetFieldByNumber(plhs[0],i,2,mxCreateString("unknown"));
                            mxSetFieldByNumber(plhs[0],i,3,mxCreateString("disconnected"));
                            mxSetFieldByNumber(plhs[0],i,4,mxCreateString("Disconnected due to server or network problem"));
                            break;
                        case 2: // Normal connection
                            mxSetFieldByNumber(plhs[0],i,2,mxCreateString(NativeTypeStrings[ca_field_type(CHNLS[i].CHID)]));
                            mxSetFieldByNumber(plhs[0],i,3,mxCreateString("connected"));
                            mxSetFieldByNumber(plhs[0],i,4,mxCreateString("Normal connection"));
                            break;
                        case 3: // Disconnected by user
                            mxSetFieldByNumber(plhs[0],i,2,mxCreateString("unknown"));
                            mxSetFieldByNumber(plhs[0],i,3,mxCreateString("disconnected"));
                            mxSetFieldByNumber(plhs[0],i,4,mxCreateString("Permanently disconnected (cleared) by the user"));                    
                            break;
                    }    
                }
            }
            else
            {   mexWarnMsgTxt("No connected PV's found"); 
                plhs[0] = mxCreateDoubleMatrix(0,0,mxREAL);
                
            }
            break;    
        
            
        case 11: // MCAINFO return info for 1 channel by handle number 
            Hndl = (int)mxGetScalar(prhs[1]);
            if(Hndl<1 || Hndl>HandlesUsed)
                mexErrMsgTxt("Handle out of range");  
                
            plhs[0] = mxCreateStructMatrix(1,1,6,MCAInfoFields);

            mxSetFieldByNumber(plhs[0],0,0,mxCreateString(ca_name(CHNLS[Hndl-1].CHID)));
            mxSetFieldByNumber(plhs[0],0,1,mxCreateScalarDouble(ca_element_count(CHNLS[Hndl-1].CHID)));
            mxSetFieldByNumber(plhs[0],0,5,mxCreateString(ca_host_name(CHNLS[Hndl-1].CHID)));
            
            switch(ca_state(CHNLS[Hndl-1].CHID))
            {  case 1: // Disconnected due to Server or Network - may reconnect 
                    mxSetFieldByNumber(plhs[0],0,2,mxCreateString("unknown"));
                    mxSetFieldByNumber(plhs[0],0,3,mxCreateString("disconnected"));
                    mxSetFieldByNumber(plhs[0],0,4,mxCreateString("Disconnected due to server or network problem"));
                    break;
                case 2: // Normal connection
                    mxSetFieldByNumber(plhs[0],0,2,mxCreateString(NativeTypeStrings[ca_field_type(CHNLS[Hndl-1].CHID)]));
                    mxSetFieldByNumber(plhs[0],0,3,mxCreateString("connected"));
                    mxSetFieldByNumber(plhs[0],0,4,mxCreateString("Normal connection"));
                    break;
                case 3: // Disconnected by user
                    mxSetFieldByNumber(plhs[0],0,2,mxCreateString("unknown"));
                    mxSetFieldByNumber(plhs[0],0,3,mxCreateString("disconnected"));
                    mxSetFieldByNumber(plhs[0],0,4,mxCreateString("Permanently disconnected (cleared) by the user"));                    
                    break;
            };    
            
        break;    
        
        case 12: // MCASTATE return an array of status (1 - OK, 0 - disconnected or cleared) 
            if(HandlesUsed>0)
            {   plhs[0] = mxCreateDoubleMatrix(1,HandlesUsed,mxREAL);
                myDblPr = mxGetPr(plhs[0]);
                for(i=0;i<HandlesUsed;i++)
                    myDblPr[i] = (double)(ca_state(CHNLS[i].CHID)==2);
            }
            else
            {   mexWarnMsgTxt("No connected PV's found"); 
                plhs[0] = mxCreateDoubleMatrix(0,0,mxREAL);
                
            }
            break;    
        
        
        case 30: // poll
            ca_poll();
        break;
            

        case 50: // MCAGET Get PV values by their MCA handles
            
            for(i=0;i<nrhs-1;i++) // First loop: place all ca_get requests in the buffer
            {   Hndl = (int)mxGetScalar(prhs[1+i]); //start from[1]:  [0] argument is the commnads switch
                if(Hndl<1 || Hndl>HandlesUsed)
                    mexErrMsgTxt("Invalid Handle");
                
                RequestType = dbf_type_to_DBR(ca_field_type(CHNLS[Hndl-1].CHID));
                Cnt = ca_element_count(CHNLS[Hndl-1].CHID);
                status = ca_array_get(RequestType,Cnt,CHNLS[Hndl-1].CHID,CHNLS[Hndl-1].DataBuffer);
                if(status!=ECA_NORMAL)
                    mexPrintf("Error in call to ca_array_get\n");
            }   
            
            status = ca_pend_io(MCA_GET_TIMEOUT);
            if(status!=ECA_NORMAL)
                mexErrMsgTxt("... ca_pend_io call timed out \n");
            
            
            for(i=0;i<nrhs-1;i++) // Another loop to copy data from temp structures to MATLAB
            
            {   Hndl = (int)mxGetScalar(prhs[1+i]);
                RequestType = RequestType = dbf_type_to_DBR(ca_field_type(CHNLS[Hndl-1].CHID));
                Cnt = ca_element_count(CHNLS[Hndl-1].CHID);
                
                if(RequestType==DBR_STRING)
                {   if(Cnt==1)
                        plhs[i] = mxCreateString((char*)(*((dbr_string_t*)(CHNLS[Hndl-1].DataBuffer))));
                    else
                    {   plhs[i] = mxCreateCellMatrix(1,Cnt);
                        for(j=0;j<Cnt;j++)
                            mxSetCell(plhs[i], j, mxCreateString((char*)(*((dbr_string_t*)(CHNLS[Hndl-1].DataBuffer)+j))));
                    }
                }
                
                else 
                {   plhs[i] = mxCreateDoubleMatrix(1,Cnt,mxREAL);
                    myDblPr = mxGetPr(plhs[i]);
                                        
                    switch(dbf_type_to_DBR(ca_field_type(CHNLS[Hndl-1].CHID)))
                    
                    {   case DBR_INT: // As defined in db_access.h DBR_INT = DBR_SHORT = 1
                        for(j=0;j<Cnt;j++)
                            myDblPr[j]= (double)(*((dbr_short_t*)(CHNLS[Hndl-1].DataBuffer)+j));
                        break;    
                 
                        case DBR_FLOAT:
                        for(j=0;j<Cnt;j++)
                            myDblPr[j]= (double)(*((dbr_float_t*)(CHNLS[Hndl-1].DataBuffer)+j));
                        break;
                
                        case DBR_ENUM:
                        for(j=0;j<Cnt;j++)
                            myDblPr[j]= (double)(*((dbr_enum_t*)(CHNLS[Hndl-1].DataBuffer)+j));
                        break;
                
                        case DBR_CHAR:
                        for(j=0;j<Cnt;j++)
                            myDblPr[j]= (double)(*((dbr_char_t*)(CHNLS[Hndl-1].DataBuffer)+j));
                        break;
                    
                        case DBR_LONG:
                        for(j=0;j<Cnt;j++)
                            myDblPr[j]= (double)(*((dbr_long_t*)(CHNLS[Hndl-1].DataBuffer)+j));
                        break;
                    
                        case DBR_DOUBLE:
                        for(j=0;j<Cnt;j++)
                            myDblPr[j]= (double)(*((dbr_double_t*)(CHNLS[Hndl-1].DataBuffer)+j));
                        break;
                    } 

                }
                
            } break;            

            case 51: // MCAGET Get scalar PV of the same type 
                     // second argument is an array of handles
                     // returns an array of values
            
            myDblPr = mxGetPr(prhs[1]);
            M = mxGetM(prhs[1]);
            N = mxGetN(prhs[1]);
            
            NumHandles = M*N;
            plhs[0] = mxCreateDoubleMatrix(M,N,mxREAL);
            
            for(i=0;i<NumHandles;i++) // First loop: place all ca_get requests in the buffer
            {   Hndl = (int)myDblPr[i];
                if(Hndl<1 || Hndl>HandlesUsed)
                    mexErrMsgTxt("Invalid Handle");
                
                RequestType = dbf_type_to_DBR(ca_field_type(CHNLS[Hndl-1].CHID));
                status = ca_array_get(DBR_DOUBLE,1,CHNLS[Hndl-1].CHID,mxGetPr(plhs[0])+i);
                if(status!=ECA_NORMAL)
                    mexPrintf("Error in call to ca_array_get\n");
            }   
            
            status = ca_pend_io(MCA_GET_TIMEOUT);
            if(status!=ECA_NORMAL)
                mexErrMsgTxt("... ca_pend_io call timed out \n");
            
            break;            

        
       
        case 70: // MCAPUT
            NumHandles = (nrhs-1)/2;
            for(i=0;i<NumHandles;i++)
            {   j = 2+i*2;
                Hndl = (int)mxGetScalar(prhs[1+i*2]); 
                if(Hndl<1 || Hndl>HandlesUsed)
                    mexErrMsgTxt("Handle out of range - no values written");
                // Set the status to 0 - mcaput_callback will write 1, if successful
                CHNLS[Hndl-1].LastPutStatus = 0;    
                RequestType = dbf_type_to_DBR(ca_field_type(CHNLS[Hndl-1].CHID));            
                Cnt = ca_element_count(CHNLS[Hndl-1].CHID);
                
                
                // If a value to write is passed as a string - the number of elements to write 
                //   is 1 , NOT the length of the string returned by mxGetNumberOfElements
                if(mxIsChar(prhs[j])) 
                    L=1;
                else
                    L = min(mxGetNumberOfElements(prhs[j]),Cnt);
                

                // Copy double or string data from MATLAB prhs[] to DataBuffer
                // on each channel 
                
                if(RequestType==DBR_STRING)
                {   // STRING type is is passed as a cell array of strings
                    // A a 1-row MATLAB character array (1 string) may also be passed as a value
                    
                    if(mxIsChar(prhs[j]))
                    {   mxGetString(prhs[j], StrBuffer, sizeof(dbr_string_t));
                        strcpy((char*)(*((dbr_string_t*)(CHNLS[Hndl-1].DataBuffer))),StrBuffer);
                        
                    }
                    else if(mxIsCell(prhs[j]))
                    {   for(k=0;k<L;k++)
                        {   mxGetString(mxGetCell(prhs[j],k), StrBuffer, sizeof(dbr_string_t));
                            strcpy((char*)(*((dbr_string_t*)(CHNLS[Hndl-1].DataBuffer)+k)),StrBuffer);
                        }
                    }
                }
                else
                {   myDblPr = mxGetPr(prhs[j]); 
                    switch(RequestType)
                    {   
                        case DBR_INT: // As defined in db_access.h DBR_INT = DBR_SHORT = 1
                        for(k=0;k<L;k++)
                            *((dbr_short_t*)(CHNLS[Hndl-1].DataBuffer)+k) = (dbr_short_t)(myDblPr[k]);
                        break;    
                 
                        case DBR_FLOAT:
                        for(k=0;k<L;k++)
                            *((dbr_float_t*)(CHNLS[Hndl-1].DataBuffer)+k) = (dbr_float_t)(myDblPr[k]);
                        break;
                
                        case DBR_ENUM:
                        for(k=0;k<L;k++)
                            *((dbr_enum_t*)(CHNLS[Hndl-1].DataBuffer)+k) = (dbr_enum_t)(myDblPr[k]);
                        break;
                
                        case DBR_CHAR:
                        for(k=0;k<L;k++)
                            *((dbr_char_t*)(CHNLS[Hndl-1].DataBuffer)+k) = (dbr_char_t)(myDblPr[k]);
                        break;
                   
                        case DBR_LONG:
                        for(k=0;k<L;k++)
                            *((dbr_long_t*)(CHNLS[Hndl-1].DataBuffer)+k) = (dbr_long_t)(myDblPr[k]);
                        break;
                    
                        case DBR_DOUBLE:
                        for(k=0;k<L;k++)
                            *((dbr_double_t*)(CHNLS[Hndl-1].DataBuffer)+k) = (dbr_double_t)(myDblPr[k]);
                        break;
                    } 
                }            
 
                // place request in the que 
                status = ca_array_put_callback(RequestType,L,CHNLS[Hndl-1].CHID,CHNLS[Hndl-1].DataBuffer,
                                                   mcaput_callback,&(CHNLS[Hndl-1].LastPutStatus));

                 if(status!=ECA_NORMAL)
                    mexPrintf("ca_array_put_callback failed\n");
            }   
            
            status = ca_pend_event(MCA_PUT_TIMEOUT);
            
            plhs[0]=mxCreateDoubleMatrix(1,NumHandles,mxREAL);
            myDblPr = mxGetPr(plhs[0]);  
            
            for(i=0;i<NumHandles;i++)
            {   Hndl = (int)mxGetScalar(prhs[1+i*2]);
                myDblPr[i] = (double)CHNLS[Hndl-1].LastPutStatus;
                
            }
           
            break;
        
       case 80: // MCAPUT - fast unconfirmed put for scalar numeric PV's
        
            myDblPr = mxGetPr(prhs[1]);
            M = mxGetM(prhs[1]);
            N = mxGetN(prhs[1]);                
            NumHandles = M*N;
            
            plhs[0] = mxCreateDoubleMatrix(M,N,mxREAL);
            myDblPr = mxGetPr(plhs[0]);  
            
            for(i=0;i<NumHandles;i++)
            {   myDblPr = mxGetPr(plhs[0]);
                Hndl = (int)(*(mxGetPr(prhs[1])+i)); 
                if(Hndl<1 || Hndl>HandlesUsed)
                    mexErrMsgTxt("Handle out of range - no values written");

                status = ca_array_put(DBR_DOUBLE,1,CHNLS[Hndl-1].CHID,mxGetPr(prhs[2])+i);

                if(status!=ECA_NORMAL)
                    {   myDblPr[i] = 0;
                        //mexPrintf("ca_array_put_callback failed\n");
                    }
                    else
                    {   myDblPr[i] = 1;
                    }
                    
            }   
            
            status = ca_pend_io(MCA_PUT_TIMEOUT);
            
            break;    
                 
            
        case 100: // MCAMON install Monitor or replace MonitorCBString
            
            Hndl = (int)mxGetScalar(prhs[1]); 
            
            // Check if the handle is within range 
            if(Hndl<1 || Hndl>HandlesUsed)
            {   plhs[0]=mxCreateScalarDouble(0);
                mexErrMsgTxt("Invalid Handle");
            }
            
            if(CHNLS[Hndl-1].EVID) // if VID is not NULL - another monitor is already installed - replace MonitorCBString
            {   if(CHNLS[Hndl-1].MonitorCBString) // Free memory for occupied by the old MonitorCBString
                {   mxFree(CHNLS[Hndl-1].MonitorCBString);
                    CHNLS[Hndl-1].MonitorCBString = NULL;
                }
                if(nrhs>2) // Check if the new string is specified
                {   if(mxIsChar(prhs[2]))
                    {   buflen = mxGetM(prhs[2])*mxGetN(prhs[2])+1;
                        CHNLS[Hndl-1].MonitorCBString = (char *)mxMalloc(buflen);
                        mexMakeMemoryPersistent(CHNLS[Hndl-1].MonitorCBString);
                        mxGetString(prhs[2],CHNLS[Hndl-1].MonitorCBString,buflen); 
                    } 
                    else
                        mexErrMsgTxt("Third argument must be a string\n");
                }
                plhs[0]=mxCreateScalarDouble(1);
            }
            else // No monitor is presently installed;
            {   RequestType = dbf_type_to_DBR(ca_field_type(CHNLS[Hndl-1].CHID)); // Closest to the native 
            
            
                if(nrhs>2)
                {   if(mxIsChar(prhs[2]))
                    {   buflen = mxGetM(prhs[2])*mxGetN(prhs[2])+1;
                        CHNLS[Hndl-1].MonitorCBString = (char *)mxMalloc(buflen);
                        mexMakeMemoryPersistent(CHNLS[Hndl-1].MonitorCBString);
                        mxGetString(prhs[2],CHNLS[Hndl-1].MonitorCBString,buflen); 
                    } 
                    else
                        mexErrMsgTxt("Third argument must be a string\n");
                }
                else
                    CHNLS[Hndl-1].MonitorCBString = NULL;  // Set MonitorCBString to NULL so that mcaMonitorEventHandler only copies data to CACHE
            
                // Count argument set to 0 - native count
                status = ca_add_array_event(RequestType,0,CHNLS[Hndl-1].CHID, mcaMonitorEventHandler, &CHNLS[Hndl-1], 0.0, 0.0, 0.0, &(CHNLS[Hndl-1].EVID));
              
                if(status!=ECA_NORMAL)
                {   mexPrintf("ca_add_array_event failed\n");      
                    plhs[0]=mxCreateScalarDouble(0);
                }
                else
                {   ca_poll();
                    plhs[0]=mxCreateScalarDouble(1);
                }
            }   
            break;
            
        case 200: // Clear Monitor MCACLEARMON
            
            Hndl = (int)mxGetScalar(prhs[1]); 
            if(Hndl<1 || Hndl>HandlesUsed)
                mexErrMsgTxt("Invalid Handle");
            if(!CHNLS[Hndl-1].EVID) 
                mexErrMsgTxt("No monitor installed - can not clear");
                
            status = ca_clear_event(CHNLS[Hndl-1].EVID);
            if(status!=ECA_NORMAL)
                mexPrintf("ca_clear_event failed\n");
                
            // Set the EVID pointer to NULL (ca_clear_event dos not do it by itself)
            // to use as a FLAG that no monitors are installed 
            CHNLS[Hndl-1].EVID = NULL;
            // Reset
            CHNLS[Hndl-1].MonitorEventCount = 0;    
            // If there is Callback String - destroy it
            if(CHNLS[Hndl-1].MonitorCBString)
            {   mxFree(CHNLS[Hndl-1].MonitorCBString); 
                CHNLS[Hndl-1].MonitorCBString =NULL;
            }
                
          
        break;
          
        case 300: // MCACACHE Get Cached values of a monitored PV
            for(i=0;i<nrhs-1;i++)
            {   Hndl = (int)mxGetScalar(prhs[1+i]);
                // if(Hndl<1 || Hndl>HandlesUsed || !CHNLS[Hndl-1].CACHE)
                if(Hndl<1 || Hndl>HandlesUsed)
                    plhs[i] = mxCreateDoubleMatrix(0,0,mxREAL);
                else
                    {   plhs[i] = mxDuplicateArray(CHNLS[Hndl-1].CACHE);
                        CHNLS[Hndl-1].MonitorEventCount = 0;
                    }
            }       
          
        break;
        
        case 500: // MCAMON Info on installed monitors
            L = 0;
            HndlArray = (int*)mxCalloc(HandlesUsed,sizeof(int));
            
            for(i=0;i<HandlesUsed;i++) // Count installed monitors
            {   if(CHNLS[i].EVID)
                HndlArray[L++]=i+1;
            }       
            
            if(L>0)
            {   plhs[0] = mxCreateDoubleMatrix(1,L,mxREAL);
                myDblPr = mxGetPr(plhs[0]);
                plhs[1] = mxCreateCellMatrix(1,L);
                for(i=0;i<L;i++)
                {   myDblPr[i] = (double)HndlArray[i];
                    mxSetCell(plhs[1],i,mxCreateString(CHNLS[HndlArray[i]-1].MonitorCBString));
                }
            }
            else
            {   plhs[0] = mxCreateDoubleMatrix(0,0,mxREAL);
                plhs[1] = mxCreateCellMatrix(0,0);
            }
            
        break;
        
        case 510: // MCAMONEVENTS Event count fot monitors
                       
            plhs[0] = mxCreateDoubleMatrix(1,HandlesUsed,mxREAL);
            myDblPr = mxGetPr(plhs[0]);
            for(i=0;i<HandlesUsed;i++)
                myDblPr[i]=(double)(CHNLS[i].MonitorEventCount);
            
        break;
        
        case 1000: // print timeout settings
            plhs[0] = mxCreateDoubleMatrix(3,1,mxREAL);
            mexPrintf("MCA timeout settings\n:");
            mexPrintf("mcaopen\t%f [s]\n",  MCA_SEARCH_TIMEOUT );
            mexPrintf("mcaget\t%f [s]\n",  MCA_GET_TIMEOUT ); 
            mexPrintf("mcaput\t%f [s]\n",  MCA_PUT_TIMEOUT );

            myDblPr = mxGetPr(plhs[0]);
            myDblPr[0] = MCA_SEARCH_TIMEOUT;
            myDblPr[1] = MCA_GET_TIMEOUT;
            myDblPr[2] = MCA_PUT_TIMEOUT;

            
        break;
        
        
        case 1001: // set MCA_SEARCH_TIMEOUT
            // return delay value
            MCA_SEARCH_TIMEOUT = mxGetScalar(prhs[1]);
            plhs[0] = mxCreateScalarDouble(MCA_SEARCH_TIMEOUT);
        break;
        
        case 1002: // set MCA_GET_TIMEOUT
            // return delay value
            MCA_GET_TIMEOUT = mxGetScalar(prhs[1]);
            plhs[0] = mxCreateScalarDouble(MCA_GET_TIMEOUT);
        break;
        
        case 1003: // set MCA_PUT_TIMEOUT
            // return delay value
            MCA_PUT_TIMEOUT = mxGetScalar(prhs[1]);
            plhs[0] = mxCreateScalarDouble(MCA_PUT_TIMEOUT);
        break;

    } 
}
static void medmConnectEventCb(struct connection_handler_args args) {
    int status;
    Channel *pCh = (Channel *)ca_puser(args.chid);
    int count;

  /* Increment the event counter */
    caTask.caEventCount++;

  /* Check for valid values */
    if(globalDisplayListTraversalMode != DL_EXECUTE) return;
    if(!pCh || !pCh->chid || !pCh->pr) {
	medmPostMsg(0,"medmConnectEventCb: Invalid channel information\n");
	return;
    }

  /* Do a get every time a channel is connected or reconnected and has
   * read access.  The get will cause the graphical info callback to
   * be called */
    if(args.op == CA_OP_CONN_UP && ca_read_access(pCh->chid)) {
	status = ca_array_get_callback(
	  dbf_type_to_DBR_CTRL(ca_field_type(args.chid)),
	  1, args.chid, medmUpdateGraphicalInfoCb, NULL);
	if(status != ECA_NORMAL) {
	    medmPostMsg(0,"medmConnectEventCb: "
	      "ca_array_get_callback [%s]:\n %s\n",
	      ca_name(pCh->chid)?ca_name(pCh->chid):"Unknown",
	      ca_message(status));
#if DEBUG_CONNECTION
# if 0
	    system("netstat | grep iocacis");
# endif
	    print("  pCh->chid %s args.chid\n",
	      pCh->chid == args.chid?"==":"!=");
	    print(
	      "  Channel Name: %s\n"
	      "  State: %s\n"
	      "  Native Type: %s\n"
	      "  Native Count: %hu\n"
	      "  Access: %s%s\n"
	      "  IOC: %s\n",
	      args.chid?ca_name(args.chid):"Unavailable",
	      args.chid?ca_state(args.chid) == cs_never_conn?"Never":
	      ca_state(args.chid) == cs_prev_conn?"Prev":
	      ca_state(args.chid) == cs_conn?"Conn":
	      ca_state(args.chid) == cs_closed?"Closed":"Unknown":"Unavailable",
	      args.chid?dbf_type_to_text(ca_field_type(args.chid)):"Unavailable",
	      args.chid?ca_element_count(args.chid):0,
	      args.chid?(ca_read_access(args.chid)?"R":"None"):"Unavailable",
	      args.chid?(ca_write_access(args.chid)?"W":""):"",
	      args.chid?ca_host_name(args.chid):"Unavailable");
#endif
	}
    }

  /* Handle four cases: connected or not and previously connected or not */
    if(args.op == CA_OP_CONN_UP) {
      /* Connected */
	if(pCh->previouslyConnected == False) {
	  /* Connected and not previously connected */
	  /* Set these first so they will be right for the updateValueCb */
	    pCh->pr->elementCount = ca_element_count(pCh->chid);
	    pCh->pr->dataType = ca_field_type(args.chid);
	    pCh->pr->connected = True;
	    caTask.channelConnected++;
	  /* Add the access-rights-change callback and the
	     significant-change (value, alarm or severity)
	     callback. Don't call the updateValueCb because
	     ca_replace_access_rights_event will call it. */
	    status = ca_replace_access_rights_event(
	      pCh->chid,medmReplaceAccessRightsEventCb);
	    if(status != ECA_NORMAL) {
		medmPostMsg(0,"medmConnectEventCb: "
		  "ca_replace_access_rights_event [%s]: %s\n",
		  ca_name(pCh->chid)?ca_name(pCh->chid):"Unknown",
		  ca_message(status));
	    }
	    if(pCh->pr->currentCount == -2) {
                count=0;
            } else {
                count=ca_element_count(pCh->chid);
            }
#ifdef __USING_TIME_STAMP__
	    status = ca_add_array_event(
	      dbf_type_to_DBR_TIME(ca_field_type(pCh->chid)),
	      count, pCh->chid,
	      medmUpdateChannelCb, pCh, 0.0,0.0,0.0, &(pCh->evid));
#else
	    status = ca_add_array_event(
	      dbf_type_to_DBR_STS(ca_field_type(pCh->chid)),
	      count, pCh->chid,
	      medmUpdateChannelCb, pCh, 0.0,0.0,0.0, &(pCh->evid));
#endif
	    if(status != ECA_NORMAL) {
	      /* Set the pointer to NULL in case CA didn't.  We don't
                 want to use it or clear it later. */
#if DEBUG_CONNECTION
		if(!pCh->evid) {
		    print("medmConnectEventCb: ca_add_array_event: \n"
		      "  status[%d] != ECA_NORMAL and pCh->evid != NULL\n",
		      status);
		}
#endif
		pCh->evid = NULL;
		medmPostMsg(0,"medmConnectEventCb: "
		  "ca_add_array_event [%s]:\n %s\n",
		  ca_name(pCh->chid)?ca_name(pCh->chid):"Unknown",
		  ca_message(status));
#if DEBUG_CONNECTION
# if 0
		system("netstat | grep iocacis");
# endif
		print("  pCh->chid %s args.chid\n",
		  pCh->chid == args.chid?"==":"!=");
		print(
		  "  Channel Name: %s\n"
		  "  State: %s\n"
		  "  Native Type: %s\n"
		  "  Native Count: %hu\n"
		  "  Access: %s%s\n"
		  "  IOC: %s\n",
		  args.chid?ca_name(args.chid):"Unavailable",
		  args.chid?ca_state(args.chid) == cs_never_conn?"Never":
		  ca_state(args.chid) == cs_prev_conn?"Prev":
		  ca_state(args.chid) == cs_conn?"Conn":
		  ca_state(args.chid) == cs_closed?"Closed":"Unknown":"Unavailable",
		  args.chid?dbf_type_to_text(ca_field_type(args.chid)):"Unavailable",
		  args.chid?ca_element_count(args.chid):0,
		  args.chid?(ca_read_access(args.chid)?"R":"None"):"Unavailable",
		  args.chid?(ca_write_access(args.chid)?"W":""):"",
		  args.chid?ca_host_name(args.chid):"Unavailable");
#endif
	    }
	  /* Set this one last so ca_replace_access_rights_event can
             use the old value */
	    pCh->previouslyConnected = True;
	} else {
	  /* Connected and previously connected */
	    pCh->pr->connected = True;
	    caTask.channelConnected++;
	    if(pCh->pr->updateValueCb)
	      pCh->pr->updateValueCb((XtPointer)pCh->pr);
	}
    } else {
      /* Not connected */
	if(pCh->previouslyConnected == False) {
	  /* Not connected and not previously connected */
	  /* Probably doesn't happen -- if CA can't connect, this
	   *   routine never gets called */
	    pCh->pr->connected = False;
	} else {
	  /* Not connected but previously connected */
	    pCh->pr->connected = False;
	    caTask.channelConnected--;
	    if(pCh->pr->updateValueCb)
	      pCh->pr->updateValueCb((XtPointer)pCh->pr);
	}
    }
}
static void pvInfoWriteInfo(void)
{
    time_t now;
    struct tm *tblock;
    char timeStampStr[TIME_STRING_MAX];
    Record *pR;
    chid chId;
    char *descVal;
#if defined(DBR_CLASS_NAME) && DO_RTYP
    char *rtypVal;
#endif
    DlElement *pE;
    char *elementType;
    static char noType[]="Unknown";
    struct dbr_time_string timeVal;
    char string[1024];     /* Danger: Fixed length */
    XmTextPosition curpos = 0;
    int i, j;

  /* Free the timeout */
    if(pvInfoTimerOn) {
	XtRemoveTimeOut(pvInfoTimeoutId);
	pvInfoTimerOn = False;
    }

  /* Just clean up and abort if we get called with no pvInfo
   *   (Shouldn't happen) */
    if(!pvInfo) return;

  /* Get timestamp */
    time(&now);
    tblock = localtime(&now);
    strftime(timeStampStr, TIME_STRING_MAX, STRFTIME_FORMAT"\n", tblock);
    timeStampStr[TIME_STRING_MAX-1]='\0';

  /* Get element type */
    pE = pvInfoElement;
    if(pE &&
      pE->type >= MIN_DL_ELEMENT_TYPE && pE->type <= MAX_DL_ELEMENT_TYPE) {
	elementType=elementType(pE->type);
    } else {
	elementType=noType;
    }

  /* Heading */
    sprintf(string, "           PV Information\n\nObject: %s\n%s\n",
      elementType, timeStampStr);
    XmTextSetInsertionPosition(pvInfoMessageBox, 0);
    XmTextSetString(pvInfoMessageBox, string);
    curpos+=strlen(string);

  /* Loop over the records to print information */
    for(i=0; i < nPvInfoPvs; i++) {
      /* Check for a valid record */
	if(!pvInfo[i].pvOk) continue;

	chId = pvInfo[i].pvChid;
	timeVal = pvInfo[i].timeVal;
	pR = pvInfo[i].record;

      /* Name */
	sprintf(string, "%s\n"
	  "======================================\n",
	  ca_name(chId));
      /* DESC */
	descVal = pvInfo[i].descVal;
	if(pvInfo[i].descOk && descVal) {
	    sprintf(string, "%sDESC: %s\n", string, descVal);
	} else {
	    sprintf(string, "%sDESC: %s\n", string, NOT_AVAILABLE);
	}
	if(pvInfo[i].descChid) ca_clear_channel(pvInfo[i].descChid);
#if defined(DBR_CLASS_NAME) && DO_RTYP
      /* RTYP */
	rtypVal = pvInfo[i].rtypVal;
	if(pvInfo[i].rtypOk && rtypVal && *rtypVal) {
	    sprintf(string, "%sRTYP: %s\n", string, rtypVal);
	} else {
	    sprintf(string, "%sRTYP: %s\n", string, NOT_AVAILABLE);
	}
#endif
      /* Items from chid */
	sprintf(string, "%sTYPE: %s\n", string,
	  dbf_type_to_text(ca_field_type(chId)));
      /* ca_element_count is defined differently in 3.14 vs. 3.13 */
	sprintf(string, "%sCOUNT: %lu\n", string,
	  (unsigned long)ca_element_count(chId));
	sprintf(string, "%sACCESS: %s%s\n", string,
	  ca_read_access(chId)?"R":"", ca_write_access(chId)?"W":"");
	sprintf(string, "%sIOC: %s\n", string, ca_host_name(chId));
	if(timeVal.value) {
	    char fracPart[10];

	  /* Do the value */
	    if(ca_element_count(chId) == 1) {
		sprintf(string, "%sVALUE: %s\n", string,
		  timeVal.value);
	    } else {
		sprintf(string, "%sFIRST VALUE: %s\n", string,
		  timeVal.value);
	    }
	  /* Convert the seconds part of the timestamp to UNIX time */
	    now = timeVal.stamp.secPastEpoch + 631152000ul;
	    tblock = localtime(&now);
	    strftime(timeStampStr, TIME_STRING_MAX, "%a %b %d, %Y %H:%M:%S",
	      tblock);
	    timeStampStr[TIME_STRING_MAX-1]='\0';
	  /* Get the fractional part.  This assumes strftime truncates
             seconds rather than rounding them, which seems to be the
             case. */
	    sprintf(fracPart, "%09d",timeVal.stamp.nsec);
	  /* Truncate to 3 figures */
	    fracPart[3]='\0';
	    sprintf(timeStampStr,"%s.%s", timeStampStr, fracPart);
	    timeStampStr[TIME_STRING_MAX-1]='\0';
	    sprintf(string, "%sSTAMP: %s\n", string, timeStampStr);
#if DEBUG_TIMESTAMP
	  /* This prints 9 significant figures and requires 3.13 base */
	    {
		char tsTxt[32];
		sprintf(string,"%sSTAMP: %s\n",string,
		  tsStampToText(&timeVal.stamp,TS_TEXT_MONDDYYYY,tsTxt));
	    }
#endif
	} else {
	    sprintf(string, "%sVALUE: %s\n", string, NOT_AVAILABLE);
	    sprintf(string, "%sSTAMP: %s\n", string, NOT_AVAILABLE);
	}
	XmTextInsert(pvInfoMessageBox, curpos, string);
	curpos+=strlen(string);

      /* Alarms */
	switch(pR->severity) {
	case NO_ALARM:
	    sprintf(string, "ALARM: NO\n");
	    break;
	case MINOR_ALARM:
	    sprintf(string, "ALARM: MINOR\n");
	    break;
	case MAJOR_ALARM:
	    sprintf(string, "ALARM: MAJOR\n");
	    break;
	case INVALID_ALARM:
	    sprintf(string, "ALARM: INVALID\n");
	    break;
	default:
	    sprintf(string, "ALARM: Unknown\n");
	    break;
	}
	XmTextInsert(pvInfoMessageBox, curpos, string);
	curpos+=strlen(string);

      /* Items from record */
	sprintf(string, "\n");
	switch(ca_field_type(chId)) {
        case DBF_STRING:
	    break;
        case DBF_ENUM:
	    sprintf(string, "%sSTATES: %d\n",
	      string, (int)(pR->hopr+1.1));
	  /* KE: Bad way to use a double */
	    if(pR->hopr) {
		for (j=0; j <= pR->hopr; j++) {
		    sprintf(string, "%sSTATE %2d: %s\n",
		      string, j, pR->stateStrings[j]);
		}
	    }
	    break;
        case DBF_CHAR:
        case DBF_INT:
        case DBF_LONG:
	    sprintf(string, "%sHOPR: %g  LOPR: %g\n",
	      string, pR->hopr, pR->lopr);
	    break;
        case DBF_FLOAT:
        case DBF_DOUBLE:
	    if(pR->precision >= 0 && pR->precision <= 17) {
		sprintf(string, "%sPRECISION: %d\n",
		  string, pR->precision);
	    } else {
		sprintf(string, "%sPRECISION: %d [Bad Value]\n",
		  string, pR->precision);
	    }
	    sprintf(string, "%sHOPR: %g  LOPR: %g\n",
	      string, pR->hopr, pR->lopr);
	    break;
        default:
	    break;
	}
	sprintf(string, "%s\n", string);
	XmTextInsert(pvInfoMessageBox, curpos, string);
	curpos+=strlen(string);
    }

  /* Pop it up unless we have left EXECUTE mode */
    if(globalDisplayListTraversalMode == DL_EXECUTE) {
	XtSetSensitive(pvInfoS, True);
	XtPopup(pvInfoS, XtGrabNone);
    }

  /* Free space */
    if(pvInfo) free((char *)pvInfo);
    pvInfo = NULL;
    pvInfoElement = NULL;
}