how can use dcmqrscp to receive data to a specified direction?

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
zagwin
Posts: 22
Joined: Wed, 2011-03-09, 03:13

how can use dcmqrscp to receive data to a specified direction?

#1 Post by zagwin »

Hi,

I am using dcmqrscp as a mini database. Whenever it receives data from a
remote computer which sending data using storescu.exe, it will store all data
(slice) to the root direction of the database, that is, all slices from different studies located in the same direction.

Can I store different cases to different sub-directions? Thanks a lot
Matt

J. Riesmeier
DCMTK Developer
Posts: 2498
Joined: Tue, 2011-05-03, 14:38
Location: Oldenburg, Germany
Contact:

#2 Post by J. Riesmeier »

Yes, but this requires changes to the source code of the dcmqrdb module. The storescp tool already supports the creation of sub-directories (see documentation).

zagwin
Posts: 22
Joined: Wed, 2011-03-09, 03:13

#3 Post by zagwin »

First, thanks foryour reply.
Yes, but this requires changes to the source code of the dcmqrdb module.
I have read the source code for a long time, however, I cannot find where I should modify. It is too complex.

Can you give me more detail suggestions? such as, which file(s) I should modify?

Thanks a lot!
Matt

J. Riesmeier
DCMTK Developer
Posts: 2498
Joined: Tue, 2011-05-03, 14:38
Location: Oldenburg, Germany
Contact:

#4 Post by J. Riesmeier »

I searched for "saveFile" and found DcmQueryRetrieveStoreContext::writeToFile() in file "dcmqrdb/libsrc/dcmqrcbs.cc". From there go back to find the right place for your changes ...

zagwin
Posts: 22
Joined: Wed, 2011-03-09, 03:13

#5 Post by zagwin »

Thanks very much, I will try, and I will report what I find.
J. Riesmeier wrote:I searched for "saveFile" and found DcmQueryRetrieveStoreContext::writeToFile() in file "dcmqrdb/libsrc/dcmqrcbs.cc". From there go back to find the right place for your changes ...
Matt

Xiao
Posts: 36
Joined: Wed, 2011-05-25, 07:53

#6 Post by Xiao »

I modified dcmqrdbi.cc, dcmqrsrv.cc, dcmqrdba.h, dcmqrdbi.h to classify the received image files according to studyinstanceUID.
I tested and it seems ok.

add function:
OFCondition DcmQueryRetrieveIndexDatabaseHandle::makeNewStoreFileName(
const char *SOPClassUID,
const char * /* SOPInstanceUID */ ,
const char *StudyInstanceUID,
char *newImageFileName)
{

OFString filename;
char prefix[12];

const char *m = dcmSOPClassUIDToModality(SOPClassUID);
if (m==NULL) m = "XX";
sprintf(prefix, "%s_", m);
// unsigned int seed = fnamecreator.hashString(SOPInstanceUID);
unsigned int seed = (unsigned int)time(NULL);
newImageFileName[0]=0; // return empty string in case of error
//add by xiaohk 20110907
OFString subdirectoryName = handle_->storageArea;
subdirectoryName += PATH_SEPARATOR;
subdirectoryName += StudyInstanceUID;
// check if the subdirectory already exists
// if it already exists dump a warning
if( OFStandard::dirExists(subdirectoryName) )
COUT << "subdirectory for study already exists: " << subdirectoryName;
else
{
// if it does not exist create it
COUT << "creating new subdirectory for study: " << subdirectoryName;
#ifdef HAVE_WINDOWS_H
if( _mkdir( subdirectoryName.c_str() ) == -1 )
#else
if( mkdir( subdirectoryName.c_str(), S_IRWXU | S_IRWXG | S_IRWXO ) == -1 )
#endif
{
COUT << "could not create subdirectory for study: " << subdirectoryName;
return DcmQRIndexDatabaseError;
}
}

if (! fnamecreator.makeFilename(seed, subdirectoryName.c_str(), prefix, ".dcm", filename)) return DcmQRIndexDatabaseError;
//end

strcpy(newImageFileName, filename.c_str());
return EC_Normal;
}

modified function:
static DcmQueryRetrieveDatabaseHandle *db_Handle = NULL;
struct StoreCallbackData
{
char* imageFileName;
DcmFileFormat* dcmff;
T_ASC_Association* assoc;
};

