fw4spl
ioDicomWeb/src/ioDicomWeb/SSeriesPusher.cpp
1 /* ***** BEGIN LICENSE BLOCK *****
2  * FW4SPL - Copyright (C) IRCAD, 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 "ioDicomWeb/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 <fwNetworkIO/exceptions/Base.hpp>
22 #include <fwNetworkIO/helper/Series.hpp>
23 #include <fwNetworkIO/http/Request.hpp>
24 
25 #include <fwPreferences/helper.hpp>
26 
27 #include <fwServices/macros.hpp>
28 
29 namespace ioDicomWeb
30 {
31 
32 //------------------------------------------------------------------------------
33 
34 static const ::fwServices::IService::KeyType s_SERIES_IN = "selectedSeries";
35 
36 //------------------------------------------------------------------------------
37 
39  m_isPushing(false)
40 {
41 }
42 //------------------------------------------------------------------------------
43 
45 {
46 }
47 
48 //------------------------------------------------------------------------------
49 
51 {
52  ::fwServices::IService::ConfigType configuration = this->getConfigTree();
53  //Parse server port and hostname
54  if(configuration.count("server"))
55  {
56  const std::string serverInfo = configuration.get("server", "");
57  const std::string::size_type splitPosition = serverInfo.find(':');
58  SLM_ASSERT("Server info not formatted correctly", splitPosition != std::string::npos);
59 
60  const std::string hostnameStr = serverInfo.substr(0, splitPosition);
61  const std::string portStr = serverInfo.substr(splitPosition + 1, serverInfo.size());
62 
63  m_serverHostnameKey = this->getPreferenceKey(hostnameStr);
64  m_serverPortKey = this->getPreferenceKey(portStr);
65 
66  if(m_serverHostnameKey.empty())
67  {
68  m_serverHostname = hostnameStr;
69  }
70  if(m_serverPortKey.empty())
71  {
72  m_serverPort = std::stoi(portStr);
73  }
74  }
75  else
76  {
77  throw ::fwTools::Failed("'server' element not found");
78  }
79 }
80 
81 // -----------------------------------------------------------------------------
82 
83 std::string SSeriesPusher::getPreferenceKey(const std::string& key) const
84 {
85  std::string keyResult;
86  const size_t first = key.find('%');
87  const size_t last = key.rfind('%');
88  if (first == 0 && last == key.size() - 1)
89  {
90  keyResult = key.substr(1, key.size() - 2);
91  }
92  return keyResult;
93 }
94 
95 //------------------------------------------------------------------------------
96 
98 {
99 }
100 
101 //------------------------------------------------------------------------------
102 
104 {
105 }
106 
107 //------------------------------------------------------------------------------
108 
110 {
111  if(!m_serverHostnameKey.empty())
112  {
113  const std::string hostname = ::fwPreferences::getPreference(m_serverHostnameKey);
114  if(!hostname.empty())
115  {
116  m_serverHostname = hostname;
117  }
118  }
119  if(!m_serverPortKey.empty())
120  {
121  const std::string port = ::fwPreferences::getPreference(m_serverPortKey);
122  if(!port.empty())
123  {
124  m_serverPort = std::stoi(port);
125  }
126  }
127 
128  ::fwData::Vector::csptr selectedSeries = this->getInput< ::fwData::Vector >(s_SERIES_IN);
129 
130  if(m_isPushing)
131  {
132  // Display a message to inform the user that the service is already pushing data.
134  messageBox.setTitle("Pushing Series");
135  messageBox.setMessage( "The service is already pushing data. Please wait until the pushing is done "
136  "before sending a new push request." );
137  messageBox.setIcon(::fwGui::dialog::IMessageDialog::INFO);
138  messageBox.addButton(::fwGui::dialog::IMessageDialog::OK);
139  messageBox.show();
140  }
141  else if(selectedSeries->empty())
142  {
143  // Display a message to inform the user that there is no series selected.
145  messageBox.setTitle("Pushing Series");
146  messageBox.setMessage( "Unable to push series, there is no series selected." );
147  messageBox.setIcon(::fwGui::dialog::IMessageDialog::INFO);
148  messageBox.addButton(::fwGui::dialog::IMessageDialog::OK);
149  messageBox.show();
150  }
151  else
152  {
153  // Push series to the PACS
154  this->pushSeries();
155  }
156 }
157 
158 //------------------------------------------------------------------------------
159 
160 void SSeriesPusher::pushSeries()
161 {
162  m_isPushing = true;
163 
164  ::fwData::Vector::csptr seriesVector = this->getInput< ::fwData::Vector >(s_SERIES_IN);
165 
166  const std::vector< ::fwMedData::DicomSeries::sptr > dataVector =
167  seriesVector->getDataContainer< ::fwMedData::DicomSeries >();
168  // Connect to PACS
169  const size_t seriesVectorSize = seriesVector->size();
170  size_t nbSeriesSuccess = 0;
171  for(const auto& dicomSeries : dataVector)
172  {
173  nbSeriesSuccess++;
174 
175  ::fwMedData::DicomSeries::DicomContainerType dicomContainer = dicomSeries->getDicomContainer();
176  const size_t dicomContainerSize = dicomContainer.size();
177 
178  size_t nbInstanceSuccess = 0;
179  try
180  {
181  for(const auto& item : dicomContainer)
182  {
183  const ::fwMemory::BufferObject::sptr bufferObj = item.second;
184  const ::fwMemory::BufferObject::Lock lockerDest(bufferObj);
185  const char* buffer = static_cast<char*>(lockerDest.getBuffer());
186  const size_t size = bufferObj->getSize();
187 
188  const QByteArray fileBuffer = QByteArray::fromRawData(buffer, size);
189 
191  const std::string pacsServer("http://" + m_serverHostname + ":" + std::to_string(m_serverPort));
192  ::fwNetworkIO::http::Request::sptr request =
193  ::fwNetworkIO::http::Request::New(pacsServer + "/instances");
194  QByteArray seriesAnswer;
195  if (fileBuffer.size() != 0)
196  {
197  seriesAnswer = m_clientQt.post(request, fileBuffer);
198  if (!seriesAnswer.isEmpty())
199  {
200  nbInstanceSuccess++;
201  }
202  }
203  if (dicomContainerSize == nbInstanceSuccess)
204  {
205  this->displayMessage("Upload successful: " + std::to_string(nbSeriesSuccess) + "/" +
206  std::to_string(seriesVectorSize), false);
207  }
208  }
209  }
210  catch (::fwNetworkIO::exceptions::HostNotFound& exception)
211  {
212  std::stringstream ss;
213  ss << "Host not found.\n"
214  << "Please check your configuration: \n"
215  << "Pacs host name: " << m_serverHostname << "\n"
216  << "Pacs port: " << m_serverPort << "\n";
217  this->displayMessage(ss.str(), true);
218  SLM_WARN(exception.what());
219  }
220  }
221 
222  // Set pushing boolean to false
223  m_isPushing = false;
224 }
225 
226 //------------------------------------------------------------------------------
227 
228 void SSeriesPusher::displayMessage(const std::string& message, bool error) const
229 {
230  SLM_WARN_IF("Error: " + message, error);
232  messageBox.setTitle((error ? "Error" : "Information"));
233  messageBox.setMessage( message );
234  messageBox.setIcon(error ? (::fwGui::dialog::IMessageDialog::CRITICAL): (::fwGui::dialog::IMessageDialog::INFO));
235  messageBox.addButton(::fwGui::dialog::IMessageDialog::OK);
236  messageBox.show();
237 }
238 
239 //------------------------------------------------------------------------------
240 
241 } // namespace ioDicomWeb
FWNETWORKIO_API QByteArray post(Request::sptr request, const QByteArray &body)
Performs POST request.
Definition: ClientQt.cpp:124
static FWNETWORKIO_API Request::sptr New(const std::string &url)
Creates a new Request with given url.
Definition: Request.cpp:26
virtual FWGUI_API void setMessage(const std::string &msg) override
Set the message.
Defines the generic message box for IHM. Use the Delegate design pattern.
virtual IODICOMWEB_API void starting() override
Does nothing.
#define SLM_WARN(message)
Definition: spyLog.hpp:261
virtual FWGUI_API void addButton(IMessageDialog::Buttons button) override
Add a button (OK, YES_NO, YES, NO, CANCEL)
IODICOMWEB_API void updating() override
Checks the configuration and push the series.
virtual FWGUI_API IMessageDialog::Buttons show() override
Show the message box and return the clicked button.
#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
IODICOMWEB_API SSeriesPusher() noexcept
Constructor.
virtual IODICOMWEB_API ~SSeriesPusher() noexcept
Destructor.
virtual FWGUI_API void setIcon(IMessageDialog::Icons icon) override
Set the icon (CRITICAL, WARNING, INFO or QUESTION)
virtual IODICOMWEB_API void stopping() override
Does nothing.
Implements exception for an HTTP host not found errors.
ioDicomWeb contains services use to deal with PACS through HTTP.
virtual IODICOMWEB_API void configuring() override
Gets the configuration.
const DicomContainerType & getDicomContainer() const
Dicom container.
virtual FWGUI_API void setTitle(const std::string &title) override
Set the title of the message box.
#define SLM_WARN_IF(message, cond)
Definition: spyLog.hpp:265
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.
Definition: IService.cpp:247