7 #include "ioDicomWeb/SSliceIndexDicomPullerEditor.hpp" 9 #include <fwCom/Signal.hpp> 10 #include <fwCom/Signal.hxx> 11 #include <fwCom/Signals.hpp> 12 #include <fwCom/Slot.hpp> 13 #include <fwCom/Slots.hpp> 14 #include <fwCom/Slots.hxx> 16 #include <fwData/Array.hpp> 17 #include <fwData/Composite.hpp> 18 #include <fwData/Image.hpp> 19 #include <fwData/Integer.hpp> 21 #include <fwDataTools/fieldHelper/Image.hpp> 22 #include <fwDataTools/helper/Array.hpp> 23 #include <fwDataTools/helper/Composite.hpp> 25 #include <fwGui/dialog/MessageDialog.hpp> 27 #include <fwGuiQt/container/QtContainer.hpp> 29 #include <fwMedData/DicomSeries.hpp> 30 #include <fwMedData/ImageSeries.hpp> 31 #include <fwMedData/SeriesDB.hpp> 33 #include <fwMedDataTools/helper/SeriesDB.hpp> 35 #include <fwNetworkIO/exceptions/Base.hpp> 37 #include <fwPreferences/helper.hpp> 39 #include <fwServices/macros.hpp> 40 #include <fwServices/registry/ActiveWorkers.hpp> 41 #include <fwServices/registry/ObjectService.hpp> 43 #include <fwThread/Timer.hpp> 45 #include <fwTools/System.hpp> 47 #include <boost/asio/placeholders.hpp> 48 #include <boost/filesystem/fstream.hpp> 49 #include <boost/filesystem/operations.hpp> 50 #include <boost/foreach.hpp> 52 #include <QApplication> 54 #include <QHBoxLayout> 55 #include <QMouseEvent> 81 ::fwRuntime::ConfigurationElement::sptr config =
m_configuration->findConfigurationElement(
"config");
82 SLM_ASSERT(
"The service ::ioDicomWeb::SPacsConfigurationInitializer must have " 83 "a \"config\" element.", config);
88 ::boost::tie(success, m_dicomReaderType) = config->getSafeAttributeValue(
"dicomReader");
89 SLM_ASSERT(
"It should be a \"dicomReader\" tag in the ::ioDicomWeb::SSliceIndexDicomPullerEditor " 90 "config element.", success);
93 ::fwRuntime::ConfigurationElement::sptr readerConfig = config->findConfigurationElement(
"dicomReaderConfig");
95 (readerConfig && readerConfig->size() == 1) ? readerConfig->getElements()[0] :
nullptr;
99 ::boost::tie(success, delayStr) = config->getSafeAttributeValue(
"delay");
102 m_delay = ::boost::lexical_cast<
unsigned int >(delayStr);
105 if(m_delayTimer && m_delayTimer->isRunning())
107 m_delayTimer->stop();
108 m_delayTimer.reset();
112 m_delayTimer->setFunction( [ = ]()
114 this->triggerNewSlice();
117 m_delayTimer->setOneShot(
true);
122 std::string SSliceIndexDicomPullerEditor::getPreferenceKey(
const std::string& key)
const 124 std::string keyResult;
125 const size_t first = key.find(
'%');
126 const size_t last = key.rfind(
'%');
127 if (first == 0 && last == key.size() - 1)
129 keyResult = key.substr(1, key.size() - 2);
139 ::fwGuiQt::container::QtContainer::sptr qtContainer = fwGuiQt::container::QtContainer::dynamicCast(getContainer());
141 QHBoxLayout* layout =
new QHBoxLayout();
143 ::fwMedData::DicomSeries::csptr dicomSeries = this->getInOut< ::fwMedData::DicomSeries >(
"series");
144 SLM_ASSERT(
"DicomSeries should not be null !", dicomSeries);
145 m_numberOfSlices = dicomSeries->getNumberOfInstances();
148 m_sliceIndexSlider =
new QSlider(Qt::Horizontal);
149 layout->addWidget(m_sliceIndexSlider, 1);
150 m_sliceIndexSlider->setRange(0, static_cast<int>(m_numberOfSlices-1));
151 m_sliceIndexSlider->setValue(static_cast<int>(m_numberOfSlices/2));
154 m_sliceIndexLineEdit =
new QLineEdit();
155 layout->addWidget(m_sliceIndexLineEdit, 0);
156 m_sliceIndexLineEdit->setReadOnly(
true);
157 m_sliceIndexLineEdit->setMaximumWidth(80);
159 std::stringstream ss;
160 ss << m_sliceIndexSlider->value() <<
" / " << (m_numberOfSlices-1);
161 m_sliceIndexLineEdit->setText(std::string(ss.str()).c_str());
163 qtContainer->setLayout(layout);
166 QObject::connect(m_sliceIndexSlider, SIGNAL(valueChanged(
int)),
this, SLOT(changeSliceIndex(
int)));
169 m_tempSeriesDB = ::fwMedData::SeriesDB::New();
174 ::fwIO::IReader::sptr dicomReader;
175 dicomReader = ::fwIO::IReader::dynamicCast(srvFactory->create(m_dicomReaderType));
176 SLM_ASSERT(
"Unable to create a reader of type: \"" + m_dicomReaderType +
"\" in " 177 "::ioDicomWeb::SSliceIndexDicomPullerEditor.", dicomReader);
178 ::fwServices::OSR::registerService(m_tempSeriesDB, ::fwIO::s_DATA_KEY,
179 ::fwServices::IService::AccessType::INOUT, dicomReader);
182 dicomReader->setConfiguration(m_readerConfig);
185 dicomReader->configure();
186 dicomReader->start();
188 m_dicomReader = dicomReader;
191 m_axialIndex = ::fwData::Integer::New(0);
192 m_frontalIndex = ::fwData::Integer::New(0);
193 m_sagittalIndex = ::fwData::Integer::New(0);
198 if(m_delayTimer->isRunning())
200 m_delayTimer->stop();
203 m_delayTimer->setDuration(std::chrono::milliseconds(m_delay));
204 m_delayTimer->start();
208 this->triggerNewSlice();
216 if(m_delayTimer && m_delayTimer->isRunning())
218 m_delayTimer->stop();
222 if(!m_dicomReader.expired())
224 m_dicomReader.lock()->stop();
225 ::fwServices::OSR::unregisterService(m_dicomReader.lock());
239 void SSliceIndexDicomPullerEditor::changeSliceIndex(
int)
242 std::stringstream ss;
243 ss << m_sliceIndexSlider->value() <<
" / " << (m_numberOfSlices-1);
244 m_sliceIndexLineEdit->setText(std::string(ss.str()).c_str());
249 if(m_delayTimer->isRunning())
251 m_delayTimer->stop();
254 m_delayTimer->start();
258 this->triggerNewSlice();
264 void SSliceIndexDicomPullerEditor::triggerNewSlice()
267 ::fwMedData::DicomSeries::csptr dicomSeries = this->getInOut< ::fwMedData::DicomSeries >(
"series");
268 SLM_ASSERT(
"DicomSeries should not be null !", dicomSeries);
271 const size_t selectedSliceIndex =
static_cast<size_t>(m_sliceIndexSlider->value()) +
272 dicomSeries->getFirstInstanceNumber();
273 OSLM_TRACE(
"triggered new slice : " << selectedSliceIndex);
274 if(!dicomSeries->isInstanceAvailable(selectedSliceIndex))
276 this->pullInstance();
280 this->readImage(selectedSliceIndex);
286 void SSliceIndexDicomPullerEditor::readImage(
size_t selectedSliceIndex)
289 ::fwMedData::DicomSeries::csptr dicomSeries = this->getInOut< ::fwMedData::DicomSeries >(
"series");
290 SLM_ASSERT(
"DicomSeries should not be null !", dicomSeries);
291 if( dicomSeries->getModality() !=
"CT" && dicomSeries->getModality() !=
"MR" && dicomSeries->getModality() !=
"XA")
298 ::boost::filesystem::path tmpPath = path /
"tmp";
300 SLM_INFO(
"Create " + tmpPath.string());
301 ::boost::filesystem::create_directories(tmpPath);
303 const auto& binaries = dicomSeries->getDicomContainer();
304 auto iter = binaries.find(selectedSliceIndex);
305 OSLM_ASSERT(
"Index '"<<selectedSliceIndex<<
"' is not found in DicomSeries", iter != binaries.end());
307 const ::fwMemory::BufferObject::sptr bufferObj = iter->second;
308 const ::fwMemory::BufferObject::Lock lockerDest(bufferObj);
309 const char* buffer =
static_cast<char*
>(lockerDest.getBuffer());
310 const size_t size = bufferObj->getSize();
312 ::boost::filesystem::path dest = tmpPath / std::to_string(selectedSliceIndex);
313 ::boost::filesystem::ofstream fs(dest, std::ios::binary|std::ios::trunc);
314 FW_RAISE_IF(
"Can't open '" << tmpPath <<
"' for write.", !fs.good());
316 fs.write(buffer, size);
320 m_dicomReader.lock()->setFolder(tmpPath);
321 if(!m_dicomReader.expired())
323 m_dicomReader.lock()->update();
325 if(m_dicomReader.expired() || m_dicomReader.lock()->isStopped())
336 ::fwMedData::ImageSeries::sptr imageSeries;
338 if(m_tempSeriesDB->getContainer().size() > 0)
340 imageSeries = ::fwMedData::ImageSeries::dynamicCast(*(m_tempSeriesDB->getContainer().begin()));
345 ::fwData::Image::sptr newImage = imageSeries->getImage();
349 m_frontalIndex->setValue(static_cast<int>(newSize[0]/2));
351 m_sagittalIndex->setValue(static_cast<int>(newSize[1]/2));
357 ::boost::system::error_code ec;
358 ::boost::filesystem::remove_all(path, ec);
359 SLM_ERROR_IF(
"remove_all error for path " + path.string() +
": " + ec.message(), ec.value());
364 void SSliceIndexDicomPullerEditor::pullInstance()
366 ::fwServices::IService::ConfigType configuration = this->
getConfigTree();
368 if(configuration.count(
"server"))
370 const std::string serverInfo = configuration.get(
"server",
"");
371 const std::string::size_type splitPosition = serverInfo.find(
':');
372 SLM_ASSERT(
"Server info not formatted correctly", splitPosition != std::string::npos);
374 const std::string hostnameStr = serverInfo.substr(0, splitPosition);
375 const std::string portStr = serverInfo.substr(splitPosition + 1, serverInfo.size());
377 m_serverHostnameKey = this->getPreferenceKey(hostnameStr);
378 m_serverPortKey = this->getPreferenceKey(portStr);
379 if(m_serverHostnameKey.empty())
381 m_serverHostname = hostnameStr;
383 if(m_serverPortKey.empty())
385 m_serverPort = std::stoi(portStr);
390 throw ::fwTools::Failed(
"'server' element not found");
393 if(!m_serverHostnameKey.empty())
395 const std::string hostname = ::fwPreferences::getPreference(m_serverHostnameKey);
396 if(!hostname.empty())
398 m_serverHostname = hostname;
401 if(!m_serverPortKey.empty())
403 const std::string port = ::fwPreferences::getPreference(m_serverPortKey);
406 m_serverPort = std::stoi(port);
414 ::fwMedData::DicomSeries::sptr dicomSeries = this->getInOut< ::fwMedData::DicomSeries >(
"series");
415 SLM_ASSERT(
"DicomSeries should not be null !", dicomSeries);
418 size_t selectedSliceIndex =
static_cast<size_t>(m_sliceIndexSlider->value()) +
419 dicomSeries->getFirstInstanceNumber();
421 std::string seriesInstanceUID = dicomSeries->getInstanceUID();
425 query.insert(
"SeriesInstanceUID", seriesInstanceUID.c_str());
428 body.insert(
"Level",
"Series");
429 body.insert(
"Query", query);
430 body.insert(
"Limit", 0);
433 const std::string pacsServer(
"http://" + m_serverHostname +
":" + std::to_string(m_serverPort));
437 pacsServer +
"/tools/find");
438 QByteArray seriesAnswer;
441 seriesAnswer = m_clientQt.
post(request, QJsonDocument(body).toJson());
445 std::stringstream ss;
446 ss <<
"Host not found:\n" 447 <<
" Please check your configuration: \n" 448 <<
"Pacs host name: " << m_serverHostname <<
"\n" 449 <<
"Pacs port: " << m_serverPort <<
"\n";
451 this->displayErrorMessage(ss.str());
454 QJsonDocument jsonResponse = QJsonDocument::fromJson(seriesAnswer);
455 const QJsonArray& seriesArray = jsonResponse.array();
458 const std::string& seriesUID = seriesArray.at(0).toString().toStdString();
460 const std::string& instancesUrl(pacsServer +
"/series/" + seriesUID);
462 const QByteArray& instancesAnswer =
464 jsonResponse = QJsonDocument::fromJson(instancesAnswer);
465 const QJsonObject& jsonObj = jsonResponse.object();
466 const QJsonArray& instancesArray = jsonObj[
"Instances"].toArray();
467 const std::string& instanceUID =
468 instancesArray.at(static_cast<int>(selectedSliceIndex)).toString().toStdString();
471 std::string instancePath;
472 const std::string& instanceUrl(pacsServer +
"/instances/" + instanceUID +
"/file");
479 std::stringstream ss;
480 ss <<
"Content not found: \n" 481 <<
"Unable download the DICOM instance. \n";
483 this->displayErrorMessage(ss.str());
488 dicomSeries->addDicomPath(selectedSliceIndex, instancePath);
489 this->readImage(selectedSliceIndex);
493 std::stringstream ss;
494 ss <<
"Unknown error.";
495 this->displayErrorMessage(ss.str());
502 void SSliceIndexDicomPullerEditor::displayErrorMessage(
const std::string& message)
const 508 messageBox.
setIcon(::fwGui::dialog::IMessageDialog::CRITICAL);
509 messageBox.
addButton(::fwGui::dialog::IMessageDialog::OK);
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.
#define OSLM_ASSERT(message, cond)
work like 'assert' from 'cassert', with in addition a message logged by spylog (with FATAL loglevel) ...
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.
FWGUI_API void destroy()
Stops sub-views and toobar services. Destroys view, sub-views and toolbar containers.
FWNETWORKIO_API std::string getFile(Request::sptr request)
Retrieves data over network.
#define OSLM_TRACE(message)
#define SLM_WARN(message)
IODICOMWEB_API void updating() override
Does nothing.
FWSERVICES_API void setOutput(const ::fwServices::IService::KeyType &key, const ::fwData::Object::sptr &object, size_t index=0)
Register an output object at a given key in the OSR, replacing it if it already exists.
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_ERROR_IF(message, cond)
#define SLM_ASSERT(message, cond)
work like 'assert' from 'cassert', with in addition a message logged by spylog (with FATAL loglevel) ...
::fwRuntime::ConfigurationElement::sptr m_configuration
Configuration element used to configure service internal state using a generic XML like structure TOD...
FWGUI_API void create()
Creates view, sub-views and toolbar containers. Manages sub-views and toobar services.
virtual FWGUI_API void setIcon(IMessageDialog::Icons icon) override
Set the icon (CRITICAL, WARNING, INFO or QUESTION)
virtual IODICOMWEB_API void starting() override
Creates the wigdets, connect the signal, register the DICOM reader and starts the callback timer...
Implements exception for HTTP content not found errors.
Implements exception for an HTTP host not found errors.
virtual IODICOMWEB_API void stopping() override
Stops the timer, unregister the DICOM reader and destroy the created widgets.
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 ~SSliceIndexDicomPullerEditor() noexcept
Destructor.
std::shared_ptr< ::fwThread::Worker > m_associatedWorker
Associated worker.
#define SLM_INFO(message)
virtual IODICOMWEB_API void configuring() override
Gets the configurations and creates a timer on a worker.
IODICOMWEB_API SSliceIndexDicomPullerEditor() noexcept
Constructor.
::fwData::Array::SizeType SizeType
Image size type.
virtual FWGUI_API void setTitle(const std::string &title) override
Set the title of the message box.
FWGUI_API void initialize()
Initialize managers.
FWSERVICES_API ConfigType getConfigTree() const
Return the configuration, in an boost property tree.