fw4spl
SeriesEnquirer.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 2009-2018.
3  * Distributed under the terms of the GNU Lesser General Public License (LGPL) as
4  * published by the Free Software Foundation.
5  * ****** END LICENSE BLOCK ****** */
6 
7 #include "fwPacsIO/SeriesEnquirer.hpp"
8 
9 #include "fwPacsIO/exceptions/NegociateAssociationFailure.hpp"
10 #include "fwPacsIO/exceptions/NetworkInitializationFailure.hpp"
11 #include "fwPacsIO/exceptions/PresentationContextMissing.hpp"
12 #include "fwPacsIO/exceptions/RequestFailure.hpp"
13 #include "fwPacsIO/exceptions/TagMissing.hpp"
14 
15 #include <fwCore/spyLog.hpp>
16 
17 #include <fwDcmtkTools/Dictionary.hpp>
18 
19 #include <fwTools/System.hpp>
20 
21 #include <boost/filesystem/operations.hpp>
22 #include <boost/foreach.hpp>
23 
24 #include <dcmtk/config/osconfig.h>
25 #include <dcmtk/dcmdata/dcfilefo.h>
26 #include <dcmtk/dcmnet/diutil.h>
27 
28 namespace fwPacsIO
29 {
30 
31 const ::fwCom::Slots::SlotKeyType SeriesEnquirer::s_PROGRESS_CALLBACK_SLOT = "CGetProgressCallback";
32 
34  m_moveApplicationTitle(""),
35  m_path(""),
36  m_progressCallback(ProgressCallbackSlotType::sptr()),
37  m_instanceIndex(0)
38 {
39 
40 }
41 
42 // ----------------------------------------------------------------------------
43 
45 {
46 }
47 
48 // ----------------------------------------------------------------------------
49 
50 Uint8 SeriesEnquirer::findUncompressedPC(const OFString& sopClass)
51 {
52  Uint8 pc;
53  pc = this->findPresentationContextID(sopClass, UID_LittleEndianExplicitTransferSyntax);
54  if (pc == 0)
55  {
56  pc = this->findPresentationContextID(sopClass, UID_BigEndianExplicitTransferSyntax);
57  }
58  if (pc == 0)
59  {
60  pc = this->findPresentationContextID(sopClass, UID_LittleEndianImplicitTransferSyntax);
61  }
62  return pc;
63 }
64 
65 // ----------------------------------------------------------------------------
66 
67 void SeriesEnquirer::initialize(const std::string& applicationTitle, const std::string& peerHostName,
68  unsigned short peerPort, const std::string& peerApplicationTitle,
69  const std::string& moveApplicationTitle,
70  ProgressCallbackSlotType::sptr progressCallback)
71 {
72  //Save move application title for move requests
73  m_moveApplicationTitle = moveApplicationTitle;
74 
75  //Store Callback
76  m_progressCallback = progressCallback;
77 
78  //Creating folder
80  if (!m_path.empty() && !::boost::filesystem::exists(m_path))
81  {
82  ::boost::filesystem::create_directories(m_path);
83  }
84 
85  //Load dictionary
87 
88  //Configure network connection
89  this->setAETitle(applicationTitle.c_str());
90  this->setPeerHostName(peerHostName.c_str());
91  this->setPeerPort(peerPort);
92  this->setPeerAETitle(peerApplicationTitle.c_str());
93 
94  // Clear presentation context
95  this->clearPresentationContexts();
96 
97  // Use presentation context for FIND/MOVE in study root, propose all uncompressed transfer syntaxes
98  OFList < OFString > transferSyntaxes;
99  transferSyntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
100  transferSyntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
101  transferSyntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
102  transferSyntaxes.push_back(UID_JPEGProcess1TransferSyntax);
103  transferSyntaxes.push_back(UID_JPEGProcess2_4TransferSyntax);
104  transferSyntaxes.push_back(UID_JPEGProcess3_5TransferSyntax);
105  transferSyntaxes.push_back(UID_JPEGProcess6_8TransferSyntax);
106  transferSyntaxes.push_back(UID_JPEGProcess7_9TransferSyntax);
107  transferSyntaxes.push_back(UID_JPEGProcess10_12TransferSyntax);
108  transferSyntaxes.push_back(UID_JPEGProcess11_13TransferSyntax);
109  transferSyntaxes.push_back(UID_JPEGProcess14TransferSyntax);
110  transferSyntaxes.push_back(UID_JPEGProcess15TransferSyntax);
111  transferSyntaxes.push_back(UID_JPEGProcess16_18TransferSyntax);
112  transferSyntaxes.push_back(UID_JPEGProcess17_19TransferSyntax);
113  transferSyntaxes.push_back(UID_JPEGProcess20_22TransferSyntax);
114  transferSyntaxes.push_back(UID_JPEGProcess21_23TransferSyntax);
115  transferSyntaxes.push_back(UID_JPEGProcess24_26TransferSyntax);
116  transferSyntaxes.push_back(UID_JPEGProcess25_27TransferSyntax);
117  transferSyntaxes.push_back(UID_JPEGProcess28TransferSyntax);
118  transferSyntaxes.push_back(UID_JPEGProcess29TransferSyntax);
119  transferSyntaxes.push_back(UID_JPEGProcess14SV1TransferSyntax);
120  transferSyntaxes.push_back(UID_JPEGLSLosslessTransferSyntax);
121  transferSyntaxes.push_back(UID_JPEGLSLossyTransferSyntax);
122  transferSyntaxes.push_back(UID_RLELosslessTransferSyntax);
123  transferSyntaxes.push_back(UID_DeflatedExplicitVRLittleEndianTransferSyntax);
124  transferSyntaxes.push_back(UID_JPEG2000LosslessOnlyTransferSyntax);
125  transferSyntaxes.push_back(UID_JPEG2000TransferSyntax);
126  transferSyntaxes.push_back(UID_MPEG2MainProfileAtMainLevelTransferSyntax);
127  transferSyntaxes.push_back(UID_MPEG2MainProfileAtHighLevelTransferSyntax);
128  transferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionLosslessOnlyTransferSyntax);
129  transferSyntaxes.push_back(UID_JPEG2000Part2MulticomponentImageCompressionTransferSyntax);
130  transferSyntaxes.push_back(UID_JPIPReferencedTransferSyntax);
131  transferSyntaxes.push_back(UID_JPIPReferencedDeflateTransferSyntax);
132  transferSyntaxes.push_back(UID_RFC2557MIMEEncapsulationTransferSyntax);
133  transferSyntaxes.push_back(UID_XMLEncodingTransferSyntax);
134 
135  // Add Verification SOP Class presentation context
136  this->addPresentationContext(UID_VerificationSOPClass, transferSyntaxes);
137 
138  // Add study presentation context
139  this->addPresentationContext(UID_FINDStudyRootQueryRetrieveInformationModel, transferSyntaxes);
140  this->addPresentationContext(UID_MOVEStudyRootQueryRetrieveInformationModel, transferSyntaxes);
141  this->addPresentationContext(UID_GETStudyRootQueryRetrieveInformationModel, transferSyntaxes);
142 
143  // Add presentation context for C-GET store requests
144  for (Uint16 j = 0; j < numberOfDcmLongSCUStorageSOPClassUIDs; j++)
145  {
146  this->addPresentationContext(dcmLongSCUStorageSOPClassUIDs[j], transferSyntaxes, ASC_SC_ROLE_SCP);
147  }
148 
149 }
150 
151 // ----------------------------------------------------------------------------
152 
154 {
155  // Initialize network
156  OFCondition result = this->initNetwork();
157  if (result.bad())
158  {
159  const std::string msg = "Unable to set up the network: " + std::string(result.text());
160  throw ::fwPacsIO::exceptions::NetworkInitializationFailure(msg);
161  }
162 
163  // Negotiate Association
164  result = this->negotiateAssociation();
165  if (result.bad())
166  {
167  const std::string msg = "Unable to negotiate association: " + std::string(result.text());
168  throw ::fwPacsIO::exceptions::NegociateAssociationFailure(msg);
169  }
170 
171  return true;
172 
173 }
174 
175 // ----------------------------------------------------------------------------
176 
178 {
179  this->closeAssociation(DCMSCU_RELEASE_ASSOCIATION);
180 }
181 
182 // ----------------------------------------------------------------------------
183 
185 {
186  return this->isConnected();
187 }
188 
189 // ----------------------------------------------------------------------------
190 
192 {
193  return this->sendECHORequest(0).good();
194 }
195 
196 // ----------------------------------------------------------------------------
197 
198 OFList< QRResponse* > SeriesEnquirer::sendFindRequest(DcmDataset dataset)
199 {
200  OFList< QRResponse* > findResponses;
201 
202  // Try to find a presentation context
203  T_ASC_PresentationContextID presID = this->findUncompressedPC(UID_FINDStudyRootQueryRetrieveInformationModel);
204  if (presID == 0)
205  {
206  const std::string msg = "There is no uncompressed presentation context for Study Root FIND";
207  throw ::fwPacsIO::exceptions::PresentationContextMissing(msg);
208  }
209 
210  // Send the request
211  OFCondition result = this->sendFINDRequest(presID, &dataset, &findResponses);
212 
213  return findResponses;
214 }
215 
216 // ----------------------------------------------------------------------------
217 
218 OFCondition SeriesEnquirer::sendMoveRequest(DcmDataset dataset)
219 {
220  // Be sure that the needed informations are set
221  SLM_ASSERT("The path where to store the series is not set.", !m_path.empty());
222  SLM_ASSERT("The move application title is not set.", !m_moveApplicationTitle.empty());
223 
224  // Try to find a presentation context
225  T_ASC_PresentationContextID presID = this->findUncompressedPC(UID_MOVEStudyRootQueryRetrieveInformationModel);
226  SLM_WARN_IF("There is no uncompressed presentation context for Study Root MOVE", presID == 0);
227 
228  // Fetches all images of this particular study
229  OFCondition result;
230  OFList< RetrieveResponse* > dataResponse;
231  return this->sendMOVERequest(presID, m_moveApplicationTitle.c_str(), &dataset, &dataResponse);
232 }
233 
234 // ----------------------------------------------------------------------------
235 
236 OFCondition SeriesEnquirer::sendGetRequest(DcmDataset dataset)
237 {
238  // Be sure that the needed informations are set
239  SLM_ASSERT("The path where to store the series is not set.", !m_path.empty());
240 
241  // Try to find a presentation context
242  T_ASC_PresentationContextID presID = this->findUncompressedPC(UID_GETStudyRootQueryRetrieveInformationModel);
243 
244  if (presID == 0)
245  {
246  SLM_WARN("There is no uncompressed presentation context for Study Root GET");
247  }
248 
249  // Fetches all images of this particular study
250  OFCondition result;
251  OFList< RetrieveResponse* > dataResponse;
252  return this->sendCGETRequest(presID, &dataset, &dataResponse);
253 }
254 
255 // ----------------------------------------------------------------------------
256 
257 OFCondition SeriesEnquirer::sendStoreRequest(const ::boost::filesystem::path& path)
258 {
259  // Try to find a presentation context
260  T_ASC_PresentationContextID presID = this->findUncompressedPC(UID_MOVEStudyRootQueryRetrieveInformationModel);
261 
262  if (presID == 0)
263  {
264  SLM_WARN("There is no uncompressed presentation context for Study Root GET");
265  }
266 
267  Uint16 rspStatusCode;
268  OFCondition result = this->sendSTORERequest(presID, OFString(path.string().c_str()), 0, rspStatusCode);
269  OSLM_WARN("PACS RESPONSE :" << rspStatusCode);
270  return result;
271 }
272 
273 // ----------------------------------------------------------------------------
274 
275 OFCondition SeriesEnquirer::sendStoreRequest(const CSPTR(DcmDataset)& dataset)
276 {
277  // Try to find a presentation context
278  T_ASC_PresentationContextID presID = this->findUncompressedPC(UID_MOVEStudyRootQueryRetrieveInformationModel);
279 
280  if (presID == 0)
281  {
282  SLM_WARN("There is no uncompressed presentation context for Study Root GET");
283  }
284 
285  Uint16 rspStatusCode;
286  // const_cast required to use bad DCMTK sendSTORERequest API
287  DcmDataset* datasetPtr = const_cast<DcmDataset*>(dataset.get());
288  OFCondition result = this->sendSTORERequest(presID, OFString(""), datasetPtr, rspStatusCode);
289  OSLM_WARN("PACS RESPONSE :" << rspStatusCode);
290  return result;
291 }
292 
293 // ----------------------------------------------------------------------------
294 
295 OFList< QRResponse* > SeriesEnquirer::findSeriesByPatientName(const std::string& name)
296 {
297  // Dataset used to store query informations
298  DcmDataset dataset;
299  dataset.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "SERIES");
300  dataset.putAndInsertOFStringArray(DCM_SeriesInstanceUID, "");
301 
302  // Search by patient name
303  std::string searchString = "*" + name + "*";
304  dataset.putAndInsertOFStringArray(DCM_PatientName, searchString.c_str());
305 
306  // Fields needed by DICOMSeries
307  //dataset.putAndInsertOFStringArray(DCM_PatientName, "");
308  dataset.putAndInsertOFStringArray(DCM_PatientID, "");
309  dataset.putAndInsertOFStringArray(DCM_PatientBirthDate, "");
310  dataset.putAndInsertOFStringArray(DCM_PatientSex, "");
311  dataset.putAndInsertOFStringArray(DCM_StudyInstanceUID, "");
312  dataset.putAndInsertOFStringArray(DCM_StudyDate, "");
313  dataset.putAndInsertOFStringArray(DCM_StudyTime, "");
314  dataset.putAndInsertOFStringArray(DCM_ReferringPhysicianName, "");
315  dataset.putAndInsertOFStringArray(DCM_StudyDescription, "");
316  dataset.putAndInsertOFStringArray(DCM_PatientAge, "");
317  dataset.putAndInsertOFStringArray(DCM_InstitutionName, "");
318  dataset.putAndInsertOFStringArray(DCM_Modality, "");
319  dataset.putAndInsertOFStringArray(DCM_SeriesDate, "");
320  dataset.putAndInsertOFStringArray(DCM_SeriesTime, "");
321  dataset.putAndInsertOFStringArray(DCM_SeriesDescription, "");
322  dataset.putAndInsertOFStringArray(DCM_PerformingPhysicianName, "");
323 
324  // Number of instances
325  dataset.putAndInsertOFStringArray(DCM_NumberOfSeriesRelatedInstances, "");
326 
327  return this->sendFindRequest(dataset);
328 }
329 
330 // ----------------------------------------------------------------------------
331 
332 OFList< QRResponse* > SeriesEnquirer::findSeriesByDate(const std::string& fromDate, const std::string& toDate)
333 {
334  // Dataset used to store query informations
335  DcmDataset dataset;
336  dataset.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "SERIES");
337  dataset.putAndInsertOFStringArray(DCM_SeriesInstanceUID, "");
338 
339  // Search by date
340  std::string searchString = fromDate + "-" + toDate;
341  dataset.putAndInsertOFStringArray(DCM_StudyDate, searchString.c_str());
342 
343  // Fields needed by DICOMSeries
344  dataset.putAndInsertOFStringArray(DCM_PatientName, "");
345  dataset.putAndInsertOFStringArray(DCM_PatientID, "");
346  dataset.putAndInsertOFStringArray(DCM_PatientBirthDate, "");
347  dataset.putAndInsertOFStringArray(DCM_PatientSex, "");
348  dataset.putAndInsertOFStringArray(DCM_StudyInstanceUID, "");
349  //dataset.putAndInsertOFStringArray(DCM_StudyDate, "");
350  dataset.putAndInsertOFStringArray(DCM_StudyTime, "");
351  dataset.putAndInsertOFStringArray(DCM_ReferringPhysicianName, "");
352  dataset.putAndInsertOFStringArray(DCM_StudyDescription, "");
353  dataset.putAndInsertOFStringArray(DCM_PatientAge, "");
354  dataset.putAndInsertOFStringArray(DCM_InstitutionName, "");
355  dataset.putAndInsertOFStringArray(DCM_Modality, "");
356  dataset.putAndInsertOFStringArray(DCM_SeriesDate, "");
357  dataset.putAndInsertOFStringArray(DCM_SeriesTime, "");
358  dataset.putAndInsertOFStringArray(DCM_SeriesDescription, "");
359  dataset.putAndInsertOFStringArray(DCM_PerformingPhysicianName, "");
360 
361  // Number of instances
362  dataset.putAndInsertOFStringArray(DCM_NumberOfSeriesRelatedInstances, "");
363 
364  return this->sendFindRequest(dataset);
365 }
366 
367 // ----------------------------------------------------------------------------
368 
369 OFList< QRResponse* > SeriesEnquirer::findSeriesByUID(const std::string& uid)
370 {
371  // Dataset used to store query informations
372  DcmDataset dataset;
373  dataset.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "SERIES");
374 
375  // Search by series UID
376  dataset.putAndInsertOFStringArray(DCM_SeriesInstanceUID, uid.c_str());
377 
378  // Fields needed by DICOMSeries
379  dataset.putAndInsertOFStringArray(DCM_PatientName, "");
380  dataset.putAndInsertOFStringArray(DCM_PatientID, "");
381  dataset.putAndInsertOFStringArray(DCM_PatientBirthDate, "");
382  dataset.putAndInsertOFStringArray(DCM_PatientSex, "");
383  dataset.putAndInsertOFStringArray(DCM_StudyInstanceUID, "");
384  dataset.putAndInsertOFStringArray(DCM_StudyDate, "");
385  dataset.putAndInsertOFStringArray(DCM_StudyTime, "");
386  dataset.putAndInsertOFStringArray(DCM_ReferringPhysicianName, "");
387  dataset.putAndInsertOFStringArray(DCM_StudyDescription, "");
388  dataset.putAndInsertOFStringArray(DCM_PatientAge, "");
389  dataset.putAndInsertOFStringArray(DCM_InstitutionName, "");
390  dataset.putAndInsertOFStringArray(DCM_Modality, "");
391  dataset.putAndInsertOFStringArray(DCM_SeriesDate, "");
392  dataset.putAndInsertOFStringArray(DCM_SeriesTime, "");
393  dataset.putAndInsertOFStringArray(DCM_SeriesDescription, "");
394  dataset.putAndInsertOFStringArray(DCM_PerformingPhysicianName, "");
395 
396  // Number of instances
397  dataset.putAndInsertOFStringArray(DCM_NumberOfSeriesRelatedInstances, "");
398 
399  return this->sendFindRequest(dataset);
400 }
401 
402 // ----------------------------------------------------------------------------
403 
404 std::string SeriesEnquirer::findSOPInstanceUID(const std::string& seriesInstanceUID, unsigned int instanceNumber)
405 {
406  // Dataset used to store query informations
407  DcmDataset dataset;
408  dataset.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "IMAGE");
409  dataset.putAndInsertOFStringArray(DCM_SeriesInstanceUID, seriesInstanceUID.c_str());
410  dataset.putAndInsertOFStringArray(DCM_SOPInstanceUID, "");
411  std::stringstream ss;
412  ss << instanceNumber;
413  dataset.putAndInsertOFStringArray(DCM_InstanceNumber, ss.str().c_str());
414 
415  OFList< QRResponse* > responses = this->sendFindRequest(dataset);
416  OFIterator< QRResponse* > it = responses.begin();
417  std::string sopInstanceUID = "";
418  if(it != responses.end() && (*it)->m_dataset)
419  {
420  OFString sop;
421  (*it)->m_dataset->findAndGetOFStringArray(DCM_SOPInstanceUID, sop);
422  sopInstanceUID = sop.c_str();
423  }
424 
425  //Release responses
426  while (!responses.empty())
427  {
428  delete responses.front();
429  responses.pop_front();
430  }
431 
432  return sopInstanceUID;
433 
434 }
435 
436 // ----------------------------------------------------------------------------
437 
438 void SeriesEnquirer::pullSeriesUsingMoveRetrieveMethod(InstanceUIDContainer instanceUIDContainer)
439 {
440  // Reset instance count
441  m_instanceIndex = 0;
442 
443  DcmDataset dataset;
444  OFCondition result;
445 
446  for( std::string seriesInstanceUID: instanceUIDContainer )
447  {
448  dataset.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "SERIES");
449  dataset.putAndInsertOFStringArray(DCM_SeriesInstanceUID, seriesInstanceUID.c_str());
450 
451  // Fetches all images of this particular study
452  result = this->sendMoveRequest(dataset);
453 
454  if (result.good())
455  {
456  SLM_TRACE("Received series " + seriesInstanceUID);
457  }
458  else
459  {
460  const std::string msg = "Unable to send a C-MOVE request to the server. "
461  "(Series instance UID =" + std::string(seriesInstanceUID.c_str()) +") : "
462  + std::string(result.text());
463  throw ::fwPacsIO::exceptions::RequestFailure(msg);
464  }
465  }
466 
467 }
468 
469 // ----------------------------------------------------------------------------
470 
471 void SeriesEnquirer::pullSeriesUsingGetRetrieveMethod(InstanceUIDContainer instanceUIDContainer)
472 {
473  // Reset instance count
474  m_instanceIndex = 0;
475 
476  DcmDataset dataset;
477  OFCondition result;
478 
479  for( std::string seriesInstanceUID: instanceUIDContainer )
480  {
481  dataset.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "SERIES");
482  dataset.putAndInsertOFStringArray(DCM_SeriesInstanceUID, seriesInstanceUID.c_str());
483 
484  // Fetches all images of this particular study
485  result = this->sendGetRequest(dataset);
486 
487  if (result.good())
488  {
489  SLM_TRACE("Received series " + seriesInstanceUID);
490  }
491  else
492  {
493  const std::string msg = "Unable to send a C-GET request to the server. "
494  "(Series instance UID =" + std::string(seriesInstanceUID.c_str()) +") : "
495  + std::string(result.text());
496  throw ::fwPacsIO::exceptions::RequestFailure(msg);
497  }
498  }
499 }
500 
501 // ----------------------------------------------------------------------------
502 
503 void SeriesEnquirer::pullInstanceUsingMoveRetrieveMethod(const std::string& seriesInstanceUID,
504  const std::string& sopInstanceUID)
505 {
506  // Reset instance count
507  m_instanceIndex = 0;
508 
509  DcmDataset dataset;
510  OFCondition result;
511  dataset.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "IMAGE");
512  dataset.putAndInsertOFStringArray(DCM_SeriesInstanceUID, seriesInstanceUID.c_str());
513 
514  dataset.putAndInsertOFStringArray(DCM_SOPInstanceUID, sopInstanceUID.c_str());
515  dataset.putAndInsertOFStringArray(DCM_InstanceNumber, "");
516 
517  // Fetches all images of this particular study
518  result = this->sendMoveRequest(dataset);
519 
520  if (result.good())
521  {
522  SLM_TRACE("Received instance " + seriesInstanceUID + " - " + sopInstanceUID);
523  }
524  else
525  {
526  const std::string msg = "Unable to send a C-MOVE request to the server. "
527  "(Series instance UID =" + std::string(seriesInstanceUID.c_str()) +") : "
528  + std::string(result.text());
529  throw ::fwPacsIO::exceptions::RequestFailure(msg);
530  }
531 }
532 
533 // ----------------------------------------------------------------------------
534 
535 void SeriesEnquirer::pullInstanceUsingGetRetrieveMethod(const std::string& seriesInstanceUID,
536  const std::string& sopInstanceUID)
537 {
538  // Reset instance count
539  m_instanceIndex = 0;
540 
541  DcmDataset dataset;
542  OFCondition result;
543  dataset.putAndInsertOFStringArray(DCM_QueryRetrieveLevel, "IMAGE");
544  dataset.putAndInsertOFStringArray(DCM_SeriesInstanceUID, seriesInstanceUID.c_str());
545 
546  dataset.putAndInsertOFStringArray(DCM_SOPInstanceUID, sopInstanceUID.c_str());
547  dataset.putAndInsertOFStringArray(DCM_InstanceNumber, "");
548 
549  // Fetches all images of this particular study
550  result = this->sendGetRequest(dataset);
551 
552  if (result.good())
553  {
554  SLM_TRACE("Received instance " + seriesInstanceUID + " - " + sopInstanceUID);
555  }
556  else
557  {
558  const std::string msg = "Unable to send a C-GET request to the server. "
559  "(Series instance UID =" + std::string(seriesInstanceUID.c_str()) +") : "
560  + std::string(result.text());
561  throw ::fwPacsIO::exceptions::RequestFailure(msg);
562  }
563 }
564 
565 // ----------------------------------------------------------------------------
566 
567 void SeriesEnquirer::pushSeries(const InstancePathContainer& pathContainer)
568 {
569  // Reset instance count
570  m_instanceIndex = 0;
571 
572  OFCondition result;
573 
574  // Send images to pacs
575  for(const ::boost::filesystem::path& path: pathContainer)
576  {
577  result = this->sendStoreRequest(path);
578 
579  if (result.good())
580  {
581  SLM_TRACE("Instance sent.");
582  }
583  else
584  {
585  const std::string msg = "Unable to send a C-STORE request to the server : " + std::string(result.text());
586  throw ::fwPacsIO::exceptions::RequestFailure(msg);
587  }
588 
589  // Notify callback
591  {
592  m_progressCallback->asyncRun("", ++m_instanceIndex, path.string());
593  }
594 
595  }
596 }
597 
598 // ----------------------------------------------------------------------------
599 
600 void SeriesEnquirer::pushSeries(const DatasetContainer& datasetContainer)
601 {
602  // Reset instance count
603  m_instanceIndex = 0;
604  OFCondition result;
605  // Send images to pacs
606  for(const auto& dataset : datasetContainer)
607  {
608  result = this->sendStoreRequest(dataset);
609 
610  if (result.good())
611  {
612  SLM_TRACE("Instance sent.");
613  }
614  else
615  {
616  const std::string msg = "Unable to send a C-STORE request to the server : " + std::string(result.text());
617  throw ::fwPacsIO::exceptions::RequestFailure(msg);
618  }
619 
620  // Notify callback
622  {
623  m_progressCallback->asyncRun("", ++m_instanceIndex, "");
624  }
625  }
626 }
627 
628 // ----------------------------------------------------------------------------
629 
631  const T_ASC_PresentationContextID presID, RetrieveResponse* response, OFBool& waitForNextResponse)
632 {
633  OFCondition result = DcmSCU::handleMOVEResponse(presID, response, waitForNextResponse);
634 
635  // Check error status
636  bool error = (response->m_status != STATUS_Success) && (response->m_status != STATUS_Pending);
637 
638  // Notify error
639  if(error)
640  {
641  const std::string msg = "Unable to perform a C-MOVE operation.";
642  throw ::fwPacsIO::exceptions::RequestFailure(msg);
643  }
644 
645  return result;
646 }
647 
648 // ----------------------------------------------------------------------------
649 
651  const T_ASC_PresentationContextID presID, DcmDataset* incomingObject,
652  OFBool& continueCGETSession, Uint16& cStoreReturnStatus)
653 {
654  OFCondition result;
655 
656  if (incomingObject != NULL)
657  {
658  //Find the series UID
659  OFString seriesID;
660  if(incomingObject->findAndGetOFStringArray(DCM_SeriesInstanceUID, seriesID).good())
661  {
662  SLM_TRACE("Series Instance UID: " + std::string(seriesID.c_str()));
663  }
664 
665  //Find the instance UID
666  OFString iname;
667  if (incomingObject->findAndGetOFStringArray(DCM_SOPInstanceUID, iname).good())
668  {
669  SLM_TRACE("SOP Instance UID: " + std::string(iname.c_str()));
670  }
671 
672  //Create Folder
673  ::boost::filesystem::path seriesPath = ::boost::filesystem::path(m_path.string() + seriesID.c_str() + "/");
674  if (!::boost::filesystem::exists(seriesPath))
675  {
676  ::boost::filesystem::create_directories(seriesPath);
677  }
678 
679  //Save the file in the specified folder (Create new meta header for gdcm reader)
680  std::string filePath = seriesPath.string() + iname.c_str();
681  DcmFileFormat fileFormat(incomingObject);
682  fileFormat.saveFile(filePath.c_str(), EXS_Unknown, EET_UndefinedLength,
683  EGL_recalcGL, EPD_noChange, 0, 0, EWM_createNewMeta);
684 
685  // Notify callback
687  {
688  m_progressCallback->asyncRun(seriesID.c_str(), ++m_instanceIndex, filePath);
689  }
690  }
691 
692  return result;
693 }
694 
695 // ----------------------------------------------------------------------------
696 
697 } //namespace fwPacsIO
static FWDCMTKTOOLS_API void loadDictionary()
Load the DICOM dictionary.
Definition: Dictionary.cpp:25
#define CSPTR(_cls_)
FWPACSIO_API void pullSeriesUsingGetRetrieveMethod(InstanceUIDContainer instanceUIDContainer)
Pull series using C-GET requests.
FWPACSIO_API OFList< QRResponse * > findSeriesByPatientName(const std::string &name)
Find series by patient name.
virtual FWPACSIO_API OFCondition handleSTORERequest(const T_ASC_PresentationContextID presID, DcmDataset *incomingObject, OFBool &continueCGETSession, Uint16 &cStoreReturnStatus) override
Handle STORE Request (Override)
FWPACSIO_API OFList< QRResponse * > findSeriesByDate(const std::string &fromDate, const std::string &toDate)
Find series by study date.
ProgressCallbackSlotType::sptr m_progressCallback
Progress callback slot.
fwPacsIO contains classes used to communicate with a PACS.
FWPACSIO_API ~SeriesEnquirer()
Destructor.
FWPACSIO_API void pushSeries(const InstancePathContainer &pathContainer)
Push instances using C-STORE requests.
FWPACSIO_API OFList< QRResponse * > sendFindRequest(DcmDataset dataset)
Send Find Request.
::boost::filesystem::path m_path
Path where the files must be saved.
#define SLM_WARN(message)
Definition: spyLog.hpp:261
FWPACSIO_API bool isConnectedToPacs() const
Return true if there is an existing association.
FWPACSIO_API bool pingPacs()
Assemble and send C-ECHO request.
virtual FWPACSIO_API OFCondition handleMOVEResponse(const T_ASC_PresentationContextID presID, RetrieveResponse *response, OFBool &waitForNextResponse) override
Handle MOVE Response (Override)
FWPACSIO_API void initialize(const std::string &applicationTitle, const std::string &peerHostName, unsigned short peerPort, const std::string &peerApplicationTitle, const std::string &moveApplicationTitle="", ProgressCallbackSlotType::sptr progressCallback=ProgressCallbackSlotType::sptr())
Initialize the connection.
FWPACSIO_API void pullSeriesUsingMoveRetrieveMethod(InstanceUIDContainer instanceUIDContainer)
Pull series using C-MOVE requests.
FWPACSIO_API SeriesEnquirer()
Constructor.
FWPACSIO_API void disconnect()
Release association.
#define OSLM_WARN(message)
Definition: spyLog.hpp:263
#define SLM_ASSERT(message, cond)
work like &#39;assert&#39; from &#39;cassert&#39;, with in addition a message logged by spylog (with FATAL loglevel) ...
Definition: spyLog.hpp:308
FWPACSIO_API std::string findSOPInstanceUID(const std::string &seriesInstanceUID, unsigned int instanceNumber)
Find SOPInstanceUID of the specified instance.
static FWTOOLS_APIconst::boost::filesystem::path getTemporaryFolder(const std::string &subFolderPrefix="") noexcept
Returns a unique per-process temporary folder. The top level temporary folder will be automatically d...
Definition: System.cpp:148
FWPACSIO_API bool connect()
Negotiate Association.
FWPACSIO_API OFCondition sendMoveRequest(DcmDataset dataset)
Send Move Request.
#define SLM_TRACE(message)
Definition: spyLog.hpp:228
FWPACSIO_API OFCondition sendStoreRequest(const ::boost::filesystem::path &path)
Send Store Request.
FWPACSIO_API OFCondition sendGetRequest(DcmDataset dataset)
Send Get Request.
std::string m_moveApplicationTitle
MOVE destination AE Title.
FWPACSIO_API void pullInstanceUsingGetRetrieveMethod(const std::string &seriesInstanceUID, const std::string &sopInstanceUID)
Pull instance using C-GET requests.
unsigned int m_instanceIndex
Dowloaded instance index.
FWPACSIO_API void pullInstanceUsingMoveRetrieveMethod(const std::string &seriesInstanceUID, const std::string &sopInstanceUID)
Pull instance using C-MOVE requests.
This file defines SpyLog macros. These macros are used to log messages to a file or to the console du...
FWPACSIO_API Uint8 findUncompressedPC(const OFString &sopClass)
Find uncompressed presentation context.
#define SLM_WARN_IF(message, cond)
Definition: spyLog.hpp:265
FWPACSIO_API OFList< QRResponse * > findSeriesByUID(const std::string &uid)
Find series by series UID.