DcmSCU example program

This is example code how to program an actual DICOM client with the DcmSCU class. In particular, it demonstrates
  1. how to send an ECHO to the server (usually a PACS),
  2. receive a list of studies the PACS has (Query)
  3. and downloads them (Retrieve). For this last step, a separate Storage SCP like storescp is needed which actually receives the study images.

The code uses the public DICOM server at www.dicomserver.co.uk which is offered by Dave Harvey (MedicalObjects). :-) There are also logs you can check at the server in order to debug your application.

  1/* 
  2 * 
  3 *  Copyright (C) 2011-2012, OFFIS e.V. 
  4 *  All rights reserved.  See COPYRIGHT file for details. 
  5 * 
  6 *  This software and supporting documentation were developed by 
  7 * 
  8 *    OFFIS e.V. 
  9 *    R&D Division Health 
 10 *    Escherweg 2 
 11 *    D-26121 Oldenburg, Germany 
 12 * 
 13 * 
 14 *  Module:  dcmnet 
 15 * 
 16 *  Author:  Michael Onken 
 17 * 
 18 *  Purpose: Test for move feature of the DcmSCU class 
 19 */ 
 20
 21#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */ 
 22#include "dcmtk/dcmnet/testscu.h" 
 23#include "dcmtk/dcmnet/diutil.h" 
 24
 25#define OFFIS_CONSOLE_APPLICATION "testscu" 
 26
 27static OFLogger echoscuLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION); 
 28
 29static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v" 
 30  OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $"; 
 31
 32// our application entity title used for calling the peer machine 
 33#define APPLICATIONTITLE     "TEST-SCU" 
 34
 35// host name of the peer machine 
 36#define PEERHOSTNAME         "www.dicomserver.co.uk" 
 37
 38// TCP/IP port to connect to the peer machine 
 39#define PEERPORT 11112 
 40
 41// application entity title of the peer machine 
 42#define PEERAPPLICATIONTITLE "MOVESCP" 
 43
 44// MOVE destination AE Title 
 45#define MOVEAPPLICATIONTITLE "TEST-SCU" 
 46
 47static Uint8 findUncompressedPC(const OFString& sopClass, 
 48                                DcmSCU& scu) 
 49{ 
 50  Uint8 pc; 
 51  pc = scu.findPresentationContextID(sopClass, UID_LittleEndianExplicitTransferSyntax); 
 52  if (pc == 0) 
 53    pc = scu.findPresentationContextID(sopClass, UID_BigEndianExplicitTransferSyntax); 
 54  if (pc == 0) 
 55    pc = scu.findPresentationContextID(sopClass, UID_LittleEndianImplicitTransferSyntax); 
 56  return pc; 
 57} 
 58
 59// ** 
 60
 61int main(int argc, char *argv[]) 
 62{ 
 63  /* Setup DICOM connection parameters */ 
 64  OFLog::configure(OFLogger::DEBUG_LOG_LEVEL); 
 65  DcmTestSCU scu; 
 66  // set AE titles 
 67  scu.setAETitle(APPLICATIONTITLE); 
 68  scu.setPeerHostName(PEERHOSTNAME); 
 69  scu.setPeerPort(PEERPORT); 
 70  scu.setPeerAETitle(PEERAPPLICATIONTITLE); 
 71  // Use presentation context for FIND/MOVE in study root, propose all uncompressed transfer syntaxes 
 72  OFList<OFString> ts; 
 73  ts.push_back(UID_LittleEndianExplicitTransferSyntax); 
 74  ts.push_back(UID_BigEndianExplicitTransferSyntax); 
 75  ts.push_back(UID_LittleEndianImplicitTransferSyntax); 
 76  scu.addPresentationContext(UID_FINDStudyRootQueryRetrieveInformationModel, ts); 
 77  scu.addPresentationContext(UID_MOVEStudyRootQueryRetrieveInformationModel, ts); 
 78  scu.addPresentationContext(UID_VerificationSOPClass, ts); 
 79
 80  /* Initialize network */ 
 81  OFCondition result = scu.initNetwork(); 
 82  if (result.bad()) 
 83  { 
 84    DCMNET_ERROR("Unable to set up the network: " << result.text()); 
 85    return 1; 
 86  } 
 87
 88  /* Negotiate Association */ 
 89  result = scu.negotiateAssociation(); 
 90  if (result.bad()) 
 91  { 
 92    DCMNET_ERROR("Unable to negotiate association: " << result.text()); 
 93    return 1; 
 94  } 
 95
 96  /* Let's look whether the server is listening: 
 97     Assemble and send C-ECHO request 
 98   */ 
 99  result = scu.sendECHORequest(0); 
100  if (result.bad()) 
101  { 
102    DCMNET_ERROR("Could not process C-ECHO with the server: " << result.text()); 
103    return 1; 
104  } 
105
106  /* Assemble and send C-FIND request */ 
107  OFList<QRResponse*> findResponses; 
108  DcmDataset req; 
109  req.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "STUDY"); 
110  req.putAndInsertOFStringArray(DCM_StudyInstanceUID, ""); 
111  T_ASC_PresentationContextID presID = findUncompressedPC(UID_FINDStudyRootQueryRetrieveInformationModel, scu);
112  if (presID == 0) 
113  { 
114    DCMNET_ERROR("There is no uncompressed presentation context for Study Root FIND"); 
115    return 1; 
116  } 
117  result = scu.sendFINDRequest(presID, &req, &findResponses); 
118  if (result.bad()) 
119    return 1; 
120  else 
121    DCMNET_INFO("There are " << findResponses.size() << " studies available"); 
122
123  /* Assemble and send C-MOVE request, for each study identified above*/ 
124  presID = findUncompressedPC(UID_MOVEStudyRootQueryRetrieveInformationModel, scu); 
125  if (presID == 0) 
126  { 
127    DCMNET_ERROR("There is no uncompressed presentation context for Study Root MOVE"); 
128    return 1; 
129  } 
130  OFListIterator(QRResponse*) study = findResponses.begin(); 
131  Uint32 studyCount = 1; 
132  OFBool failed = OFFalse; 
133  // Every while loop run will get all image for a specific study 
134  while (study != findResponses.end() && result.good())
135  { 
136    // be sure we are not in the last response which does not have a dataset 
137    if ( (*study)->m_dataset != NULL) 
138    { 
139      OFString studyInstanceUID; 
140      result = (*study)->m_dataset->findAndGetOFStringArray(DCM_StudyInstanceUID, studyInstanceUID); 
141      // only try to get study if we actually have study instance uid, otherwise skip it 
142      if (result.good()) 
143      { 
144        req.putAndInsertOFStringArray(DCM_StudyInstanceUID, studyInstanceUID); 
145        // fetches all images of this particular study 
146        result = scu.sendMOVERequest(presID, MOVEAPPLICATIONTITLE, &req, NULL /* we are not interested into responses*/); 
147        if (result.good()) 
148        { 
149          DCMNET_INFO("Received study #" << std::setw(7) << studyCount << ": " << studyInstanceUID); 
150          studyCount++; 
151        } 
152      }
153    } 
154    study++;
155  } 
156  if (result.bad()) 
157  { 
158    DCMNET_ERROR("Unable to retrieve all studies: " << result.text()); 
159  }
160  while (!findResponses.empty())
161  {
162    delete findResponses.front();
163    findResponses.pop_front();
164  }
165  /* Release association */ 
166  scu.closeAssociation(DCMSCU_RELEASE_ASSOCIATION);
167  return 0; 
168}

P.S: The header file would be trivial:

 1#ifndef TESTSCU_H 
 2#define TESTSCU_H 
 3
 4#include "dcmtk/config/osconfig.h"  /* make sure OS specific configuration is included first */ 
 5
 6#include "dcmtk/dcmnet/scu.h"     /* Covers most common dcmdata classes */ 
 7
 8class DcmTestSCU : public DcmSCU 
 9{ 
10
11public: 
12
13  DcmTestSCU()  {} 
14  ~DcmTestSCU() {} 
15
16}; 
17
18#endif // TESTSCU_H 

Note

sendMOVERequest() and corresponding code was added after the 3.6.0 release. You need to use a current snapshot version in order to compile and run the code.