820 lines
22 KiB
C++
820 lines
22 KiB
C++
#include "dcm_store.h"
|
|
|
|
//----------------------------------------------------------------------------------------------------
|
|
// dcm_cstore_callback
|
|
|
|
dcm_cstore_callback::dcm_cstore_callback()
|
|
{
|
|
}
|
|
|
|
dcm_cstore_callback::~dcm_cstore_callback()
|
|
{
|
|
}
|
|
|
|
////dcm_cstore_callback::dcm_cstore_callback(const dcm_cstore_callback& other)
|
|
////{
|
|
////}
|
|
////
|
|
////dcm_cstore_callback& dcm_cstore_callback::operator=(const dcm_cstore_callback& other)
|
|
////{
|
|
////}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------
|
|
// static function
|
|
|
|
static void progressCallback(void *callbackData, T_DIMSE_StoreProgress *progress, T_DIMSE_C_StoreRQ *request)
|
|
{
|
|
dcm_cstore_callback *callback = reinterpret_cast<dcm_cstore_callback *>(callbackData);
|
|
if (callback)
|
|
callback->callback(progress, request);
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------------------
|
|
// dcm-cstore
|
|
|
|
dcm_cstore::dcm_cstore(const char *peerIp, unsigned long peerPort, const char *peerTitle, const char *ourTitle)
|
|
: abortAssociation(false)
|
|
, maxReceivedPDULength(ASC_DEFAULTMAXPDU)
|
|
, maxSendPDULength(ASC_DEFAULTMAXPDU)
|
|
, networkTransferSyntax(EXS_Unknown)
|
|
, readMode(ERM_autoDetect)
|
|
, scanDir(true)
|
|
, recurse(true)
|
|
, scanPattern("")
|
|
, haltOnUnsuccessfulStore(true)
|
|
, unsuccessfulStoreEncountered(false)
|
|
, lastStatusCode(STATUS_Success)
|
|
, proposeOnlyRequiredPresentationContexts(false)
|
|
, combineProposedTransferSyntaxes(false)
|
|
, repeatCount(1)
|
|
, inventPatientCount(25)
|
|
, inventStudyCount(50)
|
|
, inventSeriesCount(100)
|
|
, inventSOPInstanceInformation(false)
|
|
, correctUIDPadding(false)
|
|
, patientNamePrefix("OFFIS^TEST_PN_")
|
|
, patientIDPrefix("PID_")
|
|
, studyIDPrefix("SID_")
|
|
, accessionNumberPrefix("")
|
|
, configFile(NULL)
|
|
, profileName(NULL)
|
|
, blockMode(DIMSE_BLOCKING)
|
|
, dimse_tiemout(0)
|
|
, acse_tiemout(30)
|
|
, socket_tiemout(60)
|
|
, identMode(ASC_USER_IDENTITY_NONE)
|
|
, user("")
|
|
, password("")
|
|
, identFile_("")
|
|
, identResponse(false)
|
|
, cstore_callback(nullptr)
|
|
, patientCounter(0)
|
|
, studyCounter(0)
|
|
, seriesCounter(0)
|
|
, imageCounter(0)
|
|
{
|
|
peerIp_ = peerIp;
|
|
peerPort_ = peerPort;
|
|
peerTitle_ = peerTitle;
|
|
ourTitle_ = ourTitle;
|
|
}
|
|
|
|
dcm_cstore::~dcm_cstore()
|
|
{
|
|
}
|
|
|
|
void dcm_cstore::set_dimse_tiemout(int timeout)
|
|
{
|
|
dimse_tiemout = timeout;
|
|
}
|
|
|
|
void dcm_cstore::set_acse_tiemout(int timeout)
|
|
{
|
|
acse_tiemout = timeout;
|
|
}
|
|
|
|
void dcm_cstore::set_ma_received_pdu_length(unsigned long length)
|
|
{
|
|
maxReceivedPDULength = length;
|
|
}
|
|
|
|
void dcm_cstore::set_max_send_pdu_length(unsigned long length)
|
|
{
|
|
maxSendPDULength = length;
|
|
}
|
|
|
|
void dcm_cstore::set_store_callback(dcm_cstore_callback *callback)
|
|
{
|
|
cstore_callback = callback;
|
|
}
|
|
|
|
int dcm_cstore::secondsSince1970()
|
|
{
|
|
time_t t = time(NULL);
|
|
return static_cast<int>(t);
|
|
}
|
|
|
|
std::string dcm_cstore::intToString(int i)
|
|
{
|
|
char numbuf[32];
|
|
sprintf(numbuf, "%d", i);
|
|
return numbuf;
|
|
}
|
|
|
|
std::string dcm_cstore::makeUID(std::string basePrefix, int counter)
|
|
{
|
|
std::string prefix = basePrefix + "." + intToString(counter);
|
|
char uidbuf[65];
|
|
std::string uid = dcmGenerateUniqueIdentifier(uidbuf, prefix.c_str());
|
|
return uid;
|
|
}
|
|
|
|
bool dcm_cstore::updateStringAttributeValue(DcmItem *dataset, const DcmTagKey &key, std::string &value)
|
|
{
|
|
DcmStack stack;
|
|
DcmTag tag(key);
|
|
|
|
OFCondition cond = EC_Normal;
|
|
cond = dataset->search(key, stack, ESM_fromHere, false);
|
|
if (cond != EC_Normal)
|
|
{
|
|
// log >> "updateStringAttributeValue : cannot find
|
|
return false;
|
|
}
|
|
|
|
DcmElement *elem = static_cast<DcmElement *>(stack.top());
|
|
DcmVR vr(elem->ident());
|
|
if (elem->getLength() > vr.getMaxValueLength())
|
|
{
|
|
// log >> undateStringAttributeValue : INTERNAL ERROR
|
|
return false;
|
|
}
|
|
|
|
cond = elem->putOFStringArray(value);
|
|
if (cond != EC_Normal)
|
|
{
|
|
// log >> updateStringAttributeValue : cannot put string in attribute
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void dcm_cstore::replaceSOPInstanceInformation(DcmDataset *dataset)
|
|
{
|
|
std::string seriesInstanceUID;
|
|
std::string seriesNumber;
|
|
std::string studyInstanceUID;
|
|
std::string studyID;
|
|
std::string accessionNumber;
|
|
std::string patientID;
|
|
std::string patientName;
|
|
|
|
if (seriesInstanceUID.empty())
|
|
seriesInstanceUID = makeUID(SITE_SERIES_UID_ROOT, static_cast<int>(seriesCounter));
|
|
if (seriesNumber.empty())
|
|
seriesNumber = intToString(static_cast<int>(seriesCounter));
|
|
if (studyInstanceUID.empty())
|
|
studyInstanceUID = makeUID(SITE_STUDY_UID_ROOT, static_cast<int>(studyCounter));
|
|
if (studyID.empty())
|
|
studyID = studyIDPrefix + intToString(static_cast<int>(secondsSince1970())) + intToString(static_cast<int>(studyCounter));
|
|
if (accessionNumber.empty())
|
|
accessionNumber = accessionNumberPrefix + intToString(secondsSince1970()) + intToString(static_cast<int>(studyCounter));
|
|
if (patientID.empty())
|
|
patientID = patientIDPrefix + intToString(secondsSince1970()) + intToString(static_cast<int>(patientCounter));
|
|
if (patientName.empty())
|
|
patientName = patientNamePrefix + intToString(secondsSince1970()) + intToString(static_cast<int>(patientCounter));
|
|
|
|
if (imageCounter >= inventSeriesCount)
|
|
{
|
|
imageCounter = 0;
|
|
seriesCounter++;
|
|
seriesInstanceUID = makeUID(SITE_SERIES_UID_ROOT, static_cast<int>(seriesCounter));
|
|
seriesNumber = intToString(static_cast<int>(seriesCounter));
|
|
}
|
|
if (seriesCounter >= inventStudyCount)
|
|
{
|
|
seriesCounter = 0;
|
|
studyCounter++;
|
|
studyInstanceUID = makeUID(SITE_STUDY_UID_ROOT, static_cast<int>(studyCounter));
|
|
studyID = studyIDPrefix + intToString(secondsSince1970()) + intToString(static_cast<int>(studyCounter));
|
|
accessionNumber = accessionNumberPrefix + intToString(secondsSince1970()) + intToString(static_cast<int>(studyCounter));
|
|
}
|
|
if (studyCounter >= inventPatientCount)
|
|
{
|
|
studyCounter = 0;
|
|
patientCounter++;
|
|
patientID = patientIDPrefix + intToString(secondsSince1970()) + intToString(static_cast<int>(patientCounter));
|
|
patientName = patientNamePrefix + intToString(secondsSince1970()) + intToString(static_cast<int>(patientCounter));
|
|
}
|
|
|
|
std::string sopInstanceUID = makeUID(SITE_INSTANCE_UID_ROOT, static_cast<int>(imageCounter));
|
|
std::string imageNumber = intToString(static_cast<int>(imageCounter));
|
|
|
|
updateStringAttributeValue(dataset, DCM_PatientName, patientName);
|
|
updateStringAttributeValue(dataset, DCM_PatientID, patientID);
|
|
updateStringAttributeValue(dataset, DCM_StudyInstanceUID, studyInstanceUID);
|
|
updateStringAttributeValue(dataset, DCM_StudyID, studyID);
|
|
updateStringAttributeValue(dataset, DCM_SeriesInstanceUID, seriesInstanceUID);
|
|
updateStringAttributeValue(dataset, DCM_SeriesNumber, seriesNumber);
|
|
updateStringAttributeValue(dataset, DCM_SOPInstanceUID, sopInstanceUID);
|
|
updateStringAttributeValue(dataset, DCM_InstanceNumber, imageNumber);
|
|
|
|
imageCounter++;
|
|
}
|
|
|
|
OFCondition dcm_cstore::addStoragePresentationContexts(T_ASC_Parameters *params, std::list<std::string> &sopClasses)
|
|
{
|
|
std::string preferredTransferSyntax;
|
|
if (networkTransferSyntax == EXS_Unknown)
|
|
{
|
|
if (gLocalByteOrder == E_ByteOrder::EBO_LittleEndian)
|
|
{
|
|
preferredTransferSyntax = UID_LittleEndianExplicitTransferSyntax;
|
|
}
|
|
else
|
|
{
|
|
preferredTransferSyntax = UID_BigEndianExplicitTransferSyntax;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DcmXfer xfer(networkTransferSyntax);
|
|
preferredTransferSyntax = xfer.getXferID();
|
|
}
|
|
|
|
std::list<std::string>::iterator s_cur;
|
|
std::list<std::string>::iterator s_end;
|
|
|
|
std::list<std::string> fallbackSyntaxes;
|
|
if ((networkTransferSyntax != EXS_LittleEndianImplicit) &&
|
|
(networkTransferSyntax != EXS_MPEG2MainProfileAtMainLevel) &&
|
|
(networkTransferSyntax != EXS_MPEG2MainProfileAtHighLevel) &&
|
|
(networkTransferSyntax != EXS_MPEG4HighProfileLevel4_1) &&
|
|
(networkTransferSyntax != EXS_MPEG4BDcompatibleHighProfileLevel4_1) &&
|
|
(networkTransferSyntax != EXS_MPEG4HighProfileLevel4_2_For2DVideo) &&
|
|
(networkTransferSyntax != EXS_MPEG4HighProfileLevel4_2_For3DVideo) &&
|
|
(networkTransferSyntax != EXS_MPEG4StereoHighProfileLevel4_2) &&
|
|
(networkTransferSyntax != EXS_HEVCMainProfileLevel5_1) &&
|
|
(networkTransferSyntax != EXS_HEVCMain10ProfileLevel5_1))
|
|
{
|
|
fallbackSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
|
|
fallbackSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
|
|
fallbackSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
|
|
|
|
fallbackSyntaxes.remove(preferredTransferSyntax);
|
|
}
|
|
|
|
std::list<std::string> combinedSyntaxes;
|
|
s_cur = fallbackSyntaxes.begin();
|
|
s_end = fallbackSyntaxes.end();
|
|
combinedSyntaxes.push_back(preferredTransferSyntax);
|
|
while (s_cur != s_end)
|
|
{
|
|
if (!isaListMember(combinedSyntaxes, *s_cur))
|
|
{
|
|
combinedSyntaxes.push_back(*s_cur);
|
|
}
|
|
++s_cur;
|
|
}
|
|
|
|
if (!proposeOnlyRequiredPresentationContexts)
|
|
{
|
|
for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs; i++)
|
|
{
|
|
sopClasses.push_back(dcmShortSCUStorageSOPClassUIDs[i]);
|
|
}
|
|
}
|
|
|
|
std::list<std::string> sops;
|
|
s_cur = sopClasses.begin();
|
|
s_end = sopClasses.end();
|
|
while (s_cur != s_end)
|
|
{
|
|
if (!isaListMember(sops, *s_cur))
|
|
{
|
|
sops.push_back(*s_cur);
|
|
}
|
|
++s_cur;
|
|
}
|
|
|
|
OFCondition cond = EC_Normal;
|
|
int pid = 1;
|
|
s_cur = sops.begin();
|
|
s_end = sops.end();
|
|
while (s_cur != s_end && cond.good())
|
|
{
|
|
if (pid > 255)
|
|
{
|
|
// log >> Too many presentation contexts
|
|
return ASC_BADPRESENTATIONCONTEXTID;
|
|
}
|
|
|
|
if (combineProposedTransferSyntaxes)
|
|
{
|
|
addPresentationContext(params, pid, *s_cur, combinedSyntaxes);
|
|
pid += 2;
|
|
}
|
|
else
|
|
{
|
|
cond = addPresentationContext(params, pid, *s_cur, preferredTransferSyntax);
|
|
pid += 2;
|
|
|
|
if (fallbackSyntaxes.size() > 0)
|
|
{
|
|
if (pid > 255)
|
|
{
|
|
// log >> Too many presentation contexes
|
|
return ASC_BADPRESENTATIONCONTEXTID;
|
|
}
|
|
|
|
cond = addPresentationContext(params, pid, *s_cur, fallbackSyntaxes);
|
|
pid += 2;
|
|
}
|
|
}
|
|
++s_cur;
|
|
}
|
|
|
|
return cond;
|
|
}
|
|
|
|
bool dcm_cstore::findSOPClassAndInstanceInFile(const char *fname, char *sopClass, size_t sopClassSize, char *sopInstance, size_t sopInstanceSize)
|
|
{
|
|
DcmFileFormat ff;
|
|
if (!ff.loadFile(fname, EXS_Unknown, EGL_noChange, DCM_MaxReadLength, readMode).good())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool found = DU_findSOPClassAndInstanceInDataSet(ff.getMetaInfo(), sopClass, sopClassSize, sopInstance, sopInstanceSize, correctUIDPadding);
|
|
if (!found)
|
|
found = DU_findSOPClassAndInstanceInDataSet(ff.getDataset(), sopClass, sopClassSize, sopInstance, sopInstanceSize, correctUIDPadding);
|
|
|
|
return found;
|
|
}
|
|
|
|
OFCondition dcm_cstore::configureUserIdentityRequest(T_ASC_Parameters *params)
|
|
{
|
|
OFCondition cond = EC_Normal;
|
|
switch (identMode)
|
|
{
|
|
case ASC_USER_IDENTITY_USER:
|
|
{
|
|
cond = ASC_setIdentRQUserOnly(params, user, identResponse);
|
|
break;
|
|
}
|
|
case ASC_USER_IDENTITY_USER_PASSWORD:
|
|
{
|
|
cond = ASC_setIdentRQUserPassword(params, user, password, identResponse);
|
|
break;
|
|
}
|
|
case ASC_USER_IDENTITY_KERBEROS:
|
|
case ASC_USER_IDENTITY_SAML:
|
|
case ASC_USER_IDENTITY_JWT:
|
|
{
|
|
OFFile identFile;
|
|
if (!identFile.fopen(identFile_.c_str(), "rb"))
|
|
{
|
|
// log >> Unable to open Kerberbose, SAML or JWT file
|
|
return EC_IllegalCall;
|
|
}
|
|
|
|
offile_off_t result = identFile.fseek(0, SEEK_END);
|
|
if (result != 0)
|
|
return EC_IllegalParameter;
|
|
offile_off_t filesize = identFile.ftell();
|
|
if (filesize > 65535)
|
|
{
|
|
// log >> "Kerberos, SAML or JWT file is larger than 65535 bytes, bytes after that position are ignored
|
|
filesize = 65535;
|
|
}
|
|
|
|
char *buf = new char[static_cast<unsigned int>(filesize)];
|
|
size_t bytesRead = identFile.fread(buf, 1, static_cast<size_t>(filesize));
|
|
identFile.fclose();
|
|
if (bytesRead == 0)
|
|
{
|
|
// log >> Unable to read Kerberos, SAML or JWT info file
|
|
delete[] buf;
|
|
return EC_IllegalCall;
|
|
}
|
|
|
|
if (identMode == ASC_USER_IDENTITY_KERBEROS)
|
|
cond = ASC_setIdentRQKerberos(params, buf, static_cast<Uint16>(bytesRead), identResponse);
|
|
else if (identMode == ASC_USER_IDENTITY_SAML)
|
|
cond = ASC_setIdentRQSaml(params, buf, static_cast<Uint16>(bytesRead), identResponse);
|
|
else
|
|
cond = ASC_setIdentRQJwt(params, buf, static_cast<Uint16>(bytesRead), identResponse);
|
|
delete[] buf;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
cond = EC_IllegalCall;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cond.bad())
|
|
{
|
|
// log >> Failed
|
|
}
|
|
|
|
return cond;
|
|
}
|
|
|
|
OFCondition dcm_cstore::checkUserIdentityResponse(T_ASC_Parameters *params)
|
|
{
|
|
if (params == NULL)
|
|
return ASC_NULLKEY;
|
|
if (identMode == ASC_USER_IDENTITY_NONE || !identResponse)
|
|
return EC_Normal;
|
|
if (identMode == ASC_USER_IDENTITY_USER || identMode == ASC_USER_IDENTITY_USER_PASSWORD)
|
|
{
|
|
UserIdentityNegotiationSubItemAC *rsp = params->DULparams.ackUserIdentNeg;
|
|
if (rsp == NULL)
|
|
{
|
|
// log >> User Identity Negotitation failed: Positive response requested but none received
|
|
return ASC_USERIDENTIFICATIONFAILED;
|
|
}
|
|
}
|
|
|
|
return EC_Normal;
|
|
}
|
|
|
|
bool dcm_cstore::isaListMember(std::list<std::string> &lst, std::string &s)
|
|
{
|
|
std::list<std::string>::iterator cur = lst.begin();
|
|
std::list<std::string>::iterator end = lst.end();
|
|
|
|
bool found = false;
|
|
while (cur != end && !found)
|
|
{
|
|
found = (s == *cur);
|
|
++cur;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
OFCondition dcm_cstore::addPresentationContext(T_ASC_Parameters *params, int presentationContextId, const std::string &abstractSyntax, const std::string &transferSyntax, T_ASC_SC_ROLE proposedRole)
|
|
{
|
|
const char *c_p = transferSyntax.c_str();
|
|
OFCondition cond = ASC_addPresentationContext(params, presentationContextId, abstractSyntax.c_str(), &c_p, 1, proposedRole);
|
|
return cond;
|
|
}
|
|
|
|
OFCondition dcm_cstore::addPresentationContext(T_ASC_Parameters *params, int presentationContextId, const std::string &abstracySyntax, const std::list<std::string> &transferSyntaxList, T_ASC_SC_ROLE proposedRole)
|
|
{
|
|
const char **transferSyntaxes = new const char*[transferSyntaxList.size()];
|
|
int transferSyntaxCount = 0;
|
|
|
|
std::list<std::string>::const_iterator s_cur = transferSyntaxList.begin();
|
|
std::list<std::string>::const_iterator s_end = transferSyntaxList.end();
|
|
while (s_cur != s_end)
|
|
{
|
|
transferSyntaxes[transferSyntaxCount++] = (*s_cur).c_str();
|
|
++s_cur;
|
|
}
|
|
|
|
OFCondition cond = ASC_addPresentationContext(params, presentationContextId, abstracySyntax.c_str(), transferSyntaxes, transferSyntaxCount, proposedRole);
|
|
delete[] transferSyntaxes;
|
|
return cond;
|
|
}
|
|
|
|
OFCondition dcm_cstore::storeSCU(T_ASC_Association *assoc, const char *fname)
|
|
{
|
|
DIC_US msgId = assoc->nextMsgID++;
|
|
T_ASC_PresentationContextID presID;
|
|
T_DIMSE_C_StoreRQ req;
|
|
T_DIMSE_C_StoreRSP rsp;
|
|
DIC_UI sopClass;
|
|
DIC_UI sopInstance;
|
|
DcmDataset *statusDetail = NULL;
|
|
unsuccessfulStoreEncountered = true;
|
|
|
|
DcmFileFormat dcmff;
|
|
OFCondition cond = dcmff.loadFile(fname, EXS_Unknown, EGL_noChange, DCM_MaxReadLength, readMode);
|
|
if (cond.bad())
|
|
{
|
|
// log >> Bad DICOM file
|
|
return cond;
|
|
}
|
|
|
|
if (inventSOPInstanceInformation)
|
|
{
|
|
replaceSOPInstanceInformation(dcmff.getDataset());
|
|
}
|
|
|
|
if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sizeof(sopClass), sopInstance, sizeof(sopInstance), correctUIDPadding))
|
|
{
|
|
// log >> No SOP Class or Instance UID in file
|
|
return DIMSE_BADDATA;
|
|
}
|
|
|
|
DcmXfer filexfer(dcmff.getDataset()->getOriginalXfer());
|
|
if (filexfer.isNotEncapsulated() && networkTransferSyntax == EXS_DeflatedLittleEndianExplicit)
|
|
{
|
|
filexfer = EXS_DeflatedLittleEndianExplicit;
|
|
}
|
|
|
|
if (filexfer.getXfer() != EXS_Unknown)
|
|
{
|
|
presID = ASC_findAcceptedPresentationContextID(assoc, sopClass, filexfer.getXferID());
|
|
}
|
|
else
|
|
{
|
|
presID = ASC_findAcceptedPresentationContextID(assoc, sopClass);
|
|
}
|
|
if (presID == 0)
|
|
{
|
|
const char *modalityName = dcmSOPClassUIDToModality(sopClass);
|
|
if (!modalityName)
|
|
modalityName = dcmFindNameOfUID(sopClass);
|
|
if (!modalityName)
|
|
modalityName = "unknown SOP class";
|
|
// log >> No presentation context for :
|
|
return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
|
|
}
|
|
|
|
T_ASC_PresentationContext pc;
|
|
ASC_findAcceptedPresentationContext(assoc->params, presID, &pc);
|
|
DcmXfer netTransfer(pc.acceptedTransferSyntax);
|
|
|
|
bzero(reinterpret_cast<char *>(&req), sizeof(req));
|
|
req.MessageID = msgId;
|
|
OFStandard::strlcpy(req.AffectedSOPClassUID, sopClass, sizeof(req.AffectedSOPClassUID));
|
|
OFStandard::strlcpy(req.AffectedSOPInstanceUID, sopInstance, sizeof(req.AffectedSOPInstanceUID));
|
|
req.DataSetType = DIMSE_DATASET_PRESENT;
|
|
req.Priority = DIMSE_PRIORITY_MEDIUM;
|
|
cond = DIMSE_storeUser(assoc, presID, &req, NULL, dcmff.getDataset(), progressCallback, cstore_callback, blockMode, dimse_tiemout, &rsp, &statusDetail, NULL, static_cast<long>(OFStandard::getFileSize(fname)));
|
|
if (cond == EC_Normal && (rsp.DimseStatus == STATUS_Success || DICOM_WARNING_STATUS(rsp.DimseStatus)))
|
|
{
|
|
unsuccessfulStoreEncountered = false;
|
|
}
|
|
lastStatusCode = rsp.DimseStatus;
|
|
if (cond == EC_Normal)
|
|
{
|
|
// log >> Recevied Store Response
|
|
}
|
|
else
|
|
{
|
|
// log >> Store Failed, file
|
|
}
|
|
|
|
return cond;
|
|
}
|
|
|
|
OFCondition dcm_cstore::cstore(T_ASC_Association *assoc, const std::string &fname)
|
|
{
|
|
OFCondition cond = EC_Normal;
|
|
int n = static_cast<int>(repeatCount);
|
|
while (cond.good() && n-- && !(haltOnUnsuccessfulStore && unsuccessfulStoreEncountered))
|
|
{
|
|
cond = storeSCU(assoc, fname.c_str());
|
|
}
|
|
|
|
if (!haltOnUnsuccessfulStore)
|
|
{
|
|
cond = EC_Normal;
|
|
}
|
|
|
|
return cond;
|
|
}
|
|
|
|
int dcm_cstore::docstore(std::string dir)
|
|
{
|
|
std::list<std::string> fileNameList;
|
|
std::list<std::string> sopClassUIDList;
|
|
std::list<std::string> sopInstanceUIDList;
|
|
|
|
T_ASC_Network * net;
|
|
T_ASC_Parameters * params;
|
|
DIC_NODENAME peerHost;
|
|
T_ASC_Association * assoc;
|
|
DcmAssociationConfiguration asccfg;
|
|
|
|
OFStandard::initializeNetwork();
|
|
if (!dcmDataDict.isDictionaryLoaded())
|
|
{
|
|
// log >> "no data dictionary loaded, check environment variable
|
|
}
|
|
|
|
std::list<std::string> inputFiles;
|
|
OFStandard::searchDirectoryRecursively(dir.c_str(), inputFiles, scanPattern, "", recurse);
|
|
if (inputFiles.empty())
|
|
{
|
|
return e_no_input_file;
|
|
}
|
|
|
|
DcmFileFormat dfile;
|
|
char sopClassUID[128];
|
|
char sopInstanceUID[128];
|
|
bool ignoreName;
|
|
const char * currentFileName = NULL;
|
|
std::list<std::string>::iterator if_iter = inputFiles.begin();
|
|
std::list<std::string>::iterator if_last = inputFiles.end();
|
|
while (if_iter != if_last)
|
|
{
|
|
ignoreName = false;
|
|
currentFileName = (*if_iter).c_str();
|
|
if (OFStandard::fileExists(currentFileName))
|
|
{
|
|
if (proposeOnlyRequiredPresentationContexts)
|
|
{
|
|
if (!findSOPClassAndInstanceInFile(currentFileName, sopClassUID, sizeof(sopClassUID), sopInstanceUID, sizeof(sopInstanceUID)))
|
|
{
|
|
ignoreName = true;
|
|
if (haltOnUnsuccessfulStore)
|
|
{
|
|
// missing SOP class (or instance) in file
|
|
return e_no_presentation_context;
|
|
}
|
|
}
|
|
else if (!dcmIsaStorageSOPClassUID(sopClassUID, ESSC_All))
|
|
{
|
|
ignoreName = true;
|
|
if (haltOnUnsuccessfulStore)
|
|
{
|
|
// unknown storage SOP class in file
|
|
return e_unknown_storage_sopclass;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sopClassUIDList.push_back(sopClassUID);
|
|
sopInstanceUIDList.push_back(sopInstanceUID);
|
|
}
|
|
}
|
|
|
|
if (!ignoreName)
|
|
{
|
|
fileNameList.push_back(currentFileName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (haltOnUnsuccessfulStore)
|
|
{
|
|
// cannot access file
|
|
return e_cannot_access_file;
|
|
}
|
|
}
|
|
++if_iter;
|
|
}
|
|
|
|
OFCondition cond = ASC_initializeNetwork(NET_REQUESTOR, 0, acse_tiemout, &net);
|
|
if (cond.bad())
|
|
{
|
|
// log >> ASC_initializeNetwork Failed
|
|
return cond.code();
|
|
}
|
|
|
|
cond = ASC_createAssociationParameters(¶ms, maxReceivedPDULength);
|
|
if (cond.bad())
|
|
{
|
|
// log >> ASC_createAssociationParameters Failed
|
|
return cond.code();
|
|
}
|
|
|
|
ASC_setAPTitles(params, ourTitle_, peerTitle_, NULL);
|
|
|
|
sprintf(peerHost, "%s:%d", peerIp_, static_cast<int>(peerPort_));
|
|
ASC_setPresentationAddresses(params, OFStandard::getHostName().c_str(), peerHost);
|
|
|
|
if (identMode != ASC_USER_IDENTITY_NONE)
|
|
{
|
|
cond = configureUserIdentityRequest(params);
|
|
if (cond.bad())
|
|
return cond.code();
|
|
}
|
|
|
|
if (profileName)
|
|
{
|
|
std::string sprofile;
|
|
const unsigned char * c = reinterpret_cast<const unsigned char *>(profileName);
|
|
while (*c)
|
|
{
|
|
if (!isspace(*c))
|
|
sprofile += static_cast<char>(toupper(*c));
|
|
++c;
|
|
}
|
|
cond = asccfg.setAssociationParameters(sprofile.c_str(), *params);
|
|
}
|
|
else
|
|
{
|
|
cond = addStoragePresentationContexts(params, sopClassUIDList);
|
|
}
|
|
|
|
if (cond.bad())
|
|
{
|
|
// log >> addStoragePresentationContexts List Failed
|
|
return cond.code();
|
|
}
|
|
|
|
cond = ASC_requestAssociation(net, params, &assoc);
|
|
if (cond.bad())
|
|
{
|
|
if (cond == DUL_ASSOCIATIONREJECTED)
|
|
{
|
|
// Association Rejected
|
|
return cond.code();
|
|
}
|
|
else
|
|
{
|
|
// C-Store Association Request Failed
|
|
return cond.code();
|
|
}
|
|
}
|
|
|
|
if (ASC_countAcceptedPresentationContexts(params) == 0)
|
|
{
|
|
// No Acceptable Presentation Contexts
|
|
return e_no_acceptable_presentation_context;
|
|
}
|
|
|
|
cond = checkUserIdentityResponse(params);
|
|
if (cond.bad())
|
|
{
|
|
// checkUserIdentityResponse Failed
|
|
return cond.code();
|
|
}
|
|
|
|
// Association Accepted
|
|
cond = EC_Normal;
|
|
std::list<std::string>::iterator iter = fileNameList.begin();
|
|
std::list<std::string>::iterator enditer = fileNameList.end();
|
|
while (iter != enditer && cond.good())
|
|
{
|
|
cond = cstore(assoc, *iter);
|
|
++iter;
|
|
}
|
|
|
|
if (cond == EC_Normal)
|
|
{
|
|
if (abortAssociation)
|
|
{
|
|
// Aborting Association
|
|
cond = ASC_abortAssociation(assoc);
|
|
if (cond.bad())
|
|
{
|
|
// Association Abort Failed
|
|
return cond.code();
|
|
}
|
|
else
|
|
{
|
|
// Releasing Association
|
|
cond = ASC_releaseAssociation(assoc);
|
|
if (cond.bad())
|
|
{
|
|
// Association Release Failed
|
|
return cond.code();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (cond == DUL_PEERREQUESTEDRELEASE)
|
|
{
|
|
// Protocol Error: Peer request release
|
|
cond = ASC_abortAssociation(assoc);
|
|
if (cond.bad())
|
|
{
|
|
// Association Abort Failed
|
|
return cond.code();
|
|
}
|
|
}
|
|
else if (cond == DUL_PEERABORTEDASSOCIATION)
|
|
{
|
|
// Peer Aborted Association
|
|
}
|
|
else
|
|
{
|
|
// Store SCU Failed
|
|
cond = ASC_abortAssociation(assoc);
|
|
if (cond.bad())
|
|
{
|
|
// Association Abort Failed
|
|
return cond.code();
|
|
}
|
|
}
|
|
|
|
cond = ASC_destroyAssociation(&assoc);
|
|
if (cond.bad())
|
|
{
|
|
// ASC_destroyAssociation Failed
|
|
return cond.code();
|
|
}
|
|
|
|
cond = ASC_dropNetwork(&net);
|
|
if (cond.bad())
|
|
{
|
|
// ASC_dropNetwork Failed
|
|
return cond.code();
|
|
}
|
|
|
|
OFStandard::shutdownNetwork();
|
|
return e_ok;
|
|
} |