static void printChidInfo(chid chid, char *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), message:%s\n", ca_read_access(chid),ca_write_access(chid),ca_state(chid), message); };
PyObject *pv_getter_pvval(pvobject * self, void *closure) { if (self->chanId == NULL || ca_state(self->chanId) != cs_conn || self->buff == NULL) { PYCA_ERR("pvval: indef"); } int i; dbr_plaintype data; int dim = ca_element_count(self->chanId); chtype type = xxx_ca_field_type(self->chanId); PyObject *returnvalue = NULL; /* build the returned tuple */ if (dim > 1) returnvalue = PyTuple_New(dim); switch (type) { case DBR_STRING: if (dim > 1) for (i = 0; i < dim; i++) { ca_module_utilsextract(self->buff, xxx_ca_field_type (self->chanId), i, &data); PyTuple_SetItem(returnvalue, i, PyString_FromString(data.s)); } else returnvalue = PyString_FromString(((struct dbr_time_string *) self->buff)->value); break; case DBR_DOUBLE: if (dim > 1) for (i = 0; i < dim; i++) { ca_module_utilsextract(self->buff, xxx_ca_field_type (self->chanId), i, &data); PyTuple_SetItem(returnvalue, i, PyFloat_FromDouble(data.d)); } else returnvalue = PyFloat_FromDouble(((struct dbr_time_double *) self->buff)->value); break; case DBR_LONG: if (dim > 1) for (i = 0; i < dim; i++) { ca_module_utilsextract(self->buff, xxx_ca_field_type (self->chanId), i, &data); PyTuple_SetItem(returnvalue, i, PyLong_FromLong(data.l)); } else returnvalue = PyLong_FromLong(((struct dbr_time_long *) self->buff)->value); break; case DBR_SHORT: if (dim > 1) for (i = 0; i < dim; i++) { ca_module_utilsextract(self->buff, xxx_ca_field_type (self->chanId), i, &data); PyTuple_SetItem(returnvalue, i, PyInt_FromLong(data.i)); } else returnvalue = PyInt_FromLong(((struct dbr_time_short *) self->buff)->value); break; case DBR_CHAR: if (dim > 1) for (i = 0; i < dim; i++) { ca_module_utilsextract(self->buff, xxx_ca_field_type (self->chanId), i, &data); PyTuple_SetItem(returnvalue, i, PyInt_FromLong(data.c)); } else returnvalue = PyInt_FromLong(((struct dbr_time_char *) self->buff)->value); break; case DBR_ENUM: if (dim > 1) for (i = 0; i < dim; i++) { ca_module_utilsextract(self->buff, xxx_ca_field_type (self->chanId), i, &data); PyTuple_SetItem(returnvalue, i, PyInt_FromLong(data.e)); } else returnvalue = PyInt_FromLong(((struct dbr_time_enum *) self->buff)->value); break; case DBR_FLOAT: if (dim > 1) for (i = 0; i < dim; i++) { ca_module_utilsextract(self->buff, xxx_ca_field_type (self->chanId), i, &data); PyTuple_SetItem(returnvalue, i, PyFloat_FromDouble(data.f)); } else returnvalue = PyFloat_FromDouble(((struct dbr_time_float *) self->buff)->value); break; default: PYCA_ERR("pvval: invalid"); break; } return returnvalue; }
int caget (pv *pvs, int nPvs, OutputT format, chtype dbrType, unsigned long reqElems) { unsigned int i; int n, result; for (n = 0; n < nPvs; n++) { /* Set up pvs structure */ /* -------------------- */ /* Get natural type and array count */ pvs[n].nElems = ca_element_count(pvs[n].chid); pvs[n].dbfType = ca_field_type(pvs[n].chid); pvs[n].dbrType = dbrType; /* Set up value structures */ pvs[n].dbrType = dbf_type_to_DBR_TIME(pvs[n].dbfType); /* Use native type */ if (dbr_type_is_ENUM(pvs[n].dbrType)) /* Enums honour -n option */ { if (enumAsNr) pvs[n].dbrType = DBR_TIME_INT; else pvs[n].dbrType = DBR_TIME_STRING; } if (reqElems == 0 || pvs[n].nElems < reqElems) /* Adjust array count */ pvs[n].reqElems = pvs[n].nElems; else pvs[n].reqElems = reqElems; /* Issue CA request */ /* ---------------- */ if (ca_state(pvs[n].chid) == cs_conn) { nConn++; pvs[n].onceConnected = 1; /* Allocate value structure */ pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].reqElems)); if(!pvs[n].value){ fprintf(stderr,"Allocation failed\n"); exit(1); } result = ca_array_get(pvs[n].dbrType, pvs[n].reqElems, pvs[n].chid, pvs[n].value); pvs[n].status = result; } else { pvs[n].status = ECA_DISCONN; } } if (!nConn) return 1; /* No connection? We're done. */ /* Wait for completion */ /* ------------------- */ result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) fprintf(stderr, "Read operation timed out: PV data was not read.\n"); /* Print the data */ /* -------------- */ for (n = 0; n < nPvs; n++) { switch (format) { case plain: /* Emulate old caput behaviour */ if (pvs[n].reqElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name); else printf("%s", pvs[n].name); printf("%c", fieldSeparator); case terse: if (pvs[n].status == ECA_DISCONN) printf("*** not connected\n"); else if (pvs[n].status == ECA_NORDACCESS) printf("*** no read access\n"); else if (pvs[n].status != ECA_NORMAL) printf("*** CA error %s\n", ca_message(pvs[n].status)); else if (pvs[n].value == 0) printf("*** no data available (timeout)\n"); else { if (charArrAsStr && dbr_type_is_CHAR(pvs[n].dbrType) && (reqElems || pvs[n].reqElems > 1)) { dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType); int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s)); char *d = calloc(dlen+1, sizeof(char)); if(!d){ fprintf(stderr,"Allocation failed\n"); exit(1); } epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, strlen((char*)s)); printf("%s", d); free(d); } else { if (reqElems || pvs[n].nElems > 1) printf("%lu%c", pvs[n].reqElems, fieldSeparator); for (i=0; i<pvs[n].reqElems; ++i) { if (i) printf ("%c", fieldSeparator); printf("%s", val2str(pvs[n].value, pvs[n].dbrType, i)); } } printf("\n"); } break; case all: print_time_val_sts(&pvs[n], reqElems); break; default : break; } } return 0; }
static void dbCaTask(void *arg) { taskwdInsert(0, NULL, NULL); SEVCHK(ca_context_create(ca_enable_preemptive_callback), "dbCaTask calling ca_context_create"); dbCaClientContext = ca_current_context (); SEVCHK(ca_add_exception_event(exceptionCallback,NULL), "ca_add_exception_event"); epicsEventSignal(startStopEvent); /* channel access event loop */ while (TRUE){ do { epicsEventMustWait(workListEvent); } while (dbCaCtl == ctlPause); while (TRUE) { /* process all requests in workList*/ caLink *pca; short link_action; int status; epicsMutexMustLock(workListLock); if (!(pca = (caLink *)ellGet(&workList))){ /* Take off list head */ epicsMutexUnlock(workListLock); if (dbCaCtl == ctlExit) goto shutdown; break; /* workList is empty */ } link_action = pca->link_action; pca->link_action = 0; if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding; epicsMutexUnlock(workListLock); /* Give back immediately */ if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */ dbCaLinkFree(pca); /* No alarm is raised. Since link is changing so what? */ continue; /* No other link_action makes sense */ } if (link_action & CA_CONNECT) { status = ca_create_channel( pca->pvname,connectionCallback,(void *)pca, CA_PRIORITY_DB_LINKS, &(pca->chid)); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_create_channel %s\n", ca_message(status)); printLinks(pca); continue; } dbca_chan_count++; status = ca_replace_access_rights_event(pca->chid, accessRightsCallback); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask replace_access_rights_event %s\n", ca_message(status)); printLinks(pca); } continue; /*Other options must wait until connect*/ } if (ca_state(pca->chid) != cs_conn) continue; if (link_action & CA_WRITE_NATIVE) { assert(pca->pputNative); if (pca->putType == CA_PUT) { status = ca_array_put( pca->dbrType, pca->nelements, pca->chid, pca->pputNative); } else if (pca->putType==CA_PUT_CALLBACK) { status = ca_array_put_callback( pca->dbrType, pca->nelements, pca->chid, pca->pputNative, putCallback, pca); } else { status = ECA_PUTFAIL; } if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_array_put %s\n", ca_message(status)); printLinks(pca); } epicsMutexMustLock(pca->lock); if (status == ECA_NORMAL) pca->newOutNative = FALSE; epicsMutexUnlock(pca->lock); } if (link_action & CA_WRITE_STRING) { assert(pca->pputString); if (pca->putType == CA_PUT) { status = ca_array_put( DBR_STRING, 1, pca->chid, pca->pputString); } else if (pca->putType==CA_PUT_CALLBACK) { status = ca_array_put_callback( DBR_STRING, 1, pca->chid, pca->pputString, putCallback, pca); } else { status = ECA_PUTFAIL; } if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_array_put %s\n", ca_message(status)); printLinks(pca); } epicsMutexMustLock(pca->lock); if (status == ECA_NORMAL) pca->newOutString = FALSE; epicsMutexUnlock(pca->lock); } /*CA_GET_ATTRIBUTES before CA_MONITOR so that attributes available * before the first monitor callback */ if (link_action & CA_GET_ATTRIBUTES) { status = ca_get_callback(DBR_CTRL_DOUBLE, pca->chid, getAttribEventCallback, pca); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_get_callback %s\n", ca_message(status)); printLinks(pca); } } if (link_action & CA_MONITOR_NATIVE) { size_t element_size; element_size = dbr_value_size[ca_field_type(pca->chid)]; epicsMutexMustLock(pca->lock); pca->pgetNative = dbCalloc(pca->nelements, element_size); epicsMutexUnlock(pca->lock); status = ca_add_array_event( ca_field_type(pca->chid)+DBR_TIME_STRING, ca_element_count(pca->chid), pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_add_array_event %s\n", ca_message(status)); printLinks(pca); } } if (link_action & CA_MONITOR_STRING) { epicsMutexMustLock(pca->lock); pca->pgetString = dbCalloc(1, MAX_STRING_SIZE); epicsMutexUnlock(pca->lock); status = ca_add_array_event(DBR_TIME_STRING, 1, pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_add_array_event %s\n", ca_message(status)); printLinks(pca); } } } SEVCHK(ca_flush_io(), "dbCaTask"); } shutdown: taskwdRemove(0); if (dbca_chan_count == 0) ca_context_destroy(); else fprintf(stderr, "dbCa: chan_count = %d at shutdown\n", dbca_chan_count); epicsEventSignal(startStopEvent); }
static void connectionCallback(struct connection_handler_args arg) { caLink *pca; short link_action = 0; struct link *plink; pca = ca_puser(arg.chid); assert(pca); epicsMutexMustLock(pca->lock); plink = pca->plink; if (!plink) goto done; pca->isConnected = (ca_state(arg.chid) == cs_conn); if (!pca->isConnected) { struct pv_link *ppv_link = &plink->value.pv_link; dbCommon *precord = ppv_link->precord; pca->nDisconnect++; if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) scanOnce(precord); goto done; } pca->hasReadAccess = ca_read_access(arg.chid); pca->hasWriteAccess = ca_write_access(arg.chid); if (pca->gotFirstConnection) { if (pca->nelements != ca_element_count(arg.chid) || pca->dbrType != ca_field_type(arg.chid)) { /* BUG: We have no way to clear any old subscription with the * originally chosen data type/size. That will continue * to send us data and will result in an assert() fail. */ /* Let next dbCaGetLink and/or dbCaPutLink determine options */ plink->value.pv_link.pvlMask &= ~(pvlOptInpNative | pvlOptInpString | pvlOptOutNative | pvlOptOutString); pca->gotInNative = 0; pca->gotOutNative = 0; pca->gotInString = 0; pca->gotOutString = 0; free(pca->pgetNative); pca->pgetNative = 0; free(pca->pgetString); pca->pgetString = 0; free(pca->pputNative); pca->pputNative = 0; free(pca->pputString); pca->pputString = 0; } } pca->gotFirstConnection = TRUE; pca->nelements = ca_element_count(arg.chid); pca->dbrType = ca_field_type(arg.chid); if ((plink->value.pv_link.pvlMask & pvlOptInpNative) && !pca->pgetNative) { link_action |= CA_MONITOR_NATIVE; } if ((plink->value.pv_link.pvlMask & pvlOptInpString) && !pca->pgetString) { link_action |= CA_MONITOR_STRING; } if ((plink->value.pv_link.pvlMask & pvlOptOutNative) && pca->gotOutNative) { link_action |= CA_WRITE_NATIVE; } if ((plink->value.pv_link.pvlMask & pvlOptOutString) && pca->gotOutString) { link_action |= CA_WRITE_STRING; } pca->gotAttributes = 0; if (pca->dbrType != DBR_STRING) { link_action |= CA_GET_ATTRIBUTES; } done: if (link_action) addAction(pca, link_action); epicsMutexUnlock(pca->lock); }
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); } } }
/* Find an unconnected PV and attempt to connect to it. That should * restart the searches for all other unresolved PVs. */ void retryConnections(void) { int i,j; const char *pvname=NULL; chid retryChid; int status; #if DEBUG_RETRY print("retryConnections:\n"); print(" freeListSize=%d freeListCount=%d\n", caTask.freeListSize,caTask.freeListCount); print(" pageSize=%d pageCount=%d\n", caTask.pageSize,caTask.pageCount); print(" nextpage=%d nextFree=%d\n", caTask.nextPage,caTask.nextFree); print(" channelCount=%d channelConnected=%d\n", caTask.channelCount,caTask.channelConnected); if(caTask.nextPage != caTask.pageCount-1) { print(" caTask.nextPage != caTask.pageCount-1\n"); } #else /* Check if all channels are connected */ if(caTask.channelCount == caTask.channelConnected) { medmPostMsg(1,"retryConnections: All channels are connected\n"); XBell(display, 50); return; } #endif /* Find an unconnected PV */ for(i=0; i < caTask.pageCount; i++) { int jmax=(i == caTask.nextPage)?caTask.nextFree:CA_PAGE_SIZE; for(j=0; j < jmax; j++) { Channel *pCh=&caTask.pages[i][j]; if(pCh->chid && ca_state(pCh->chid) != cs_conn) { pvname=ca_name(pCh->chid); break; } } if(pvname) break; } #if DEBUG_RETRY print(" Found %s\n",pvname?pvname:"Not found"); if(!pvname) return; #else if(!pvname) { medmPostMsg(1,"retryConnections: Failed to find unconnected PV\n"); return; } #endif /* Search */ status=ca_search_and_connect(pvname,&retryChid,NULL,NULL); if(status != ECA_NORMAL) { medmPostMsg(1,"retryConnections: ca_search failed for %s: %s\n", pvname, ca_message(status)); } /* Wait. The searches will only continue for this time. Keep the * time short as the interface is frozen, and most of the searches * occur at the start of the sequence. Testing indicated: * * RETRY_TIMEOUT Searches * 30 15 * 5 10 * 3 9 * 2 9 * 1 8 * * but this may vary owing to tuning and may change with new releases. */ ca_pend_io(RETRY_TIMEOUT); /* Clear the channel */ status = ca_clear_channel(retryChid); if(status != ECA_NORMAL) { medmPostMsg(1,"retryConnections: ca_clear_channel failed for %s: %s\n", pvname, ca_message(status)); } }
void popupPvInfo(DisplayInfo *displayInfo) { DlElement *pE; Record **records; chid chId; int i, status; Record *pR; Channel *pCh; char descName[MAX_TOKEN_LENGTH]; char *pDot; double connTimeout; #if DEBUG_PVINFO XUngrabPointer(display,CurrentTime); #endif /* Check if another call is in progress */ if(pvInfo) { medmPostMsg(1,"popupPvInfo: " "Another PV Info request is already in progress\n" " It is probably having problems\n" " Wait for it to finish\n"); return; } /* Create the dialog box if it has not been created */ if(!pvInfoS) createPvInfoDlg(); /* Get the records */ records = getPvInfoFromDisplay(displayInfo, &nPvInfoPvs, &pE); if(!records) return; pvInfoElement = pE; /* Allocate space */ pvInfo = (PvInfo *)calloc(nPvInfoPvs, sizeof(PvInfo)); if(!pvInfo) { medmPostMsg(1,"popupPvInfo: Memory allocation error\n"); if(records) free(records); if(pvInfoS && XtIsManaged(pvInfoS)) return; } /* Loop over the records, initialize, and initiate search for DESC */ for(i=0; i < nPvInfoPvs; i++) { /* Initialize */ pvInfo[i].pvChid = NULL; pvInfo[i].pvOk = False; pvInfo[i].timeOk = False; pvInfo[i].descChid = NULL; pvInfo[i].descOk = False; strcpy(pvInfo[i].descVal, NOT_AVAILABLE); #if defined(DBR_CLASS_NAME) && DO_RTYP pvInfo[i].rtypOk = False; strcpy(pvInfo[i].rtypVal, NOT_AVAILABLE); #endif /* Check for a valid record */ if(records[i]) { pR = pvInfo[i].record = records[i]; pCh = getChannelFromRecord(pR); if(!pCh) continue; if(!pCh->chid) continue; chId = pvInfo[i].pvChid = pCh->chid; } else continue; pvInfo[i].pvOk = True; /* Don't try the others unless the PV is connected */ if(ca_state(chId) != cs_conn || !ca_read_access(chId)) continue; /* Construct the DESC name */ strcpy(descName,ca_name(chId)); pDot = strchr(descName,'.'); if(pDot) { /* Assume it is a name with a field and replace the field * with DESC */ strcpy(pDot,".DESC"); } else { /* Append .DESC */ strcat(descName,".DESC"); } /* Search for the DESC */ status = ca_search(descName, &pvInfo[i].descChid); if(status == ECA_NORMAL) { pvInfo[i].descOk = True; } else { medmPostMsg(1,"popupPvInfo: DESC: ca_search for %s: %s\n", descName, ca_message(status)); } } /* Free the records, they are now stored in pvInfo */ if(records) free(records); /* Wait for the searches (Timeouts should be uncommon) */ status=ca_pend_io(CA_PEND_IO_TIME); if(status != ECA_NORMAL) { medmPostMsg(1,"popupPvInfo: Waited %g seconds. " "Did not find the DESC information (%s).\n", CA_PEND_IO_TIME, descName); } /* Loop over the records and do the gets */ nPvInfoCbs = 0; for(i=0; i < nPvInfoPvs; i++) { if(!pvInfo[i].pvOk) continue; /* Don't try the others unless the PV is connected */ chId = pvInfo[i].pvChid; if(ca_state(chId) != cs_conn || !ca_read_access(chId)) continue; /* Get the DESC */ if(ca_state(pvInfo[i].descChid) == cs_conn && ca_read_access(pvInfo[i].descChid)) { /* Do the get */ status = ca_get_callback(DBR_STRING, pvInfo[i].descChid, pvInfoDescGetCb, &pvInfo[i]); if(status == ECA_NORMAL) { nPvInfoCbs++; } else { pvInfo[i].descOk = False; medmPostMsg(1,"pvInfoConnectCb: DESC: ca_array_get_callback" " for %s: %s\n", ca_name(pvInfo[i].descChid), ca_message(status)); } } else { pvInfo[i].descOk = False; } /* Get the time value as a string */ status = ca_get_callback(DBR_TIME_STRING, chId, pvInfoTimeGetCb, &pvInfo[i]); if(status == ECA_NORMAL) { nPvInfoCbs++; } else { medmPostMsg(1,"popupPvInfo: STAMP: ca_get_callback for %s: %s\n", ca_name(chId), ca_message(status)); } #if defined(DBR_CLASS_NAME) && DO_RTYP /* Get the RTYP */ status = ca_get_callback(DBR_CLASS_NAME, chId, pvInfoRtypGetCb, &pvInfo[i]); if(status == ECA_NORMAL) { nPvInfoCbs++; } else { medmPostMsg(1,"popupPvInfo: RTYP: ca_get_callback for %s: %s\n", ca_name(chId), ca_message(status)); } #endif } /* Add a timeout and poll if there are callbacks * The timeout is a safety net and should never be called * All callbacks should come back inside the EPICS_CA_CONN_TMO * Wait for 2 times this */ if(nPvInfoCbs) { ca_poll(); /* May not be really necessary here */ status = envGetDoubleConfigParam(&EPICS_CA_CONN_TMO, &connTimeout); if (status == 0) pvInfoTime = (unsigned long)(2000.*connTimeout+.5); else pvInfoTime = PVINFO_TIMEOUT; pvInfoTimeoutId = XtAppAddTimeOut(appContext, pvInfoTime, pvInfoTimeout, NULL); pvInfoTimerOn = True; } else { pvInfoWriteInfo(); } #if DEBUG_PVINFO print("popupPvInfo: nPvInfoCbs=%d timeout=%ld\n", nPvInfoCbs, nPvInfoCbs?pvInfoTime:0L); #endif }