1103 lines
30 KiB
C++
1103 lines
30 KiB
C++
#include "dcm_move.h"
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// static function
|
|
static void moveCallback(void *callbackData, T_DIMSE_C_MoveRQ *request, int responseCount, T_DIMSE_C_MoveRSP *response)
|
|
{
|
|
dcm_cmove_callback * callback = reinterpret_cast<dcm_cmove_callback *>(callbackData);
|
|
if (callback)
|
|
{
|
|
callback->callback(request, responseCount, response);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------------------------
|
|
// c-move callback
|
|
dcm_cmove_callback::dcm_cmove_callback()
|
|
{
|
|
}
|
|
|
|
dcm_cmove_callback::~dcm_cmove_callback()
|
|
{
|
|
}
|
|
|
|
void dcm_cmove_callback::setAssoc(T_ASC_Association * assoc)
|
|
{
|
|
assoc = assoc;
|
|
}
|
|
|
|
void dcm_cmove_callback::setPresetnationContextID(const T_ASC_PresentationContextID& presId)
|
|
{
|
|
presId_ = presId;
|
|
}
|
|
|
|
void dcm_cmove_callback::setCancelAfterNReponse(int n)
|
|
{
|
|
cancelAfterNResponse_ = n;
|
|
}
|
|
|
|
////dcm_cmove_callback::dcm_cmove_callback(const dcm_cmove_callback& other)
|
|
////{
|
|
////}
|
|
////
|
|
////dcm_cmove_callback& dcm_cmove_callback::operator=(const dcm_cmove_callback& other)
|
|
////{
|
|
////}
|
|
|
|
|
|
// default implementation
|
|
dcm_cmove_callback_default::dcm_cmove_callback_default()
|
|
{
|
|
}
|
|
|
|
dcm_cmove_callback_default::~dcm_cmove_callback_default()
|
|
{
|
|
|
|
}
|
|
|
|
void dcm_cmove_callback_default::callback(T_DIMSE_C_MoveRQ *request, int responseCount, T_DIMSE_C_MoveRSP *response)
|
|
{
|
|
OFCondition cond = EC_Normal;
|
|
if (cancelAfterNResponse_ == responseCount)
|
|
{
|
|
cond = DIMSE_sendCancelRequest(assoc_, presId_, request->MessageID);
|
|
if (cond != EC_Normal)
|
|
{
|
|
// log << Cancel Request Failed
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// static function
|
|
static void storeSCPCallback(void *callbackData, T_DIMSE_StoreProgress *progress, T_DIMSE_C_StoreRQ *req, char *imageFileName, DcmDataset **imageDataSet, T_DIMSE_C_StoreRSP *rsp, DcmDataset **statusDetail)
|
|
{
|
|
dcm_cmove_storescp_callback *callback = reinterpret_cast<dcm_cmove_storescp_callback *>(callbackData);
|
|
if (callback)
|
|
{
|
|
callback->callback(progress, req, imageFileName, imageDataSet, rsp, statusDetail);
|
|
}
|
|
}
|
|
|
|
static void subOpCallback(void *callbackData, T_ASC_Network *aNet, T_ASC_Association **subAssoc)
|
|
{
|
|
dcm_cmove *cbdata = reinterpret_cast<dcm_cmove *>(callbackData);
|
|
if (cbdata == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (aNet == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (*subAssoc == NULL)
|
|
{
|
|
cbdata->acceptSubAssoc(aNet, subAssoc);
|
|
}
|
|
else
|
|
{
|
|
cbdata->subOpSCP(subAssoc);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------
|
|
// c-store scp callback
|
|
|
|
dcm_cmove_storescp_callback::dcm_cmove_storescp_callback()
|
|
{
|
|
}
|
|
|
|
dcm_cmove_storescp_callback::~dcm_cmove_storescp_callback()
|
|
{
|
|
}
|
|
|
|
void dcm_cmove_storescp_callback::setImageFileName(char * imageFileName)
|
|
{
|
|
imageFile_ = imageFileName;
|
|
}
|
|
|
|
void dcm_cmove_storescp_callback::setDcmFileFormat(DcmFileFormat * dcmff)
|
|
{
|
|
dcmff_ = dcmff;
|
|
}
|
|
|
|
void dcm_cmove_storescp_callback::setAssoc(T_ASC_Association * assoc)
|
|
{
|
|
assoc_ = assoc;
|
|
}
|
|
|
|
////dcm_cmove_storescp_callback::dcm_cmove_storescp_callback(const dcm_cmove_storescp_callback& other)
|
|
////{
|
|
////}
|
|
////
|
|
////dcm_cmove_storescp_callback& dcm_cmove_storescp_callback::operator=(const dcm_cmove_storescp_callback& other)
|
|
////{
|
|
////}
|
|
|
|
|
|
// default implementation
|
|
dcm_cmove_storescp_callback_default::dcm_cmove_storescp_callback_default(std::string outputDirectory)
|
|
: dcm_cmove_storescp_callback()
|
|
, abortDuringStore_(false)
|
|
, abortAfterStore_(false)
|
|
, sleepDuring_(0)
|
|
, bitPreserving_(false)
|
|
, ignore_(false)
|
|
, writeTransferSyntax_(EXS_Unknown)
|
|
, sequenceType_(EET_ExplicitLength)
|
|
, groupLength_(EGL_recalcGL)
|
|
, paddingType_(EPD_withoutPadding)
|
|
, filepad_(0)
|
|
, itempad_(0)
|
|
, useMetaheader_(true)
|
|
{
|
|
outputDirectory_ = outputDirectory.c_str();
|
|
}
|
|
|
|
dcm_cmove_storescp_callback_default::~dcm_cmove_storescp_callback_default()
|
|
{
|
|
}
|
|
|
|
void dcm_cmove_storescp_callback_default::callback(T_DIMSE_StoreProgress *progress, T_DIMSE_C_StoreRQ *request, char *imageFileName, DcmDataset **imageDataSet, T_DIMSE_C_StoreRSP *response, DcmDataset **statusDetail)
|
|
{
|
|
DIC_UI sopClass;
|
|
DIC_UI sopInstance;
|
|
|
|
if ((abortDuringStore_ && progress->state != DIMSE_StoreBegin)
|
|
|| (abortAfterStore_ && progress->state == DIMSE_StoreEnd))
|
|
{
|
|
// log >> ABORT initiated
|
|
ASC_abortAssociation(assoc_);
|
|
response->DimseStatus = STATUS_STORE_Refused_OutOfResources;
|
|
return;
|
|
}
|
|
|
|
if (sleepDuring_ > 0)
|
|
{
|
|
OFStandard::sleep(static_cast<unsigned int>(sleepDuring_));
|
|
}
|
|
|
|
switch (progress->state)
|
|
{
|
|
case DIMSE_StoreBegin:
|
|
// log << "Recv: "
|
|
break;
|
|
case DIMSE_StoreEnd:
|
|
// log << "End"
|
|
break;
|
|
default:
|
|
// log << "."
|
|
break;
|
|
}
|
|
|
|
if (progress->state == DIMSE_StoreEnd)
|
|
{
|
|
*statusDetail = NULL;
|
|
if ((imageDataSet != NULL) && (*imageDataSet != NULL) && !bitPreserving_ && !ignore_)
|
|
{
|
|
std::string ofname;
|
|
OFStandard::combineDirAndFilename(ofname, outputDirectory_, imageFile_, true);
|
|
if (OFStandard::fileExists(ofname))
|
|
{
|
|
//log << "DICOM file already exists, overwriting:
|
|
}
|
|
|
|
E_TransferSyntax xfer = writeTransferSyntax_;
|
|
if (xfer == EXS_Unknown)
|
|
{
|
|
xfer = (*imageDataSet)->getOriginalXfer();
|
|
}
|
|
|
|
OFCondition cond = dcmff_->saveFile(ofname.c_str(), xfer, sequenceType_, groupLength_, paddingType_, static_cast<Uint32>(filepad_), static_cast<Uint32>(itempad_), useMetaheader_ ? EWM_fileformat : EWM_dataset);
|
|
if (cond.bad())
|
|
{
|
|
// log << cannot write DICOM file
|
|
response->DimseStatus = STATUS_STORE_Refused_OutOfResources;
|
|
OFStandard::deleteFile(ofname);
|
|
}
|
|
|
|
if ((response->DimseStatus == STATUS_Success) && (!ignore_))
|
|
{
|
|
if (!DU_findSOPClassAndInstanceInDataSet(*imageDataSet, sopClass, sizeof(sopClass), sopClass, sizeof(sopInstance), correctUIDPadding_))
|
|
{
|
|
// log << bad DICOM file
|
|
response->DimseStatus = STATUS_STORE_Error_CannotUnderstand;
|
|
}
|
|
else if (strcmp(sopClass, request->AffectedSOPClassUID) != 0)
|
|
{
|
|
response->DimseStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
|
|
}
|
|
else if (strcmp(sopInstance, request->AffectedSOPInstanceUID) != 0)
|
|
{
|
|
response->DimseStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------------
|
|
// dcm-cmove
|
|
|
|
typedef struct {
|
|
const char * findSyntax;
|
|
const char * moveSyntax;
|
|
} QuerySyntax;
|
|
|
|
static QuerySyntax querySyntax[3] =
|
|
{
|
|
{ UID_FINDPatientRootQueryRetrieveInformationModel, UID_MOVEPatientRootQueryRetrieveInformationModel },
|
|
{ UID_FINDStudyRootQueryRetrieveInformationModel, UID_MOVEStudyRootQueryRetrieveInformationModel },
|
|
{ UID_RETIRED_FINDPatientStudyOnlyQueryRetrieveInformationModel, UID_RETIRED_MOVEPatientStudyOnlyQueryRetrieveInformationModel }
|
|
};
|
|
|
|
dcm_cmove::dcm_cmove(const char *peerIp, unsigned long peerPort, const char *peerTitle, unsigned long ourPort, const char *ourTile)
|
|
: cmove_callback(NULL)
|
|
, storescp_callback(NULL)
|
|
, sleepAfter(0)
|
|
, sleepDuring(0)
|
|
, maxPDU(ASC_DEFAULTMAXPDU)
|
|
, useMetaheader(true)
|
|
, acceptAllXfer(true)
|
|
, in_networkTransferSyntax(EXS_Unknown)
|
|
, out_networkTransferSyntax(EXS_Unknown)
|
|
, bitPreserving(false)
|
|
, ignore(false)
|
|
, repeatCount(1)
|
|
, abortAssociation(false)
|
|
, queryModel(QMPatientRoot)
|
|
, blockMode(DIMSE_BLOCKING)
|
|
, dimse_timeout(0)
|
|
, acse_timeout(30)
|
|
, ignorePendingDatasets(true)
|
|
, net(NULL)
|
|
, overrideKeys(NULL)
|
|
{
|
|
peerIp_ = peerIp;
|
|
peerPort_ = peerPort;
|
|
peerTitle_ = peerTitle;
|
|
ourPort_ = ourPort;
|
|
ourTitle_ = new char[1024];
|
|
sprintf(ourTitle_, "%s", ourTile);
|
|
|
|
}
|
|
|
|
dcm_cmove::~dcm_cmove()
|
|
{
|
|
if (ourTitle_ != NULL)
|
|
{
|
|
delete[] ourTitle_;
|
|
}
|
|
}
|
|
|
|
void dcm_cmove::set_sleep_after(unsigned long after)
|
|
{
|
|
sleepAfter = after;
|
|
}
|
|
|
|
void dcm_cmove::set_sleep_during(unsigned long during)
|
|
{
|
|
sleepDuring = during;
|
|
}
|
|
|
|
void dcm_cmove::set_dimse_timeout(int timeout)
|
|
{
|
|
dimse_timeout = timeout;
|
|
}
|
|
|
|
void dcm_cmove::set_acse_timeout(int timeout)
|
|
{
|
|
acse_timeout = timeout;
|
|
}
|
|
|
|
void dcm_cmove::set_cmove_callback(dcm_cmove_callback * callback)
|
|
{
|
|
cmove_callback = callback;
|
|
}
|
|
|
|
void dcm_cmove::set_cmove_scp_callback(dcm_cmove_storescp_callback * callback)
|
|
{
|
|
storescp_callback = callback;
|
|
}
|
|
|
|
int dcm_cmove::move_by_patient_id(std::string patient_id)
|
|
{
|
|
queryModel = QMPatientRoot;
|
|
|
|
addOverrideKey("0008,0052=PATIENT");
|
|
|
|
std::string patient_id_ = "0010,0020=";
|
|
patient_id_ += patient_id;
|
|
addOverrideKey(patient_id_.c_str());
|
|
|
|
return docmove();
|
|
}
|
|
|
|
int dcm_cmove::move_by_study_uid(std::string study_uid)
|
|
{
|
|
queryModel = QMStudyRoot;
|
|
|
|
addOverrideKey("0008,0052=STUDY");
|
|
|
|
std::string studyinstance_uid_ = "0020,000D=";
|
|
studyinstance_uid_ += study_uid;
|
|
addOverrideKey(studyinstance_uid_.c_str());
|
|
|
|
return docmove();
|
|
}
|
|
|
|
int dcm_cmove::move_by_series_uid(std::string study_uid, std::string series_uid)
|
|
{
|
|
queryModel = QMStudyRoot;
|
|
|
|
addOverrideKey("0008,0052=SERIES");
|
|
|
|
std::string studyinstance_uid_ = "0020,000D=";
|
|
studyinstance_uid_ += study_uid;
|
|
addOverrideKey(studyinstance_uid_.c_str());
|
|
|
|
std::string seriesinstance_uid_ = "0020,000E=";
|
|
seriesinstance_uid_ += series_uid;
|
|
addOverrideKey(seriesinstance_uid_.c_str());
|
|
|
|
return docmove();
|
|
}
|
|
|
|
int dcm_cmove::move_by_image_uid(std::string study_uid, std::string series_uid, std::string image_uid)
|
|
{
|
|
queryModel = QMStudyRoot;
|
|
|
|
addOverrideKey("0008,0052=IMAGE");
|
|
|
|
std::string studyinstance_uid_ = "0020,000D=";
|
|
studyinstance_uid_ += study_uid;
|
|
addOverrideKey(studyinstance_uid_.c_str());
|
|
|
|
std::string seriesinstance_uid_ = "0020,000E=";
|
|
seriesinstance_uid_ += series_uid;
|
|
addOverrideKey(seriesinstance_uid_.c_str());
|
|
|
|
std::string image_uid_ = "0008,0018=";
|
|
image_uid_ += image_uid;
|
|
addOverrideKey(image_uid_.c_str());
|
|
|
|
return docmove();
|
|
}
|
|
|
|
void dcm_cmove::addOverrideKey(const char *s)
|
|
{
|
|
unsigned int g = 0xffff;
|
|
unsigned int e = 0xffff;
|
|
int n = 0;
|
|
std::string dicName, valStr;
|
|
|
|
n = sscanf(s, "%x,%x=", &g, &e);
|
|
std::string toParse = s;
|
|
size_t eqPos = toParse.find('=');
|
|
if (n < 2)
|
|
{
|
|
if (eqPos != std::string::npos)
|
|
{
|
|
dicName = toParse.substr(0, eqPos).c_str();
|
|
valStr = toParse.substr(eqPos + 1, toParse.length());
|
|
}
|
|
else
|
|
{
|
|
dicName = s;
|
|
}
|
|
|
|
DcmTagKey key(0xffff, 0xffff);
|
|
const DcmDataDictionary& globalDataDict = dcmDataDict.rdlock();
|
|
const DcmDictEntry * dicent = globalDataDict.findEntry(dicName.c_str());
|
|
dcmDataDict.rdunlock();
|
|
if (dicent != NULL)
|
|
{
|
|
key = dicent->getKey();
|
|
g = key.getGroup();
|
|
e = key.getElement();
|
|
}
|
|
else
|
|
{
|
|
// log << bad key format or dictionary name not found in dictionary
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (eqPos != std::string::npos)
|
|
{
|
|
valStr = toParse.substr(eqPos + 1, toParse.length());
|
|
}
|
|
}
|
|
|
|
DcmTag tag(g, e);
|
|
if (tag.error() != EC_Normal)
|
|
{
|
|
// log << unknown tag
|
|
}
|
|
DcmElement *elem = DcmItem::newDicomElement(tag);
|
|
if (elem == NULL)
|
|
{
|
|
// log << cannot create element for tag
|
|
}
|
|
if (!valStr.empty())
|
|
{
|
|
if (elem->putString(valStr.c_str()).bad())
|
|
{
|
|
// log << cannot put tag value
|
|
}
|
|
}
|
|
|
|
if (overrideKeys == NULL)
|
|
{
|
|
overrideKeys = new DcmDataset;
|
|
}
|
|
if (overrideKeys->insert(elem, true).bad())
|
|
{
|
|
// log << cannot insert tag
|
|
}
|
|
}
|
|
|
|
OFCondition dcm_cmove::addPresentationContext(T_ASC_Parameters * params, T_ASC_PresentationContextID pid, const char * abstractSyntax)
|
|
{
|
|
const char *transferSyntax[] = {NULL, NULL, NULL, NULL};
|
|
int numTransferSyntaxes = 0;
|
|
|
|
switch (out_networkTransferSyntax)
|
|
{
|
|
case EXS_LittleEndianImplicit:
|
|
transferSyntax[0] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 1;
|
|
break;
|
|
case EXS_LittleEndianExplicit:
|
|
transferSyntax[0] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntax[1] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntax[2] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 3;
|
|
break;
|
|
case EXS_BigEndianExplicit:
|
|
transferSyntax[0] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntax[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntax[2] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 3;
|
|
break;
|
|
default:
|
|
if (gLocalByteOrder == EBO_LittleEndian)
|
|
{
|
|
transferSyntax[0] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntax[1] = UID_BigEndianExplicitTransferSyntax;
|
|
}
|
|
else
|
|
{
|
|
transferSyntax[0] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntax[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
}
|
|
transferSyntax[2] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 3;
|
|
break;
|
|
}
|
|
|
|
return ASC_addPresentationContext(params, pid, abstractSyntax, transferSyntax, numTransferSyntaxes);
|
|
}
|
|
|
|
void dcm_cmove::substituteOverrideKeys(DcmDataset *dset)
|
|
{
|
|
if (overrideKeys == NULL)
|
|
return;
|
|
|
|
DcmDataset keys(*overrideKeys);
|
|
unsigned long elemCount = keys.card();
|
|
for (unsigned long i = 0; i < elemCount; i++)
|
|
{
|
|
DcmElement *elem = keys.remove(static_cast<unsigned long>(0));
|
|
dset->insert(elem, true);
|
|
}
|
|
}
|
|
|
|
OFCondition dcm_cmove::acceptSubAssoc(T_ASC_Network *aNet, T_ASC_Association **assoc)
|
|
{
|
|
const char *knownAbstractSyntaxes[] =
|
|
{
|
|
UID_VerificationSOPClass
|
|
};
|
|
const char* transferSyntaxes[] =
|
|
{
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //10
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //20
|
|
NULL //+1
|
|
};
|
|
int numTransferSyntaxes;
|
|
|
|
OFCondition cond = ASC_receiveAssociation(aNet, assoc, maxPDU);
|
|
if (cond.good())
|
|
{
|
|
switch (in_networkTransferSyntax)
|
|
{
|
|
case EXS_LittleEndianImplicit:
|
|
transferSyntaxes[0] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 1;
|
|
break;
|
|
case EXS_LittleEndianExplicit:
|
|
transferSyntaxes[0] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[1] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 3;
|
|
break;
|
|
case EXS_BigEndianExplicit:
|
|
transferSyntaxes[0] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 3;
|
|
break;
|
|
case EXS_JPEGProcess14SV1:
|
|
transferSyntaxes[0] = UID_JPEGProcess14SV1TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_JPEGProcess1:
|
|
transferSyntaxes[0] = UID_JPEGProcess1TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_JPEGProcess2_4:
|
|
transferSyntaxes[0] = UID_JPEGProcess2_4TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_JPEG2000LosslessOnly:
|
|
transferSyntaxes[0] = UID_JPEG2000LosslessOnlyTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_JPEG2000:
|
|
transferSyntaxes[0] = UID_JPEG2000TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_JPEGLSLossless:
|
|
transferSyntaxes[0] = UID_JPEGLSLosslessTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_JPEGLSLossy:
|
|
transferSyntaxes[0] = UID_JPEGLSLossyTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_MPEG2MainProfileAtMainLevel:
|
|
transferSyntaxes[0] = UID_MPEG2MainProfileAtMainLevelTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes;
|
|
break;
|
|
case EXS_MPEG2MainProfileAtHighLevel:
|
|
transferSyntaxes[0] = UID_MPEG2MainProfileAtHighLevelTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_MPEG4HighProfileLevel4_1:
|
|
transferSyntaxes[0] = UID_MPEG4HighProfileLevel4_1TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_MPEG4BDcompatibleHighProfileLevel4_1:
|
|
transferSyntaxes[0] = UID_MPEG4BDcompatibleHighProfileLevel4_1TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_MPEG4HighProfileLevel4_2_For2DVideo:
|
|
transferSyntaxes[0] = UID_MPEG4HighProfileLevel4_2_For2DVideoTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_MPEG4HighProfileLevel4_2_For3DVideo:
|
|
transferSyntaxes[0] = UID_MPEG4HighProfileLevel4_2_For3DVideoTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_MPEG4StereoHighProfileLevel4_2:
|
|
transferSyntaxes[0] = UID_MPEG4StereoHighProfileLevel4_2TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_HEVCMainProfileLevel5_1:
|
|
transferSyntaxes[0] = UID_HEVCMainProfileLevel5_1TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_HEVCMain10ProfileLevel5_1:
|
|
transferSyntaxes[0] = UID_HEVCMain10ProfileLevel5_1TransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
case EXS_RLELossless:
|
|
transferSyntaxes[0] = UID_RLELosslessTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[2] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[3] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 4;
|
|
break;
|
|
default:
|
|
if (acceptAllXfer)
|
|
{
|
|
transferSyntaxes[0] = UID_JPEG2000TransferSyntax;
|
|
transferSyntaxes[1] = UID_JPEG2000LosslessOnlyTransferSyntax;
|
|
transferSyntaxes[2] = UID_JPEGProcess2_4TransferSyntax;
|
|
transferSyntaxes[3] = UID_JPEGProcess1TransferSyntax;
|
|
transferSyntaxes[4] = UID_JPEGProcess14SV1TransferSyntax;
|
|
transferSyntaxes[5] = UID_JPEGLSLossyTransferSyntax;
|
|
transferSyntaxes[6] = UID_JPEGLSLosslessTransferSyntax;
|
|
transferSyntaxes[7] = UID_RLELosslessTransferSyntax;
|
|
transferSyntaxes[8] = UID_MPEG2MainProfileAtMainLevelTransferSyntax;
|
|
transferSyntaxes[9] = UID_MPEG2MainProfileAtHighLevelTransferSyntax;
|
|
transferSyntaxes[10] = UID_MPEG4HighProfileLevel4_1TransferSyntax;
|
|
transferSyntaxes[11] = UID_MPEG4BDcompatibleHighProfileLevel4_1TransferSyntax;
|
|
transferSyntaxes[12] = UID_MPEG4HighProfileLevel4_2_For2DVideoTransferSyntax;
|
|
transferSyntaxes[13] = UID_MPEG4HighProfileLevel4_2_For3DVideoTransferSyntax;
|
|
transferSyntaxes[14] = UID_MPEG4StereoHighProfileLevel4_2TransferSyntax;
|
|
transferSyntaxes[15] = UID_HEVCMainProfileLevel5_1TransferSyntax;
|
|
transferSyntaxes[16] = UID_HEVCMain10ProfileLevel5_1TransferSyntax;
|
|
transferSyntaxes[17] = UID_DeflatedExplicitVRLittleEndianTransferSyntax;
|
|
if (gLocalByteOrder == EBO_LittleEndian)
|
|
{
|
|
transferSyntaxes[18] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[19] = UID_BigEndianExplicitTransferSyntax;
|
|
}
|
|
else
|
|
{
|
|
transferSyntaxes[18] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[19] = UID_LittleEndianExplicitTransferSyntax;
|
|
}
|
|
transferSyntaxes[20] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 21;
|
|
}
|
|
else
|
|
{
|
|
if (gLocalByteOrder == EBO_LittleEndian)
|
|
{
|
|
transferSyntaxes[0] = UID_LittleEndianExplicitTransferSyntax;
|
|
transferSyntaxes[1] = UID_BigEndianExplicitTransferSyntax;
|
|
}
|
|
else
|
|
{
|
|
transferSyntaxes[0] = UID_BigEndianExplicitTransferSyntax;
|
|
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
|
|
}
|
|
transferSyntaxes[2] = UID_LittleEndianImplicitTransferSyntax;
|
|
numTransferSyntaxes = 3;
|
|
}
|
|
break;
|
|
}
|
|
|
|
cond = ASC_acceptContextsWithPreferredTransferSyntaxes((*assoc)->params, knownAbstractSyntaxes, DIM_OF(knownAbstractSyntaxes), transferSyntaxes, numTransferSyntaxes);
|
|
if (cond.good())
|
|
{
|
|
cond = ASC_acceptContextsWithPreferredTransferSyntaxes((*assoc)->params, dcmAllStorageSOPClassUIDs, numberOfDcmAllStorageSOPClassUIDs, transferSyntaxes, numTransferSyntaxes);
|
|
}
|
|
}
|
|
|
|
|
|
if (cond.good())
|
|
{
|
|
cond = ASC_acknowledgeAssociation(*assoc);
|
|
}
|
|
if (cond.good())
|
|
{
|
|
// log << Sub-Association Acknowledged Success
|
|
if (ASC_countAcceptedPresentationContexts((*assoc)->params) == 0)
|
|
{
|
|
// log << but no valid presentation contexes;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// log << Sub-Association Acknowleged Failed
|
|
ASC_dropAssociation(*assoc);
|
|
ASC_destroyAssociation(assoc);
|
|
}
|
|
|
|
return cond;
|
|
}
|
|
|
|
|
|
OFCondition dcm_cmove::echoSCP(T_ASC_Association *assoc, T_DIMSE_Message * msg, T_ASC_PresentationContextID presID)
|
|
{
|
|
T_DIMSE_C_EchoRQ *req = &msg->msg.CEchoRQ;
|
|
|
|
OFCondition cond = DIMSE_sendEchoResponse(assoc, presID, req, STATUS_Success, NULL);
|
|
if (cond.bad())
|
|
{
|
|
// log << Echo SCP Failed
|
|
}
|
|
return cond;
|
|
}
|
|
|
|
OFCondition dcm_cmove::storeSCP(T_ASC_Association *assoc, T_DIMSE_Message * msg, T_ASC_PresentationContextID presID)
|
|
{
|
|
OFCondition cond = EC_Normal;
|
|
T_DIMSE_C_StoreRQ *req;
|
|
char imageFileName[2048];
|
|
|
|
req = &msg->msg.CStoreRQ;
|
|
if (ignore)
|
|
{
|
|
#ifdef _WIN32
|
|
tmpnam(imageFileName);
|
|
#else
|
|
OFStandard::strlcpy(imageFileName, NULL_DEVICE_NAME, 2048);
|
|
#endif // _WIN32
|
|
}
|
|
else
|
|
{
|
|
sprintf(imageFileName, "%s.%s", dcmSOPClassUIDToModality(req->AffectedSOPClassUID), req->AffectedSOPInstanceUID);
|
|
}
|
|
|
|
if (storescp_callback == NULL)
|
|
storescp_callback = new dcm_cmove_storescp_callback_default(".");
|
|
//will not be called actually
|
|
storescp_callback->setAssoc(assoc);
|
|
DcmFileFormat dcmff; // ?
|
|
storescp_callback->setDcmFileFormat(&dcmff);
|
|
storescp_callback->setImageFileName(imageFileName);
|
|
|
|
if (assoc && assoc->params)
|
|
{
|
|
const char *aet = assoc->params->DULparams.callingAPTitle;
|
|
if (aet)
|
|
{
|
|
dcmff.getMetaInfo()->putAndInsertString(DCM_SourceApplicationEntityTitle, aet);
|
|
}
|
|
}
|
|
|
|
DcmDataset *dset = dcmff.getDataset();
|
|
if (bitPreserving)
|
|
{
|
|
cond = DIMSE_storeProvider(assoc, presID, req, imageFileName, useMetaheader,
|
|
NULL, storeSCPCallback, reinterpret_cast<void *>(storescp_callback),
|
|
blockMode, dimse_timeout);
|
|
}
|
|
else
|
|
{
|
|
cond = DIMSE_storeProvider(assoc, presID, req, NULL, useMetaheader,
|
|
&dset, storeSCPCallback, reinterpret_cast<void *>(storescp_callback),
|
|
blockMode, dimse_timeout);
|
|
}
|
|
|
|
if (cond.bad())
|
|
{
|
|
if (!ignore)
|
|
{
|
|
if (strcmp(imageFileName, NULL_DEVICE_NAME) != 0)
|
|
{
|
|
OFStandard::deleteFile(imageFileName);
|
|
}
|
|
}
|
|
#ifdef _WIN32
|
|
else if (ignore)
|
|
{
|
|
if (strcmp(imageFileName, NULL_DEVICE_NAME) != 0)
|
|
{
|
|
OFStandard::deleteFile(imageFileName);
|
|
}
|
|
}
|
|
#endif // _WIN32
|
|
}
|
|
|
|
if (sleepAfter > 0)
|
|
{
|
|
OFStandard::sleep(static_cast<unsigned int>(sleepDuring));
|
|
}
|
|
|
|
return cond;
|
|
}
|
|
|
|
OFCondition dcm_cmove::subOpSCP(T_ASC_Association **subAssoc)
|
|
{
|
|
T_DIMSE_Message msg;
|
|
T_ASC_PresentationContextID presID;
|
|
|
|
if (!ASC_dataWaiting(*subAssoc, 0))
|
|
return DIMSE_NODATAAVAILABLE;
|
|
|
|
OFCondition cond = DIMSE_receiveCommand(*subAssoc, blockMode, dimse_timeout, &presID, &msg, NULL);
|
|
if (cond == EC_Normal)
|
|
{
|
|
switch (msg.CommandField)
|
|
{
|
|
case DIMSE_C_ECHO_RQ:
|
|
cond = echoSCP(*subAssoc, &msg, presID);
|
|
break;
|
|
case DIMSE_C_STORE_RQ:
|
|
cond = storeSCP(*subAssoc, &msg, presID);
|
|
break;
|
|
default:
|
|
cond = DIMSE_BADCOMMANDTYPE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cond == DUL_PEERREQUESTEDRELEASE)
|
|
{
|
|
cond = ASC_acknowledgeRelease(*subAssoc);
|
|
ASC_dropSCPAssociation(*subAssoc);
|
|
ASC_destroyAssociation(subAssoc);
|
|
return cond;
|
|
}
|
|
else if (cond == DUL_PEERABORTEDASSOCIATION)
|
|
{
|
|
}
|
|
else if (cond != EC_Normal)
|
|
{
|
|
cond = ASC_abortAssociation(*subAssoc);
|
|
}
|
|
|
|
if (cond != EC_Normal)
|
|
{
|
|
ASC_dropAssociation(*subAssoc);
|
|
ASC_destroyAssociation(subAssoc);
|
|
}
|
|
|
|
return cond;
|
|
}
|
|
|
|
OFCondition dcm_cmove::moveSCU(T_ASC_Association * assoc, const char * fname)
|
|
{
|
|
T_ASC_PresentationContextID presID;
|
|
T_DIMSE_C_MoveRQ req;
|
|
T_DIMSE_C_MoveRSP rsp;
|
|
DIC_US msgId = assoc->nextMsgID;
|
|
DcmDataset *rspIds = NULL;
|
|
const char *sopClass;
|
|
DcmDataset *statusDetail = NULL;
|
|
|
|
DcmFileFormat dcmff;
|
|
if (fname != NULL)
|
|
{
|
|
if (dcmff.loadFile(fname).bad())
|
|
{
|
|
// log << bad DICOM file
|
|
return DIMSE_BADDATA;
|
|
}
|
|
}
|
|
|
|
substituteOverrideKeys(dcmff.getDataset());
|
|
sopClass = querySyntax[queryModel].moveSyntax;
|
|
presID = ASC_findAcceptedPresentationContextID(assoc, sopClass);
|
|
if (presID == 0)
|
|
{
|
|
return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
|
|
}
|
|
|
|
if (cmove_callback == NULL)
|
|
cmove_callback = new dcm_cmove_callback_default();
|
|
cmove_callback->setAssoc(assoc);
|
|
cmove_callback->setPresetnationContextID(presID);
|
|
|
|
req.MessageID = msgId;
|
|
OFStandard::strlcpy(req.AffectedSOPClassUID, sopClass, sizeof(req.AffectedSOPClassUID));
|
|
req.Priority = DIMSE_PRIORITY_MEDIUM;
|
|
req.DataSetType = DIMSE_DATASET_PRESENT;
|
|
if (ourTitle_ == NULL)
|
|
{
|
|
ourTitle_ = new char[1024];
|
|
ASC_getAPTitles(assoc->params, req.MoveDestination, sizeof(req.MoveDestination), ourTitle_, 1023, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
OFStandard::strlcpy(req.MoveDestination, ourTitle_, sizeof(req.MoveDestination));
|
|
}
|
|
|
|
OFCondition cond = DIMSE_moveUser(assoc, presID, &req, dcmff.getDataset(), moveCallback, reinterpret_cast<void *>(cmove_callback),
|
|
blockMode, dimse_timeout, net, subOpCallback, reinterpret_cast<void *>(this), &rsp, &statusDetail, &rspIds, ignorePendingDatasets);
|
|
if (cond == EC_Normal)
|
|
{
|
|
if ((rsp.DimseStatus == STATUS_Success) || (rsp.DimseStatus == STATUS_MOVE_Cancel_SubOperationsTerminatedDueToCancelIndication))
|
|
{
|
|
}
|
|
else if (rsp.DimseStatus == STATUS_MOVE_Warning_SubOperationsCompleteOneOrMoreFailures)
|
|
{
|
|
// log << response with warning
|
|
}
|
|
else
|
|
{
|
|
// log << respone with error
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// log << Move Request Failed
|
|
}
|
|
|
|
if (statusDetail != NULL)
|
|
delete statusDetail;
|
|
if (rspIds != NULL)
|
|
delete rspIds;
|
|
|
|
return cond;
|
|
}
|
|
|
|
OFCondition dcm_cmove::cmove(T_ASC_Association *assoc, const char *fname)
|
|
{
|
|
OFCondition cond = EC_Normal;
|
|
int n = static_cast<int>(repeatCount);
|
|
while (cond.good() && n--)
|
|
{
|
|
cond = moveSCU(assoc, fname);
|
|
}
|
|
|
|
return cond;
|
|
}
|
|
|
|
int dcm_cmove::docmove()
|
|
{
|
|
T_ASC_Parameters *params = NULL;
|
|
DIC_NODENAME peerHost;
|
|
T_ASC_Association *assoc = NULL;
|
|
|
|
OFStandard::initializeNetwork();
|
|
if (!dcmDataDict.isDictionaryLoaded())
|
|
return e_no_dictionary_loaded;
|
|
|
|
T_ASC_NetworkRole role = (ourPort_ > 0) ? NET_ACCEPTORREQUESTOR : NET_REQUESTOR;
|
|
OFCondition cond = ASC_initializeNetwork(role, static_cast<int>(ourPort_), acse_timeout, &net);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_initialize_network;
|
|
}
|
|
|
|
if (OFStandard::dropPrivileges().bad())
|
|
{
|
|
return e_cannot_set_uid;
|
|
}
|
|
|
|
cond = ASC_createAssociationParameters(¶ms, maxPDU);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_create_association_parameters;
|
|
}
|
|
|
|
ASC_setAPTitles(params, ourTitle_, peerTitle_, NULL);
|
|
sprintf(peerHost, "%s:%d", peerIp_, static_cast<int>(peerPort_));
|
|
ASC_setPresentationAddresses(params, OFStandard::getHostName().c_str(), peerHost);
|
|
|
|
cond = addPresentationContext(params, 1, querySyntax[queryModel].findSyntax);
|
|
cond = addPresentationContext(params, 3, querySyntax[queryModel].moveSyntax);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_create_association_parameters;
|
|
}
|
|
|
|
cond = ASC_requestAssociation(net, params, &assoc);
|
|
if (cond.bad())
|
|
{
|
|
if (cond == DUL_ASSOCIATIONREJECTED)
|
|
{
|
|
return e_association_rejected;
|
|
}
|
|
else
|
|
{
|
|
return e_cannot_negotiate_association;
|
|
}
|
|
}
|
|
|
|
if (ASC_countAcceptedPresentationContexts(params) == 0)
|
|
{
|
|
return e_no_acceptable_presentation_context;
|
|
}
|
|
|
|
cond = EC_Normal;
|
|
cond = cmove(assoc, NULL);
|
|
if (cond == EC_Normal)
|
|
{
|
|
if (abortAssociation)
|
|
{
|
|
cond = ASC_abortAssociation(assoc);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_abort_association;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cond = ASC_releaseAssociation(assoc);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_release_association;
|
|
}
|
|
}
|
|
}
|
|
else if (cond == DUL_PEERREQUESTEDRELEASE)
|
|
{
|
|
cond = ASC_abortAssociation(assoc);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_abort_association;
|
|
}
|
|
}
|
|
else if (cond == DUL_PEERABORTEDASSOCIATION)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
cond = ASC_abortAssociation(assoc);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_abort_association;
|
|
}
|
|
}
|
|
|
|
cond = ASC_destroyAssociation(&assoc);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_destroy_association;
|
|
}
|
|
cond = ASC_dropNetwork(&net);
|
|
if (cond.bad())
|
|
{
|
|
return e_cannot_drop_network;
|
|
}
|
|
|
|
OFStandard::shutdownNetwork();
|
|
|
|
return e_ok;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|