ctkDICOMQuery.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.commontk.org/LICENSE
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QSqlQuery>
  16. #include <QSqlRecord>
  17. #include <QVariant>
  18. #include <QDate>
  19. #include <QStringList>
  20. #include <QSet>
  21. #include <QFile>
  22. #include <QDirIterator>
  23. #include <QFileInfo>
  24. #include <QDebug>
  25. // ctkDICOMCore includes
  26. #include "ctkDICOMQuery.h"
  27. #include "ctkLogger.h"
  28. // DCMTK includes
  29. #ifndef WIN32
  30. #define HAVE_CONFIG_H
  31. #endif
  32. #include "dcmtk/dcmnet/dimse.h"
  33. #include "dcmtk/dcmnet/diutil.h"
  34. #include <dcmtk/dcmdata/dcfilefo.h>
  35. #include <dcmtk/dcmdata/dcfilefo.h>
  36. #include <dcmtk/dcmdata/dcdeftag.h>
  37. #include <dcmtk/dcmdata/dcdatset.h>
  38. #include <dcmtk/ofstd/ofcond.h>
  39. #include <dcmtk/ofstd/ofstring.h>
  40. #include <dcmtk/ofstd/ofstd.h> /* for class OFStandard */
  41. #include <dcmtk/dcmdata/dcddirif.h> /* for class DicomDirInterface */
  42. #include <dcmtk/dcmnet/scu.h>
  43. static ctkLogger logger ( "org.commontk.dicom.DICOMQuery" );
  44. //------------------------------------------------------------------------------
  45. class ctkDICOMQueryPrivate
  46. {
  47. public:
  48. ctkDICOMQueryPrivate();
  49. ~ctkDICOMQueryPrivate();
  50. QString CallingAETitle;
  51. QString CalledAETitle;
  52. QString Host;
  53. int Port;
  54. QMap<QString,QVariant> Filters;
  55. DcmSCU SCU;
  56. DcmDataset* query;
  57. QStringList StudyInstanceUIDList;
  58. };
  59. //------------------------------------------------------------------------------
  60. // ctkDICOMQueryPrivate methods
  61. //------------------------------------------------------------------------------
  62. ctkDICOMQueryPrivate::ctkDICOMQueryPrivate()
  63. {
  64. query = new DcmDataset();
  65. }
  66. //------------------------------------------------------------------------------
  67. ctkDICOMQueryPrivate::~ctkDICOMQueryPrivate()
  68. {
  69. delete query;
  70. }
  71. //------------------------------------------------------------------------------
  72. // ctkDICOMQuery methods
  73. //------------------------------------------------------------------------------
  74. ctkDICOMQuery::ctkDICOMQuery()
  75. : d_ptr(new ctkDICOMQueryPrivate)
  76. {
  77. }
  78. //------------------------------------------------------------------------------
  79. ctkDICOMQuery::~ctkDICOMQuery()
  80. {
  81. }
  82. //------------------------------------------------------------------------------
  83. void ctkDICOMQuery::addStudyInstanceUID ( QString s )
  84. {
  85. Q_D(ctkDICOMQuery);
  86. d->StudyInstanceUIDList.append ( s );
  87. }
  88. /// Set methods for connectivity
  89. //------------------------------------------------------------------------------
  90. void ctkDICOMQuery::setCallingAETitle ( QString callingAETitle )
  91. {
  92. Q_D(ctkDICOMQuery);
  93. d->CallingAETitle = callingAETitle;
  94. }
  95. //------------------------------------------------------------------------------
  96. const QString& ctkDICOMQuery::callingAETitle()
  97. {
  98. Q_D(ctkDICOMQuery);
  99. return d->CallingAETitle;
  100. }
  101. //------------------------------------------------------------------------------
  102. void ctkDICOMQuery::setCalledAETitle ( QString calledAETitle )
  103. {
  104. Q_D(ctkDICOMQuery);
  105. d->CalledAETitle = calledAETitle;
  106. }
  107. //------------------------------------------------------------------------------
  108. const QString& ctkDICOMQuery::calledAETitle()
  109. {
  110. Q_D(ctkDICOMQuery);
  111. return d->CalledAETitle;
  112. }
  113. //------------------------------------------------------------------------------
  114. void ctkDICOMQuery::setHost ( QString host )
  115. {
  116. Q_D(ctkDICOMQuery);
  117. d->Host = host;
  118. }
  119. //------------------------------------------------------------------------------
  120. const QString& ctkDICOMQuery::host()
  121. {
  122. Q_D(ctkDICOMQuery);
  123. return d->Host;
  124. }
  125. //------------------------------------------------------------------------------
  126. void ctkDICOMQuery::setPort ( int port )
  127. {
  128. Q_D(ctkDICOMQuery);
  129. d->Port = port;
  130. }
  131. //------------------------------------------------------------------------------
  132. int ctkDICOMQuery::port()
  133. {
  134. Q_D(ctkDICOMQuery);
  135. return d->Port;
  136. }
  137. //------------------------------------------------------------------------------
  138. void ctkDICOMQuery::setFilters ( QMap<QString,QVariant> filters )
  139. {
  140. Q_D(ctkDICOMQuery);
  141. d->Filters = filters;
  142. }
  143. //------------------------------------------------------------------------------
  144. QMap<QString,QVariant> ctkDICOMQuery::filters()
  145. {
  146. Q_D(ctkDICOMQuery);
  147. return d->Filters;
  148. }
  149. //------------------------------------------------------------------------------
  150. QStringList ctkDICOMQuery::studyInstanceUIDQueried()
  151. {
  152. Q_D(ctkDICOMQuery);
  153. return d->StudyInstanceUIDList;
  154. }
  155. //------------------------------------------------------------------------------
  156. void ctkDICOMQuery::query(ctkDICOMDatabase& database )
  157. {
  158. // ctkDICOMDatabase::setDatabase ( database );
  159. Q_D(ctkDICOMQuery);
  160. if ( database.database().isOpen() )
  161. {
  162. logger.debug ( "DB open in Query" );
  163. emit progress("DB open in Query");
  164. }
  165. else
  166. {
  167. logger.debug ( "DB not open in Query" );
  168. emit progress("DB not open in Query");
  169. }
  170. emit progress(0);
  171. d->StudyInstanceUIDList.clear();
  172. d->SCU.setAETitle ( OFString(this->callingAETitle().toStdString().c_str()) );
  173. d->SCU.setPeerAETitle ( OFString(this->calledAETitle().toStdString().c_str()) );
  174. d->SCU.setPeerHostName ( OFString(this->host().toStdString().c_str()) );
  175. d->SCU.setPeerPort ( this->port() );
  176. logger.error ( "Setting Transfer Syntaxes" );
  177. emit progress("Setting Transfer Syntaxes");
  178. emit progress(10);
  179. OFList<OFString> transferSyntaxes;
  180. transferSyntaxes.push_back ( UID_LittleEndianExplicitTransferSyntax );
  181. transferSyntaxes.push_back ( UID_BigEndianExplicitTransferSyntax );
  182. transferSyntaxes.push_back ( UID_LittleEndianImplicitTransferSyntax );
  183. d->SCU.addPresentationContext ( UID_FINDStudyRootQueryRetrieveInformationModel, transferSyntaxes );
  184. // d->SCU.addPresentationContext ( UID_VerificationSOPClass, transferSyntaxes );
  185. if ( !d->SCU.initNetwork().good() )
  186. {
  187. logger.error( "Error initializing the network" );
  188. emit progress("Error initializing the network");
  189. emit progress(100);
  190. return;
  191. }
  192. logger.debug ( "Negotiating Association" );
  193. emit progress("Negatiating Association");
  194. emit progress(20);
  195. d->SCU.negotiateAssociation();
  196. // Clear the query
  197. unsigned long elements = d->query->card();
  198. // Clean it out
  199. for ( unsigned long i = 0; i < elements; i++ )
  200. {
  201. d->query->remove ( 0ul );
  202. }
  203. d->query->insertEmptyElement ( DCM_PatientID );
  204. d->query->insertEmptyElement ( DCM_PatientsName );
  205. d->query->insertEmptyElement ( DCM_PatientsBirthDate );
  206. d->query->insertEmptyElement ( DCM_StudyID );
  207. d->query->insertEmptyElement ( DCM_StudyInstanceUID );
  208. d->query->insertEmptyElement ( DCM_StudyDescription );
  209. d->query->insertEmptyElement ( DCM_StudyDate );
  210. d->query->insertEmptyElement ( DCM_SeriesNumber );
  211. d->query->insertEmptyElement ( DCM_SeriesDescription );
  212. d->query->insertEmptyElement ( DCM_SeriesInstanceUID );
  213. d->query->insertEmptyElement ( DCM_StudyTime );
  214. d->query->insertEmptyElement ( DCM_SeriesDate );
  215. d->query->insertEmptyElement ( DCM_SeriesTime );
  216. d->query->insertEmptyElement ( DCM_Modality );
  217. d->query->insertEmptyElement ( DCM_ModalitiesInStudy );
  218. d->query->insertEmptyElement ( DCM_AccessionNumber );
  219. d->query->insertEmptyElement ( DCM_NumberOfSeriesRelatedInstances ); // Number of images in the series
  220. d->query->insertEmptyElement ( DCM_NumberOfStudyRelatedInstances ); // Number of images in the series
  221. d->query->insertEmptyElement ( DCM_NumberOfStudyRelatedSeries ); // Number of images in the series
  222. d->query->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" );
  223. foreach( QString key, d->Filters.keys() )
  224. {
  225. if ( key == QString("Name") )
  226. {
  227. // make the filter a wildcard in dicom style
  228. d->query->putAndInsertString( DCM_PatientsName,
  229. (QString("*") + d->Filters[key].toString() + QString("*")).toAscii().data());
  230. }
  231. if ( key == QString("Study") )
  232. {
  233. // make the filter a wildcard in dicom style
  234. d->query->putAndInsertString( DCM_StudyDescription,
  235. (QString("*") + d->Filters[key].toString() + QString("*")).toAscii().data());
  236. }
  237. if ( key == QString("Series") )
  238. {
  239. // make the filter a wildcard in dicom style
  240. d->query->putAndInsertString( DCM_SeriesDescription,
  241. (QString("*") + d->Filters[key].toString() + QString("*")).toAscii().data());
  242. }
  243. if ( key == QString("ID") )
  244. {
  245. // make the filter a wildcard in dicom style
  246. d->query->putAndInsertString( DCM_PatientID,
  247. (QString("*") + d->Filters[key].toString() + QString("*")).toAscii().data());
  248. }
  249. if ( key == QString("Modalities") )
  250. {
  251. // make the filter be an "OR" of modalities using backslash (dicom-style)
  252. QString modalitySearch("");
  253. foreach (QString modality, d->Filters[key].toStringList())
  254. {
  255. modalitySearch += modality + QString("\\");
  256. }
  257. modalitySearch.chop(1); // remove final backslash
  258. logger.debug("modalitySearch " + modalitySearch);
  259. d->query->putAndInsertString( DCM_ModalitiesInStudy, modalitySearch.toAscii().data() );
  260. }
  261. }
  262. if ( d->Filters.keys().contains("StartDate") && d->Filters.keys().contains("EndDate") )
  263. {
  264. QString dateRange = d->Filters["StartDate"].toString() +
  265. QString("-") +
  266. d->Filters["EndDate"].toString();
  267. d->query->putAndInsertString ( DCM_StudyDate, dateRange.toAscii().data() );
  268. logger.debug("Query on study time " + dateRange);
  269. }
  270. emit progress(30);
  271. FINDResponses *responses = new FINDResponses();
  272. Uint16 presentationContex = 0;
  273. presentationContex = d->SCU.findPresentationContextID ( UID_FINDStudyRootQueryRetrieveInformationModel, UID_LittleEndianExplicitTransferSyntax );
  274. if ( presentationContex == 0 )
  275. {
  276. presentationContex = d->SCU.findPresentationContextID ( UID_FINDStudyRootQueryRetrieveInformationModel, UID_BigEndianExplicitTransferSyntax );
  277. }
  278. if ( presentationContex == 0 )
  279. {
  280. presentationContex = d->SCU.findPresentationContextID ( UID_FINDStudyRootQueryRetrieveInformationModel, UID_LittleEndianImplicitTransferSyntax );
  281. }
  282. if ( presentationContex == 0 )
  283. {
  284. logger.error ( "Failed to find acceptable presentation context" );
  285. emit progress("Failed to find acceptable presentation context");
  286. }
  287. else
  288. {
  289. logger.info ( "Found useful presentation context" );
  290. emit progress("Found useful presentation context");
  291. }
  292. emit progress(40);
  293. OFCondition status = d->SCU.sendFINDRequest ( presentationContex, d->query, responses );
  294. if ( status.good() )
  295. {
  296. logger.debug ( "Find succeded" );
  297. emit progress("Find succeded");
  298. }
  299. else
  300. {
  301. logger.error ( "Find failed" );
  302. emit progress("Find failed");
  303. }
  304. emit progress(50);
  305. for ( OFListIterator(FINDResponse*) it = responses->begin(); it != responses->end(); it++ )
  306. {
  307. DcmDataset *dataset = (*it)->m_dataset;
  308. if ( dataset != NULL )
  309. {
  310. database.insert ( dataset );
  311. OFString StudyInstanceUID;
  312. dataset->findAndGetOFString ( DCM_StudyInstanceUID, StudyInstanceUID );
  313. this->addStudyInstanceUID ( QString ( StudyInstanceUID.c_str() ) );
  314. }
  315. }
  316. delete responses;
  317. // Now search each Study
  318. d->query->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" );
  319. float progressRatio = 25. / d->StudyInstanceUIDList.count();
  320. int i = 0;
  321. foreach ( QString StudyInstanceUID, d->StudyInstanceUIDList )
  322. {
  323. logger.debug ( "Starting Series C-FIND for Series: " + StudyInstanceUID );
  324. emit progress(QString("Starting Series C-FIND for Series: ") + StudyInstanceUID);
  325. emit progress(50 + (progressRatio * i++));
  326. d->query->putAndInsertString ( DCM_StudyInstanceUID, StudyInstanceUID.toStdString().c_str() );
  327. responses = new FINDResponses();
  328. status = d->SCU.sendFINDRequest ( 0, d->query, responses );
  329. if ( status.good() )
  330. {
  331. for ( OFListIterator(FINDResponse*) it = responses->begin(); it != responses->end(); it++ )
  332. {
  333. DcmDataset *dataset = (*it)->m_dataset;
  334. if ( dataset != NULL )
  335. {
  336. database.insert ( dataset );
  337. }
  338. }
  339. logger.debug ( "Find succeded for Series: " + StudyInstanceUID );
  340. emit progress(QString("Find succeded for Series: ") + StudyInstanceUID);
  341. }
  342. else
  343. {
  344. logger.error ( "Find failed for Series: " + StudyInstanceUID );
  345. emit progress(QString("Find failed for Series: ") + StudyInstanceUID);
  346. }
  347. emit progress(50 + (progressRatio * i++));
  348. delete responses;
  349. }
  350. d->SCU.closeAssociation ( DUL_PEERREQUESTEDRELEASE );
  351. emit progress(100);
  352. }