Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ADApp/ADSrc/asynNDArrayDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ asynNDArrayDriver::asynNDArrayDriver(const char *portName, int maxAddr, int numP
createParam(NDFileCaptureString, asynParamInt32, &NDFileCapture);
createParam(NDFileDeleteDriverFileString, asynParamInt32, &NDFileDeleteDriverFile);
createParam(NDFileLazyOpenString, asynParamInt32, &NDFileLazyOpen);
createParam(NDFileCreateDirString, asynParamInt32, &NDFileCreateDir);
createParam(NDFileTempSuffixString, asynParamOctet, &NDFileTempSuffix);
createParam(NDAttributesFileString, asynParamOctet, &NDAttributesFile);
createParam(NDArrayDataString, asynParamGenericPointer, &NDArrayData);
Expand Down Expand Up @@ -586,6 +587,7 @@ asynNDArrayDriver::asynNDArrayDriver(const char *portName, int maxAddr, int numP
setIntegerParam(NDAutoIncrement, 0);
setStringParam (NDFileTemplate, "%s%s_%3.3d.dat");
setIntegerParam(NDFileNumCaptured, 0);
setIntegerParam(NDFileCreateDir, 0);
setStringParam (NDFileTempSuffix, "");

setIntegerParam(NDPoolMaxBuffers, this->pNDArrayPool->maxBuffers());
Expand Down
3 changes: 2 additions & 1 deletion ADApp/ADSrc/asynNDArrayDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ typedef enum {
#define NDFileCaptureString "CAPTURE" /**< (asynInt32, r/w) Start or stop capturing arrays */
#define NDFileDeleteDriverFileString "DELETE_DRIVER_FILE" /**< (asynInt32, r/w) Delete driver file */
#define NDFileLazyOpenString "FILE_LAZY_OPEN" /**< (asynInt32, r/w) Don't open file until first frame arrives in Stream mode */
#define NDFileCreateDirString "CREATE_DIR" /**< (asynInt32, r/w) If set then create the target directory before writing file */
#define NDFileTempSuffixString "FILE_TEMP_SUFFIX" /**< (asynOctet, r/w) Temporary filename suffix while writing data to file. The file will be renamed (suffix removed) upon closing the file. */


#define NDAttributesFileString "ND_ATTRIBUTES_FILE" /**< (asynOctet, r/w) Attributes file name */

/* The detector array data */
Expand Down Expand Up @@ -151,6 +151,7 @@ class epicsShareFunc asynNDArrayDriver : public asynPortDriver {
int NDFileCapture;
int NDFileDeleteDriverFile;
int NDFileLazyOpen;
int NDFileCreateDir;
int NDFileTempSuffix;
int NDAttributesFile;
int NDArrayData;
Expand Down
17 changes: 17 additions & 0 deletions ADApp/Db/NDFile.template
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ record(bi, "$(P)$(R)FilePathExists_RBV")
field(SCAN, "I/O Intr")
}

record(longout, "$(P)$(R)CreateDirectory")
{
field(PINI, "YES")
field(DTYP, "asynInt32")
field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))CREATE_DIR")
field(VAL, "0" )
info(autosaveFields, "VAL")
}

record(longin, "$(P)$(R)CreateDirectory_RBV")
{
field(DTYP, "asynInt32")
field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))CREATE_DIR")
field(VAL, "")
field(SCAN, "I/O Intr")
}

