fw4spl
ioPacs/src/ioPacs/SSeriesPusher.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 "ioPacs/SSeriesPusher.hpp"
8 
9 #include <fwCom/Signal.hpp>
10 #include <fwCom/Signal.hxx>
11 #include <fwCom/Slots.hpp>
12 #include <fwCom/Slots.hxx>
13 
14 #include <fwData/Vector.hpp>
15 
16 #include <fwGui/dialog/MessageDialog.hpp>
17 
18 #include <fwMedData/DicomSeries.hpp>
19 #include <fwMedData/Series.hpp>
20 
21 #include <fwPacsIO/exceptions/Base.hpp>
22 #include <fwPacsIO/helper/Series.hpp>
23 
24 #include <fwServices/macros.hpp>
25 
26 #include <boost/foreach.hpp>
27 
28 #include <dcmtk/dcmdata/dcistrmb.h>
29 
30 namespace ioPacs
31 {
32 
33 fwServicesRegisterMacro( ::fwServices::IController, ::ioPacs::SSeriesPusher, ::fwData::Object );
34 
35 //------------------------------------------------------------------------------
36 
37 const ::fwCom::Slots::SlotKeyType SSeriesPusher::s_DISPLAY_SLOT = "displayMessage";
38 
39 const ::fwCom::Signals::SignalKeyType SSeriesPusher::s_PROGRESSED_SIG = "progressed";
40 const ::fwCom::Signals::SignalKeyType SSeriesPusher::s_STARTED_PROGRESS_SIG = "startedProgress";
41 const ::fwCom::Signals::SignalKeyType SSeriesPusher::s_STOPPED_PROGRESS_SIG = "stoppedProgress";
42 
43 //------------------------------------------------------------------------------
44 
46  m_progressbarId("pushDicomProgressBar"),
47  m_isPushing(false)
48 {
49  // Internal slots
50  m_slotDisplayMessage = newSlot(s_DISPLAY_SLOT, &SSeriesPusher::displayMessage, this);
51  m_slotProgressCallback = newSlot(::fwPacsIO::SeriesEnquirer::s_PROGRESS_CALLBACK_SLOT,
53 
54  // Public signals
55  m_sigProgressed = newSignal<ProgressedSignalType>(s_PROGRESSED_SIG);
56  m_sigStartedProgress = newSignal<StartedProgressSignalType>(s_STARTED_PROGRESS_SIG);
57  m_sigStoppedProgress = newSignal<StoppedProgressSignalType>(s_STOPPED_PROGRESS_SIG);
58 }
59 //------------------------------------------------------------------------------
60 
62 {
63 }
64 
65 //------------------------------------------------------------------------------
66 
67 void SSeriesPusher::info(std::ostream& _sstream )
68 {
69  _sstream << "SSeriesPusher::info";
70 }
71 
72 //------------------------------------------------------------------------------
73 
75 {
76 }
77 
78 //------------------------------------------------------------------------------
79 
81 {
82  // Create enquirer
83  m_seriesEnquirer = ::fwPacsIO::SeriesEnquirer::New();
84 
85  // Worker
86  m_pushSeriesWorker = ::fwThread::Worker::New();
87 
88  // Get pacs configuration
89  m_pacsConfiguration = this->getInput< ::fwPacsIO::data::PacsConfiguration>("pacsConfig");
90  SLM_ASSERT("The pacs configuration object should not be null.", m_pacsConfiguration);
91 }
92 
93 //------------------------------------------------------------------------------
94 
96 {
97 }
98 
99 //------------------------------------------------------------------------------
100 
102 {
103  ::fwData::Vector::csptr selectedSeries = this->getInput< ::fwData::Vector >("selectedSeries");
104 
105  if(m_isPushing)
106  {
107  // Display a message to inform the user that the service is already pushing data.
109  messageBox.setTitle("Pushing Series");
110  messageBox.setMessage( "The service is already pushing data. Please wait until the pushing is done "
111  "before sending a new push request." );
112  messageBox.setIcon(::fwGui::dialog::IMessageDialog::INFO);
113  messageBox.addButton(::fwGui::dialog::IMessageDialog::OK);
114  messageBox.show();
115  }
116  else if(selectedSeries->empty())
117  {
118  // Display a message to inform the user that there is no series selected.
120  messageBox.setTitle("Pushing Series");
121  messageBox.setMessage( "Unable to push series, there is no series selected." );
122  messageBox.setIcon(::fwGui::dialog::IMessageDialog::INFO);
123  messageBox.addButton(::fwGui::dialog::IMessageDialog::OK);
124  messageBox.show();
125  }
126  else
127  {
128  // Initialize enquirer
129  m_seriesEnquirer->initialize(
130  m_pacsConfiguration->getLocalApplicationTitle(),
131  m_pacsConfiguration->getPacsHostName(),
132  m_pacsConfiguration->getPacsApplicationPort(),
133  m_pacsConfiguration->getPacsApplicationTitle(),
134  m_pacsConfiguration->getMoveApplicationTitle(),
136 
137  // Set pushing boolean to true
138  m_isPushing = true;
139 
140  // Check whether some selected series are already on the PACS or not
141  bool pushOK = this->checkSeriesOnPACS();
142  if(pushOK)
143  {
144  // Push series to the PACS
145  m_pushSeriesWorker->post(std::bind(&::ioPacs::SSeriesPusher::pushSeries, this));
146  }
147 
148  }
149 
150 }
151 
152 //------------------------------------------------------------------------------
154 {
155  // Return true if the push operation must be performed
156  bool result = true;
157 
158  ::fwData::Vector::csptr seriesVector = this->getInput< ::fwData::Vector >("selectedSeries");
159 
160  // Catch any errors
161  try
162  {
163  // Find which selected series must be pushed
164  DicomSeriesContainerType duplicateSeriesVector;
165 
166  // Connect to PACS
167  m_seriesEnquirer->connect();
168 
169  ::fwData::Vector::ConstIteratorType it = seriesVector->begin();
170  for(; it != seriesVector->end(); ++it)
171  {
172  ::fwMedData::DicomSeries::csptr series = ::fwMedData::DicomSeries::dynamicCast(*it);
173  SLM_ASSERT("The SeriesDB should contain only DicomSeries.", series);
174 
175  // Try to find series on PACS
176  OFList< QRResponse* > responses;
177  responses = m_seriesEnquirer->findSeriesByUID(series->getInstanceUID());
178 
179  // If the series has been found on the PACS
180  if(responses.size() > 1)
181  {
182  duplicateSeriesVector.push_back(series);
183  }
184 
186  }
187 
188  // Disconnect from PACS
189  m_seriesEnquirer->disconnect();
190 
191  // Inform the user that some series are already on the PACS
192  if(!duplicateSeriesVector.empty())
193  {
194  ::std::stringstream ss;
195  ss << "Those series are already on the PACS: \n";
196 
197  // Display duplicated Series
198  for(const ::fwMedData::Series::csptr& series: duplicateSeriesVector)
199  {
200  std::string description = series->getDescription();
201  description = (description.empty()) ? "[No description]" : description;
202  ss << "- " << description << std::endl;
203  }
204 
205  ss << std::endl << "Would you like to perform the operation anyway ?" << std::endl
206  << "(This will result in a merge operation)";
207 
209  messageBox.setTitle("Duplicate series");
210  messageBox.setMessage( ss.str() );
211  messageBox.setIcon(::fwGui::dialog::IMessageDialog::INFO);
212  messageBox.addButton(::fwGui::dialog::IMessageDialog::OK);
213  messageBox.addButton(::fwGui::dialog::IMessageDialog::CANCEL);
214  ::fwGui::dialog::IMessageDialog::Buttons answer = messageBox.show();
215 
216  result = (answer == ::fwGui::dialog::IMessageDialog::OK);
217 
218  }
219  }
220  catch (::fwPacsIO::exceptions::Base& exception)
221  {
222  ::std::stringstream ss;
223  ss << "Unable to connect to the pacs. Please check your configuration: \n"
224  << "Pacs host name: " << m_pacsConfiguration->getPacsHostName() << "\n"
225  << "Pacs application title: " << m_pacsConfiguration->getPacsApplicationTitle() << "\n"
226  << "Pacs port: " << m_pacsConfiguration->getPacsApplicationPort() << "\n";
227  m_slotDisplayMessage->asyncRun(ss.str(), true);
228  SLM_WARN(exception.what());
229  result = false;
230 
231  // Set pushing boolean to false
232  m_isPushing = false;
233 
234  }
235 
236  return result;
237 }
238 
239 //------------------------------------------------------------------------------
240 
242 {
243  ::fwData::Vector::csptr seriesVector = this->getInput< ::fwData::Vector >("selectedSeries");
244 
245  // Catch any errors
246  try
247  {
248  // List of dicom slice that must be pushed
249  std::vector< CSPTR(DcmDataset) > dicomContainer;
250 
251  // Connect to PACS
252  for(const auto& series : *seriesVector)
253  {
254  ::fwMedData::DicomSeries::csptr dicomSeries = ::fwMedData::DicomSeries::dynamicCast(series);
255  SLM_ASSERT("The SeriesDB should contain only DicomSeries.", dicomSeries);
256 
257  for(const auto& item : dicomSeries->getDicomContainer())
258  {
259  DcmFileFormat fileFormat;
260  ::fwMemory::BufferObject::sptr bufferObj = item.second;
261  const size_t buffSize = bufferObj->getSize();
262  ::fwMemory::BufferObject::Lock lock(bufferObj);
263  char* buffer = static_cast< char* >( lock.getBuffer() );
264 
265  DcmInputBufferStream is;
266  is.setBuffer(buffer, offile_off_t(buffSize));
267  is.setEos();
268 
269  fileFormat.transferInit();
270  if (!fileFormat.read(is).good())
271  {
272  FW_RAISE("Unable to read Dicom file '"<< bufferObj->getStreamInfo().fsFile.string() <<"'");
273  }
274 
275  fileFormat.loadAllDataIntoMemory();
276  fileFormat.transferEnd();
277 
278  const DcmDataset* dataset = fileFormat.getAndRemoveDataset();
279  CSPTR(DcmDataset) datasetPtr(dataset);
280  dicomContainer.push_back(datasetPtr);
281  }
282  }
283 
284  // Number of instances that must be uploaded
285  m_instanceCount = dicomContainer.size();
286 
287  // Connect from PACS
288  m_seriesEnquirer->connect();
290 
291  // Push series
292  m_seriesEnquirer->pushSeries(dicomContainer);
293 
294  // Disconnect from PACS
295  m_seriesEnquirer->disconnect();
296  }
297  catch (::fwPacsIO::exceptions::Base& exception)
298  {
299  ::std::stringstream ss;
300  ss << "Unable to connect to the pacs. Please check your configuration: \n"
301  << "Pacs host name: " << m_pacsConfiguration->getPacsHostName() << "\n"
302  << "Pacs application title: " << m_pacsConfiguration->getPacsApplicationTitle() << "\n"
303  << "Pacs port: " << m_pacsConfiguration->getPacsApplicationPort() << "\n";
304  m_slotDisplayMessage->asyncRun(ss.str(), true);
305  SLM_WARN(exception.what());
306  }
307 
308  // Set pushing boolean to false
309  m_isPushing = false;
310 
311 }
312 
313 //------------------------------------------------------------------------------
314 
315 void SSeriesPusher::progressCallback(const std::string& seriesInstanceUID, unsigned int instanceNumber,
316  const std::string& filePath)
317 {
318  if(instanceNumber < (m_instanceCount-1))
319  {
320  float percentage = static_cast<float>(instanceNumber)/static_cast<float>(m_instanceCount);
321  m_sigProgressed->asyncEmit(m_progressbarId, percentage, "Pushing series...");
322  }
323  else
324  {
326  }
327 }
328 
329 //------------------------------------------------------------------------------
330 
331 void SSeriesPusher::displayMessage(const ::std::string& message, bool error) const
332 {
333  SLM_WARN_IF("Error: " + message, error);
335  messageBox.setTitle((error ? "Error" : "Information"));
336  messageBox.setMessage( message );
337  messageBox.setIcon(error ? (::fwGui::dialog::IMessageDialog::CRITICAL): (::fwGui::dialog::IMessageDialog::INFO));
338  messageBox.addButton(::fwGui::dialog::IMessageDialog::OK);
339  messageBox.show();
340 }
341 
342 //------------------------------------------------------------------------------
343 
344 } // namespace ioPacs
#define CSPTR(_cls_)
long unsigned int m_instanceCount
Total number of instances that must be uploaded.
This service is used to push a DICOM series to a PACS.
::fwPacsIO::SeriesEnquirer::sptr m_seriesEnquirer
Series enquirer.
ioPacs contains services use to deal with PACS using DCMTK library.
virtual FWGUI_API void setMessage(const std::string &msg) override
Set the message.
IOPACS_API void displayMessage(const std::string &message, bool error) const
Display a message.
Defines the generic message box for IHM. Use the Delegate design pattern.
::fwThread::Worker::sptr m_pushSeriesWorker
Push Worker.
bool m_isPushing
Is pushing is set to true when we are puishing series.
IOPACS_API void progressCallback(const std::string &seriesInstanceUID, unsigned int instanceNumber, const std::string &filePath)
Progress callback.
std::string m_progressbarId
Progress Bar ID.
StoppedProgressSignalType::sptr m_sigStoppedProgress
Signal emitted when the bar is stopping.
base class for BufferObject Lock
#define SLM_WARN(message)
Definition: spyLog.hpp:261
IOPACS_API bool checkSeriesOnPACS()
Check whether some series are already on the PACS.
::fwPacsIO::SeriesEnquirer::ProgressCallbackSlotType::sptr m_slotProgressCallback
Slot to call progressCallback method.
virtual FWGUI_API void addButton(IMessageDialog::Buttons button) override
Add a button (OK, YES_NO, YES, NO, CANCEL)
StartedProgressSignalType::sptr m_sigStartedProgress
Signal emitted when the bar is starting.
virtual FWGUI_API IMessageDialog::Buttons show() override
Show the message box and return the clicked button.
This interface defines control service API. Does nothing particularly, can be considered as a default...
Definition: IController.hpp:23
LockBase< T >::BufferType getBuffer() const
Returns BufferObject&#39;s buffer pointer.
DisplayMessageSlotType::sptr m_slotDisplayMessage
Slot to call displayMessage method;.
static const ::fwCom::Signals::SignalKeyType s_PROGRESSED_SIG
Key in m_signals map of signal m_sigProgressed.
IOPACS_API SSeriesPusher() noexcept
Constructor.
#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
virtual IOPACS_API void stopping() override
Override.
ProgressedSignalType::sptr m_sigProgressed
Signal emitted when the bar is progressing.
Base class for each data object.
virtual FWGUI_API void setIcon(IMessageDialog::Icons icon) override
Set the icon (CRITICAL, WARNING, INFO or QUESTION)
IOPACS_API void updating() override
Override.
virtual IOPACS_API void starting() override
Override.
virtual IOPACS_API void configuring() override
Does nothing.
virtual IOPACS_API ~SSeriesPusher() noexcept
Destructor.
virtual FWGUI_API void setTitle(const std::string &title) override
Set the title of the message box.
::fwPacsIO::data::PacsConfiguration::csptr m_pacsConfiguration
Pacs Configuration object.
IOPACS_API void pushSeries()
Pull Series.
IOPACS_API void info(std::ostream &_sstream) override
Override.
#define SLM_WARN_IF(message, cond)
Definition: spyLog.hpp:265
static FWPACSIO_API void releaseResponses(OFList< QRResponse * > responses)
Release the responses.