this code is a simple skeleton for cget and dcmtk implementation. I test it some series and some images. If some thing wrong, please warn me. I will use this logic in our viewer. Ofcourse there are lots of part copy-paste from dcmtk library.
Best regards.
Code: Select all
// getscu001.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <dcmtk/dcmnet/dimse.h>
#include <dcmtk/dcmdata/dcfilefo.h>
#include <dcmtk/dcmdata/dcdeftag.h>
#include <dcmtk/dcmimgle/dcmimage.h>
#include <vtkImageViewer2.h>
#include <vtkImageData.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
T_ASC_Network *m_net;
T_ASC_Parameters *m_params = NULL;
vtkImageData *image = NULL;
DcmDataset *m_imageDataSet = NULL;
typedef struct tagQuerySyntax
{
const char *findSyntax;
const char *getSyntax;
}TQuerySyntax;
static TQuerySyntax querySyntax[3] =
{
{UID_FINDPatientRootQueryRetrieveInformationModel, UID_GETPatientRootQueryRetrieveInformationModel},
{UID_FINDStudyRootQueryRetrieveInformationModel, UID_GETStudyRootQueryRetrieveInformationModel},
{UID_FINDPatientStudyOnlyQueryRetrieveInformationModel, UID_GETPatientStudyOnlyQueryRetrieveInformationModel}
};
typedef enum tagStandartQueryInformationModel
{
sqimPatient = 0,
sqimStudy,
sqimPSOnly
}TStandartQueryInformationModel;
int m_opt_abstractSyntax = sqimPatient;
DcmDataset *m_overrideKeys;
static bool addPresentationContext(T_ASC_Parameters*);
static bool getSCU(T_ASC_Association*);
int _tmain(int argc, _TCHAR* argv[])
{
WSAData winSockData;
WORD winSockVersionNeeded = MAKEWORD( 1, 1 );// we need at least version 1.1
if( WSAStartup(winSockVersionNeeded, &winSockData) )
{//error
std::cout<<"error in WSAStartup()\n";
return 1;
}
OFCondition cond = ASC_initializeNetwork(NET_REQUESTOR, 0, 10, &m_net);
if(cond.bad())
{
std::cout<<"error in ASC_initializeNetwork()\n";
WSACleanup();
return 1;
}
cond = ASC_createAssociationParameters(&m_params, ASC_DEFAULTMAXPDU);//opt_maxReceivePDULength from parameters in findscu.exe
if (cond.bad())
{
std::cout<<"error in ASC_createAssociationParameters()\n";
ASC_dropNetwork(&m_net);
WSACleanup();
return 1;
}
//ASC_setAPTitles(m_params, "ANY-SCU", "ANY-SCP", "ANY-SCU");
ASC_setAPTitles(m_params, "HAKAN", "AK_CLIENT", NULL);
cond = ASC_setTransportLayerType(m_params, false);
if (cond.bad())
{
std::cout<<"error in ASC_setTransportLayerType()...try to continue\n";
}
char localHost[DIC_NODENAME_LEN + 1];
gethostname(localHost, sizeof(localHost) - 1);
std::stringstream peerHost;
//std::string m_host("87.106.65.167");
std::string m_host("192.168.67.155");
int m_port = 105;
peerHost<<m_host<<":"<<m_port;
ASC_setPresentationAddresses(m_params, localHost, peerHost.str().c_str());
if( !ASC_countPresentationContexts(m_params) )
{
if( !addPresentationContext(m_params) )
{//error
std::cout<<"error in addPresentationContext()\n";
ASC_dropNetwork(&m_net);
WSACleanup();
return 1;
}
}
T_ASC_Association *assoc;
cond = ASC_requestAssociation(m_net, m_params, &assoc);
if( cond.bad() )
{
std::cout<<"error in ASC_requestAssociation()\n";
ASC_dropNetwork(&m_net);
WSACleanup();
return 1;
}
// count the presentation contexts which have been accepted by the SCP If there are none, finish the execution
if( !ASC_countAcceptedPresentationContexts(m_params) )
{//error
std::cout<<"error in ASC_countAcceptedPresentationContexts()\n";
ASC_releaseAssociation(assoc);
ASC_destroyAssociation(&assoc);
ASC_dropNetwork(&m_net);
WSACleanup();
return 1;
}
getSCU(assoc);
cond = ASC_releaseAssociation(assoc);
if (cond.bad())
{
std::cout<<"error in ASC_releaseAssociation()\n";
}
cond = ASC_destroyAssociation(&assoc);
if (cond.bad())
{
std::cout<<"error in ASC_destroyAssociation()\n";
}
cond = ASC_dropNetwork(&m_net);
if (cond.bad())
{//error
std::cout<<"error in ASC_dropNetwork()\n";
}
WSACleanup();
if( !image )
{
std::cout<<"error image is NULL\n";
return 1;
}
vtkImageViewer2 *ImageViewer = vtkImageViewer2::New();
ImageViewer->SetInput( image );
ImageViewer->SetColorLevel(127);
ImageViewer->SetColorWindow(255);
vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
ImageViewer->SetupInteractor(iren);
ImageViewer->Render();
ImageViewer->GetRenderer()->ResetCamera();
iren->Start();
ImageViewer->Delete();
image->Delete();
iren->Delete();
return 0;
}
static void subOpCallback(
void *callbackData,
T_DIMSE_StoreProgress *progress,// progress state
T_DIMSE_C_StoreRQ *request,// original store request
char *imageFileName, DcmDataset **imageDataSet,// being received into
T_DIMSE_C_StoreRSP *response,// final store response
DcmDataset **statusDetail)
{
if( progress->state != DIMSE_StoreEnd )
return;
// do not send status detail information
*statusDetail = NULL;
if( imageDataSet && *imageDataSet )
{
m_imageDataSet = *imageDataSet;
//DcmFileFormat dcmf(*imageDataSet);
//dcmf.saveFile("test.dcm",(*imageDataSet)->getOriginalXfer());
//delete *imageDataSet;
}
}
static void substituteOverrideKeys(DcmDataset *dset)
{
DcmDataset keys(*m_overrideKeys);
// put the override keys into dset replacing existing tags
unsigned long elemCount = keys.card();
for (unsigned long i=0; i<elemCount; i++)
{
DcmElement *elem = keys.remove((unsigned long)0);
dset->insert(elem, OFTrue);
}
}
static void GetUserCallback( void *callbackData, T_DIMSE_C_GetRQ *request, int responseCount, T_DIMSE_C_GetRSP *response)
{
if( response->DimseStatus == STATUS_Success )
return;
/*
DcmFileFormat dcmf(m_imageDataSet);
dcmf.saveFile("test.dcm",m_imageDataSet->getOriginalXfer());
delete m_imageDataSet;
*/
static unsigned size = 0;
const DiPixel *dp;
DicomImage di(m_imageDataSet, m_imageDataSet->getOriginalXfer(), 0UL, 0UL, 0UL);
dp = di.getInterData();
long int val;
int dim[3];
m_imageDataSet->findAndGetLongInt(DCM_Columns, val);
dim[0] = val;
m_imageDataSet->findAndGetLongInt(DCM_Rows, val);
dim[1] = val;
if( !image )
{
dim[2] = response->NumberOfCompletedSubOperations + response->NumberOfRemainingSubOperations;
double spacing[3];
m_imageDataSet->findAndGetFloat64(DCM_PixelSpacing, spacing[0], 0);
m_imageDataSet->findAndGetFloat64(DCM_PixelSpacing, spacing[1], 1);
spacing[2] = 1;
//vtkImageData *image = vtkImageData::New();
image = vtkImageData::New();
image->SetOrigin(0,0,0);
image->SetDimensions(dim);
image->SetSpacing(spacing);
switch(dp->getRepresentation())
{
case EPR_Uint8:
image->SetScalarTypeToUnsignedChar();
size = sizeof(unsigned char);
break;
case EPR_Sint8:
image->SetScalarTypeToChar();
size = sizeof(char);
break;
case EPR_Uint16:
image->SetScalarTypeToUnsignedShort();
size = sizeof(unsigned short);
break;
case EPR_Sint16:
image->SetScalarTypeToShort();
size = sizeof(short);
break;
case EPR_Uint32:
image->SetScalarTypeToUnsignedInt();
size = sizeof(unsigned);
break;
case EPR_Sint32:
image->SetScalarTypeToInt();
size = sizeof(int);
break;
default:break;
}
image->AllocateScalars();
}
std::memcpy(image->GetScalarPointer(0,0, response->NumberOfCompletedSubOperations - 1), dp->getData(), dim[0] * dim[1] * size);
image->Update();
//m_windowCenter = 255;
//m_windowWidth = 127;
delete m_imageDataSet;
}
OFCondition MY_DIMSE_getUser(
T_ASC_Association *assoc,
T_ASC_PresentationContextID presID,
T_DIMSE_C_GetRQ *request,
DcmDataset *requestIdentifiers,
DIMSE_GetUserCallback callback, void *callbackData,
T_DIMSE_BlockingMode blockMode, int timeout,
T_ASC_Network *net,
//DIMSE_SubOpProviderCallback subOpCallback,
DIMSE_StoreProviderCallback subOpCallback,
void *subOpCallbackData,
T_DIMSE_C_GetRSP *response, DcmDataset **statusDetail,
DcmDataset **rspIds)
{
DcmDataset *imageDataSet = NULL;
T_DIMSE_Message req, rsp;
DIC_US msgId;
int responseCount = 0;
DIC_US status = STATUS_Pending;
if (requestIdentifiers == NULL)
return DIMSE_NULLKEY;
bzero((char*)&req, sizeof(req));
bzero((char*)&rsp, sizeof(rsp));
req.CommandField = DIMSE_C_GET_RQ;
request->DataSetType = DIMSE_DATASET_PRESENT;
req.msg.CGetRQ = *request;
msgId = request->MessageID;
OFCondition cond = DIMSE_sendMessageUsingMemoryData(assoc, presID, &req, NULL, requestIdentifiers, NULL, NULL);
if (cond != EC_Normal)
return cond;
// receive responses
while (cond == EC_Normal )//&& status == STATUS_Pending
{
bzero((char*)&rsp, sizeof(rsp));
cond = DIMSE_receiveCommand(assoc, blockMode, timeout, &presID, &rsp, statusDetail);
if (cond != EC_Normal)
break;//return cond;
switch( rsp.CommandField )
{
case DIMSE_C_STORE_RQ:
//cond = DIMSE_storeProvider(assoc,presID, &rsp.msg.CStoreRQ,"test.dcm",true,NULL,NULL,NULL,blockMode,timeout);
imageDataSet = new DcmDataset();
cond = DIMSE_storeProvider(assoc, presID, &rsp.msg.CStoreRQ, NULL, true, &imageDataSet, subOpCallback, subOpCallbackData, blockMode, timeout);
break;
case DIMSE_C_GET_RSP:
if( callback )
callback(callbackData, &req.msg.CGetRQ, ++responseCount/* !! */, &rsp.msg.CGetRSP);
if( rsp.msg.CGetRSP.DimseStatus == STATUS_Success )
return EC_Normal;
break;
default:
char buf1[256];
sprintf(buf1, "DIMSE: Unexpected Response Command Field: 0x%x", (unsigned)rsp.CommandField);
return makeDcmnetCondition(DIMSEC_UNEXPECTEDRESPONSE, OF_error, buf1);
}
} //while (cond == EC_Normal )
return cond;
}
static bool getSCU(T_ASC_Association *assoc)
{
DIC_US msgId = assoc->nextMsgID++;
T_ASC_PresentationContextID presId;
T_DIMSE_C_GetRQ req;
T_DIMSE_C_GetRSP rsp;
DcmDataset *statusDetail = NULL;
DcmDataset *rspIds = NULL;
const char *sopClass = querySyntax[m_opt_abstractSyntax].getSyntax;
presId = ASC_findAcceptedPresentationContextID(assoc, sopClass);
if (presId == 0)
return false;//DIMSE_NOVALIDPRESENTATIONCONTEXTID;
bzero((char*)&req, sizeof(req));
req.MessageID = msgId;
strcpy(req.AffectedSOPClassUID, sopClass);
req.Priority = DIMSE_PRIORITY_MEDIUM;
req.DataSetType = DIMSE_DATASET_PRESENT;
//QUERY
DcmFileFormat dcmf;// = new DcmFileFormat();
m_overrideKeys = dcmf.getDataset();//new DcmDataset();
DcmElement *elem = newDicomElement(DCM_QueryRetrieveLevel);
//elem->putString("IMAGE");
elem->putString("SERIES");
m_overrideKeys->insert(elem,true);
elem = newDicomElement(DCM_PatientID);
elem->putString("7Thjq7g");
m_overrideKeys->insert(elem,true);
elem = newDicomElement(DCM_StudyInstanceUID);
elem->putString("1.2.840.113704.1.111.5600.1107858801.1");
m_overrideKeys->insert(elem,true);
elem = newDicomElement(DCM_SeriesInstanceUID);
elem->putString("1.2.840.113704.1.111.5600.1107859261.7");
//elem->putString("1.2.840.113704.1.111.5600.1107859163.3");
m_overrideKeys->insert(elem,true);
/*
elem = newDicomElement(DCM_SOPInstanceUID);
elem->putString("1.2.840.113704.7.1.1.6124.1107859994.7");
m_overrideKeys->insert(elem,true);
*/
m_overrideKeys->insertEmptyElement(DCM_SOPInstanceUID,true);
substituteOverrideKeys(m_overrideKeys);
OFCondition cond = MY_DIMSE_getUser(assoc, presId, &req,
m_overrideKeys,
GetUserCallback/*NULL*/, NULL, DIMSE_BLOCKING, 0, m_net, subOpCallback, NULL, &rsp, &statusDetail, &rspIds);
if( cond.bad() )
{
delete statusDetail;
delete rspIds;
return false;
}
delete statusDetail;
delete rspIds;
return true;
}
static bool addPresentationContext(T_ASC_Parameters *params)
{
/*
** We prefer to use Explicitly encoded transfer syntaxes.
** If we are running on a Little Endian machine we prefer
** LittleEndianExplicitTransferSyntax to BigEndianTransferSyntax.
** Some SCP implementations will just select the first transfer
** syntax they support (this is not part of the standard) so
** organise the proposed transfer syntaxes to take advantage
** of such behaviour.
**
** The presentation contexts proposed here are only used for
** C-FIND and C-MOVE, so there is no need to support compressed
** transmission.
*/
const char* knownAbstractSyntaxes[] ={UID_VerificationSOPClass};
const char* transferSyntaxes[] = { NULL, NULL, NULL };
int numTransferSyntaxes = 3;
if (gLocalByteOrder == EBO_LittleEndian) // defined in dcxfer.h
{
transferSyntaxes[0] = UID_LittleEndianExplicitTransferSyntax;
transferSyntaxes[1] = UID_BigEndianExplicitTransferSyntax;
}
else
{
transferSyntaxes[0] = UID_BigEndianExplicitTransferSyntax;
transferSyntaxes[1] = UID_LittleEndianExplicitTransferSyntax;
}
transferSyntaxes[2] = UID_LittleEndianImplicitTransferSyntax;
OFCondition cond = ASC_addPresentationContext(params, 1, querySyntax[m_opt_abstractSyntax].findSyntax, transferSyntaxes, numTransferSyntaxes,ASC_SC_ROLE_SCUSCP);
cond = ASC_addPresentationContext(params, 3, querySyntax[m_opt_abstractSyntax].getSyntax, transferSyntaxes, numTransferSyntaxes,ASC_SC_ROLE_SCUSCP);
cond = ASC_addPresentationContext(params, 5, knownAbstractSyntaxes[0], transferSyntaxes, numTransferSyntaxes,ASC_SC_ROLE_SCUSCP);
T_ASC_PresentationContextID id = 7;
for(int I = 0; I < numberOfAllDcmStorageSOPClassUIDs; ++I)
{
cond = ASC_addPresentationContext(params,id,dcmAllStorageSOPClassUIDs[I],transferSyntaxes,numTransferSyntaxes,ASC_SC_ROLE_SCUSCP);
id += 2;
}
return cond.good();
}