# Filename
record(waveform, "$(P)$(R)FileName")
{
Expand Down
1 change: 1 addition & 0 deletions ADApp/Db/NDFile_settings.req
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ $(P)$(R)FileFormat
$(P)$(R)FileWriteMode
$(P)$(R)NumCapture
$(P)$(R)DeleteDriverFile
$(P)$(R)CreateDirectory
104 changes: 102 additions & 2 deletions ADApp/pluginSrc/NDPluginFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,100 @@
static const char *driverName="NDPluginFile";



/**
* Make sure all the directories in the create path exist. Returns asnySuccess
* or asynFail in the hope that this is propagated back to GDA.
*/
#define MAX_PATH_PARTS 32

#if defined(_WIN32)
#include <direct.h>
#define strtok_r(a,b,c) strtok(a,b)
#define mkdir(a,b) _mkdir(a)
#define delim "\\"
#else
#include <sys/stat.h>
#include <sys/types.h>
#define delim "/"
#endif

/** Function to create a directory path for a file.
\param[in] createPath Path to create. The final part is the file name and is not created.
\param[in] stem_size This determines how much file stem to assume exists before attempting
to create directories:
stem_size = 0 create no directories
stem_size = 1 create all directories needed (i.e. only assume root directory exists).
stem_size = 2 Assume 1 directory below the root directory exists
stem_size = -1 Assume all but one direcory exists
stem_size = -2 Assume all but two directories exist.
*/
asynStatus NDPluginFile::createDirectoryPath( char *createPath, int stem_size )
{
asynStatus result = asynSuccess;
char * parts[MAX_PATH_PARTS];
int num_parts;
char directory[MAX_FILENAME_LEN];

// Initialise the directory to create
char nextDir[MAX_FILENAME_LEN];

// Extract the next name from the directory
char* saveptr;
int i=0;

// Check for trivial case.
if (stem_size == 0) return asynSuccess;

// Check for Windows disk designator
if ( createPath[1] == ':' )
{
nextDir[0]=createPath[0];
nextDir[1]=':';
i+=2;
}

// Skip over any more delimiters
while ( (createPath[i] == '/' || createPath[i] == '\\') && i < MAX_FILENAME_LEN)
{
nextDir[i] = createPath[i];
++i;
}
nextDir[i] = 0;

// Now, tokenise the path - first making a copy because strtok is destructive
strcpy( directory, &createPath[i] );
num_parts = 0;
parts[num_parts] = strtok_r( directory, "\\/",&saveptr);
while ( parts[num_parts] != NULL ) {
parts[++num_parts] = strtok_r(NULL, "\\/",&saveptr);
}

// Handle the case if the stem size is negative
if (stem_size < 0)
{
stem_size = num_parts + stem_size;
if (stem_size < 1) stem_size = 1;
}

// Loop through parts creating dirctories
for ( i = 1; i < num_parts && result != asynError; i++ )
{
strcat(nextDir, parts[i-1]);

if ( i >= stem_size )
{
if(mkdir(nextDir, 0777) != 0 && errno != EEXIST)
{
result = asynError;
}
}
strcat(nextDir, delim);
}

return result;
}

/** Base method for opening a file
* Creates the file name with NDPluginBase::createFileName, then calls the pure virtual function openFile
* in the derived class. */
Expand Down Expand Up @@ -58,6 +152,11 @@ asynStatus NDPluginFile::openFileBase(NDFileOpenMode_t openMode, NDArray *pArray
return(status);
}
setStringParam(NDFullFileName, fullFileName);

int createDirectory;
getIntegerParam( NDFileCreateDir, &createDirectory );
if (createDirectory)
status = createDirectoryPath( fullFileName, createDirectory );

getStringParam(NDFileTempSuffix, sizeof(tempSuffix), tempSuffix);
if ( *tempSuffix != 0 &&
Expand Down Expand Up @@ -649,8 +748,9 @@ bool NDPluginFile::isFrameValid(NDArray *pArray)
// Check frame size in X and Y dimensions
if ((initInfo->xSize != info.xSize) || (initInfo->ySize != info.ySize)) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"NDPluginFile::isFrameValid: WARNING: Frame dimensions have changed X:%ld,%ld Y:%ld,%ld]\n",
(long)initInfo->xSize, (long)info.xSize, (long)initInfo->ySize, (long)info.ySize);
"NDPluginFile::isFrameValid: WARNING: Frame dimensions have changed X:%lu,%lu Y:%lu,%lu]\n",
(unsigned long)initInfo->xSize, (unsigned long)info.xSize,
(unsigned long)initInfo->ySize, (unsigned long)info.ySize);
valid = false;
}

Expand Down
3 changes: 3 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,14 @@ R2-2 (January XXX, 2015)
constraints no longer apply.

### NDPluginFile
* Created the NDFileCreateDir parameter. This allows file writers to create a controlled number
of directories in the path of the output file.
* Added the NDFileTempSuffix string parameter. When this string is not empty it will be
used as a temporary suffix on the output filename while the file is being written. When writing
is complete, the file is closed and then renamed to have the suffix removed. The rename operation
is atomic from a filesystem view and can be used by monitoring applications like rsync or inotify
to kick off processing applications.
>>>>>>> master

### NDFileHDF5
* Created separated NDFileHDF5.h so class can be exported to other applications.
Expand Down
25 changes: 25 additions & 0 deletions documentation/areaDetectorDoc.html
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,31 @@ <h3 id="asynNDArrayDriver">
bo<br />
bi</td>
</tr>
<tr>
<td>
NDFileCreateDir</td>
<td>
asynInt32</td>
<td>
r/w</td>
<td>
This parameter is used to automatically create directories if they don't exist.
If it is zero (default), no directories are created. If it is negative, then the
absolute value is the maximum of directories that will be created (i.e. -1 will
create a maximum of one directory to complete the path, -2 will create a maximum
of 2 directories). If it is positive, then at least that many directories in the
path must exist (i.e. a value of 1 will create all directories below the root
directory and 2 will not create a directory in the root directory).
</td>
<td>
CREATE_DIR</td>
<td>
$(P)$(R)CreateDirectory<br />
$(P)$(R)CreateDirectory_RBV</td>
<td>
longout<br />
longin</td>
</tr>
<tr>
<td>
NDFileTempSuffix</td>
Expand Down