Prechádzať zdrojové kódy

Add ctkDICOMQueryTest1, a simple test for ctkDICOMQuery

Doesn't test networking.
Julien Finet 14 rokov pred
rodič
commit
5246217b4f

+ 3 - 0
Libs/DICOM/Core/Testing/Cpp/CMakeLists.txt

@@ -6,6 +6,7 @@ CREATE_TEST_SOURCELIST(Tests ${KIT}CppTests.cpp
   ctkDICOMImageTest1.cpp
   ctkDICOMIndexerTest1.cpp
   ctkDICOMModelTest1.cpp
+  ctkDICOMQueryTest1.cpp
   ctkDICOMPersonNameTest1.cpp
   ctkDICOMTest1.cpp
   )
@@ -43,6 +44,8 @@ ADD_TEST( ctkDICOMModelTest1 ${KIT_TESTS}
                              ${CMAKE_CURRENT_SOURCE_DIR}/../../Resources/dicom-sample.sql)
 SET_PROPERTY(TEST ctkDICOMModelTest1 PROPERTY LABELS ${PROJECT_NAME})
 
+SIMPLE_TEST( ctkDICOMQueryTest1)
+
 ADD_TEST( ctkDICOMTest1 ${KIT_TESTS}
           ctkDICOMTest1 ${CMAKE_CURRENT_BINARY_DIR}/dicom.db
                         ${CMAKE_CURRENT_SOURCE_DIR}/../../Resources/dicom-sample.sql)

+ 119 - 0
Libs/DICOM/Core/Testing/Cpp/ctkDICOMQueryTest1.cpp

