7 #include "ioDicomWeb/SSeriesPuller.hpp" 9 #include <fwCom/Signal.hpp> 10 #include <fwCom/Signal.hxx> 11 #include <fwCom/Slots.hpp> 12 #include <fwCom/Slots.hxx> 14 #include <fwData/Vector.hpp> 16 #include <fwGui/dialog/MessageDialog.hpp> 17 #include <fwGui/dialog/ProgressDialog.hpp> 19 #include <fwMedData/DicomSeries.hpp> 21 #include <fwMedDataTools/helper/SeriesDB.hpp> 23 #include <fwNetworkIO/exceptions/Base.hpp> 24 #include <fwNetworkIO/helper/Series.hpp> 25 #include <fwNetworkIO/http/Request.hpp> 27 #include <fwPreferences/helper.hpp> 29 #include <fwServices/registry/ObjectService.hpp> 30 #include <fwServices/registry/ServiceConfig.hpp> 31 #include <fwServices/registry/ServiceFactory.hpp> 33 #include <fwTools/System.hpp> 35 #include <boost/filesystem/operations.hpp> 58 ::fwRuntime::ConfigurationElement::sptr config =
m_configuration->findConfigurationElement(
"config");
59 SLM_ASSERT(
"The service ::ioDicomWeb::SSeriesPuller must have a \"config\" element.", config);
64 ::boost::tie(success, m_dicomReaderType) = config->getSafeAttributeValue(
"dicomReader");
65 SLM_ASSERT(
"It should be a \"dicomReader\" in the ::ioDicomWeb::SSeriesPuller config element.", success);
68 ::boost::tie(success, m_dicomReaderSrvConfig) = config->getSafeAttributeValue(
"dicomReaderConfig");
70 ::fwServices::IService::ConfigType configuration = this->
getConfigTree();
72 if(configuration.count(
"server"))
74 const std::string serverInfo = configuration.get(
"server",
"");
75 const std::string::size_type splitPosition = serverInfo.find(
':');
76 SLM_ASSERT(
"Server info not formatted correctly", splitPosition != std::string::npos);
78 const std::string hostnameStr = serverInfo.substr(0, splitPosition);
79 const std::string portStr = serverInfo.substr(splitPosition + 1, serverInfo.size());
81 m_serverHostnameKey = this->getPreferenceKey(hostnameStr);
82 m_serverPortKey = this->getPreferenceKey(portStr);
84 if(m_serverHostnameKey.empty())
86 m_serverHostname = hostnameStr;
88 if(m_serverPortKey.empty())
90 m_serverPort = std::stoi(portStr);
95 throw ::fwTools::Failed(
"'server' element not found");
102 std::string SSeriesPuller::getPreferenceKey(
const std::string& key)
const 104 std::string keyResult;
105 const size_t first = key.find(
'%');
106 const size_t last = key.rfind(
'%');
107 if (first == 0 && last == key.size() - 1)
109 keyResult = key.substr(1, key.size() - 2);
119 m_destinationSeriesDB = this->getInOut< ::fwMedData::SeriesDB>(
"seriesDB");
120 SLM_ASSERT(
"The 'seriesDB' key doesn't exist.", m_destinationSeriesDB);
123 m_tempSeriesDB = ::fwMedData::SeriesDB::New();
128 ::fwIO::IReader::dynamicCast(srvFactory->create(m_dicomReaderType));
129 SLM_ASSERT(
"Unable to create a reader of type: \"" + m_dicomReaderType +
"\" in ::ioDicomWeb::SSeriesPuller.",
131 ::fwServices::OSR::registerService(m_tempSeriesDB, ::fwIO::s_DATA_KEY,
132 ::fwServices::IService::AccessType::INOUT, m_dicomReader);
134 if(!m_dicomReaderSrvConfig.empty())
137 ::fwRuntime::ConfigurationElement::csptr readerConfig =
139 m_dicomReaderSrvConfig,
"::fwIO::IReader");
141 SLM_ASSERT(
"Sorry, there is no service configuration " 142 << m_dicomReaderSrvConfig
143 <<
" for ::fwIO::IReader", readerConfig);
145 m_dicomReader->setConfiguration( ::fwRuntime::ConfigurationElement::constCast(readerConfig) );
148 m_dicomReader->configure();
149 m_dicomReader->start();
157 m_dicomReader->stop();
158 ::fwServices::OSR::unregisterService(m_dicomReader);
165 if(!m_serverHostnameKey.empty())
167 const std::string hostname = ::fwPreferences::getPreference(m_serverHostnameKey);
168 if(!hostname.empty())
170 m_serverHostname = hostname;
173 if(!m_serverPortKey.empty())
175 const std::string port = ::fwPreferences::getPreference(m_serverPortKey);
178 m_serverPort = std::stoi(port);
182 ::fwData::Vector::csptr selectedSeries = this->getInput< ::fwData::Vector >(
"selectedSeries");
188 messageBox.
setTitle(
"Pulling Series");
189 messageBox.
setMessage(
"The service is already pulling data. Please wait until the pulling is done " 190 "before sending a new pull request." );
191 messageBox.
setIcon(::fwGui::dialog::IMessageDialog::INFO);
192 messageBox.
addButton(::fwGui::dialog::IMessageDialog::OK);
195 else if(selectedSeries->empty())
199 messageBox.
setTitle(
"Pulling Series");
200 messageBox.
setMessage(
"Unable to pull series, there is no series selected. " );
201 messageBox.
setIcon(::fwGui::dialog::IMessageDialog::INFO);
202 messageBox.
addButton(::fwGui::dialog::IMessageDialog::OK);
213 void SSeriesPuller::pullSeries()
219 m_pullingDicomSeriesMap.clear();
228 ::fwData::Vector::csptr selectedSeries = this->getInput< ::fwData::Vector >(
"selectedSeries");
231 DicomSeriesContainerType pullSeriesVector;
232 DicomSeriesContainerType selectedSeriesVector;
234 ::fwData::Vector::ConstIteratorType it = selectedSeries->begin();
235 for(; it != selectedSeries->end(); ++it)
237 ::fwMedData::DicomSeries::sptr series = ::fwMedData::DicomSeries::dynamicCast(*it);
241 std::find(m_localSeries.begin(), m_localSeries.end(), series->getInstanceUID()) == m_localSeries.end())
244 m_pullingDicomSeriesMap[series->getInstanceUID()] = series;
246 pullSeriesVector.push_back(series);
247 m_instanceCount += series->getNumberOfInstances();
249 selectedSeriesVector.push_back(series);
253 if(!pullSeriesVector.empty())
256 const InstanceUIDContainerType& seriesInstancesUIDs =
258 for(
const std::string& seriesInstancesUID : seriesInstancesUIDs )
262 query.insert(
"SeriesInstanceUID", seriesInstancesUID.c_str());
265 body.insert(
"Level",
"Series");
266 body.insert(
"Query", query);
267 body.insert(
"Limit", 0);
270 const std::string pacsServer(
"http://" + m_serverHostname +
":" + std::to_string(m_serverPort));
274 pacsServer +
"/tools/find");
275 QByteArray seriesAnswer;
278 seriesAnswer = m_clientQt.
post(request, QJsonDocument(body).toJson());
282 std::stringstream ss;
283 ss <<
"Host not found:\n" 284 <<
" Please check your configuration: \n" 285 <<
"Pacs host name: " << m_serverHostname <<
"\n" 286 <<
"Pacs port: " << m_serverPort <<
"\n";
288 this->displayErrorMessage(ss.str());
292 QJsonDocument jsonResponse = QJsonDocument::fromJson(seriesAnswer);
293 const QJsonArray& seriesArray = jsonResponse.array();
295 const size_t seriesArraySize = seriesArray.count();
296 for(
size_t i = 0; i < seriesArraySize; ++i)
298 const std::string& seriesUID = seriesArray.at(i).toString().toStdString();
301 const std::string& instancesUrl(pacsServer +
"/series/" + seriesUID);
302 const QByteArray& instancesAnswer =
304 jsonResponse = QJsonDocument::fromJson(instancesAnswer);
305 const QJsonObject& jsonObj = jsonResponse.object();
306 const QJsonArray& instancesArray = jsonObj[
"Instances"].toArray();
308 const size_t instancesArraySize = instancesArray.count();
309 for(
size_t j = 0; j < instancesArraySize; ++j)
311 const std::string& instanceUID = instancesArray.at(j).toString().toStdString();
314 const std::string instanceUrl(pacsServer +
"/instances/" + instanceUID +
"/file");
322 std::stringstream ss;
323 ss <<
"Content not found: \n" 324 <<
"Unable download the DICOM instance. \n";
326 this->displayErrorMessage(ss.str());
331 ::boost::filesystem::path instancePath = m_path.parent_path() / seriesInstancesUID;
332 QDir().mkpath(instancePath.string().c_str());
334 instancePath /= m_path.filename();
335 QFile().rename(m_path.string().c_str(), instancePath.string().c_str());
336 m_path = m_path.parent_path() / seriesInstancesUID;
345 this->readLocalSeries(selectedSeriesVector);
354 std::stringstream ss;
355 ss <<
"Unknown error.";
356 this->displayErrorMessage(ss.str());
364 void SSeriesPuller::readLocalSeries(DicomSeriesContainerType selectedSeries)
367 const InstanceUIDContainerType& alreadyLoadedSeries =
373 for(const ::fwMedData::Series::sptr& series: selectedSeries)
375 const std::string& selectedSeriesUID = series->getInstanceUID();
378 if(std::find(m_localSeries.begin(), m_localSeries.end(), selectedSeriesUID) == m_localSeries.end())
380 m_localSeries.push_back(selectedSeriesUID);
384 if(std::find(alreadyLoadedSeries.begin(), alreadyLoadedSeries.end(),
385 selectedSeriesUID) == alreadyLoadedSeries.end())
388 tempSDBhelper.clear();
390 m_dicomReader->setFolder(m_path);
391 m_dicomReader->update();
395 sDBhelper.
merge(m_tempSeriesDB);
403 void SSeriesPuller::displayErrorMessage(
const std::string& message)
const 409 messageBox.
setIcon(::fwGui::dialog::IMessageDialog::CRITICAL);
410 messageBox.
addButton(::fwGui::dialog::IMessageDialog::OK);
virtual IODICOMWEB_API void stopping() override
Stops the DICOM reader.
FWNETWORKIO_API QByteArray post(Request::sptr request, const QByteArray &body)
Performs POST request.
Base class of fwNetworkIO Exception.
static FWNETWORKIO_API Request::sptr New(const std::string &url)
Creates a new Request with given url.
static FWNETWORKIO_API InstanceUIDContainer toSeriesInstanceUIDContainer(DicomSeriesContainer series)
Convert std::vector< ::fwMedData::DicomSeries > to series instance uid container. ...
virtual FWGUI_API void setMessage(const std::string &msg) override
Set the message.
IODICOMWEB_API void updating() override
Checks the configuration and pull the series.
Defines the generic message box for IHM. Use the Delegate design pattern.
FWNETWORKIO_API std::string getFile(Request::sptr request)
Retrieves data over network.
#define SLM_WARN(message)
virtual FWGUI_API void addButton(IMessageDialog::Buttons button) override
Add a button (OK, YES_NO, YES, NO, CANCEL)
FWNETWORKIO_API QByteArray get(Request::sptr request)
Retrieves data over network.
virtual FWGUI_API IMessageDialog::Buttons show() override
Show the message box and return the clicked button.
#define SLM_ASSERT(message, cond)
work like 'assert' from 'cassert', with in addition a message logged by spylog (with FATAL loglevel) ...
virtual IODICOMWEB_API void configuring() override
Gets the configuration.
::fwRuntime::ConfigurationElement::sptr m_configuration
Configuration element used to configure service internal state using a generic XML like structure TOD...
virtual FWGUI_API void setIcon(IMessageDialog::Icons icon) override
Set the icon (CRITICAL, WARNING, INFO or QUESTION)
Implements exception for HTTP content not found errors.
Implements exception for an HTTP host not found errors.
ioDicomWeb contains services use to deal with PACS through HTTP.
static FWSERVICES_API ServiceFactory::sptr getDefault()
Return the unique Instance, create it if required at first access.
virtual IODICOMWEB_API void starting() override
Registers the DICOM reader.
static FWSERVICES_API ServiceConfig::sptr getDefault()
Return the default global instance of ServiceConfig.
virtual IODICOMWEB_API ~SSeriesPuller() noexcept
Destructor.
IODICOMWEB_API SSeriesPuller() noexcept
Constructor.
virtual FWGUI_API void setTitle(const std::string &title) override
Set the title of the message box.
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.