/** Base method for writing a file * Handles logic for NDFileModeSingle, NDFileModeCapture and NDFileModeStream when the derived class does or * does not support NDPulginFileMultiple. Calls writeFile in the derived class. */ asynStatus NDPluginFile::writeFileBase() { int status = asynSuccess; int fileWriteMode; int numCapture, numCaptured; int i; bool doLazyOpen; int deleteDriverFile; NDArray *pArray; NDAttribute *pAttribute; char driverFileName[MAX_FILENAME_LEN]; char errorMessage[256]; static const char* functionName = "writeFileBase"; /* Make sure there is a valid array */ if (!this->pArrays[0]) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s: ERROR, must collect an array to get dimensions first\n", driverName, functionName); return(asynError); } NDArray *pArrayOut = this->pArrays[0]; // Must increase reference count on this array because another thread might decrement the count on pArrays[0] // when we have the mutex unlocked pArrayOut->reserve(); getIntegerParam(NDFileWriteMode, &fileWriteMode); getIntegerParam(NDFileNumCapture, &numCapture); getIntegerParam(NDFileNumCaptured, &numCaptured); setIntegerParam(NDFileWriteStatus, NDFileWriteOK); setStringParam(NDFileWriteMessage, ""); /* We unlock the overall mutex here because we want the callbacks to be able to queue new * frames without waiting while we write files here. The only restriction is that the * callbacks must not modify any part of the class structure that we use here. */ switch(fileWriteMode) { case NDFileModeSingle: setIntegerParam(NDWriteFile, 1); callParamCallbacks(); // Some file writing plugins (e.g. HDF5) use the value of NDFileNumCaptured // even in single mode setIntegerParam(NDFileNumCaptured, 1); status = this->openFileBase(NDFileModeWrite, pArrayOut); if (status == asynSuccess) { this->unlock(); epicsMutexLock(this->fileMutexId); status = this->writeFile(pArrayOut); epicsMutexUnlock(this->fileMutexId); this->lock(); NDPluginDriver::endProcessCallbacks(pArrayOut, true, true); if (status) { epicsSnprintf(errorMessage, sizeof(errorMessage)-1, "Error writing file, status=%d", status); asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s %s\n", driverName, functionName, errorMessage); setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, errorMessage); } else { status = this->closeFileBase(); } } setIntegerParam(NDWriteFile, 0); callParamCallbacks(); break; case NDFileModeCapture: /* Write the file */ if (!this->pCapture) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s: ERROR, no capture buffer present\n", driverName, functionName); setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, "ERROR, no capture buffer present"); break; } setIntegerParam(NDWriteFile, 1); callParamCallbacks(); if (this->supportsMultipleArrays) status = this->openFileBase(NDFileModeWrite | NDFileModeMultiple, pArrayOut); if (status == asynSuccess) { for (i=0; i<numCaptured; i++) { pArray = this->pCapture[i]; if (!this->supportsMultipleArrays) status = this->openFileBase(NDFileModeWrite, pArray); else this->attrFileNameCheck(); if (status == asynSuccess) { this->unlock(); epicsMutexLock(this->fileMutexId); status = this->writeFile(pArray); epicsMutexUnlock(this->fileMutexId); this->lock(); NDPluginDriver::endProcessCallbacks(pArray, true, true); if (status) { epicsSnprintf(errorMessage, sizeof(errorMessage)-1, "Error writing file, status=%d", status); asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s %s\n", driverName, functionName, errorMessage); setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, errorMessage); } else { if (!this->supportsMultipleArrays) status = this->closeFileBase(); } } } } freeCaptureBuffer(numCapture); if ((status == asynSuccess) && this->supportsMultipleArrays) status = this->closeFileBase(); this->registerInitFrameInfo(NULL); setIntegerParam(NDFileNumCaptured, 0); setIntegerParam(NDWriteFile, 0); callParamCallbacks(); break; case NDFileModeStream: doLazyOpen = this->lazyOpen && (numCaptured == 0); if (!this->supportsMultipleArrays || doLazyOpen) status = this->openFileBase(NDFileModeWrite | NDFileModeMultiple, pArrayOut); else this->attrFileNameCheck(); if (!this->isFrameValid(pArrayOut)) { setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, "Invalid frame. Ignoring."); status = asynError; } if (status == asynSuccess) { this->unlock(); epicsMutexLock(this->fileMutexId); status = this->writeFile(pArrayOut); epicsMutexUnlock(this->fileMutexId); this->lock(); NDPluginDriver::endProcessCallbacks(pArrayOut, true, true); if (status) { epicsSnprintf(errorMessage, sizeof(errorMessage)-1, "Error writing file, status=%d", status); asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s %s\n", driverName, functionName, errorMessage); setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, errorMessage); } else { status = this->attrFileCloseCheck(); if (!this->supportsMultipleArrays) status = this->closeFileBase(); } } break; default: asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s: ERROR, unknown fileWriteMode %d\n", driverName, functionName, fileWriteMode); break; } /* Check to see if we should delete the original file * Only do this if all of the following conditions are met * - DeleteOriginalFile is true * - There were no errors above * - The NDFullFileName attribute is present and contains a non-blank string */ getIntegerParam(NDFileDeleteDriverFile, &deleteDriverFile); if ((status == asynSuccess) && deleteDriverFile) { pAttribute = pArrayOut->pAttributeList->find("DriverFileName"); if (pAttribute) { status = pAttribute->getValue(NDAttrString, driverFileName, sizeof(driverFileName)); if ((status == asynSuccess) && (strlen(driverFileName) > 0)) { status = remove(driverFileName); if (status != 0) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s: error deleting file %s, error=%s\n", driverName, functionName, driverFileName, strerror(errno)); } } } } // Decrease reference count pArrayOut->release(); return (asynStatus) status; }
/** Handles the logic for when NDFileCapture changes state, starting or stopping capturing or streaming NDArrays * to a file. * \param[in] capture Flag to start or stop capture; 1=start capture, 0=stop capture. */ asynStatus NDPluginFile::doCapture(int capture) { /* This function is called from write32 whenever capture is started or stopped */ asynStatus status = asynSuccess; int fileWriteMode; NDArray *pArray = this->pArrays[0]; NDArrayInfo_t arrayInfo; int i; int numCapture; static const char* functionName = "doCapture"; /* Make sure there is a valid array if capture is set to 1 */ if (!pArray && !this->lazyOpen) { if (capture == 0){ /* No error here, but just return straight away as stop capture is non operation */ return(asynSuccess); } else { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s: ERROR, must collect an array to get dimensions first\n", driverName, functionName); return(asynError); } } /* Decide whether or not to use the NDAttribute named "fileprefix" to create the filename */ if (pArray) { if( pArray->pAttributeList->find(FILEPLUGIN_NAME) != NULL) this->useAttrFilePrefix = true; } getIntegerParam(NDFileWriteMode, &fileWriteMode); getIntegerParam(NDFileNumCapture, &numCapture); switch(fileWriteMode) { case NDFileModeSingle: /* It is an error to set capture=1 in this mode, set to 0 */ setIntegerParam(NDFileCapture, 0); break; case NDFileModeCapture: if (capture) { /* Capturing was just started */ setIntegerParam(NDFileNumCaptured, 0); if (!pArray) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s ERROR: No arrays collected: cannot allocate capture buffer\n", driverName, functionName); return(asynError); } pArray->getInfo(&arrayInfo); this->registerInitFrameInfo(pArray); this->pCapture = (NDArray **)calloc(numCapture, sizeof(NDArray *)); if (!this->pCapture) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s ERROR: cannot allocate capture buffer\n", driverName, functionName); setIntegerParam(NDFileCapture, 0); return(asynError); } for (i=0; i<numCapture; i++) { pCapture[i] = new NDArray; if (!this->pCapture[i]) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s ERROR: cannot allocate capture buffer %d\n", driverName, functionName, i); setIntegerParam(NDFileCapture, 0); freeCaptureBuffer(numCapture); return(asynError); } this->pCapture[i]->dataSize = arrayInfo.totalBytes; this->pCapture[i]->pData = malloc(arrayInfo.totalBytes); this->pCapture[i]->ndims = pArray->ndims; if (!this->pCapture[i]->pData) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s ERROR: cannot allocate capture array for buffer %d\n", driverName, functionName, i); setIntegerParam(NDFileCapture, 0); freeCaptureBuffer(numCapture); return(asynError); } } } else { /* Stop capturing, nothing to do, setting the parameter is all that is needed */ } break; case NDFileModeStream: if (capture) { /* Streaming was just started */ if (this->supportsMultipleArrays && !this->useAttrFilePrefix && !this->lazyOpen) status = this->openFileBase(NDFileModeWrite | NDFileModeMultiple, pArray); setIntegerParam(NDFileNumCaptured, 0); setIntegerParam(NDWriteFile, 1); } else { /* Streaming was just stopped */ if (this->supportsMultipleArrays) status = this->closeFileBase(); setIntegerParam(NDFileCapture, 0); setIntegerParam(NDWriteFile, 0); } } return(status); }
/** Base method for writing a file * Handles logic for NDFileModeSingle, NDFileModeCapture and NDFileModeStream when the derived class does or * does not support NDPulginFileMultiple. Calls writeFile in the derived class. */ asynStatus NDPluginFile::writeFileBase() { int status = asynSuccess; int fileWriteMode; int numCapture, numCaptured; int i; int deleteDriverFile; NDArray *pArray; NDAttribute *pAttribute; char driverFileName[MAX_FILENAME_LEN]; char errorMessage[256]; const char* functionName = "writeFileBase"; /* Make sure there is a valid array */ if (!this->pArrays[0]) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ERROR, must collect an array to get dimensions first\n", driverName, functionName); return(asynError); } getIntegerParam(NDFileWriteMode, &fileWriteMode); getIntegerParam(NDFileNumCapture, &numCapture); getIntegerParam(NDFileNumCaptured, &numCaptured); setIntegerParam(NDFileWriteStatus, NDFileWriteOK); setStringParam(NDFileWriteMessage, ""); /* We unlock the overall mutex here because we want the callbacks to be able to queue new * frames without waiting while we write files here. The only restriction is that the * callbacks must not modify any part of the class structure that we use here. */ switch(fileWriteMode) { case NDFileModeSingle: setIntegerParam(NDWriteFile, 1); callParamCallbacks(); status = this->openFileBase(NDFileModeWrite, this->pArrays[0]); if (status == asynSuccess) { this->unlock(); epicsMutexLock(this->fileMutexId); status = this->writeFile(this->pArrays[0]); epicsMutexUnlock(this->fileMutexId); this->lock(); if (status) { epicsSnprintf(errorMessage, sizeof(errorMessage)-1, "Error writing file, status=%d", status); asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s %s\n", driverName, functionName, errorMessage); setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, errorMessage); } else { status = this->closeFileBase(); } } setIntegerParam(NDWriteFile, 0); callParamCallbacks(); break; case NDFileModeCapture: /* Write the file */ if (!this->pCapture) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ERROR, no capture buffer present\n", driverName, functionName); setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, "ERROR, no capture buffer present"); break; } setIntegerParam(NDWriteFile, 1); callParamCallbacks(); if (this->supportsMultipleArrays) status = this->openFileBase(NDFileModeWrite | NDFileModeMultiple, this->pArrays[0]); if (status == asynSuccess) { for (i=0; i<numCaptured; i++) { pArray = this->pCapture[i]; if (!this->supportsMultipleArrays) status = this->openFileBase(NDFileModeWrite, pArray); if (status == asynSuccess) { this->unlock(); epicsMutexLock(this->fileMutexId); status = this->writeFile(pArray); epicsMutexUnlock(this->fileMutexId); this->lock(); if (status) { epicsSnprintf(errorMessage, sizeof(errorMessage)-1, "Error writing file, status=%d", status); asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s %s\n", driverName, functionName, errorMessage); setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, errorMessage); } else { if (!this->supportsMultipleArrays) status = this->closeFileBase(); } } } } freeCaptureBuffer(numCapture); if ((status == asynSuccess) && this->supportsMultipleArrays) status = this->closeFileBase(); setIntegerParam(NDFileNumCaptured, 0); setIntegerParam(NDWriteFile, 0); callParamCallbacks(); break; case NDFileModeStream: if (!this->supportsMultipleArrays) status = this->openFileBase(NDFileModeWrite | NDFileModeMultiple, this->pArrays[0]); if (status == asynSuccess) { this->unlock(); epicsMutexLock(this->fileMutexId); status = this->writeFile(this->pArrays[0]); epicsMutexUnlock(this->fileMutexId); this->lock(); if (status) { epicsSnprintf(errorMessage, sizeof(errorMessage)-1, "Error writing file, status=%d", status); asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s %s\n", driverName, functionName, errorMessage); setIntegerParam(NDFileWriteStatus, NDFileWriteError); setStringParam(NDFileWriteMessage, errorMessage); } else { if (!this->supportsMultipleArrays) status = this->closeFileBase(); } } break; default: asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: ERROR, unknown fileWriteMode %d\n", driverName, functionName, fileWriteMode); break; } /* Check to see if we should delete the original file * Only do this if all of the following conditions are met * - DeleteOriginalFile is true * - There were no errors above * - The NDFullFileName attribute is present and contains a non-blank string */ getIntegerParam(NDFileDeleteDriverFile, &deleteDriverFile); if ((status == asynSuccess) && deleteDriverFile) { pAttribute = this->pArrays[0]->pAttributeList->find("DriverFileName"); if (pAttribute) { status = pAttribute->getValue(NDAttrString, driverFileName, sizeof(driverFileName)); if ((status == asynSuccess) && (strlen(driverFileName) > 0)) { status = remove(driverFileName); if (status != 0) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: error deleting file %s, error=%s\n", driverName, functionName, driverFileName, strerror(errno)); } } } } return((asynStatus)status); }