ctkDICOMRetrieve.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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. #include <stdexcept>
  15. // Qt includes
  16. #include <QSqlQuery>
  17. #include <QSqlRecord>
  18. #include <QVariant>
  19. #include <QDate>
  20. #include <QStringList>
  21. #include <QSet>
  22. #include <QFile>
  23. #include <QDirIterator>
  24. #include <QFileInfo>
  25. #include <QDebug>
  26. // ctkDICOMCore includes
  27. #include "ctkDICOMRetrieve.h"
  28. #include "ctkLogger.h"
  29. // DCMTK includes
  30. #ifndef WIN32
  31. #define HAVE_CONFIG_H
  32. #endif
  33. #include "dcmtk/dcmnet/dimse.h"
  34. #include "dcmtk/dcmnet/diutil.h"
  35. #include <dcmtk/dcmdata/dcfilefo.h>
  36. #include <dcmtk/dcmdata/dcfilefo.h>
  37. #include <dcmtk/dcmdata/dcdeftag.h>
  38. #include <dcmtk/dcmdata/dcdatset.h>
  39. #include <dcmtk/ofstd/ofcond.h>
  40. #include <dcmtk/ofstd/ofstring.h>
  41. #include <dcmtk/ofstd/ofstd.h> /* for class OFStandard */
  42. #include <dcmtk/dcmdata/dcddirif.h> /* for class DicomDirInterface */
  43. #include <dcmtk/dcmjpeg/djdecode.h> /* for dcmjpeg decoders */
  44. #include <dcmtk/dcmjpeg/djencode.h> /* for dcmjpeg encoders */
  45. #include <dcmtk/dcmdata/dcrledrg.h> /* for DcmRLEDecoderRegistration */
  46. #include <dcmtk/dcmdata/dcrleerg.h> /* for DcmRLEEncoderRegistration */
  47. #include <dcmtk/dcmnet/scu.h>
  48. #include "dcmtk/oflog/oflog.h"
  49. static ctkLogger logger("org.commontk.dicom.DICOMRetrieve");
  50. //------------------------------------------------------------------------------
  51. class ctkDICOMRetrievePrivate
  52. {
  53. public:
  54. ctkDICOMRetrievePrivate();
  55. ~ctkDICOMRetrievePrivate();
  56. QString CallingAETitle;
  57. QString CalledAETitle;
  58. QString Host;
  59. int CallingPort;
  60. int CalledPort;
  61. DcmSCU SCU; // TODO: not used yet.
  62. DcmDataset* parameters;
  63. QString MoveDestinationAETitle;
  64. QSharedPointer<ctkDICOMDatabase> RetrieveDatabase;
  65. // do the retrieve, handling both series and study retrieves
  66. enum RetrieveType { RetrieveSeries, RetrieveStudy };
  67. bool retrieve ( QString UID, RetrieveType retriveType );
  68. };
  69. //------------------------------------------------------------------------------
  70. // ctkDICOMRetrievePrivate methods
  71. //------------------------------------------------------------------------------
  72. ctkDICOMRetrievePrivate::ctkDICOMRetrievePrivate()
  73. {
  74. this->parameters = new DcmDataset();
  75. this->RetrieveDatabase = QSharedPointer<ctkDICOMDatabase> (0);
  76. this->CallingPort = 0;
  77. this->CalledPort = 0;
  78. }
  79. //------------------------------------------------------------------------------
  80. ctkDICOMRetrievePrivate::~ctkDICOMRetrievePrivate()
  81. {
  82. delete this->parameters;
  83. }
  84. //------------------------------------------------------------------------------
  85. bool ctkDICOMRetrievePrivate::retrieve ( QString UID, RetrieveType retriveType ) {
  86. if ( !this->RetrieveDatabase )
  87. {
  88. logger.error ( "Must have RetrieveDatabase for retrieve transaction" );
  89. return false;
  90. }
  91. // Register the JPEG libraries in case we need them
  92. // (registration only happens once, so it's okay to call repeatedly)
  93. // register global JPEG decompression codecs
  94. DJDecoderRegistration::registerCodecs();
  95. // register global JPEG compression codecs
  96. DJEncoderRegistration::registerCodecs();
  97. // register RLE compression codec
  98. DcmRLEEncoderRegistration::registerCodecs();
  99. // register RLE decompression codec
  100. DcmRLEDecoderRegistration::registerCodecs();
  101. // Set the DCMTK log level
  102. log4cplus::Logger rootLogger = log4cplus::Logger::getRoot();
  103. rootLogger.setLogLevel(log4cplus::DEBUG_LOG_LEVEL);
  104. // TODO: use this->SCU instead ?
  105. DcmSCU scu;
  106. scu.setAETitle ( OFString(this->CallingAETitle.toStdString().c_str()) );
  107. scu.setPort ( this->CallingPort );
  108. scu.setPeerAETitle ( OFString(this->CalledAETitle.toStdString().c_str()) );
  109. scu.setPeerHostName ( OFString(this->Host.toStdString().c_str()) );
  110. scu.setPeerPort ( this->CalledPort );
  111. scu.setMoveDestinationAETitle ( OFString(this->MoveDestinationAETitle.toStdString().c_str()) );
  112. logger.info ( "Setting Transfer Syntaxes" );
  113. OFList<OFString> transferSyntaxes;
  114. transferSyntaxes.push_back ( UID_LittleEndianExplicitTransferSyntax );
  115. transferSyntaxes.push_back ( UID_BigEndianExplicitTransferSyntax );
  116. transferSyntaxes.push_back ( UID_LittleEndianImplicitTransferSyntax );
  117. scu.addPresentationContext ( UID_FINDStudyRootQueryRetrieveInformationModel, transferSyntaxes );
  118. scu.addPresentationContext ( UID_MOVEStudyRootQueryRetrieveInformationModel, transferSyntaxes );
  119. if ( !scu.initNetwork().good() )
  120. {
  121. logger.error ( "Error initializing the network" );
  122. return false;
  123. }
  124. logger.debug ( "Negotiating Association" );
  125. if ( !scu.negotiateAssociation().good() )
  126. {
  127. logger.error ( "Error negotiating association" );
  128. return false;;
  129. }
  130. logger.debug ( "Setting Parameters" );
  131. // Clear the query
  132. unsigned long elements = this->parameters->card();
  133. // Clean it out
  134. for ( unsigned long i = 0; i < elements; i++ )
  135. {
  136. this->parameters->remove ( 0ul );
  137. }
  138. if ( retriveType == RetrieveSeries )
  139. {
  140. this->parameters->putAndInsertString ( DCM_QueryRetrieveLevel, "SERIES" );
  141. this->parameters->putAndInsertString ( DCM_SeriesInstanceUID, UID.toStdString().c_str() );
  142. }
  143. else
  144. {
  145. this->parameters->putAndInsertString ( DCM_QueryRetrieveLevel, "STUDY" );
  146. this->parameters->putAndInsertString ( DCM_StudyInstanceUID, UID.toStdString().c_str() );
  147. }
  148. logger.debug ( "Sending Move Request" );
  149. MOVEResponses *responses = new MOVEResponses();
  150. OFCondition status = scu.sendMOVERequest ( 0, this->parameters, responses );
  151. if (!status.good())
  152. {
  153. logger.error ( "MOVE Request failed: " + QString ( status.text() ) );
  154. return false;
  155. }
  156. logger.debug ( "Find succeded" );
  157. logger.debug ( "Making Output Directory" );
  158. QDir directory = QDir( RetrieveDatabase->databaseDirectory() );
  159. if ( responses->begin() == responses->end() )
  160. {
  161. logger.error ( "No responses!" );
  162. throw std::runtime_error( std::string("No responses!") );
  163. }
  164. // Write the responses out to disk
  165. for ( OFListIterator(FINDResponse*) it = responses->begin(); it != responses->end(); it++ )
  166. {
  167. DcmDataset *dataset = (*it)->m_dataset;
  168. if ( dataset != NULL )
  169. {
  170. logger.debug ( "Got a valid dataset" );
  171. // Save in correct directory
  172. E_TransferSyntax output_transfersyntax = dataset->getOriginalXfer();
  173. dataset->chooseRepresentation( output_transfersyntax, NULL );
  174. if ( !dataset->canWriteXfer( output_transfersyntax ) )
  175. {
  176. // Pick EXS_LittleEndianExplicit as our default
  177. output_transfersyntax = EXS_LittleEndianExplicit;
  178. }
  179. DcmXfer opt_oxferSyn( output_transfersyntax );
  180. if ( !dataset->chooseRepresentation( opt_oxferSyn.getXfer(), NULL ).bad() )
  181. {
  182. DcmFileFormat* fileformat = new DcmFileFormat ( dataset );
  183. // Follow dcmdjpeg example
  184. fileformat->loadAllDataIntoMemory();
  185. OFString SOPInstanceUID;
  186. dataset->findAndGetOFString ( DCM_SOPInstanceUID, SOPInstanceUID );
  187. QFileInfo fi ( directory, QString ( SOPInstanceUID.c_str() ) );
  188. logger.debug ( "Saving file: " + fi.absoluteFilePath() );
  189. status = fileformat->saveFile ( fi.absoluteFilePath().toStdString().c_str(), opt_oxferSyn.getXfer() );
  190. if ( !status.good() )
  191. {
  192. logger.error ( "Error saving file: " + fi.absoluteFilePath() + " Error is " + status.text() );
  193. }
  194. RetrieveDatabase->insert( dataset, true );
  195. delete fileformat;
  196. }
  197. }
  198. }
  199. delete responses;
  200. //if ( !scu.dropNetwork().good() )
  201. //{
  202. //logger.error ( "Error dropping the network" );
  203. //return false;
  204. //}
  205. return true;
  206. }
  207. //------------------------------------------------------------------------------
  208. // ctkDICOMRetrieve methods
  209. //------------------------------------------------------------------------------
  210. ctkDICOMRetrieve::ctkDICOMRetrieve()
  211. : d_ptr(new ctkDICOMRetrievePrivate)
  212. {
  213. }
  214. //------------------------------------------------------------------------------
  215. ctkDICOMRetrieve::~ctkDICOMRetrieve()
  216. {
  217. }
  218. //------------------------------------------------------------------------------
  219. /// Set methods for connectivity
  220. void ctkDICOMRetrieve::setCallingAETitle( const QString& callingAETitle )
  221. {
  222. Q_D(ctkDICOMRetrieve);
  223. d->CallingAETitle = callingAETitle;
  224. }
  225. //------------------------------------------------------------------------------
  226. QString ctkDICOMRetrieve::callingAETitle() const
  227. {
  228. Q_D(const ctkDICOMRetrieve);
  229. return d->CallingAETitle;
  230. }
  231. //------------------------------------------------------------------------------
  232. void ctkDICOMRetrieve::setCalledAETitle( const QString& calledAETitle )
  233. {
  234. Q_D(ctkDICOMRetrieve);
  235. d->CalledAETitle = calledAETitle;
  236. }
  237. //------------------------------------------------------------------------------
  238. QString ctkDICOMRetrieve::calledAETitle()const
  239. {
  240. Q_D(const ctkDICOMRetrieve);
  241. return d->CalledAETitle;
  242. }
  243. //------------------------------------------------------------------------------
  244. void ctkDICOMRetrieve::setHost( const QString& host )
  245. {
  246. Q_D(ctkDICOMRetrieve);
  247. d->Host = host;
  248. }
  249. //------------------------------------------------------------------------------
  250. QString ctkDICOMRetrieve::host()const
  251. {
  252. Q_D(const ctkDICOMRetrieve);
  253. return d->Host;
  254. }
  255. //------------------------------------------------------------------------------
  256. void ctkDICOMRetrieve::setCallingPort( int port )
  257. {
  258. Q_D(ctkDICOMRetrieve);
  259. d->CallingPort = port;
  260. }
  261. //------------------------------------------------------------------------------
  262. int ctkDICOMRetrieve::callingPort()const
  263. {
  264. Q_D(const ctkDICOMRetrieve);
  265. return d->CallingPort;
  266. }
  267. //------------------------------------------------------------------------------
  268. void ctkDICOMRetrieve::setCalledPort( int port )
  269. {
  270. Q_D(ctkDICOMRetrieve);
  271. d->CalledPort = port;
  272. }
  273. //------------------------------------------------------------------------------
  274. int ctkDICOMRetrieve::calledPort()const
  275. {
  276. Q_D(const ctkDICOMRetrieve);
  277. return d->CalledPort;
  278. }
  279. //------------------------------------------------------------------------------
  280. void ctkDICOMRetrieve::setMoveDestinationAETitle( const QString& moveDestinationAETitle )
  281. {
  282. Q_D(ctkDICOMRetrieve);
  283. d->MoveDestinationAETitle = moveDestinationAETitle;
  284. }
  285. //------------------------------------------------------------------------------
  286. QString ctkDICOMRetrieve::moveDestinationAETitle()const
  287. {
  288. Q_D(const ctkDICOMRetrieve);
  289. return d->MoveDestinationAETitle;
  290. }
  291. //------------------------------------------------------------------------------
  292. void ctkDICOMRetrieve::setRetrieveDatabase(QSharedPointer<ctkDICOMDatabase> dicomDatabase)
  293. {
  294. Q_D(ctkDICOMRetrieve);
  295. d->RetrieveDatabase = dicomDatabase;
  296. }
  297. //------------------------------------------------------------------------------
  298. QSharedPointer<ctkDICOMDatabase> ctkDICOMRetrieve::retrieveDatabase()const
  299. {
  300. Q_D(const ctkDICOMRetrieve);
  301. return d->RetrieveDatabase;
  302. }
  303. //------------------------------------------------------------------------------
  304. bool ctkDICOMRetrieve::retrieveSeries( const QString& seriesInstanceUID )
  305. {
  306. Q_D(ctkDICOMRetrieve);
  307. logger.info ( "Starting retrieveSeries" );
  308. return d->retrieve ( seriesInstanceUID, ctkDICOMRetrievePrivate::RetrieveSeries );
  309. }
  310. //------------------------------------------------------------------------------
  311. bool ctkDICOMRetrieve::retrieveStudy( const QString& studyInstanceUID )
  312. {
  313. Q_D(ctkDICOMRetrieve);
  314. logger.info ( "Starting retrieveStudy" );
  315. return d->retrieve ( studyInstanceUID, ctkDICOMRetrievePrivate::RetrieveStudy );
  316. }