sstatic void storeCallback(
/* in */
void *callbackData,
T_DIMSE_StoreProgress *progress, /* progress state */
T_DIMSE_C_StoreRQ *req, /* original store request */
char *imageFileName, /* being received into */
DcmDataset **imageDataSet, /* being received into */
/* out */
T_DIMSE_C_StoreRSP *rsp, /* final store response */
DcmDataset **stDetail)
{
//add by xiaohk 20110907
DIC_UI sopClass;
DIC_UI sopInstance;
OFString lastStudyInstanceUID;
OFString subdirectoryPathAndName;
OFString lastStudySubdirectoryPathAndName;

// if this is the final call of this function, save the data which was received to a file
// (note that we could also save the image somewhere else, put it in database, etc.)
if (progress->state == DIMSE_StoreEnd)
{
OFString tmpStr;

// do not send status detail information
*stDetail = NULL;

// remember callback data
StoreCallbackData *cbdata = OFstatic_cast(StoreCallbackData *, callbackData);

// Concerning the following line: an appropriate status code is already set in the resp structure,
// it need not be success. For example, if the caller has already detected an out of resources problem
// then the status will reflect this. The callback function is still called to allow cleanup.
//rsp->DimseStatus = STATUS_Success;

// we want to write the received information to a file only if this information
// is present and the options opt_bitPreserving and opt_ignore are not set.
if ((imageDataSet != NULL) && (*imageDataSet != NULL))
{
OFString fileName;

// in case one of the --sort-xxx options is set, we need to perform some particular steps to
// determine the actual name of the output file
{
// determine the study instance UID in the (current) DICOM object that has just been received
OFString currentStudyInstanceUID;
if ((*imageDataSet)->findAndGetOFString(DCM_StudyInstanceUID, currentStudyInstanceUID).bad() || currentStudyInstanceUID.empty())
{
COUT << "element StudyInstanceUID " << DCM_StudyInstanceUID << " absent or empty in data set";
rsp->DimseStatus = STATUS_STORE_Error_CannotUnderstand;
return;
}

// if this is the first DICOM object that was received or if the study instance UID in the
// current DICOM object does not equal the last object's study instance UID we need to create
// a new subdirectory in which the current DICOM object will be stored
if (lastStudyInstanceUID.empty() || (lastStudyInstanceUID != currentStudyInstanceUID))
{
// if lastStudyInstanceUID is non-empty, we have just completed receiving all objects for one
// study. In such a case, we need to set a certain indicator variable (lastStudySubdirectoryPathAndName),
// so that we know that executeOnEndOfStudy() might have to be executed later. In detail, this indicator
// variable will contain the path and name of the last study's subdirectory, so that we can still remember
// this directory, when we execute executeOnEndOfStudy(). The memory that is allocated for this variable
// here will be freed after the execution of executeOnEndOfStudy().
if (!lastStudyInstanceUID.empty())
lastStudySubdirectoryPathAndName = subdirectoryPathAndName;

// create the new lastStudyInstanceUID value according to the value in the current DICOM object
lastStudyInstanceUID = currentStudyInstanceUID;
OFCondition dbcond = EC_Normal;
char file[512];
dbcond = db_Handle->makeNewStoreFileName(
req->AffectedSOPClassUID,
req->AffectedSOPInstanceUID,
currentStudyInstanceUID.c_str(),
file);
if (dbcond.bad()) {
DCMQRDB_ERROR("storeSCP: Database: makeNewStoreFileName Failed");
/* must still receive data */
strcpy(file, NULL_DEVICE_NAME);
/* callback will send back out of resources status */
}
fileName = file;
imageFileName = file;
}
}
}
}
//end
DcmQueryRetrieveStoreContext *context = OFstatic_cast(DcmQueryRetrieveStoreContext *, callbackData);
context->setFileName(imageFileName);
context->callbackHandler(progress, req, imageFileName, imageDataSet, rsp, stDetail);
}

