185
// in case opt_bitPreserving is set, do some other things
186
/* if( opt_bitPreserving )
188
// we need to set outputFileNameArray and outputFileNameArrayCnt to be
189
// able to perform the placeholder substitution in executeOnReception()
190
outputFileNameArray.push_back(OFStandard::getFilenameFromPath(tmpStr, cbdata->imageFileName));
199
// determine if the association shall be aborted
200
if( (opt_abortDuringStore && progress->state != DIMSE_StoreBegin) ||
201
(opt_abortAfterStore && progress->state == DIMSE_StoreEnd) )
203
OFLOG_INFO(storescpLogger, "ABORT initiated (due to command line options)");
204
ASC_abortAssociation((OFstatic_cast(StoreCallbackData*, callbackData))->assoc);
205
rsp->DimseStatus = STATUS_STORE_Refused_OutOfResources;
209
// if opt_sleepAfter is set, the user requires that the application shall
210
// sleep a certain amount of seconds after having received one PDU.
211
if (opt_sleepDuring > 0)
213
OFStandard::sleep(OFstatic_cast(unsigned int, opt_sleepDuring));
216
// dump some information if required (depending on the progress state)
217
// We can't use oflog for the pdu output, but we use a special logger for
218
// generating this output. If it is set to level "INFO" we generate the
219
// output, if it's set to "DEBUG" then we'll assume that there is debug output
220
// generated for each PDU elsewhere.
221
OFLogger progressLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION ".progress");
222
if (progressLogger.getChainedLogLevel() == OFLogger::INFO_LOG_LEVEL)
224
switch (progress->state)
226
case DIMSE_StoreBegin:
239
// if this is the final call of this function, save the data which was received to a file
240
// (note that we could also save the image somewhere else, put it in database, etc.)
241
if (progress->state == DIMSE_StoreEnd)
245
// do not send status detail information
246
*statusDetail = NULL;
248
// remember callback data
249
StoreCallbackData *cbdata = OFstatic_cast(StoreCallbackData *, callbackData);
251
// Concerning the following line: an appropriate status code is already set in the resp structure,
252
// it need not be success. For example, if the caller has already detected an out of resources problem
253
// then the status will reflect this. The callback function is still called to allow cleanup.
254
//rsp->DimseStatus = STATUS_Success;
256
// we want to write the received information to a file only if this information
257
// is present and the options opt_bitPreserving and opt_ignore are not set.
258
if ((imageDataSet != NULL) && (*imageDataSet != NULL) && !opt_bitPreserving && !opt_ignore)
262
// in case one of the --sort-xxx options is set, we need to perform some particular steps to
263
// determine the actual name of the output file
264
if (opt_sortStudyMode != ESM_None)
266
// determine the study instance UID in the (current) DICOM object that has just been received
267
OFString currentStudyInstanceUID;
268
if ((*imageDataSet)->findAndGetOFString(DCM_StudyInstanceUID, currentStudyInstanceUID).bad() || currentStudyInstanceUID.empty())
270
OFLOG_ERROR(storescpLogger, "element StudyInstanceUID " << DCM_StudyInstanceUID << " absent or empty in data set");
271
rsp->DimseStatus = STATUS_STORE_Error_CannotUnderstand;
275
// if --sort-on-patientname is active, we need to extract the
276
// patient's name (format: last_name^first_name)
277
OFString currentPatientName;
278
if (opt_sortStudyMode == ESM_PatientName)
281
if ((*imageDataSet)->findAndGetOFString(DCM_PatientName, tmpName).bad() || tmpName.empty())
283
// default if patient name is missing or empty
284
tmpName = "ANONYMOUS";
285
OFLOG_WARN(storescpLogger, "element PatientName " << DCM_PatientName << " absent or empty in data set, using '"
286
<< tmpName << "' instead");
289
// substitute non-ASCII characters in patient name to ASCII "equivalent"
290
const size_t length = tmpName.length();
291
for (size_t i = 0; i < length; i++)
292
mapCharacterAndAppendToString(tmpName[i], currentPatientName);
295
// if this is the first DICOM object that was received or if the study instance UID in the
296
// current DICOM object does not equal the last object's study instance UID we need to create
297
// a new subdirectory in which the current DICOM object will be stored
298
if (lastStudyInstanceUID.empty() || (lastStudyInstanceUID != currentStudyInstanceUID))
300
// if lastStudyInstanceUID is non-empty, we have just completed receiving all objects for one
301
// study. In such a case, we need to set a certain indicator variable (lastStudySubdirectoryPathAndName),
302
// so that we know that executeOnEndOfStudy() might have to be executed later. In detail, this indicator
303
// variable will contain the path and name of the last study's subdirectory, so that we can still remember
304
// this directory, when we execute executeOnEndOfStudy(). The memory that is allocated for this variable
305
// here will be freed after the execution of executeOnEndOfStudy().
306
if (!lastStudyInstanceUID.empty())
307
lastStudySubdirectoryPathAndName = subdirectoryPathAndName;
309
// create the new lastStudyInstanceUID value according to the value in the current DICOM object
310
lastStudyInstanceUID = currentStudyInstanceUID;
312
// get the current time (needed for subdirectory name)
314
dateTime.setCurrentDateTime();
316
// create a name for the new subdirectory.
318
sprintf(timestamp, "%04u%02u%02u_%02u%02u%02u%03u",
319
dateTime.getDate().getYear(), dateTime.getDate().getMonth(), dateTime.getDate().getDay(),
320
dateTime.getTime().getHour(), dateTime.getTime().getMinute(), dateTime.getTime().getIntSecond(), dateTime.getTime().getMilliSecond());
322
OFString subdirectoryName;
323
switch (opt_sortStudyMode)
326
// pattern: "[prefix]_[YYYYMMDD]_[HHMMSSMMM]"
327
subdirectoryName = opt_sortStudyDirPrefix;
328
if (!subdirectoryName.empty())
329
subdirectoryName += '_';
330
subdirectoryName += timestamp;
332
case ESM_StudyInstanceUID:
333
// pattern: "[prefix]_[Study Instance UID]"
334
subdirectoryName = opt_sortStudyDirPrefix;
335
if (!subdirectoryName.empty())
336
subdirectoryName += '_';
337
subdirectoryName += currentStudyInstanceUID;
339
case ESM_PatientName:
340
// pattern: "[Patient's Name]_[YYYYMMDD]_[HHMMSSMMM]"
341
subdirectoryName = currentPatientName;
342
subdirectoryName += '_';
343
subdirectoryName += timestamp;
349
// create subdirectoryPathAndName (string with full path to new subdirectory)
350
OFStandard::combineDirAndFilename(subdirectoryPathAndName, OFStandard::getDirNameFromPath(tmpStr, cbdata->imageFileName), subdirectoryName);
352
// check if the subdirectory already exists
353
// if it already exists dump a warning
354
if( OFStandard::dirExists(subdirectoryPathAndName) )
355
OFLOG_WARN(storescpLogger, "subdirectory for study already exists: " << subdirectoryPathAndName);
358
// if it does not exist create it
359
OFLOG_INFO(storescpLogger, "creating new subdirectory for study: " << subdirectoryPathAndName);
360
#ifdef HAVE_WINDOWS_H
361
if( _mkdir( subdirectoryPathAndName.c_str() ) == -1 )
363
if( mkdir( subdirectoryPathAndName.c_str(), S_IRWXU | S_IRWXG | S_IRWXO ) == -1 )
366
OFLOG_ERROR(storescpLogger, "could not create subdirectory for study: " << subdirectoryPathAndName);
367
rsp->DimseStatus = STATUS_STORE_Error_CannotUnderstand;
370
// all objects of a study have been received, so a new subdirectory is started.
371
// ->timename counter can be reset, because the next filename can't cause a duplicate.
372
// if no reset would be done, files of a new study (->new directory) would start with a counter in filename
374
timeNameCounter = -1;
378
// integrate subdirectory name into file name (note that cbdata->imageFileName currently contains both
379
// path and file name; however, the path refers to the output directory captured in opt_outputDirectory)
380
OFStandard::combineDirAndFilename(fileName, subdirectoryPathAndName, OFStandard::getFilenameFromPath(tmpStr, cbdata->imageFileName));
382
// update global variable outputFileNameArray
383
// (might be used in executeOnReception() and renameOnEndOfStudy)
384
outputFileNameArray.push_back(tmpStr);
386
// if no --sort-xxx option is set, the determination of the output file name is simple
389
fileName = cbdata->imageFileName;
391
// update global variables outputFileNameArray
392
// (might be used in executeOnReception() and renameOnEndOfStudy)
393
outputFileNameArray.push_back(OFStandard::getFilenameFromPath(tmpStr, fileName));
396
// determine the transfer syntax which shall be used to write the information to the file
397
E_TransferSyntax xfer = opt_writeTransferSyntax;
398
if (xfer == EXS_Unknown) xfer = (*imageDataSet)->getOriginalXfer();
400
// store file either with meta header or as pure dataset
401
OFLOG_INFO(storescpLogger, "storing DICOM file: " << fileName);
402
if (OFStandard::fileExists(fileName))
404
OFLOG_WARN(storescpLogger, "DICOM file already exists, overwriting: " << fileName);
406
OFCondition cond = cbdata->dcmff->saveFile(fileName.c_str(), xfer, opt_sequenceType, opt_groupLength,
407
opt_paddingType, OFstatic_cast(Uint32, opt_filepad), OFstatic_cast(Uint32, opt_itempad),
408
(opt_useMetaheader) ? EWM_fileformat : EWM_dataset);
411
OFLOG_ERROR(storescpLogger, "cannot write DICOM file: " << fileName << ": " << cond.text());
412
rsp->DimseStatus = STATUS_STORE_Refused_OutOfResources;
415
// check the image to make sure it is consistent, i.e. that its sopClass and sopInstance correspond
416
// to those mentioned in the request. If not, set the status in the response message variable.
417
if ((rsp->DimseStatus == STATUS_Success)&&(!opt_ignore))
419
// which SOP class and SOP instance ?
420
if (!DU_findSOPClassAndInstanceInDataSet(*imageDataSet, sopClass, sopInstance, opt_correctUIDPadding))
422
OFLOG_ERROR(storescpLogger, "bad DICOM file: " << fileName);
423
rsp->DimseStatus = STATUS_STORE_Error_CannotUnderstand;
425
else if (strcmp(sopClass, req->AffectedSOPClassUID) != 0)
427
rsp->DimseStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
429
else if (strcmp(sopInstance, req->AffectedSOPInstanceUID) != 0)
431
rsp->DimseStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
436
// in case opt_bitPreserving is set, do some other things
437
if( opt_bitPreserving )
439
// we need to set outputFileNameArray and outputFileNameArrayCnt to be
440
// able to perform the placeholder substitution in executeOnReception()
441
outputFileNameArray.push_back(OFStandard::getFilenameFromPath(tmpStr, cbdata->imageFileName));
448
188
GADAPI::PACS::IncomingDicomAssociationCommandParams::IncomingDicomAssociationCommandParams(T_ASC_Association* assoc, unsigned long rcvTimeout) {
449
189
m_pAssoc = assoc;
450
190
m_rcvTimeout = rcvTimeout;
193
GADAPI::PACS::IncomingDicomAssociationCommandParams::~IncomingDicomAssociationCommandParams()
195
if (m_pAssoc != NULL) {
196
CONDITION cond = ASC_dropSCPAssociation(m_pAssoc);
201
LOG_FATAL("IncomingDicomAssociationCommandParams", DimseCondition::dump(temp_str, cond).c_str());
204
cond = ASC_destroyAssociation(&m_pAssoc);
207
LOG_FATAL("IncomingDicomAssociationCommandParams", DimseCondition::dump(temp_str, cond).c_str());
453
212
//-----------------------------------------------------------------------------------------------
454
213
//-----------------------------------------------------------------------------------------------