static void getCallbackValue(devInt32Pvt *pPvt) { int count, size=sizeof(epicsInt32); epicsMutexLock(pPvt->mutexId); if (pPvt->ringBuffer && (epicsRingBytesUsedBytes(pPvt->ringBuffer) >= size)) { if (pPvt->ringBufferOverflows > 0) { asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, "%s devAsynInt32 getCallbackValue error, %d ring buffer overflows\n", pPvt->pr->name, pPvt->ringBufferOverflows); pPvt->ringBufferOverflows = 0; } count = epicsRingBytesGet(pPvt->ringBuffer, (char *)&pPvt->value, size); if (count == size) { asynPrint(pPvt->pasynUser, ASYN_TRACEIO_DEVICE, "%s devAsynInt32::getCallbackValue from ringBuffer value=%d\n",pPvt->pr->name,pPvt->value); pPvt->gotValue = 1; } else asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, "%s devAsynInt32 getCallbackValue error, ring read failed\n", pPvt->pr->name); } epicsMutexUnlock(pPvt->mutexId); }
static void interruptCallbackInput(void *drvPvt, asynUser *pasynUser, epicsInt32 value) { devInt32Pvt *pPvt = (devInt32Pvt *)drvPvt; dbCommon *pr = pPvt->pr; int count, size = sizeof(epicsInt32); if (pPvt->bipolar && (value & pPvt->signBit)) value |= ~pPvt->mask; asynPrint(pPvt->pasynUser, ASYN_TRACEIO_DEVICE, "%s devAsynInt32::interruptCallbackInput new value=%d\n", pr->name, value); /* There is a problem. A driver could be calling us with a value after * this record has registered for callbacks but before EPICS has set interruptAccept, * which means that scanIoRequest will return immediately. * This is very bad, because if we have pushed a value into the ring buffer * it won't get popped off because the record won't process. The values * read the next time the record processes would then be stale. * We previously worked around this problem by waiting here for interruptAccept. * But that does not work if the callback is coming from the thread that is executing * iocInit, which can happen with synchronous drivers (ASYN_CANBLOCK=0) that do callbacks * when a value is written to them, which can happen in initRecord for an output record. * Instead we just return. There will then be nothing in the ring buffer, so the first * read will do a read from the driver, which should be OK. */ if (!interruptAccept) return; /* Note that we put a lock around epicsRingBytesPut and Get because we potentially have * more than one reader, since the reader is whatever thread is processing the record */ epicsMutexLock(pPvt->mutexId); count = epicsRingBytesPut(pPvt->ringBuffer, (char *)&value, size); if (count != size) { /* There was no room in the ring buffer. In the past we just threw away * the new value. However, it is better to remove the oldest value from the * ring buffer and add the new one. That way the final value the record receives * is guaranteed to be the most recent value */ epicsInt32 dummy; count = epicsRingBytesGet(pPvt->ringBuffer, (char *)&dummy, size); if (count != size) { asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, "%s devAsynInt32 interruptCallbackInput error, ring read failed\n", pPvt->pr->name); } count = epicsRingBytesPut(pPvt->ringBuffer, (char *)&value, size); if (count != size) { asynPrint(pPvt->pasynUser, ASYN_TRACE_ERROR, "%s devAsynInt32 interruptCallbackInput error, ring put failed\n", pPvt->pr->name); } pPvt->ringBufferOverflows++; } else { /* We only need to request the record to process if we added a * new element to the ring buffer, not if we just replaced an element. */ scanIoRequest(pPvt->ioScanPvt); } epicsMutexUnlock(pPvt->mutexId); }
asynStatus drvQuadEM::doDataCallbacks() { epicsFloat64 doubleData[QE_MAX_DATA]; epicsFloat64 *pIn, *pOut; int numAverage; int numAveraged; int sampleSize = sizeof(doubleData); int count; epicsTimeStamp now; epicsFloat64 timeStamp; int arrayCounter; int i, j; size_t dims[2]; NDArray *pArrayAll, *pArraySingle; static const char *functionName = "doDataCallbacks"; getIntegerParam(P_NumAverage, &numAverage); while (1) { numAveraged = epicsRingBytesUsedBytes(ringBuffer_) / sampleSize; if (numAverage > 0) { if (numAveraged < numAverage) break; numAveraged = numAverage; setIntegerParam(P_NumAveraged, numAveraged); } else { setIntegerParam(P_NumAveraged, numAveraged); if (numAveraged < 1) break; } dims[0] = QE_MAX_DATA; dims[1] = numAveraged; epicsTimeGetCurrent(&now); getIntegerParam(NDArrayCounter, &arrayCounter); arrayCounter++; setIntegerParam(NDArrayCounter, arrayCounter); pArrayAll = pNDArrayPool->alloc(2, dims, NDFloat64, 0, 0); pArrayAll->uniqueId = arrayCounter; timeStamp = now.secPastEpoch + now.nsec / 1.e9; pArrayAll->timeStamp = timeStamp; getAttributes(pArrayAll->pAttributeList); count = epicsRingBytesGet(ringBuffer_, (char *)pArrayAll->pData, numAveraged * sampleSize); if (count != numAveraged * sampleSize) { asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ring read failed\n", driverName, functionName); return asynError; } ringCount_ -= numAveraged; unlock(); doCallbacksGenericPointer(pArrayAll, NDArrayData, QE_MAX_DATA); lock(); // Copy data to arrays for each type of data, do callbacks on that. dims[0] = numAveraged; for (i=0; i<QE_MAX_DATA; i++) { pArraySingle = pNDArrayPool->alloc(1, dims, NDFloat64, 0, 0); pArraySingle->uniqueId = arrayCounter; pArraySingle->timeStamp = timeStamp; getAttributes(pArraySingle->pAttributeList); pIn = (epicsFloat64 *)pArrayAll->pData; pOut = (epicsFloat64 *)pArraySingle->pData; for (j=0; j<numAveraged; j++) { pOut[j] = pIn[i]; pIn += QE_MAX_DATA; } unlock(); doCallbacksGenericPointer(pArraySingle, NDArrayData, i); lock(); pArraySingle->release(); } pArrayAll->release(); callParamCallbacks(); // We only loop once if numAverage==0 if (numAverage == 0) break; } setIntegerParam(P_RingOverflows, 0); callParamCallbacks(); return asynSuccess; }
/** This function computes the sums, diffs and positions, and does callbacks * \param[in] raw Array of raw current readings */ void drvQuadEM::computePositions(epicsFloat64 raw[QE_MAX_INPUTS]) { int i; int count; int numAverage; int ringOverflows; int geometry; epicsFloat64 currentOffset[QE_MAX_INPUTS]; epicsFloat64 currentScale[QE_MAX_INPUTS]; epicsFloat64 positionOffset[2]; epicsFloat64 positionScale[2]; epicsInt32 intData[QE_MAX_DATA]; epicsFloat64 doubleData[QE_MAX_DATA]; epicsFloat64 denom; static const char *functionName = "computePositions"; getIntegerParam(P_Geometry, &geometry); // If the ring buffer is full then remove the oldest entry if (epicsRingBytesFreeBytes(ringBuffer_) < (int)sizeof(doubleData)) { count = epicsRingBytesGet(ringBuffer_, (char *)&doubleData, sizeof(doubleData)); ringCount_--; getIntegerParam(P_RingOverflows, &ringOverflows); ringOverflows++; setIntegerParam(P_RingOverflows, ringOverflows); } for (i=0; i<QE_MAX_INPUTS; i++) { getDoubleParam(i, P_CurrentOffset, ¤tOffset[i]); getDoubleParam(i, P_CurrentScale, ¤tScale[i]); doubleData[i] = raw[i]*currentScale[i] - currentOffset[i]; } for (i=0; i<2; i++) { getDoubleParam(i, P_PositionOffset, &positionOffset[i]); getDoubleParam(i, P_PositionScale, &positionScale[i]); } doubleData[QESumAll] = doubleData[QECurrent1] + doubleData[QECurrent2] + doubleData[QECurrent3] + doubleData[QECurrent4]; if (geometry == QEGeometrySquare) { doubleData[QESumX] = doubleData[QESumAll]; doubleData[QESumY] = doubleData[QESumAll]; doubleData[QEDiffX] = (doubleData[QECurrent2] + doubleData[QECurrent3]) - (doubleData[QECurrent1] + doubleData[QECurrent4]); doubleData[QEDiffY] = (doubleData[QECurrent1] + doubleData[QECurrent2]) - (doubleData[QECurrent3] + doubleData[QECurrent4]); } else { doubleData[QESumX] = doubleData[QECurrent1] + doubleData[QECurrent2]; doubleData[QESumY] = doubleData[QECurrent3] + doubleData[QECurrent4]; doubleData[QEDiffX] = doubleData[QECurrent2] - doubleData[QECurrent1]; doubleData[QEDiffY] = doubleData[QECurrent4] - doubleData[QECurrent3]; } denom = doubleData[QESumX]; if (denom == 0.) denom = 1.; doubleData[QEPositionX] = (positionScale[0] * doubleData[QEDiffX] / denom) - positionOffset[0]; denom = doubleData[QESumY]; if (denom == 0.) denom = 1.; doubleData[QEPositionY] = (positionScale[1] * doubleData[QEDiffY] / denom) - positionOffset[1]; count = epicsRingBytesPut(ringBuffer_, (char *)&doubleData, sizeof(doubleData)); ringCount_++; if (count != sizeof(doubleData)) { asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: error writing ring buffer, count=%d, should be %d\n", driverName, functionName, count, (int)sizeof(doubleData)); } getIntegerParam(P_NumAverage, &numAverage); if (numAverage > 0) { if (ringCount_ >= numAverage) { triggerCallbacks(); } } for (i=0; i<QE_MAX_DATA; i++) { intData[i] = (epicsInt32)doubleData[i]; setDoubleParam(i, P_DoubleData, doubleData[i]); callParamCallbacks(i); } doCallbacksInt32Array(intData, QE_MAX_DATA, P_IntArrayData, 0); }