// Channel Access callback // Each subscription monitor or 'get' callback goes here: void ProcessVariable::value_callback(struct event_handler_args arg) { ProcessVariable *me = (ProcessVariable *) ca_puser(arg.chid); LOG_ASSERT(me != 0); // Check if there is a useful value at all. if (arg.status != ECA_NORMAL) { LOG_MSG("ProcessVariable(%s) value_callback failed: %s\n", me->getName().c_str(), ca_message(arg.status)); return; } const RawValue::Data *value = (const RawValue::Data *)arg.dbr; LOG_ASSERT(value != 0); // Catch records that never processed and give null times. if (value->stamp.secPastEpoch == 0) { nulltime_throttle.LOG_MSG("ProcessVariable::value_callback(%s) " "with invalid null timestamp\n", me->getName().c_str()); return; } // Check the nanoseconds of each incoming value. Problems will arise later // unless they are normalized to less than one second. if (value->stamp.nsec >= 1000000000L) { epicsTimeStamp s = value->stamp; s.nsec = 0; epicsTime t = s; stdString txt; epicsTime2string(t, txt); nanosecond_throttle.LOG_MSG("ProcessVariable::value_callback(%s) " "with invalid secs/nsecs %zu, %zu: %s\n", me->getName().c_str(), (size_t) value->stamp.secPastEpoch, (size_t) value->stamp.nsec, txt.c_str()); return; } try { { // Do we expect a value because of 'get' or subscription? Guard guard(__FILE__, __LINE__, *me); if (me->outstanding_gets > 0) --me->outstanding_gets; else if (me->isSubscribed(guard) == false) { LOG_MSG("ProcessVariable::value_callback(%s): not expected\n", me->getName().c_str()); return; } # ifdef CHECK_EVID if (me->ev_id) { void *user = peek_evid_userptr(me->ev_id); LOG_ASSERT(user == me); } # endif if (me->state != CONNECTED) { // After a disconnect, this can happen in the GETTING_INFO state: // The CAC lib already sends us new monitors after the re-connect, // while we wait for the ctrl-info get_callback to finish. if (me->state == GETTING_INFO) // ignore return; LOG_MSG("ProcessVariable(%s) received value_callback while %s\n", me->getName().c_str(), me->getStateStr(guard)); return; } } // Finally, notify listeners of new value. me->firePvValue(value); } catch (GenericException &e) { LOG_MSG("ProcessVariable(%s) value_callback exception:\n%s\n", me->getName().c_str(), e.what()); } }
void ProcessVariable::subscribe(Guard &guard) { guard.check(__FILE__, __LINE__, mutex); if (dbr_type == 0) throw GenericException(__FILE__, __LINE__, "Cannot subscribe to %s, never connected", getName().c_str()); // Prevent multiple subscriptions if (isSubscribed(guard)) return; // While we were unlocked, a disconnect or stop() could have happend, // in which case we need to bail out. if (id == 0 || state != CONNECTED) { LOG_MSG("'%s': Skipped subscription, state %s, id 0x%lu.\n", getName().c_str(), getStateStr(guard), (unsigned long) id); return; } chid _id = id; evid _ev_id = 0; DbrType _type = dbr_type; DbrCount _count = dbr_count; { // Release around CA call?? // -- GuardRelease release(__FILE__, __LINE__, guard); // Right now, could a stop() and ca_clear_channel(id) happen, // so that ca_create_subscription() uses an invalid id? // // Task A, CAC client: // control_callback, pvConnected, subscribe // // Task B, Engine or HTTPD: // stop, clear_channel // // LockTest.cpp indicates that the clear_channel() will wait // for the CAC library to leabe the control_callback. // So even though we unlock the ProcessVariable and somebody // could invoke stop() and set id=0, we have the copied _id, // and the ca_clear_channel(id) won't happen until we leave // the control_callback. // This of course only handles the use case of the engine // where subscribe is invoked from control_callback & pvConnected. // to be on the safe side, we keep the guard and prevent a stop(), // until we find a deadlock that forces us to reconsider.... { int status; try { status = ca_create_subscription(_type, _count, _id, DBE_LOG | DBE_ALARM, value_callback, this, &_ev_id); } catch (std::exception &e) { LOG_MSG("ProcessVariable::subscribe(%s): %s\n", getName().c_str(), e.what()); } catch (...) { LOG_MSG("ProcessVariable::subscribe(%s): Unknown Exception\n", getName().c_str()); } if (status != ECA_NORMAL) { LOG_MSG("%s: ca_create_subscription failed: %s\n", getName().c_str(), ca_message(status)); return; } Guard ctx_guard(__FILE__, __LINE__, ctx); ctx.requestFlush(ctx_guard); } } ev_id = _ev_id; LOG_ASSERT(ev_id != 0); #ifdef CHECK_EVID void *user = peek_evid_userptr(ev_id); LOG_ASSERT(user == this); #endif }
// Channel Access callback void ProcessVariable::connection_handler(struct connection_handler_args arg) { ProcessVariable *me = (ProcessVariable *) ca_puser(arg.chid); LOG_ASSERT(me != 0); try { if (arg.op == CA_OP_CONN_DOWN) { // Connection is down { Guard guard(__FILE__, __LINE__, *me); me->state = DISCONNECTED; } me->firePvDisconnected(); return; } { // else: Connection is 'up' Guard guard(__FILE__, __LINE__, *me); if (me->id == 0) { LOG_MSG("ProcessVariable(%s) received " "unexpected connection_handler\n", me->getName().c_str()); return; } # ifdef DEBUG_PV printf("ProcessVariable(%s) getting control info\n", me->getName().c_str()); # endif me->state = GETTING_INFO; // Get control information for this channel. // Bug in (at least older) CA: Works only for 1 element, // even for array channels. // We are in CAC callback, and managed to lock ourself, // so it should be OK to issue another CAC call. try { int status = ca_array_get_callback( ca_field_type(me->id)+DBR_CTRL_STRING, 1 /* ca_element_count(me->ch_id) */, me->id, control_callback, me); if (status != ECA_NORMAL) { LOG_MSG("ProcessVariable('%s') connection_handler error %s\n", me->getName().c_str(), ca_message (status)); return; } } catch (std::exception &e) { LOG_MSG("ProcessVariable::connection_handler(%s): %s\n", me->getName().c_str(), e.what()); } catch (...) { LOG_MSG("ProcessVariable::connection_handler(%s): " "Unknown Exception\n", me->getName().c_str()); } } Guard ctx_guard(__FILE__, __LINE__, me->ctx); me->ctx.requestFlush(ctx_guard); } catch (GenericException &e) { LOG_MSG("ProcessVariable(%s) connection_handler exception:\n%s\n", me->getName().c_str(), e.what()); } }
int main (int argc, char *argv[]) { int i; int result; /* CA result */ OutputT format = plain; /* User specified format */ RequestT request = get; /* User specified request type */ int isArray = 0; /* Flag for array operation */ int enumAsString = 0; /* Force ENUM values to be strings */ int count = 1; int opt; /* getopt() current option */ chtype dbrType = DBR_STRING; char *pend; EpicsStr *sbuf; double *dbuf; char *cbuf = 0; char *ebuf = 0; void *pbuf; int len = 0; int waitStatus; struct dbr_gr_enum bufGrEnum; int nPvs; /* Number of PVs */ pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */ while ((opt = getopt(argc, argv, ":cnlhatsS#:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; case 'n': /* Force interpret ENUM as index number */ enumAsNr = 1; enumAsString = 0; break; case 's': /* Force interpret ENUM as menu string */ enumAsString = 1; enumAsNr = 0; break; case 'S': /* Treat char array as (long) string */ charArrAsStr = 1; isArray = 0; break; case 't': /* Select terse output format */ format = terse; break; case 'l': /* Select long output format */ format = all; break; case 'a': /* Select array mode */ isArray = 1; charArrAsStr = 0; break; case 'c': /* Select put_callback mode */ request = callback; break; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('caput -h' for help.)\n", optarg); caTimeout = DEFAULT_TIMEOUT; } break; case '#': /* Array count */ if (sscanf(optarg,"%d", &count) != 1) { fprintf(stderr, "'%s' is not a valid array element count " "- ignored. ('caput -h' for help.)\n", optarg); count = 0; } break; case 'p': /* CA priority */ if (sscanf(optarg,"%u", &caPriority) != 1) { fprintf(stderr, "'%s' is not a valid CA priority " "- ignored. ('caget -h' for help.)\n", optarg); caPriority = DEFAULT_CA_PRIORITY; } if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; break; case 'F': /* Store this for output and tool_lib formatting */ fieldSeparator = (char) *optarg; break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('caput -h' for help.)\n", optopt); return 1; case ':': fprintf(stderr, "Option '-%c' requires an argument. ('caput -h' for help.)\n", optopt); return 1; default : usage(); return 1; } } nPvs = argc - optind; /* Remaining arg list are PV names and values */ if (nPvs < 1) { fprintf(stderr, "No pv name specified. ('caput -h' for help.)\n"); return 1; } if (nPvs == 1) { fprintf(stderr, "No value specified. ('caput -h' for help.)\n"); return 1; } nPvs = 1; /* One PV - the rest is value(s) */ epId = epicsEventCreate(epicsEventEmpty); /* Create empty EPICS event (semaphore) */ /* Start up Channel Access */ result = ca_context_create(ca_enable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ pvs = calloc (nPvs, sizeof(pv)); if (!pvs) { fprintf(stderr, "Memory allocation for channel structure failed.\n"); return 1; } /* Connect channels */ pvs[0].name = argv[optind] ; /* Copy PV name from command line */ result = connect_pvs(pvs, nPvs); /* If the connection fails, we're done */ if (result) { ca_context_destroy(); return result; } /* Get values from command line */ optind++; if (isArray) { optind++; /* In case of array skip first value (nr * of elements) - actual number of values is used */ count = argc - optind; } else { /* Concatenate the remaining line to one string * (sucks but is compatible to the former version) */ for (i = optind; i < argc; i++) { len += strlen(argv[i]); len++; } cbuf = calloc(len, sizeof(char)); if (!cbuf) { fprintf(stderr, "Memory allocation failed.\n"); return 1; } strcpy(cbuf, argv[optind]); if (argc > optind+1) { for (i = optind + 1; i < argc; i++) { strcat(cbuf, " "); strcat(cbuf, argv[i]); } } if ((argc - optind) >= 1) count = 1; argv[optind] = cbuf; } sbuf = calloc (count, sizeof(EpicsStr)); dbuf = calloc (count, sizeof(double)); if(!sbuf || !dbuf) { fprintf(stderr, "Memory allocation failed\n"); return 1; } /* ENUM? Special treatment */ if (ca_field_type(pvs[0].chid) == DBR_ENUM) { /* Get the ENUM strings */ result = ca_array_get (DBR_GR_ENUM, 1, pvs[0].chid, &bufGrEnum); result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) { fprintf(stderr, "Read operation timed out: ENUM data was not read.\n"); return 1; } if (enumAsNr) { /* Interpret values as numbers */ for (i = 0; i < count; ++i) { dbuf[i] = epicsStrtod(*(argv+optind+i), &pend); if (*(argv+optind+i) == pend) { /* Conversion didn't work */ fprintf(stderr, "Enum index value '%s' is not a number.\n", *(argv+optind+i)); return 1; } if (dbuf[i] >= bufGrEnum.no_str) { fprintf(stderr, "Warning: enum index value '%s' may be too large.\n", *(argv+optind+i)); } } dbrType = DBR_DOUBLE; } else { /* Interpret values as strings */ for (i = 0; i < count; ++i) { epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr)); *( sbuf[i]+sizeof(EpicsStr)-1 ) = '\0'; dbrType = DBR_STRING; /* Compare to ENUM strings */ for (len = 0; len < bufGrEnum.no_str; len++) if (!strcmp(sbuf[i], bufGrEnum.strs[len])) break; if (len >= bufGrEnum.no_str) { /* Not a string? Try as number */ dbuf[i] = epicsStrtod(sbuf[i], &pend); if (sbuf[i] == pend || enumAsString) { fprintf(stderr, "Enum string value '%s' invalid.\n", sbuf[i]); return 1; } if (dbuf[i] >= bufGrEnum.no_str) { fprintf(stderr, "Warning: enum index value '%s' may be too large.\n", sbuf[i]); } dbrType = DBR_DOUBLE; } } } } else { /* Not an ENUM */ if (charArrAsStr) { dbrType = DBR_CHAR; ebuf = calloc(len, sizeof(char)); if(!ebuf) { fprintf(stderr, "Memory allocation failed\n"); return 1; } count = epicsStrnRawFromEscaped(ebuf, len, cbuf, len-1) + 1; } else { for (i = 0; i < count; ++i) { epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr)); *( sbuf[i]+sizeof(EpicsStr)-1 ) = '\0'; } dbrType = DBR_STRING; } } /* Read and print old data */ if (format != terse) { printf("Old : "); result = caget(pvs, nPvs, format, 0, 0); } /* Write new data */ if (dbrType == DBR_STRING) pbuf = sbuf; else if (dbrType == DBR_CHAR) pbuf = ebuf; else pbuf = dbuf; if (request == callback) { /* Use callback version of put */ pvs[0].status = ECA_NORMAL; /* All ok at the moment */ result = ca_array_put_callback ( dbrType, count, pvs[0].chid, pbuf, put_event_handler, (void *) pvs); } else { /* Use standard put with defined timeout */ result = ca_array_put (dbrType, count, pvs[0].chid, pbuf); } result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) { fprintf(stderr, "Write operation timed out: Data was not written.\n"); return 1; } if (request == callback) { /* Also wait for callbacks */ waitStatus = epicsEventWaitWithTimeout( epId, caTimeout ); if (waitStatus) fprintf(stderr, "Write callback operation timed out\n"); /* retrieve status from callback */ result = pvs[0].status; } if (result != ECA_NORMAL) { fprintf(stderr, "Error occured writing data.\n"); return 1; } /* Read and print new data */ if (format != terse) printf("New : "); result = caget(pvs, nPvs, format, 0, 0); /* Shut down Channel Access */ ca_context_destroy(); return result; }
int main (int argc, char *argv[]) { int returncode = 0; int n; int result; /* CA result */ IntFormatT outType; /* Output type */ int opt; /* getopt() current option */ int digits = 0; /* getopt() no. of float digits */ int nPvs; /* Number of PVs */ pv* pvs; /* Array of PV structures */ LINE_BUFFER(stdout); /* Configure stdout buffering */ while ((opt = getopt(argc, argv, ":nhm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; case 'n': /* Print ENUM as index numbers */ enumAsNr=1; break; case 't': /* Select timestamp source(s) and type */ tsSrcServer = 0; tsSrcClient = 0; { int i = 0; char c; while ((c = optarg[i++])) switch (c) { case 's': tsSrcServer = 1; break; case 'c': tsSrcClient = 1; break; case 'n': break; case 'r': tsType = relative; break; case 'i': tsType = incremental; break; case 'I': tsType = incrementalByChan; break; default : fprintf(stderr, "Invalid argument '%c' " "for option '-t' - ignored.\n", c); } } break; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('camonitor -h' for help.)\n", optarg); caTimeout = DEFAULT_TIMEOUT; } break; case '#': /* Array count */ if (sscanf(optarg,"%ld", &reqElems) != 1) { fprintf(stderr, "'%s' is not a valid array element count " "- ignored. ('camonitor -h' for help.)\n", optarg); reqElems = 0; } break; case 'p': /* CA priority */ if (sscanf(optarg,"%u", &caPriority) != 1) { fprintf(stderr, "'%s' is not a valid CA priority " "- ignored. ('camonitor -h' for help.)\n", optarg); caPriority = DEFAULT_CA_PRIORITY; } if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; break; case 'm': /* Select CA event mask */ eventMask = 0; { int i = 0; char c, err = 0; while ((c = optarg[i++]) && !err) switch (c) { case 'v': eventMask |= DBE_VALUE; break; case 'a': eventMask |= DBE_ALARM; break; case 'l': eventMask |= DBE_LOG; break; case 'p': eventMask |= DBE_PROPERTY; break; default : fprintf(stderr, "Invalid argument '%s' " "for option '-m' - ignored.\n", optarg); eventMask = DBE_VALUE | DBE_ALARM; err = 1; } } break; case 's': /* Select string dbr for floating type data */ floatAsString = 1; break; case 'S': /* Treat char array as (long) string */ charArrAsStr = 1; break; case 'e': /* Select %e/%f/%g format, using <arg> digits */ case 'f': case 'g': if (sscanf(optarg, "%d", &digits) != 1) fprintf(stderr, "Invalid precision argument '%s' " "for option '-%c' - ignored.\n", optarg, opt); else { if (digits>=0 && digits<=VALID_DOUBLE_DIGITS) sprintf(dblFormatStr, "%%-.%d%c", digits, opt); else fprintf(stderr, "Precision %d for option '-%c' " "out of range - ignored.\n", digits, opt); } break; case 'l': /* Convert to long and use integer format */ case '0': /* Select integer format */ switch ((char) *optarg) { case 'x': outType = hex; break; /* x print Hex */ case 'b': outType = bin; break; /* b print Binary */ case 'o': outType = oct; break; /* o print Octal */ default : outType = dec; fprintf(stderr, "Invalid argument '%s' " "for option '-%c' - ignored.\n", optarg, opt); } if (outType != dec) { if (opt == '0') outTypeI = outType; else outTypeF = outType; } break; case 'F': /* Store this for output and tool_lib formatting */ fieldSeparator = (char) *optarg; break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('camonitor -h' for help.)\n", optopt); return 1; case ':': fprintf(stderr, "Option '-%c' requires an argument. ('camonitor -h' for help.)\n", optopt); return 1; default : usage(); return 1; } } nPvs = argc - optind; /* Remaining arg list are PV names */ if (nPvs < 1) { fprintf(stderr, "No pv name specified. ('camonitor -h' for help.)\n"); return 1; } /* Start up Channel Access */ result = ca_context_create(ca_disable_preemptive_callback); if (result != ECA_NORMAL) { fprintf(stderr, "CA error %s occurred while trying " "to start channel access.\n", ca_message(result)); return 1; } /* Allocate PV structure array */ pvs = calloc (nPvs, sizeof(pv)); if (!pvs) { fprintf(stderr, "Memory allocation for channel structures failed.\n"); return 1; } /* Connect channels */ /* Copy PV names from command line */ for (n = 0; optind < argc; n++, optind++) { pvs[n].name = argv[optind]; } /* Create CA connections */ returncode = create_pvs(pvs, nPvs, connection_handler); if ( returncode ) { return returncode; } /* Check for channels that didn't connect */ ca_pend_event(caTimeout); for (n = 0; n < nPvs; n++) { if (!pvs[n].onceConnected) print_time_val_sts(&pvs[n], reqElems); } /* Read and print data forever */ ca_pend_event(0); /* Shut down Channel Access */ ca_context_destroy(); return result; }
int main(int argc,char **argv) { char *pval; double sleepSecs = 0.0; int clearEvery = 0; int indValue = 1; int indNumberCallback = 1; while((argc>1) && (argv[1][0] == '-')) { int i; char option; int narg; option = toupper((argv[1])[1]); pval = argv[2]; argc -= 1; narg = 1; if(option=='S') { sscanf(pval,"%lf",&sleepSecs); argc -= 1; ++narg; } else if(option=='C') { sscanf(pval,"%d",&clearEvery); argc -= 1; ++narg; } else if(option=='V') { verbose = 1; } else { usageExit(); } for(i=1; i<argc; i++) argv[i] = argv[i + narg]; } if(argc != 4) usageExit(); pvname = argv[1]; pvalue1 = argv[2]; pvalue2 = argv[3]; pevent = epicsEventCreate(epicsEventEmpty); SEVCHK(ca_context_create(ca_enable_preemptive_callback), "ca_task_initialize"); while(1) { SEVCHK(ca_search(pvname,&mychid),"ca_search_and_connect"); if(ca_pend_io(3.0)!=ECA_NORMAL) { epicsThreadSleep(5.0); continue; } while(1) { epicsEventWaitStatus status; if(indValue==0) { indValue = 1; pvalue = pvalue2; } else { indValue = 0; pvalue=pvalue1; } if(++indNumberCallback >= MAXnumberCallback) indNumberCallback=0; numberCallback[indNumberCallback] = ++expectedCallback; status = ca_array_put_callback(DBR_STRING,1,mychid, pvalue,putCallback,&numberCallback[indNumberCallback]); if(status!=ECA_NORMAL) { printf("ca_array_put_callback %s\n",ca_message(status)); epicsThreadSleep(2.0); continue; } if((clearEvery>0) && ((expectedCallback % clearEvery)==0)) { ca_flush_io(); epicsThreadSleep(sleepSecs); SEVCHK(ca_clear_channel(mychid),"ca_clear_channel"); ca_flush_io(); expectedCallback = 0; epicsThreadSleep(sleepSecs); if(verbose) { printTime(); printf("Issued ca_clear_channel expectedCallback %d\n", expectedCallback); } break; } ca_flush_io(); if(verbose) { printTime(); printf("Issued ca_put_callback expectedCallback %d\n", expectedCallback); } while(1) { status = epicsEventWaitWithTimeout(pevent,10.0); if(status==epicsEventWaitTimeout) { if(verbose) { printTime(); printf("timeout after 10 seconds\n"); } continue; } break; } if(status!=epicsEventWaitOK) { int i; printTime(); printf("eventWait status %d\n",status); for(i=0; i<MAXnumberCallback; i++) numberCallback[i]=0; } epicsThreadSleep(sleepSecs); } } ca_task_exit(); return(0); }
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 eventCallback(struct event_handler_args arg) { caLink *pca = (caLink *)arg.usr; struct link *plink; size_t size; dbCommon *precord = 0; struct dbr_time_double *pdbr_time_double; dbCaCallback monitor = 0; void *userPvt = 0; assert(pca); epicsMutexMustLock(pca->lock); plink = pca->plink; if (!plink) goto done; monitor = pca->monitor; userPvt = pca->userPvt; precord = plink->value.pv_link.precord; if (arg.status != ECA_NORMAL) { if (precord) { if (arg.status != ECA_NORDACCESS && arg.status != ECA_GETFAIL) errlogPrintf("dbCa: eventCallback record %s error %s\n", precord->name, ca_message(arg.status)); } else { errlogPrintf("dbCa: eventCallback error %s\n", ca_message(arg.status)); } goto done; } assert(arg.dbr); size = arg.count * dbr_value_size[arg.type]; if (arg.type == DBR_TIME_STRING && ca_field_type(pca->chid) == DBR_ENUM) { assert(pca->pgetString); memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size); pca->gotInString = TRUE; } else switch (arg.type){ case DBR_TIME_STRING: case DBR_TIME_SHORT: case DBR_TIME_FLOAT: case DBR_TIME_ENUM: case DBR_TIME_CHAR: case DBR_TIME_LONG: case DBR_TIME_DOUBLE: assert(pca->pgetNative); memcpy(pca->pgetNative, dbr_value_ptr(arg.dbr, arg.type), size); pca->gotInNative = TRUE; break; default: errMessage(-1, "dbCa: eventCallback Logic Error\n"); break; } pdbr_time_double = (struct dbr_time_double *)arg.dbr; pca->sevr = pdbr_time_double->severity; pca->stat = pdbr_time_double->status; memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp)); if (precord) { struct pv_link *ppv_link = &plink->value.pv_link; if ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)) scanOnce(precord); } done: epicsMutexUnlock(pca->lock); if (monitor) monitor(userPvt); }
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 medmUpdateChannelCb(struct event_handler_args args) { Channel *pCh = (Channel *)ca_puser(args.chid); Boolean statusChanged = False; Boolean severityChanged = False; Boolean zeroAndNoneZeroTransition = False; Record *pr; double value; int nBytes; int allocBytes; #if DEBUG_CHANNEL_CB || DEBUG_LARGE_ARRAY const char *pvname=ca_name(args.chid); print("medmUpdateChannelCb: %s\n",pvname); if(args.status != ECA_NORMAL) { char buf[80]; envGetConfigParam(&EPICS_CA_MAX_ARRAY_BYTES,80,buf); buf[79]='\0'; print("EPICS_CA_MAX_ARRAY_BYTES: %s\n",buf); } #endif /* Increment the event counter */ caTask.caEventCount++; /* Check for valid values */ /* Same as for updateGraphicalInfoCb */ /* Don't need to check for read access, checking ECA_NORMAL is enough */ if(globalDisplayListTraversalMode != DL_EXECUTE) return; if(args.status != ECA_NORMAL) { medmPostMsg(0,"medmUpdateChannelCb: Bad status [%d] for %s: %s\n", args.status, ca_name(args.chid)?ca_name(args.chid):"Name Unknown", ca_message(args.status)); return; } if(!args.dbr) { medmPostMsg(0,"medmUpdateChannelCb: Invalid data for [%s]\n", ca_name(args.chid)?ca_name(args.chid):"Name Unknown"); return; } if(!pCh || !pCh->chid || !pCh->pr) { medmPostMsg(0,"medmUpdateChannelCb: " "Invalid channel information for [%s]\n", ca_name(args.chid)?ca_name(args.chid):"Name Unknown"); return; } if(pCh->chid != args.chid) { medmPostMsg(0,"medmUpdateChannelCb: chid from args [%x] " "does not match chid from channel [%x]\n" " [%s]\n", args.chid, pCh->chid, ca_name(args.chid)?ca_name(args.chid):"Name Unknown"); return; } pr = pCh->pr; /* Allocate space for the data */ nBytes = dbr_size_n(args.type, args.count); if(!pCh->data) { /* Allocate maximum space for the variable array data */ if(pr->currentCount == -2) { allocBytes=dbr_size_n(args.type, pr->elementCount); } else { allocBytes=nBytes; } /* No previous data, allocate */ pCh->data = (dataBuf *)malloc(allocBytes); pCh->size = allocBytes; if(!pCh->data) { medmPostMsg(1,"medmUpdateChannelCb: Memory allocation error [%s]\n", ca_name(args.chid)?ca_name(args.chid):"Name Unknown"); return; } } else if(pCh->size < nBytes) { /* Previous data exists but is too small * Free it and allocate it again */ free((char *)pCh->data); pCh->data = (dataBuf *)malloc(nBytes); pCh->size = nBytes; if(pCh->data == NULL) { medmPostMsg(1,"medmUpdateChannelCb: Memory reallocation error" " [%s]\n", ca_name(args.chid)?ca_name(args.chid):"Name Unknown"); return; } } pr->currentCount=args.count; #ifdef __USING_TIME_STAMP__ /* Copy the returned time stamp to the Record */ pr->time = ((dataBuf *)args.dbr)->s.stamp; #endif /* Set the array pointer in the Record to point to the value member of the * appropriate struct stored in the Channel * (The returned data is not stored there yet) */ switch (ca_field_type(args.chid)) { case DBF_STRING: pr->array = (XtPointer)pCh->data->s.value; break; case DBF_ENUM: pr->array = (XtPointer)&(pCh->data->e.value); break; case DBF_CHAR: pr->array = (XtPointer)&(pCh->data->c.value); break; case DBF_INT: pr->array = (XtPointer)&(pCh->data->i.value); break; case DBF_LONG: pr->array = (XtPointer)&(pCh->data->l.value); break; case DBF_FLOAT: pr->array = (XtPointer)&(pCh->data->f.value); break; case DBF_DOUBLE: pr->array = (XtPointer)&(pCh->data->d.value); break; default: break; } /* For strings and arrays copy the returned data to the Channel * (The space is allocated but the data is not stored otherwise) */ if(ca_field_type(args.chid) == DBF_STRING || ca_element_count(args.chid) > 1) { memcpy((void *)pCh->data, args.dbr, nBytes); } /* Copy the value from the returned data to the Record */ switch (ca_field_type(args.chid)) { case DBF_STRING: value = 0.0; break; case DBF_ENUM: value = (double)((dataBuf *)(args.dbr))->e.value; break; case DBF_CHAR: value = (double)((dataBuf *)(args.dbr))->c.value; break; case DBF_INT: value = (double)((dataBuf *)(args.dbr))->i.value; break; case DBF_LONG: value = (double)((dataBuf *)(args.dbr))->l.value; break; case DBF_FLOAT: value = (double)((dataBuf *)(args.dbr))->f.value; break; case DBF_DOUBLE: value = ((dataBuf *)(args.dbr))->d.value; #if DEBUG_SLIDER if(value > 150.0 || value < -150.0) { print("medmUpdateChannelCb: %g\n",value); } #endif break; default: value = 0.0; break; } /* Mark zero to nonzero transition */ if(((value == 0.0) && (pr->value != 0.0)) || ((value != 0.0) && (pr->value == 0.0))) zeroAndNoneZeroTransition = True; /* Mark status changed */ if(pr->status != ((dataBuf *)(args.dbr))->d.status) { pr->status = ((dataBuf *)(args.dbr))->d.status; statusChanged = True; } /* Mark severity changed */ if(pr->severity != ((dataBuf *)(args.dbr))->d.severity) { pr->severity = ((dataBuf *)(args.dbr))->d.severity; severityChanged = True; } /* Set the new value into the record */ pr->value = value; #if DEBUG_ERASE print("medmUpdateChannelCb: [%x]%s %g\n", pr, pr->name, pr->value); #endif /* Call the update value callback if there is a monitored change */ if(pCh->pr->updateValueCb) { if(pr->monitorValueChanged) { pr->updateValueCb((XtPointer)pr); } else if(pr->monitorStatusChanged && statusChanged) { pr->updateValueCb((XtPointer)pr); } else if(pr->monitorSeverityChanged && severityChanged) { pr->updateValueCb((XtPointer)pr); } else if(pr->monitorZeroAndNoneZeroTransition && zeroAndNoneZeroTransition) { pr->updateValueCb((XtPointer)pr); } } }
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 }
static int caAdd(char *name, Record *pr) { Channel *pCh; int status; if((caTask.freeListCount < 1) && (caTask.nextFree >= CA_PAGE_SIZE)) { /* if not enough pages, increase number of pages */ if(caTask.pageCount >= caTask.pageSize) { caTask.pageSize += CA_PAGE_COUNT; #if defined(__cplusplus) && !defined(__GNUG__) caTask.pages = (Channel **)realloc((malloc_t)caTask.pages, sizeof(Channel *)*caTask.pageSize); #else caTask.pages = (Channel **)realloc(caTask.pages, sizeof(Channel *)*caTask.pageSize); #endif if(caTask.pages == NULL) { medmPostMsg(1,"caAdd: Memory allocation error\n"); return -1; } } /* add one more page */ caTask.pages[caTask.pageCount] = (Channel *)malloc(sizeof(Channel) * CA_PAGE_SIZE); if(caTask.pages[caTask.pageCount] == NULL) { medmPostMsg(1,"caAdd: Memory allocation error\n"); return -1; } caTask.pageCount++; caTask.nextPage++; caTask.nextFree=0; } if(caTask.nextFree < CA_PAGE_SIZE) { pCh = &((caTask.pages[caTask.nextPage])[caTask.nextFree]); pCh->caId = caTask.nextPage * CA_PAGE_SIZE + caTask.nextFree; caTask.nextFree++; } else { int index; caTask.freeListCount--; index = caTask.freeList[caTask.freeListCount]; pCh = &((caTask.pages[index/CA_PAGE_SIZE])[index % CA_PAGE_SIZE]); pCh->caId = index; } pCh->data = NULL; pCh->chid = NULL; pCh->evid = NULL; pCh->size = 0; pCh->pr = pr; pCh->previouslyConnected = False; /* Do the search */ status = ca_search_and_connect(name, &(pCh->chid), medmConnectEventCb, pCh); if(status != ECA_NORMAL) { medmPostMsg(1,"caAdd: ca_search_and_connect failed: %s\n", ca_message(status)); } else { /* Cast to avoid warning from READONLY */ pCh->pr->name = (char *)ca_name(pCh->chid); } caTask.channelCount++; #if DEBUG_ADD print("caAdd: %3d name=%s\n", caTask.channelCount, name); #endif return pCh->caId; }