OFCondition DcmQueryRetrieveSCP::storeSCP(T_ASC_Association * assoc, T_DIMSE_C_StoreRQ * request,
T_ASC_PresentationContextID presId,
DcmQueryRetrieveDatabaseHandle& dbHandle,
OFBool correctUIDPadding)
{
OFCondition cond = EC_Normal;
OFCondition dbcond = EC_Normal;
char imageFileName[MAXPATHLEN+1];
DcmFileFormat dcmff;

db_Handle = &dbHandle;
DcmQueryRetrieveStoreContext context(dbHandle, options_, STATUS_Success, &dcmff, correctUIDPadding);

OFString temp_str;
DCMQRDB_INFO("Received Store SCP:" << OFendl << DIMSE_dumpMessage(temp_str, *request, DIMSE_INCOMING));

if (!dcmIsaStorageSOPClassUID(request->AffectedSOPClassUID)) {
/* callback will send back sop class not supported status */
context.setStatus(STATUS_STORE_Refused_SOPClassNotSupported);
/* must still receive data */
strcpy(imageFileName, NULL_DEVICE_NAME);
} else if (options_.ignoreStoreData_) {
strcpy(imageFileName, NULL_DEVICE_NAME);
}
#if 0 //delete by xiaohk 20110907
else {
dbcond = dbHandle.makeNewStoreFileName(
request->AffectedSOPClassUID,
request->AffectedSOPInstanceUID,
imageFileName);
if (dbcond.bad()) {
DCMQRDB_ERROR("storeSCP: Database: makeNewStoreFileName Failed");
/* must still receive data */
strcpy(imageFileName, NULL_DEVICE_NAME);
/* callback will send back out of resources status */
context.setStatus(STATUS_STORE_Refused_OutOfResources);
}
}

#ifdef LOCK_IMAGE_FILES
/* exclusively lock image file */
#ifdef O_BINARY
int lockfd = open(imageFileName, (O_WRONLY | O_CREAT | O_TRUNC | O_BINARY), 0666);
#else
int lockfd = open(imageFileName, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
#endif
if (lockfd < 0)
{
DCMQRDB_ERROR("storeSCP: file locking failed, cannot create file");

/* must still receive data */
strcpy(imageFileName, NULL_DEVICE_NAME);

/* callback will send back out of resources status */
context.setStatus(STATUS_STORE_Refused_OutOfResources);
}
else
dcmtk_flock(lockfd, LOCK_EX);
#endif

context.setFileName(imageFileName);
#endif //delete by xiaohk 20110907

// store SourceApplicationEntityTitle in metaheader
if (assoc && assoc->params)
{
const char *aet = assoc->params->DULparams.callingAPTitle;
if (aet) dcmff.getMetaInfo()->putAndInsertString(DCM_SourceApplicationEntityTitle, aet);
}

DcmDataset *dset = dcmff.getDataset();

/* we must still retrieve the data set even if some error has occured */

if (options_.bitPreserving_) { /* the bypass option can be set on the command line */
cond = DIMSE_storeProvider(assoc, presId, request, imageFileName, (int)options_.useMetaheader_,
NULL, storeCallback,
(void*)&context, options_.blockMode_, options_.dimse_timeout_);
} else {
cond = DIMSE_storeProvider(assoc, presId, request, (char *)NULL, (int)options_.useMetaheader_,
&dset, storeCallback,
(void*)&context, options_.blockMode_, options_.dimse_timeout_);
}

if (cond.bad()) {
DCMQRDB_ERROR("Store SCP Failed: " << DimseCondition::dump(temp_str, cond));
}
if (!options_.ignoreStoreData_ && (cond.bad() || (context.getStatus() != STATUS_Success)))
{
/* remove file */
if (strcmp(imageFileName, NULL_DEVICE_NAME) != 0) // don't try to delete /dev/null
{
DCMQRDB_INFO("Store SCP: Deleting Image File: %s" << imageFileName);
unlink(imageFileName);
}
dbHandle.pruneInvalidRecords();
}

#if 0 //delete by xiaohk 20110907
#ifdef LOCK_IMAGE_FILES
/* unlock image file */
if (lockfd >= 0)
{
dcmtk_flock(lockfd, LOCK_UN);
close(lockfd);
}
#endif
#endif

return cond;
}
Last edited by Xiao on Wed, 2011-09-07, 07:09, edited 2 times in total.

Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 1 guest