@@ -0,0 +1,119 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+// Qt includes
+#include <QCoreApplication>
+#include <QStringList>
+#include <QVariant>
+
+// ctkDICOMCore includes
+#include "ctkDICOMQuery.h"
+
+// STD includes
+#include <iostream>
+
+// Simple test that check the values are correctly set
+int ctkDICOMQueryTest1( int argc, char * argv [] )
+{
+  QCoreApplication app(argc, argv);
+
+  ctkDICOMQuery query;
+
+  // check default values
+  if (!query.callingAETitle().isEmpty() ||
+      !query.calledAETitle().isEmpty() ||
+      !query.host().isEmpty() ||
+      query.port() != 0 ||
+      !query.filters().isEmpty() ||
+      !query.studyInstanceUIDQueried().isEmpty())
+    {
+    std::cerr << "ctkDICOMQuery::ctkDICOMQuery() failed: "
+              << qPrintable(query.callingAETitle()) << " "
+              << qPrintable(query.calledAETitle()) << " "
+              << qPrintable(query.host()) << " "
+              << query.port() << " "
+              << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  query.setCallingAETitle("CallingAETitle");
+  if (query.callingAETitle() != "CallingAETitle")
+    {
+    std::cerr << "ctkDICOMQuery::setCallingAETitle() failed: "
+              << qPrintable(query.callingAETitle()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  query.setCalledAETitle("CalledAETitle");
+  if (query.calledAETitle() != "CalledAETitle")
+    {
+    std::cerr << "ctkDICOMQuery::setCalledAETitle() failed: "
+              << qPrintable(query.calledAETitle()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  query.setHost("host");
+  if (query.host() != "host")
+    {
+    std::cerr << "ctkDICOMQuery::setHost() failed: "
+              << qPrintable(query.host()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  query.setPort(80);
+  if (query.port() != 80)
+    {
+    std::cerr << "ctkDICOMQuery::setPort() failed: "
+              << query.port() << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  query.setCallingAETitle("CallingAETitle");
+  if (query.callingAETitle() != "CallingAETitle")
+    {
+    std::cerr << "ctkDICOMQuery::setCallingAETitle() failed: "
+              << qPrintable(query.callingAETitle()) << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  QMap<QString,QVariant> filters;
+  filters["Name"] = QString("JohnDoe");
+  filters["StartDate"] = QString("20090101");
+  filters["EndDate"] = QString("20091231");
+
+  query.setFilters(filters);
+  if (query.filters() != filters)
+    {
+    std::cerr << "ctkDICOMDatabase::setFilters() failed: "
+              << query.filters().count() << std::endl;
+    return EXIT_FAILURE;
+    }
+  ctkDICOMDatabase database;
+  query.query(database);
+  
+  // Queried studies should be empty because we use an empty database.
+  if (!query.studyInstanceUIDQueried().isEmpty())
+    {
+    std::cerr << "ctkDICOMDatabase::query() failed: "
+              << query.studyInstanceUIDQueried().count() << std::endl;
+    return EXIT_FAILURE;
+    }
+  return EXIT_SUCCESS;
+}

+ 92 - 86
Libs/DICOM/Core/ctkDICOMQuery.cpp

@@ -60,15 +60,18 @@ class ctkDICOMQueryPrivate
 public:
   ctkDICOMQueryPrivate();
   ~ctkDICOMQueryPrivate();
-  QString CallingAETitle;
-  QString CalledAETitle;
-  QString Host;
-  int Port;
-  QMap<QString,QVariant> Filters;
-  DcmSCU SCU;
-  DcmDataset* query;
-  QStringList StudyInstanceUIDList;
 
+  /// Add a StudyInstanceUID to be queried
+  void addStudyInstanceUID(const QString& StudyInstanceUID );
+
+  QString                 CallingAETitle;
+  QString                 CalledAETitle;
+  QString                 Host;
+  int                     Port;
+  QMap<QString,QVariant>  Filters;
+  DcmSCU                  SCU;
+  DcmDataset*             Query;
+  QStringList             StudyInstanceUIDList;
 };
 
 //------------------------------------------------------------------------------
@@ -77,22 +80,29 @@ public:
 //------------------------------------------------------------------------------
 ctkDICOMQueryPrivate::ctkDICOMQueryPrivate()
 {
-  query = new DcmDataset();
+  this->Query = new DcmDataset();
+  this->Port = 0;
 }
 
 //------------------------------------------------------------------------------
 ctkDICOMQueryPrivate::~ctkDICOMQueryPrivate()
 {
-  delete query;
+  delete this->Query;
 }
 
+//------------------------------------------------------------------------------
+void ctkDICOMQueryPrivate::addStudyInstanceUID( const QString& s )
+{
+  this->StudyInstanceUIDList.append ( s );
+}
 
 //------------------------------------------------------------------------------
 // ctkDICOMQuery methods
 
 //------------------------------------------------------------------------------
-ctkDICOMQuery::ctkDICOMQuery()
-   : d_ptr(new ctkDICOMQueryPrivate)
+ctkDICOMQuery::ctkDICOMQuery(QObject* parentObject)
+  : QObject(parentObject)
+  , d_ptr(new ctkDICOMQueryPrivate)
 {
 }
 
@@ -101,53 +111,46 @@ ctkDICOMQuery::~ctkDICOMQuery()
 {
 }
 
-//------------------------------------------------------------------------------
-void ctkDICOMQuery::addStudyInstanceUID ( QString s )
-{
-  Q_D(ctkDICOMQuery);
-  d->StudyInstanceUIDList.append ( s );
-}
-
 /// Set methods for connectivity
 //------------------------------------------------------------------------------
-void ctkDICOMQuery::setCallingAETitle ( QString callingAETitle )
+void ctkDICOMQuery::setCallingAETitle( const QString& callingAETitle )
 {
   Q_D(ctkDICOMQuery);
   d->CallingAETitle = callingAETitle;
 }
 
 //------------------------------------------------------------------------------
-const QString& ctkDICOMQuery::callingAETitle() 
+QString ctkDICOMQuery::callingAETitle() const
 {
-  Q_D(ctkDICOMQuery);
+  Q_D(const ctkDICOMQuery);
   return d->CallingAETitle;
 }
 
 //------------------------------------------------------------------------------
-void ctkDICOMQuery::setCalledAETitle ( QString calledAETitle )
+void ctkDICOMQuery::setCalledAETitle( const QString& calledAETitle )
 {
   Q_D(ctkDICOMQuery);
   d->CalledAETitle = calledAETitle;
 }
 
 //------------------------------------------------------------------------------
-const QString& ctkDICOMQuery::calledAETitle()
+QString ctkDICOMQuery::calledAETitle()const
 {
-  Q_D(ctkDICOMQuery);
+  Q_D(const ctkDICOMQuery);
   return d->CalledAETitle;
 }
 
 //------------------------------------------------------------------------------
-void ctkDICOMQuery::setHost ( QString host )
+void ctkDICOMQuery::setHost( const QString& host )
 {
   Q_D(ctkDICOMQuery);
   d->Host = host;
 }
 
 //------------------------------------------------------------------------------
-const QString& ctkDICOMQuery::host()
+QString ctkDICOMQuery::host() const
 {
-  Q_D(ctkDICOMQuery);
+  Q_D(const ctkDICOMQuery);
   return d->Host;
 }
 
@@ -159,30 +162,30 @@ void ctkDICOMQuery::setPort ( int port )
 }
 
 //------------------------------------------------------------------------------
-int ctkDICOMQuery::port()
+int ctkDICOMQuery::port()const
 {
-  Q_D(ctkDICOMQuery);
+  Q_D(const ctkDICOMQuery);
   return d->Port;
 }
 
 //------------------------------------------------------------------------------
-void ctkDICOMQuery::setFilters ( QMap<QString,QVariant> filters ) 
+void ctkDICOMQuery::setFilters( const QMap<QString,QVariant>& filters ) 
 {
   Q_D(ctkDICOMQuery);
   d->Filters = filters;
 }
 
 //------------------------------------------------------------------------------
-QMap<QString,QVariant> ctkDICOMQuery::filters()
+QMap<QString,QVariant> ctkDICOMQuery::filters()const
 {
-  Q_D(ctkDICOMQuery);
+  Q_D(const ctkDICOMQuery);
   return d->Filters;
 }
 
 //------------------------------------------------------------------------------
-QStringList ctkDICOMQuery::studyInstanceUIDQueried()
+QStringList ctkDICOMQuery::studyInstanceUIDQueried()const
 {
-  Q_D(ctkDICOMQuery);
+  Q_D(const ctkDICOMQuery);
   return d->StudyInstanceUIDList;
 }
 
@@ -191,6 +194,9 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
 {
   // ctkDICOMDatabase::setDatabase ( database );
   Q_D(ctkDICOMQuery);
+  // In the following, we emit progress(int) after progress(QString), this
+  // is in case the connected object doesn't refresh its ui when the progress
+  // message is updated but only if the progress value is (e.g. QProgressDialog)
   if ( database.database().isOpen() )
     {
     logger.debug ( "DB open in Query" );
@@ -234,82 +240,82 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
   d->SCU.negotiateAssociation();
 
   // Clear the query
-  unsigned long elements = d->query->card();
+  unsigned long elements = d->Query->card();
   // Clean it out
   for ( unsigned long i = 0; i < elements; i++ ) 
     {
-    d->query->remove ( 0ul );
+    d->Query->remove ( 0ul );
     }
-  d->query->insertEmptyElement ( DCM_PatientID );
-  d->query->insertEmptyElement ( DCM_PatientsName );
-  d->query->insertEmptyElement ( DCM_PatientsBirthDate );
-  d->query->insertEmptyElement ( DCM_StudyID );
-  d->query->insertEmptyElement ( DCM_StudyInstanceUID );
-  d->query->insertEmptyElement ( DCM_StudyDescription );
-  d->query->insertEmptyElement ( DCM_StudyDate );
-  d->query->insertEmptyElement ( DCM_SeriesNumber );
-  d->query->insertEmptyElement ( DCM_SeriesDescription );
-  d->query->insertEmptyElement ( DCM_SeriesInstanceUID );
-  d->query->insertEmptyElement ( DCM_StudyTime );
-  d->query->insertEmptyElement ( DCM_SeriesDate );
-  d->query->insertEmptyElement ( DCM_SeriesTime );
-  d->query->insertEmptyElement ( DCM_Modality );
-  d->query->insertEmptyElement ( DCM_ModalitiesInStudy );
-  d->query->insertEmptyElement ( DCM_AccessionNumber );
-  d->query->insertEmptyElement ( DCM_NumberOfSeriesRelatedInstances ); // Number of images in the series
-  d->query->insertEmptyElement ( DCM_NumberOfStudyRelatedInstances ); // Number of images in the series
-  d->query->insertEmptyElement ( DCM_NumberOfStudyRelatedSeries ); // Number of images in the series
-
-  d->query->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" );
+  d->Query->insertEmptyElement ( DCM_PatientID );
+  d->Query->insertEmptyElement ( DCM_PatientsName );
+  d->Query->insertEmptyElement ( DCM_PatientsBirthDate );
+  d->Query->insertEmptyElement ( DCM_StudyID );
+  d->Query->insertEmptyElement ( DCM_StudyInstanceUID );
+  d->Query->insertEmptyElement ( DCM_StudyDescription );
+  d->Query->insertEmptyElement ( DCM_StudyDate );
+  d->Query->insertEmptyElement ( DCM_SeriesNumber );
+  d->Query->insertEmptyElement ( DCM_SeriesDescription );
+  d->Query->insertEmptyElement ( DCM_SeriesInstanceUID );
+  d->Query->insertEmptyElement ( DCM_StudyTime );
+  d->Query->insertEmptyElement ( DCM_SeriesDate );
+  d->Query->insertEmptyElement ( DCM_SeriesTime );
+  d->Query->insertEmptyElement ( DCM_Modality );
+  d->Query->insertEmptyElement ( DCM_ModalitiesInStudy );
+  d->Query->insertEmptyElement ( DCM_AccessionNumber );
+  d->Query->insertEmptyElement ( DCM_NumberOfSeriesRelatedInstances ); // Number of images in the series
+  d->Query->insertEmptyElement ( DCM_NumberOfStudyRelatedInstances ); // Number of images in the series
+  d->Query->insertEmptyElement ( DCM_NumberOfStudyRelatedSeries ); // Number of images in the series
+
+  d->Query->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" );
 
   foreach( QString key, d->Filters.keys() )
-  {
-    if ( key == QString("Name") )
     {
+    if ( key == QString("Name") )
+      {
       // make the filter a wildcard in dicom style
-      d->query->putAndInsertString( DCM_PatientsName,
+      d->Query->putAndInsertString( DCM_PatientsName,
         (QString("*") + d->Filters[key].toString() + QString("*")).toAscii().data());
-    }
+      }
     if ( key == QString("Study") )
-    {
+      {
       // make the filter a wildcard in dicom style
-      d->query->putAndInsertString( DCM_StudyDescription,
+      d->Query->putAndInsertString( DCM_StudyDescription,
         (QString("*") + d->Filters[key].toString() + QString("*")).toAscii().data());
-    }
+      }
     if ( key == QString("Series") )
-    {
+      {
       // make the filter a wildcard in dicom style
-      d->query->putAndInsertString( DCM_SeriesDescription,
+      d->Query->putAndInsertString( DCM_SeriesDescription,
         (QString("*") + d->Filters[key].toString() + QString("*")).toAscii().data());
-    }
+      }
     if ( key == QString("ID") )
-    {
+      {
       // make the filter a wildcard in dicom style
-      d->query->putAndInsertString( DCM_PatientID,
+      d->Query->putAndInsertString( DCM_PatientID,
         (QString("*") + d->Filters[key].toString() + QString("*")).toAscii().data());
-    }
+      }
     if ( key == QString("Modalities") )
-    {
+      {
       // make the filter be an "OR" of modalities using backslash (dicom-style)
       QString modalitySearch("");
-      foreach (QString modality, d->Filters[key].toStringList())
-      {
+      foreach (const QString& modality, d->Filters[key].toStringList())
+        {
         modalitySearch += modality + QString("\\");
-      }
+        }
       modalitySearch.chop(1); // remove final backslash
       logger.debug("modalitySearch " + modalitySearch);
-      d->query->putAndInsertString( DCM_ModalitiesInStudy, modalitySearch.toAscii().data() );
+      d->Query->putAndInsertString( DCM_ModalitiesInStudy, modalitySearch.toAscii().data() );
+      }
     }
-  }
 
   if ( d->Filters.keys().contains("StartDate") && d->Filters.keys().contains("EndDate") )
-  {
+    {
     QString dateRange = d->Filters["StartDate"].toString() + 
                           QString("-") + 
                               d->Filters["EndDate"].toString();
-    d->query->putAndInsertString ( DCM_StudyDate, dateRange.toAscii().data() );
+    d->Query->putAndInsertString ( DCM_StudyDate, dateRange.toAscii().data() );
     logger.debug("Query on study time " + dateRange);
-  }
+    }
 
   emit progress(30);
 
@@ -319,7 +325,7 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
   presentationContex = d->SCU.findPresentationContextID ( UID_FINDStudyRootQueryRetrieveInformationModel, UID_LittleEndianExplicitTransferSyntax );
   if ( presentationContex == 0 )
     {
-  presentationContex = d->SCU.findPresentationContextID ( UID_FINDStudyRootQueryRetrieveInformationModel, UID_BigEndianExplicitTransferSyntax );
+    presentationContex = d->SCU.findPresentationContextID ( UID_FINDStudyRootQueryRetrieveInformationModel, UID_BigEndianExplicitTransferSyntax );
     }
   if ( presentationContex == 0 )
     {
@@ -338,7 +344,7 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
     }
   emit progress(40);
 
-  OFCondition status = d->SCU.sendFINDRequest ( presentationContex, d->query, responses );
+  OFCondition status = d->SCU.sendFINDRequest ( presentationContex, d->Query, responses );
   if ( status.good() )
     {
     logger.debug ( "Find succeded" );
@@ -359,13 +365,13 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
       database.insert ( dataset );
       OFString StudyInstanceUID;
       dataset->findAndGetOFString ( DCM_StudyInstanceUID, StudyInstanceUID );
-      this->addStudyInstanceUID ( QString ( StudyInstanceUID.c_str() ) );
+      d->addStudyInstanceUID ( QString ( StudyInstanceUID.c_str() ) );
       }
     }
   delete responses;
 
   // Now search each Study
-  d->query->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" );
+  d->Query->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" );
   float progressRatio = 25. / d->StudyInstanceUIDList.count();
   int i = 0;
   foreach ( QString StudyInstanceUID, d->StudyInstanceUIDList )
@@ -374,9 +380,9 @@ void ctkDICOMQuery::query(ctkDICOMDatabase& database )
     emit progress(QString("Starting Series C-FIND for Series: ") + StudyInstanceUID);
     emit progress(50 + (progressRatio * i++));
 
-    d->query->putAndInsertString ( DCM_StudyInstanceUID, StudyInstanceUID.toStdString().c_str() );
+    d->Query->putAndInsertString ( DCM_StudyInstanceUID, StudyInstanceUID.toStdString().c_str() );
     responses = new FINDResponses();
-    status = d->SCU.sendFINDRequest ( 0, d->query, responses );
+    status = d->SCU.sendFINDRequest ( 0, d->Query, responses );
     if ( status.good() )
       {
       for ( OFListIterator(FINDResponse*) it = responses->begin(); it != responses->end(); it++ )

+ 39 - 18
Libs/DICOM/Core/ctkDICOMQuery.h

@@ -39,41 +39,62 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMQuery : public QObject
   Q_OBJECT
   Q_PROPERTY(QString callingAETitle READ callingAETitle WRITE setCallingAETitle);
   Q_PROPERTY(QString calledAETitle READ calledAETitle WRITE setCallingAETitle);
+  Q_PROPERTY(QString host READ host WRITE setHost);
   Q_PROPERTY(int port READ port WRITE setPort);
 
 public:
-  explicit ctkDICOMQuery();
+  explicit ctkDICOMQuery(QObject* parent = 0);
   virtual ~ctkDICOMQuery();
   
   /// Set methods for connectivity
-  void setCallingAETitle ( QString callingAETitle );
-  const QString& callingAETitle();
-  void setCalledAETitle ( QString calledAETitle );
-  const QString& calledAETitle();
-  void setHost ( QString host );
-  const QString& host();
+  /// Empty by default
+  void setCallingAETitle ( const QString& callingAETitle );
+  QString callingAETitle()const;
+  /// Empty by default
+  void setCalledAETitle ( const QString& calledAETitle );
+  QString calledAETitle()const;
+  /// Empty by default
+  void setHost ( const QString& host );
+  QString host()const;
+  /// Specify a port for the packet headers.
+  /// \a port ranges from 0 to 65535.
+  /// 0 by default.
   void setPort ( int port );
-  int port();
+  int port()const;
   
   /// Query a remote DICOM Image Store SCP
+  /// You must at least set the host and port before calling query()
   void query(ctkDICOMDatabase& database);
 
-  /// Add a StudyInstanceUID to be queried
-  void addStudyInstanceUID( QString StudyInstanceUID );
-
   /// Access the list of study instance UIDs from the last query
-  QStringList studyInstanceUIDQueried();
+  QStringList studyInstanceUIDQueried()const;
 
   ///
-  // Filters are keyword/value pairs as generated by
-  // the ctkDICOMWidgets in a human readable (and editable)
-  // format.  The Query is responsible for converting these
-  // into the appropriate dicom syntax for the C-Find
-  void setFilters(QMap<QString,QVariant>);
-  QMap<QString,QVariant> filters();
+  /// Filters are keyword/value pairs as generated by
+  /// the ctkDICOMWidgets in a human readable (and editable)
+  /// format.  The Query is responsible for converting these
+  /// into the appropriate dicom syntax for the C-Find
+  /// Currently supports the keys: Name, Study, Series, ID, Modalities,
+  /// StartDate and EndDate
+  /// Key         DICOM Tag                Type        Example
+  /// -----------------------------------------------------------
+  /// Name        DCM_PatientsName         QString     JOHNDOE
+  /// Study       DCM_StudyDescription     QString     
+  /// Series      DCM_SeriesDescription    QString
+  /// ID          DCM_PatientID            QString
+  /// Modalities  DCM_ModalitiesInStudy    QStringList CT, MR, MN
+  /// StartDate   DCM_StudyDate            QString     20090101
+  /// EndDate     DCM_StudyDate            QString     20091231
+  /// No filter (empty) by default.
+  void setFilters(const QMap<QString,QVariant>&);
+  QMap<QString,QVariant> filters()const;
 
 signals:
+  /// Signal is emitted inside the query() function. It ranges from 0 to 100.
+  /// In case of an error, you are assured that the progress value 100 is fired
   void progress(int progress);
+  /// Signal is emitted inside the query() function. It sends the different
+  /// the function is at.
   void progress(const QString& message);
 
 protected: