diff --git a/Modules/REST/documentation/REST.dox b/Modules/REST/documentation/REST.dox index 34eab399f7..1d467e93c6 100644 --- a/Modules/REST/documentation/REST.dox +++ b/Modules/REST/documentation/REST.dox @@ -1,194 +1,194 @@ /** \page RESTModule The MITK REST Module \tableofcontents \section REST_brief Description The MITK REST Module is able to manage REST requests. The main class is the RESTManager. It is a MicroServices which can be accessed via \code{.cpp} auto *context = us::GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { //call the function you need from the service } } \endcode \subsection REST_Technical Technical background The module uses the Microsoft C++ REST SDK for REST mechanisms as well as JSON convertion and asynchronic programming. \section Use_REST How to use the REST Module You can use the REST module from two different perspectives in MITK:
  1. The Server view (receive requests from clients)
  2. The Client view (send requests to servers)
The following sections will give you an introduction on how to use which of those roles: \subsection Server_Use Use from a Server perspective To act as a server, you need to implement the IRESTObserver, which has a Notify() method that has to be implemented. In this Notify() method you specify how you want to react to incoming requests and with which data you want to respond to the requests. You can then start listening for requests from clients as shown below: \code{.cpp} auto *context = us::GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { managerService->ReceiveRequests(uri /*specify your uri which you want to receive requests for*/, this); } } \endcode If a client sends a request, the Notify method is called and a response is sent. By now, only GET-requests from clients are supported. If you want to stop listening for requests you can do this by calling \code{.cpp} auto *context = us::GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { managerService->HandleDeleteObserver(this, uri); } } \endcode You don't have to specify a uri in the HandleDeleteObserver method, if you only call managerService->HandleDeleteObserver(this);, all uris you receive requests for are deleted and you aren't listening to any requests anymore. \subsection Client_Use Use from a Client perspective The following example shows how to send requests from a client perspective: \code{.cpp} //Get the microservice auto *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { //Call the send request method which starts the actual request managerService - ->SendRequest(_XPLATSTR("https://jsonplaceholder.typicode.com/posts/1")) + ->SendRequest(U("https://jsonplaceholder.typicode.com/posts/1")) .then([=](pplx::task resultTask)/*It is important to use task-based continuation*/ { try { //Get the result of the request //This will throw an exception if the ascendent task threw an exception (e.g. invalid URI) web::json::value result = resultTask.get(); //Do something with the result (e.g. convert it to a QString to update an UI element) utility::string_t stringT = result.to_string(); std::string stringStd(stringT.begin(), stringT.end()); QString stringQ = QString::fromStdString(stringStd); //Note: if you want to update your UI, do this by using signals and slots. //The UI can't be updated from a Thread different to the Qt main thread emit UpdateLabel(stringQ); } catch (const mitk::Exception &exception) { //Exceptions from ascendent tasks are catched here MITK_ERROR << exception.what(); return; } }); } } \endcode The steps you need to make are the following:
  1. Get the microservice. You can get the microservice via the module context. If you want to use the microservice within a plug-in, you need to get the module context from the us::ModuleRegistry.
  2. Call the SendRequest method. This will start the request itself and is performed asynchronously. As soon as the response is sent by the server, the .then(...) block is executed.
  3. Choose parameters for .then(...) block. For exception handling, it is important to choose pplx::task . This is a task-based continuation. For more information, visit https://docs.microsoft.com/en-us/cpp/parallel/concrt/exception-handling-in-the-concurrency-runtime?view=vs-2017.
  4. Get the result of the request. You can get the JSON-value of the result by callint .get(). At this point, an exception is thrown if something in the previous tasks threw an exception.
  5. Do something with the result. \note If you want to modify GUI elements within the .then(...) block, you need to do this by using signals and slots because GUI elements can only be modified by th Qt Main Thread. For more information, visit https://doc.qt.io/Qt-5/thread-basics.html#gui-thread-and-worker-thread
  6. Exception handling. Here you can define the behaviour if an exception is thrown, exceptions from ascendent tasks are also catched here.
Code, which is followed by this codeblock shown above will be performed asynchronously while waiting for the result. Besides Get-Requests, you can also perform Put or Post requests by specifying a RequestType in the SendRequest method. The following example shows, how you can perform multiple tasks, encapsulated to one joined task. The steps are based on the example for one request and only the specific steps for encapsulation are described. \code{.cpp} //Get the microservice //Get microservice auto *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { //Create multiple tasks e.g. as shown below std::vector> tasks; for (int i = 0; i < 20; i++) { pplx::task singleTask = managerService->SendRequest(L"https://jsonplaceholder.typicode.com/posts/1") .then([=](pplx::task resultTask) { //Do something when a single task is done try { resultTask.get(); emit UpdateProgressBar(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }); tasks.emplace_back(singleTask); } //Create a joinTask which includes all tasks you've created auto joinTask = pplx::when_all(begin(tasks), end(tasks)); //Run asynchonously joinTask.then([=](pplx::task resultTask) { //Do something when all tasks are finished try { resultTask.get(); emit UpdateLabel("All tasks finished"); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }); } \endcode The steps you need to make are the following:
  1. Get the microservice. See example above.
  2. Create multiple tasks. In this example, 20 identical tasks are created and are saved into a vector. In general, it is possible to place any tasks in that vector.
  3. Do something when a single task is done. Here, an action is performed if a single tasks is finished. In this example, a progress bar is loaded by a specific number of percent.
  4. Create a joinTask. Here, all small tasks are encapsulated in one big task.
  5. Run joinTask asynchonously. The then(...) of the joinTask is performed when all single tasks are finished.
  6. Do something when all tasks are finished. The handling of the end of a joinTask is equivalent to the end of a single tasks.
*/ diff --git a/Modules/REST/include/mitkRESTClient.h b/Modules/REST/include/mitkRESTClient.h index d3c359140b..0c9b9cfa2a 100644 --- a/Modules/REST/include/mitkRESTClient.h +++ b/Modules/REST/include/mitkRESTClient.h @@ -1,95 +1,104 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkRESTClient_h #define mitkRESTClient_h #include #include namespace mitk { class MITKREST_EXPORT RESTClient { public: RESTClient(); ~RESTClient(); /** * @brief Executes a HTTP GET request with the given uri and returns a task waiting for a json object * * @throw mitk::Exception if request went wrong * @param uri the URI resulting the target of the HTTP request * @param the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Get(const web::uri &uri, const std::map headers); /** * @brief Executes a HTTP GET request with the given uri and and stores the byte stream in a file given by the * filePath * * @throw mitk::Exception if request went wrong * @param uri the URI resulting the target of the HTTP request * @param the additional headers to be set to the HTTP request * @return task to wait for returning an empty json object */ pplx::task Get(const web::uri &uri, const utility::string_t &filePath, const std::map headers); /** * @brief Executes a HTTP PUT request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resources * @return task to wait for with resulting json object */ pplx::task Put(const web::uri &uri, const web::json::value *content); /** * @brief Executes a HTTP POST request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resource - * @param headers the additional headers to be set to the HTTP request + * @param headers the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Post(const web::uri &uri, const web::json::value *content, const std::map headers); - /** + /** * @brief Executes a HTTP POST request with given uri and the content given as json * * @throw mitk::Exception if request went wrong * @param uri defines the URI resulting the target of the HTTP request * @param content the content as json value which should be the body of the request and thus the content of the * created resource * @param headers the additional headers to be set to the HTTP request * @return task to wait for with resulting json object */ pplx::task Post(const web::uri &uri, const std::vector *content, const std::map headers); + + private: + /** + * @brief Use this to create and init a new request with the given headers. If needed, set the body on the resulting + * request object to avoid an automatic change of the content type header when setting the body first. + */ + http_request InitRequest(const std::map headers); + + pplx::task ExecutePost(const web::uri &uri, http_request request); }; -} +} // namespace mitk #endif diff --git a/Modules/REST/src/mitkRESTClient.cpp b/Modules/REST/src/mitkRESTClient.cpp index db52f9dc94..d949aa0787 100644 --- a/Modules/REST/src/mitkRESTClient.cpp +++ b/Modules/REST/src/mitkRESTClient.cpp @@ -1,228 +1,199 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ +#include #include #include -#include -#include #include +#include using http_client = web::http::client::http_client; using http_request = web::http::http_request; using http_response = web::http::http_response; using methods = web::http::methods; using status_codes = web::http::status_codes; using file_buffer = concurrency::streams::file_buffer; using streambuf = concurrency::streams::streambuf; -mitk::RESTClient::RESTClient() -{ -} +mitk::RESTClient::RESTClient() {} -mitk::RESTClient::~RESTClient() -{ -} +mitk::RESTClient::~RESTClient() {} pplx::task mitk::RESTClient::Get(const web::uri &uri, const std::map headers) { - auto client = new http_client(uri); http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } return client->request(request).then([=](pplx::task responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); if (status_codes::OK != status) mitkThrow(); auto requestContentType = response.headers().content_type(); - if (_XPLATSTR("application/json") != requestContentType) - response.headers().set_content_type(_XPLATSTR("application/json")); + if (U("application/json") != requestContentType) + response.headers().set_content_type(U("application/json")); return response.extract_json().get(); } catch (...) { mitkThrow() << "Getting response went wrong"; } }); } pplx::task mitk::RESTClient::Get(const web::uri &uri, const utility::string_t &filePath, const std::map headers) { auto client = new http_client(uri); auto fileBuffer = std::make_shared>(); http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } // Open file stream for the specified file path return file_buffer::open(filePath, std::ios::out) .then([=](streambuf outFile) -> pplx::task { *fileBuffer = outFile; return client->request(methods::GET); }) // Write the response body into the file buffer .then([=](http_response response) -> pplx::task { auto status = response.status_code(); if (status_codes::OK != status) mitkThrow() << "GET ended up with response " << RESTUtil::convertToUtf8(response.to_string()); - + return response.body().read_to_end(*fileBuffer); }) // Close the file buffer - .then([=](size_t) { - return fileBuffer->close(); - }) + .then([=](size_t) { return fileBuffer->close(); }) // Return empty JSON object - .then([=]() { - return web::json::value(); - }); + .then([=]() { return web::json::value(); }); } pplx::task mitk::RESTClient::Put(const web::uri &uri, const web::json::value *content) { auto client = new http_client(uri); http_request request(methods::PUT); if (nullptr != content) request.set_body(*content); - + return client->request(request).then([=](pplx::task responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); if (status_codes::OK != status) mitkThrow(); // Parse content type to application/json if it isn't already. This is // important if the content type is e.g. application/dicom+json. auto requestContentType = response.headers().content_type(); - if (_XPLATSTR("application/json") != requestContentType) - response.headers().set_content_type(_XPLATSTR("application/json")); + if (U("application/json") != requestContentType) + response.headers().set_content_type(U("application/json")); return response.extract_json().get(); } catch (...) { mitkThrow() << "Getting response went wrong"; } }); } pplx::task mitk::RESTClient::Post(const web::uri &uri, const std::vector *content, const std::map headers) { - auto client = new http_client(uri); - http_request request(methods::POST); - - // first put the headers, to avoid overriding them by request.set_body() - for (auto param : headers) - { - request.headers().add(param.first, param.second); - } + auto request = InitRequest(headers); + request.set_method(methods::POST); if (nullptr != content) request.set_body(*content); - return client->request(request).then([=](pplx::task responseTask) { - try - { - auto response = responseTask.get(); - auto status = response.status_code(); - - if (status_codes::OK != status) - mitkThrow(); - - // Parse content type to application/json if it isn't already. This is - // important if the content type is e.g. application/dicom+json. - auto requestContentType = response.headers().content_type(); - if (requestContentType == _XPLATSTR("application/dicom+json")) - { - response.headers().set_content_type(_XPLATSTR("application/json")); - return response.extract_json().get(); - } - else - { - return web::json::value(response.to_string()); - } - } - catch (...) - { - mitkThrow() << "Getting response went wrong"; - } - }); + return ExecutePost(uri, request); } pplx::task mitk::RESTClient::Post(const web::uri &uri, const web::json::value *content, const std::map headers) { - auto client = new http_client(uri); - http_request request(methods::POST); + auto request = InitRequest(headers); + request.set_method(methods::POST); + + if (nullptr != content) + request.set_body(*content); + + return ExecutePost(uri, request); +} + +http_request mitk::RESTClient::InitRequest(const std::map headers) +{ + http_request request; for (auto param : headers) { request.headers().add(param.first, param.second); } + return request; +} - if (nullptr != content) - request.set_body(*content); - +pplx::task mitk::RESTClient::ExecutePost(const web::uri &uri, http_request request) +{ + auto client = new http_client(uri); return client->request(request).then([=](pplx::task responseTask) { try { auto response = responseTask.get(); auto status = response.status_code(); if (status_codes::Created != status) mitkThrow(); // Parse content type to application/json if it isn't already. This is // important if the content type is e.g. application/dicom+json. auto requestContentType = response.headers().content_type(); - if (_XPLATSTR("application/json") != requestContentType) - response.headers().set_content_type(_XPLATSTR("application/json")); + if (U("application/json") != requestContentType) + response.headers().set_content_type(U("application/json")); return response.extract_json().get(); } - catch(...) + catch (...) { mitkThrow() << "Getting response went wrong"; } }); } diff --git a/Modules/REST/src/mitkRESTServer.cpp b/Modules/REST/src/mitkRESTServer.cpp index d195835164..35fe8dd7f0 100644 --- a/Modules/REST/src/mitkRESTServer.cpp +++ b/Modules/REST/src/mitkRESTServer.cpp @@ -1,115 +1,115 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include using namespace std::placeholders; using http_listener = web::http::experimental::listener::http_listener; using http_request = web::http::http_request; using http_response = web::http::http_response; using methods = web::http::methods; using status_codes = web::http::status_codes; namespace mitk { class RESTServer::Impl { public: Impl(const web::uri &uri); ~Impl(); void HandleRequest(const http_request &request); web::http::experimental::listener::http_listener listener; web::uri uri; }; RESTServer::Impl::Impl(const web::uri &uri) : uri{uri} { } RESTServer::Impl::~Impl() { } void RESTServer::Impl::HandleRequest(const http_request &request) { web::uri_builder builder(this->listener.uri()); builder.append(request.absolute_uri()); auto uriString = builder.to_uri().to_string(); http_response response(status_codes::InternalError); - response.set_body(_XPLATSTR("There went something wrong after receiving the request.")); + response.set_body(U("There went something wrong after receiving the request.")); auto context = us::GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto manager = context->GetService(managerRef); if (manager) { // not every request contains JSON data web::json::value data = {}; if (request.headers().content_type() == U("application/json")) { data = request.extract_json().get(); } response = manager->Handle(builder.to_uri(), data, request.method()); } } request.reply(response); } } mitk::RESTServer::RESTServer(const web::uri &uri) : m_Impl{std::make_unique(uri)} { } mitk::RESTServer::~RESTServer() { } void mitk::RESTServer::OpenListener() { m_Impl->listener = http_listener(m_Impl->uri); m_Impl->listener.support(std::bind(&Impl::HandleRequest, m_Impl.get(), _1)); m_Impl->listener.support(methods::OPTIONS, std::bind(&Impl::HandleRequest, m_Impl.get(), _1)); m_Impl->listener.open().wait(); } void mitk::RESTServer::CloseListener() { m_Impl->listener.close().wait(); } web::uri mitk::RESTServer::GetUri() { return m_Impl->uri; } diff --git a/Modules/REST/test/mitkRESTClientTest.cpp b/Modules/REST/test/mitkRESTClientTest.cpp index 305706bc46..a4a1e4d39b 100644 --- a/Modules/REST/test/mitkRESTClientTest.cpp +++ b/Modules/REST/test/mitkRESTClientTest.cpp @@ -1,247 +1,247 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include class mitkRESTClientTestSuite : public mitk::TestFixture, mitk::IRESTObserver { CPPUNIT_TEST_SUITE(mitkRESTClientTestSuite); // MITK_TEST(GetRequestValidURI_ReturnsExpectedJSON); GET requests do not support content yet? MITK_TEST(MultipleGetRequestValidURI_AllTasksFinish); // MITK_TEST(PutRequestValidURI_ReturnsExpectedJSON); Does not work reliably on dart clients // MITK_TEST(PostRequestValidURI_ReturnsExpectedJSON); -- " -- MITK_TEST(GetRequestInvalidURI_ThrowsException); MITK_TEST(PutRequestInvalidURI_ThrowsException); MITK_TEST(PostRequestInvalidURI_ThrowsException); CPPUNIT_TEST_SUITE_END(); public: mitk::IRESTManager *m_Service; web::json::value m_Data; web::json::value Notify(const web::uri &, const web::json::value &, const web::http::method &) override { return m_Data; } /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_Data = web::json::value(); - m_Data[_XPLATSTR("userId")] = web::json::value(1); - m_Data[_XPLATSTR("id")] = web::json::value(1); - m_Data[_XPLATSTR("title")] = web::json::value(U("this is a title")); - m_Data[_XPLATSTR("body")] = web::json::value(U("this is a body")); + m_Data[U("userId")] = web::json::value(1); + m_Data[U("id")] = web::json::value(1); + m_Data[U("title")] = web::json::value(U("this is a title")); + m_Data[U("body")] = web::json::value(U("this is a body")); us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); if (serviceRef) { m_Service = us::GetModuleContext()->GetService(serviceRef); } if (!m_Service) { CPPUNIT_FAIL("Getting Service in setUp() failed"); } - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/clienttest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/clienttest"), this); } void tearDown() override { m_Service->HandleDeleteObserver(this); } void GetRequestValidURI_ReturnsExpectedJSON() { web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/clienttest")) + m_Service->SendRequest(U("http://localhost:8080/clienttest")) .then([&](pplx::task resultTask) { try { result = resultTask.get(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }) .wait(); CPPUNIT_ASSERT_MESSAGE("Result is the expected JSON value", result == m_Data); } void MultipleGetRequestValidURI_AllTasksFinish() { int count = 0; // Create multiple tasks e.g. as shown below std::vector> tasks; for (int i = 0; i < 20; ++i) { pplx::task singleTask = - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/clienttest")) + m_Service->SendRequest(U("http://localhost:8080/clienttest")) .then([&](pplx::task resultTask) { // Do something when a single task is done try { resultTask.get(); count +=1; } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }); tasks.emplace_back(singleTask); } // Create a joinTask which includes all tasks you've created auto joinTask = pplx::when_all(begin(tasks), end(tasks)); // Run asynchonously joinTask.then([&](pplx::task resultTask) { // Do something when all tasks are finished try { resultTask.get(); count += 1; } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }).wait(); CPPUNIT_ASSERT_MESSAGE("Multiple Requests", 21 == count); } void PutRequestValidURI_ReturnsExpectedJSON() { // optional: link might get invalid or content is changed web::json::value result; m_Service - ->SendRequest(_XPLATSTR("https://jsonplaceholder.typicode.com/posts/1"), mitk::IRESTManager::RequestType::Put, &m_Data) + ->SendRequest(U("https://jsonplaceholder.typicode.com/posts/1"), mitk::IRESTManager::RequestType::Put, &m_Data) .then([&](pplx::task resultTask) { try { result = resultTask.get(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }) .wait(); CPPUNIT_ASSERT_MESSAGE( "Result is the expected JSON value, check if the link is still valid since this is an optional test", result == m_Data); } void PostRequestValidURI_ReturnsExpectedJSON() { // optional: link might get invalid or content is changed web::json::value result; web::json::value data; - data[_XPLATSTR("userId")] = m_Data[_XPLATSTR("userId")]; - data[_XPLATSTR("title")] = m_Data[_XPLATSTR("title")]; - data[_XPLATSTR("body")] = m_Data[_XPLATSTR("body")]; + data[U("userId")] = m_Data[U("userId")]; + data[U("title")] = m_Data[U("title")]; + data[U("body")] = m_Data[U("body")]; - m_Service->SendRequest(_XPLATSTR("https://jsonplaceholder.typicode.com/posts"), mitk::IRESTManager::RequestType::Post, &data) + m_Service->SendRequest(U("https://jsonplaceholder.typicode.com/posts"), mitk::IRESTManager::RequestType::Post, &data) .then([&](pplx::task resultTask) { try { result = resultTask.get(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }) .wait(); - data[_XPLATSTR("id")] = web::json::value(101); + data[U("id")] = web::json::value(101); CPPUNIT_ASSERT_MESSAGE( "Result is the expected JSON value, check if the link is still valid since this is an optional test", result == data); } void GetException() { //Method which makes a get request to an invalid uri web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:1234/invalid")) + m_Service->SendRequest(U("http://localhost:1234/invalid")) .then([&](pplx::task resultTask) { result = resultTask.get(); }) .wait(); } void GetRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(GetException(), mitk::Exception); } void PutException() { //Method which makes a put request to an invalid uri web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Put, &m_Data) + m_Service->SendRequest(U("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Put, &m_Data) .then([&](pplx::task resultTask) { result = resultTask.get();}) .wait(); } void PutRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(PutException(), mitk::Exception); } void PostException() { //Method which makes a post request to an invalid uri web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Post, &m_Data) + m_Service->SendRequest(U("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Post, &m_Data) .then([&](pplx::task resultTask) { result = resultTask.get(); }) .wait(); } void PostRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(PostException(), mitk::Exception); } }; MITK_TEST_SUITE_REGISTRATION(mitkRESTClient) diff --git a/Modules/REST/test/mitkRESTServerTest.cpp b/Modules/REST/test/mitkRESTServerTest.cpp index be69606cf6..f5bd3a7f8b 100644 --- a/Modules/REST/test/mitkRESTServerTest.cpp +++ b/Modules/REST/test/mitkRESTServerTest.cpp @@ -1,227 +1,227 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifdef _WIN32 #include #endif #include #include #include #include #include #include #include #include #include class mitkRESTServerTestSuite : public mitk::TestFixture, mitk::IRESTObserver { CPPUNIT_TEST_SUITE(mitkRESTServerTestSuite); MITK_TEST(OpenListener_Succeed); MITK_TEST(TwoListenerSameHostSamePort_OnlyOneOpened); MITK_TEST(CloseListener_Succeed); MITK_TEST(OpenMultipleListenerCloseOne_Succeed); MITK_TEST(OpenMultipleListenerCloseAll_Succeed); // MITK_TEST(OpenListenerGetRequestSamePath_ReturnExpectedJSON); GET requests do not support content yet? MITK_TEST(CloseListener_NoRequestPossible); MITK_TEST(OpenListenerGetRequestDifferentPath_ReturnNotFound); MITK_TEST(OpenListenerCloseAndReopen_Succeed); CPPUNIT_TEST_SUITE_END(); public: mitk::IRESTManager *m_Service; web::json::value m_Data; web::json::value Notify(const web::uri &, const web::json::value &, const web::http::method &) override { return m_Data; } /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_Data = web::json::value(); - m_Data[_XPLATSTR("userId")] = web::json::value(1); - m_Data[_XPLATSTR("id")] = web::json::value(1); - m_Data[_XPLATSTR("title")] = web::json::value(U("this is a title")); - m_Data[_XPLATSTR("body")] = web::json::value(U("this is a body")); + m_Data[U("userId")] = web::json::value(1); + m_Data[U("id")] = web::json::value(1); + m_Data[U("title")] = web::json::value(U("this is a title")); + m_Data[U("body")] = web::json::value(U("this is a body")); auto serviceRef = us::GetModuleContext()->GetServiceReference(); if (serviceRef) m_Service = us::GetModuleContext()->GetService(serviceRef); if (!m_Service) CPPUNIT_FAIL("Getting Service in setUp() failed"); } void tearDown() override { m_Service->HandleDeleteObserver(this); } void OpenListener_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); } void TwoListenerSameHostSamePort_OnlyOneOpened() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/serverexample"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/serverexample"), this); CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, observer map size is two", 2 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, server map size is one", 1 == m_Service->GetServerMap().size()); } void CloseListener_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); m_Service->HandleDeleteObserver(this); CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); } void OpenMultipleListenerCloseOne_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8090/serverexample"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8090/serverexample"), this); CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size()); - m_Service->HandleDeleteObserver(this, _XPLATSTR("http://localhost:8080/servertest")); + m_Service->HandleDeleteObserver(this, U("http://localhost:8080/servertest")); CPPUNIT_ASSERT_MESSAGE("Closed one of two listeners, observer map is size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed one of two listener, server map size is one", 1 == m_Service->GetServerMap().size()); } void OpenMultipleListenerCloseAll_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8090/serverexample"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8090/serverexample"), this); CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size()); m_Service->HandleDeleteObserver(this); CPPUNIT_ASSERT_MESSAGE("Closed all listeners, observer map is empty", 0 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed all listeners, server map is empty", 0 == m_Service->GetServerMap().size()); } void OpenListenerGetRequestSamePath_ReturnExpectedJSON() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/servertest")) + m_Service->SendRequest(U("http://localhost:8080/servertest")) .then([&](pplx::task resultTask) { try { result = resultTask.get(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }) .wait(); CPPUNIT_ASSERT_MESSAGE("Opened listener and send request to same uri, returned expected JSON", result == m_Data); } void RequestToClosedListener() { web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/servertest")) + m_Service->SendRequest(U("http://localhost:8080/servertest")) .then([&](pplx::task resultTask) { result = resultTask.get(); }) .wait(); } void CloseListener_NoRequestPossible() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); m_Service->HandleDeleteObserver(this); CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); CPPUNIT_ASSERT_THROW(RequestToClosedListener(), mitk::Exception); } void RequestToDifferentPathNotFound() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/serverexample")) + m_Service->SendRequest(U("http://localhost:8080/serverexample")) .then([&](pplx::task resultTask) { result = resultTask.get(); }) .wait(); } void OpenListenerGetRequestDifferentPath_ReturnNotFound() { CPPUNIT_ASSERT_THROW(RequestToDifferentPathNotFound(), mitk::Exception); } void OpenListenerCloseAndReopen_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); m_Service->HandleDeleteObserver(this); CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(U("http://localhost:8080/servertest"), this); CPPUNIT_ASSERT_MESSAGE("Reopened listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Reopened listener, server map size is one", 1 == m_Service->GetServerMap().size()); } }; MITK_TEST_SUITE_REGISTRATION(mitkRESTServer) diff --git a/Modules/RESTService/src/mitkRESTManager.cpp b/Modules/RESTService/src/mitkRESTManager.cpp index ab3d87a5ab..b7879b0a56 100644 --- a/Modules/RESTService/src/mitkRESTManager.cpp +++ b/Modules/RESTService/src/mitkRESTManager.cpp @@ -1,247 +1,247 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include mitk::RESTManager::RESTManager() { } mitk::RESTManager::~RESTManager() { } pplx::task mitk::RESTManager::SendRequest( const web::uri &uri, const RequestType &type, const std::vector *content, const std::map headers) { pplx::task answer; auto client = new RESTClient; switch (type) { case RequestType::Post: if (nullptr == content) MITK_WARN << "Content for post is empty, this will create an empty resource"; answer = client->Post(uri, content, headers); break; default: - mitkThrow() << "Request Type not supported"; + mitkThrow() << "Request Type not supported for binary data"; } return answer; } pplx::task mitk::RESTManager::SendRequest(const web::uri &uri, const RequestType &type, const web::json::value *content, const std::map headers, const utility::string_t &filePath) { pplx::task answer; auto client = new RESTClient; switch (type) { case RequestType::Get: answer = !filePath.empty() ? client->Get(uri, filePath, headers) : client->Get(uri, headers); break; case RequestType::Post: if (nullptr == content) MITK_WARN << "Content for post is empty, this will create an empty resource"; answer = client->Post(uri, content, headers); break; case RequestType::Put: if (nullptr == content) MITK_WARN << "Content for put is empty, this will empty the ressource"; answer = client->Put(uri, content); break; default: mitkThrow() << "Request Type not supported"; } return answer; } void mitk::RESTManager::ReceiveRequest(const web::uri &uri, mitk::IRESTObserver *observer) { // New instance of RESTServer in m_ServerMap, key is port of the request auto port = uri.port(); // Checking if port is free to add a new Server if (0 == m_ServerMap.count(port)) { this->AddObserver(uri, observer); // creating server instance auto server = new RESTServer(uri.authority()); // add reference to server instance to map m_ServerMap[port] = server; // start Server server->OpenListener(); } // If there is already a server under this port else { this->RequestForATakenPort(uri, observer); } } web::http::http_response mitk::RESTManager::Handle(const web::uri &uri, const web::json::value &body, const web::http::method &method) { // Checking if there is an observer for the port and path auto key = std::make_pair(uri.port(), uri.path()); if (0 != m_Observers.count(key)) { return m_Observers[key]->Notify(uri, body, method); } // No observer under this port, return null which results in status code 404 (s. RESTServer) else { MITK_WARN << "No Observer can handle the data"; web::http::http_response response(web::http::status_codes::BadGateway); - response.set_body(_XPLATSTR("No one can handle the request under the given port.")); + response.set_body(U("No one can handle the request under the given port.")); return response; } } void mitk::RESTManager::HandleDeleteObserver(IRESTObserver *observer, const web::uri &uri) { for (auto it = m_Observers.begin(); it != m_Observers.end();) { mitk::IRESTObserver *obsMap = it->second; // Check wether observer is at this place in map if (observer == obsMap) { // Check wether it is the right uri to be deleted if (uri.is_empty() || uri.path() == it->first.second) { int port = it->first.first; bool noObserverForPort = this->DeleteObserver(it); if (noObserverForPort) { // there isn't an observer at this port, delete m_ServerMap entry for this port // close listener m_ServerMap[port]->CloseListener(); delete m_ServerMap[port]; // delete server from map m_ServerMap.erase(port); } } else { ++it; } } else { ++it; } } } const std::map &mitk::RESTManager::GetServerMap() { return m_ServerMap; } std::map, mitk::IRESTObserver *> &mitk::RESTManager::GetObservers() { return m_Observers; } void mitk::RESTManager::AddObserver(const web::uri &uri, IRESTObserver *observer) { // new observer has to be added std::pair key(uri.port(), uri.path()); m_Observers[key] = observer; } void mitk::RESTManager::RequestForATakenPort(const web::uri &uri, IRESTObserver *observer) { // Same host, means new observer but not a new server instance if (uri.authority() == m_ServerMap[uri.port()]->GetUri()) { // new observer has to be added std::pair key(uri.port(), uri.path()); // only add a new observer if there isn't already an observer for this uri if (0 == m_Observers.count(key)) { this->AddObserver(uri, observer); } else { MITK_ERROR << "Threre is already a observer handeling this data"; } } // Error, since another server can't be added under this port else { MITK_ERROR << "There is already another server listening under this port"; } } bool mitk::RESTManager::DeleteObserver(std::map, IRESTObserver *>::iterator &it) { int port = it->first.first; it = m_Observers.erase(it); for (auto observer : m_Observers) { if (port == observer.first.first) { // there still exists an observer for this port return false; } } return true; } void mitk::RESTManager::SetServerMap(const int port, RESTServer *server) { m_ServerMap[port] = server; } void mitk::RESTManager::DeleteFromServerMap(const int port) { m_ServerMap.erase(port); } void mitk::RESTManager::SetObservers(const std::pair key, IRESTObserver *observer) { m_Observers[key] = observer; } diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/DICOMweb.cpp b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/DICOMweb.cpp index 5b00f27998..a886d82e08 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/DICOMweb.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/DICOMweb.cpp @@ -1,195 +1,219 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "DICOMweb.h" -DICOMweb::DICOMweb() -{ -} +DICOMweb::DICOMweb() {} DICOMweb::DICOMweb(utility::string_t baseURI) : m_BaseURI(baseURI) { MITK_INFO << "base uri: " << mitk::RESTUtil::convertToUtf8(m_BaseURI); InitializeRESTManager(); } utility::string_t DICOMweb::CreateQIDOUri(mitk::RESTUtil::ParamMap map) { MitkUriBuilder queryBuilder(m_BaseURI + U("rs/instances")); for (auto const &element : map) { queryBuilder.append_query(element.first, element.second); } return queryBuilder.to_string(); } utility::string_t DICOMweb::CreateWADOUri(utility::string_t studyUID, - utility::string_t seriesUID, - utility::string_t instanceUID) + utility::string_t seriesUID, + utility::string_t instanceUID) { MitkUriBuilder builder(m_BaseURI + U("wado")); builder.append_query(U("requestType"), U("WADO")); builder.append_query(U("studyUID"), studyUID); builder.append_query(U("seriesUID"), seriesUID); builder.append_query(U("objectUID"), instanceUID); builder.append_query(U("contentType"), U("application/dicom")); return builder.to_string(); } utility::string_t DICOMweb::CreateSTOWUri(utility::string_t studyUID) { MitkUriBuilder builder(m_BaseURI + U("rs/studies")); builder.append_path(studyUID); return builder.to_string(); } pplx::task DICOMweb::SendSTOW(utility::string_t filePath, utility::string_t studyUID) { auto uri = CreateSTOWUri(studyUID); - // this is the working stow-rs request which supports just one dicom file packed into a multipart message - std::basic_ifstream input(filePath, std::ios::binary); + // this is the working stow-rs request which supports just one dicom file packed into a multipart message + std::ifstream input(filePath, std::ios::binary); + if (!input) + { + MITK_WARN << "could not read file to POST"; + return pplx::task(); + } std::vector result; - std::vector buffer{(std::istreambuf_iterator(input)), - (std::istreambuf_iterator())}; + std::vector buffer; + + // Stop eating new lines in binary mode!!! + input.unsetf(std::ios::skipws); + + input.seekg(0, std::ios::end); + const std::streampos fileSize = input.tellg(); + input.seekg(0, std::ios::beg); + + MITK_INFO << fileSize << " bytes will be sent."; + buffer.reserve(fileSize); // file size + std::copy( + std::istream_iterator(input), std::istream_iterator(), std::back_inserter(buffer)); + // in future more than one file should also be supported.. std::string head = ""; head += "\r\n--boundary"; head += "\r\nContent-Type: " + mitk::RESTUtil::convertToUtf8(U("application/dicom")) + "\r\n\r\n"; std::vector bodyVector(head.begin(), head.end()); std::string tail = ""; tail += "\r\n--boundary--"; result.insert(result.end(), bodyVector.begin(), bodyVector.end()); result.insert(result.end(), buffer.begin(), buffer.end()); result.insert(result.end(), tail.begin(), tail.end()); mitk::RESTUtil::ParamMap headers; - headers.insert(mitk::RESTUtil::ParamMap::value_type(U("Content-Type"), U("multipart/related; type=\"application/dicom\"; boundary=boundary"))); + headers.insert(mitk::RESTUtil::ParamMap::value_type( + U("Content-Type"), U("multipart/related; type=\"application/dicom\"; boundary=boundary"))); - return m_RESTManager->SendRequest(uri, mitk::IRESTManager::RequestType::Post, &result, headers) - .then([=](web::json::value result) { - if (result.is_null()) - { - } - }); + try + { + return m_RESTManager->SendRequest(uri, mitk::IRESTManager::RequestType::Post, &result, headers) + .then([=](web::json::value result) { + if (result.is_null()) + { + } + }); + } + catch (std::exception &e) + { + MITK_WARN << e.what(); + } + + return pplx::task(); } pplx::task DICOMweb::SendWADO(utility::string_t filePath, - utility::string_t studyUID, - utility::string_t seriesUID, - utility::string_t instanceUID) + utility::string_t studyUID, + utility::string_t seriesUID, + utility::string_t instanceUID) { auto uri = CreateWADOUri(studyUID, seriesUID, instanceUID); // don't want return something auto content = web::json::value(); return m_RESTManager->SendRequest(uri, mitk::IRESTManager::RequestType::Get, &content, {}, filePath) - .then([=](web::json::value result) - { - if (result.is_null()) + .then([=](web::json::value result) { + if (result.is_null()) { } - }); + }); } pplx::task DICOMweb::SendWADO(utility::string_t folderPath, - utility::string_t studyUID, - utility::string_t seriesUID) + utility::string_t studyUID, + utility::string_t seriesUID) { mitk::RESTUtil::ParamMap seriesInstances; seriesInstances.insert(mitk::RESTUtil::ParamMap::value_type(U("StudyInstanceUID"), studyUID)); seriesInstances.insert(mitk::RESTUtil::ParamMap::value_type(U("SeriesInstanceUID"), seriesUID)); return SendQIDO(seriesInstances).then([=](web::json::value jsonResult) -> pplx::task { auto jsonListResult = jsonResult; auto resultArray = jsonListResult.as_array(); auto firstFileName = std::string(); std::vector> tasks; for (unsigned short i = 0; i < resultArray.size(); i++) { try { auto firstResult = resultArray[i]; auto sopInstanceUIDKey = firstResult.at(U("00080018")); auto sopInstanceObject = sopInstanceUIDKey.as_object(); auto valueKey = sopInstanceObject.at(U("Value")); auto valueArray = valueKey.as_array(); auto sopInstanceUID = valueArray[0].as_string(); auto fileName = utility::string_t(sopInstanceUID).append(U(".dcm")); // save first file name as result to load series if (i == 0) { firstFileName = utility::conversions::to_utf8string(fileName); } auto filePath = utility::string_t(folderPath).append(fileName); auto task = SendWADO(filePath, studyUID, seriesUID, sopInstanceUID); tasks.push_back(task); } catch (const web::json::json_exception &e) { MITK_ERROR << e.what(); } } auto joinTask = pplx::when_all(begin(tasks), end(tasks)); auto returnTask = joinTask.then([=](void) -> std::string { auto folderPathUtf8 = utility::conversions::to_utf8string(folderPath); auto result = folderPathUtf8 + firstFileName; return result; }); return returnTask; }); } pplx::task DICOMweb::SendQIDO(mitk::RESTUtil::ParamMap map) { auto uri = CreateQIDOUri(map); mitk::RESTUtil::ParamMap headers; headers.insert(mitk::RESTUtil::ParamMap::value_type(U("Accept"), U("application/json"))); auto content = web::json::value(); return m_RESTManager->SendRequest(uri, mitk::IRESTManager::RequestType::Get, &content, headers); } void DICOMweb::InitializeRESTManager() { auto *context = us::GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { m_RESTManager = managerService; } } } diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/ReworkRequestHandler.cpp b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/ReworkRequestHandler.cpp index 968f4d5898..8c76fb08ed 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/ReworkRequestHandler.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/ReworkRequestHandler.cpp @@ -1,300 +1,300 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "ReworkRequestHandler.h" #include #include #include US_INITIALIZE_MODULE ReworkRequestHandler::ReworkRequestHandler() {} ReworkRequestHandler::ReworkRequestHandler(std::string downloadDir, utility::string_t pacsURI) : m_DownloadDir{downloadDir} { m_DicomWeb = DICOMweb(pacsURI + U("/dcm4chee-arc/aets/DCM4CHEE/")); } ReworkRequestHandler::DicomDTO ReworkRequestHandler::ExtractDTO(const web::json::value &data) { DicomDTO dto; auto messageTypeKey = data.at(U("messageType")); if (messageTypeKey.as_string() == U("downloadData")) { MITK_INFO << "within extract dto"; auto imageStudyUIDKey = data.at(U("studyUID")); auto srSeriesUIDKey = data.at(U("srSeriesUID")); auto groundTruthKey = data.at(U("groundTruth")); auto simScoreKey = data.at(U("simScoreArray")); auto minSliceStartKey = data.at(U("minSliceStart")); dto.srSeriesUID = srSeriesUIDKey.as_string(); dto.groundTruth = groundTruthKey.as_string(); dto.studyUID = imageStudyUIDKey.as_string(); dto.minSliceStart = minSliceStartKey.as_integer(); std::vector vec; web::json::array simArray = simScoreKey.as_array(); for (web::json::value score : simArray) { vec.push_back(score.as_double() * 100); } dto.simScoreArray = vec; } return dto; } web::http::http_response ReworkRequestHandler::Notify(const web::uri &uri, const web::json::value &data, const web::http::method &method) { MITK_INFO << "Incoming notify"; if (method == web::http::methods::GET) { return HandleGet(uri, data); } else if (method == web::http::methods::PUT) { return HandlePut(uri, data); } else if (method == web::http::methods::OPTIONS) { return HandleOptions(uri, data); } web::http::http_response response(web::http::status_codes::BadGateway); - response.set_body(_XPLATSTR("No one can handle http method from request")); // TODO: include method name + response.set_body(U("No one can handle http method from request")); // TODO: include method name return response; } web::http::http_response ReworkRequestHandler::HandlePut(const web::uri &uri, const web::json::value &data) { - if (uri.to_string() != U("/inject")) + if (uri.to_string() != U("/inject")) // avoid unused warning MITK_INFO << "no inject path"; emit InvokeProgress(20, {"display graph and query structured report"}); if (data == web::json::value()) { MITK_INFO << "no data in body"; web::http::http_response response(web::http::status_codes::BadRequest); response.set_body(U("No data in body of request")); // TODO: include method name return response; } MITK_INFO << mitk::RESTUtil::convertToUtf8(data.serialize()); DicomDTO dto = ExtractDTO(data); MITK_INFO << mitk::RESTUtil::convertToUtf8(dto.imageSeriesUID); emit InvokeSimilarityGraph(dto.simScoreArray, dto.minSliceStart); emit InvokeUpdateDcmMeta(dto); mitk::RESTUtil::ParamMap seriesInstancesParams; seriesInstancesParams.insert(mitk::RESTUtil::ParamMap::value_type(U("StudyInstanceUID"), dto.studyUID)); seriesInstancesParams.insert(mitk::RESTUtil::ParamMap::value_type(U("SeriesInstanceUID"), dto.srSeriesUID)); seriesInstancesParams.insert( mitk::RESTUtil::ParamMap::value_type(U("includefield"), U("0040A375"))); // Current Requested Procedure Evidence Sequence try { MITK_INFO << "send qido request"; m_DicomWeb.SendQIDO(seriesInstancesParams).then([=](web::json::value jsonResult) { auto firstResult = jsonResult[0]; auto actualListKey = firstResult.at(U("0040A375")) .as_object() .at(U("Value")) .as_array()[0] .as_object() .at(U("00081115")) .as_object() .at(U("Value")) .as_array(); MITK_INFO << "received qido response"; utility::string_t segSeriesUIDA = {}; utility::string_t segSeriesUIDB = {}; utility::string_t imageSeriesUID = {}; for (unsigned int index = 0; index < actualListKey.size(); index++) { auto element = actualListKey[index].as_object(); // get SOP class UID auto innerElement = element.at(U("00081199")).as_object().at(U("Value")).as_array()[0]; auto sopClassUID = innerElement.at(U("00081150")).as_object().at(U("Value")).as_array()[0].as_string(); auto seriesUID = element.at(U("0020000E")).as_object().at(U("Value")).as_array()[0].as_string(); if (sopClassUID == U("1.2.840.10008.5.1.4.1.1.66.4")) // SEG { if (segSeriesUIDA.length() == 0) { segSeriesUIDA = seriesUID; } else { segSeriesUIDB = seriesUID; } } else if (sopClassUID == U("1.2.840.10008.5.1.4.1.1.2")) // CT { imageSeriesUID = seriesUID; } } emit InvokeProgress(10, {"load composite context of structured report"}); MITK_INFO << "image series UID " << mitk::RESTUtil::convertToUtf8(imageSeriesUID); MITK_INFO << "seg A series UID " << mitk::RESTUtil::convertToUtf8(segSeriesUIDA); MITK_INFO << "seg B series UID " << mitk::RESTUtil::convertToUtf8(segSeriesUIDB); MITK_INFO << "Load related dicom series ..."; std::string folderPathSeries = mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_DownloadDir) + "/"; std::string pathSegA = mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_DownloadDir) + "/"; std::string pathSegB = mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_DownloadDir) + "/"; auto folderPathSegA = utility::conversions::to_string_t(pathSegA); auto folderPathSegB = utility::conversions::to_string_t(pathSegB); std::vector> tasks; auto imageSeriesTask = m_DicomWeb.SendWADO(utility::conversions::to_string_t(folderPathSeries), dto.studyUID, imageSeriesUID); auto segATask = m_DicomWeb.SendWADO(folderPathSegA, dto.studyUID, segSeriesUIDA); auto segBTask = m_DicomWeb.SendWADO(folderPathSegB, dto.studyUID, segSeriesUIDB); tasks.push_back(imageSeriesTask); tasks.push_back(segATask); tasks.push_back(segBTask); auto joinTask = pplx::when_all(begin(tasks), end(tasks)); auto filePathList = joinTask.then([&](std::vector filePathList) { emit InvokeProgress(50, {"load dicom files from disk"}); emit InvokeLoadDataSegRework(filePathList); }); }); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } web::http::http_response response(web::http::status_codes::InternalError); - response.set_body(_XPLATSTR("Something went wrong while processing the request.")); // TODO: include method name + response.set_body(U("Something went wrong while processing the request.")); // TODO: include method name return response; } web::http::http_response ReworkRequestHandler::HandleGet(const web::uri &uri, const web::json::value &data) { - if (!data.is_null()) + if (!data.is_null()) // avoid unused warning MITK_INFO << "data was not null"; auto query = web::uri(uri).query(); auto httpParams = web::uri::split_query(query); // IHE Invoke Image Display style auto requestType = httpParams.find(U("requestType")); auto errorResponse = web::http::http_response(web::http::status_codes::BadRequest); if (requestType != httpParams.end()) { if (requestType->second == U("LOAD_SERIES")) { // data extraction DicomDTO dto; dto.studyUID = httpParams.at(U("studyUID")); auto seriesUIDList = httpParams.at(U("seriesUIDList")); auto seriesUIDListUtf8 = mitk::RESTUtil::convertToUtf8(seriesUIDList); std::istringstream f(seriesUIDListUtf8); std::string s; while (getline(f, s, ',')) { dto.seriesUIDList.push_back(mitk::RESTUtil::convertToTString(s)); } emit InvokeProgress(20, {"incoming series request ..."}); // tasks std::vector> tasks; if (dto.seriesUIDList.size() > 0) { for (auto segSeriesUID : dto.seriesUIDList) { utility::string_t folderPathSeries = utility::conversions::to_string_t(mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_DownloadDir) + "/"); try { auto seriesTask = m_DicomWeb.SendWADO(folderPathSeries, dto.studyUID, segSeriesUID); tasks.push_back(seriesTask); } catch (const mitk::Exception &exception) { MITK_INFO << exception.what(); return errorResponse; } } } emit InvokeProgress(40, {"download series ..."}); try { auto joinTask = pplx::when_all(begin(tasks), end(tasks)); auto filePathList = joinTask.then([&](std::vector filePathList) { emit InvokeLoadData(filePathList); emit InvokeProgress(40, {""}); }); } catch (const mitk::Exception &exception) { MITK_INFO << exception.what(); return errorResponse; } } } else { MITK_INFO << "no requestType parameter was provided"; } return errorResponse; } web::http::http_response ReworkRequestHandler::HandleOptions(const web::uri &uri, const web::json::value &data) { - if (uri.to_string() != U("/inject")) + if (uri.to_string() != U("/inject")) // avoid unused warning MITK_INFO << "no inject path"; - if (!data.is_null()) + if (!data.is_null()) // avoid unused warning MITK_INFO << "data was not null"; MITK_INFO << "OPTIONS incoming"; web::http::http_response response(web::http::status_codes::OK); - response.headers().add(_XPLATSTR("Access-Control-Allow-Methods"), "PUT"); - response.headers().add(_XPLATSTR("Access-Control-Allow-Headers"), "Content-Type"); - response.headers().add(_XPLATSTR("Access-Control-Allow-Origin"), "http://localhost:8002"); + response.headers().add(U("Access-Control-Allow-Methods"), "PUT"); + response.headers().add(U("Access-Control-Allow-Headers"), "Content-Type"); + response.headers().add(U("Access-Control-Allow-Origin"), "http://localhost:8002"); return response; } \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp index 828449d823..98365cfa48 100644 --- a/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation.rework/src/internal/SegmentationReworkView.cpp @@ -1,541 +1,537 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "SegmentationReworkView.h" #include #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DICOMweb.h" #include #include const std::string SegmentationReworkView::VIEW_ID = "org.mitk.views.segmentationreworkview"; void SegmentationReworkView::SetFocus() {} void SegmentationReworkView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; qRegisterMetaType>("std::vector"); qRegisterMetaType>("std::vector"); qRegisterMetaType("DicomDTO"); - // m_Controls.verticalWidget->setVisible(false); - m_Controls.cleanDicomBtn->setVisible(false); + m_Controls.cleanDicomBtn->setVisible(true); m_Controls.individualWidget_2->setVisible(false); m_Controls.sliderWidget->setMinimum(1); m_Controls.sliderWidget->setMaximum(100); m_Controls.sliderWidget->setTickInterval(1); m_Controls.sliderWidget->setSingleStep(1); m_Controls.radioA->setChecked(true); connect(m_Controls.buttonUpload, &QPushButton::clicked, this, &SegmentationReworkView::UploadNewSegmentation); connect(m_Controls.buttonNewSeg, &QPushButton::clicked, this, &SegmentationReworkView::CreateNewSegmentationC); connect(m_Controls.cleanDicomBtn, &QPushButton::clicked, this, &SegmentationReworkView::CleanDicomFolder); connect(m_Controls.restartConnection, &QPushButton::clicked, this, &SegmentationReworkView::OnRestartConnection); connect(m_Controls.testConnection, &QPushButton::clicked, this, &SegmentationReworkView::TestConnection); connect(m_Controls.checkIndiv, &QCheckBox::stateChanged, this, &SegmentationReworkView::OnIndividualCheckChange); connect( m_Controls.sliderWidget, &ctkSliderWidget::valueChanged, this, &SegmentationReworkView::OnSliderWidgetChanged); m_DownloadBaseDir = mitk::IOUtil::GetTempPath() + "segrework"; MITK_INFO << "using download base dir: " << m_DownloadBaseDir; m_UploadBaseDir = mitk::IOUtil::GetTempPath() + "uploadSeg"; if (!itksys::SystemTools::FileIsDirectory(m_DownloadBaseDir)) { itk::FileTools::CreateDirectory(m_DownloadBaseDir); } if (!itksys::SystemTools::FileIsDirectory(m_UploadBaseDir)) { itk::FileTools::CreateDirectory(m_UploadBaseDir); } utility::string_t path = U("/inject"); utility::string_t port = U("4040"); - utility::string_t address = U("http://localhost:"); // TODO: replace with host + utility::string_t address = U("http://localhost:"); address.append(port); address.append(path); m_Host = "http://localhost:8000/rest-srs"; utility::string_t pacsURL = U("http://193.174.48.78:8090"); m_RequestHandler = new ReworkRequestHandler(m_DownloadBaseDir, pacsURL); connect(this, &SegmentationReworkView::InvokeProgress, this, &SegmentationReworkView::AddProgress); connect(m_RequestHandler, &ReworkRequestHandler::InvokeProgress, this, &SegmentationReworkView::AddProgress); connect( m_RequestHandler, &ReworkRequestHandler::InvokeSimilarityGraph, this, &SegmentationReworkView::SetSimilarityGraph); connect( m_RequestHandler, &ReworkRequestHandler::InvokeUpdateDcmMeta, this, &SegmentationReworkView::InitializeDcmMeta); connect(m_RequestHandler, &ReworkRequestHandler::InvokeLoadData, this, &SegmentationReworkView::LoadData); - connect(m_RequestHandler, &ReworkRequestHandler::InvokeLoadDataSegRework, this, &SegmentationReworkView::LoadDataSegRework); + connect( + m_RequestHandler, &ReworkRequestHandler::InvokeLoadDataSegRework, this, &SegmentationReworkView::LoadDataSegRework); // Get the micro service us::ModuleContext *context = us::ModuleRegistry::GetModule(1)->GetModuleContext(); auto managerRef = context->GetServiceReference(); if (managerRef) { auto managerService = context->GetService(managerRef); if (managerService) { m_ManagerService = managerService; } } // Setup PACS connection RestartConnection(pacsURL); // Setup listening server m_ManagerService->ReceiveRequest(address, m_RequestHandler); MITK_INFO << "Listening for requests at: " << utility::conversions::to_utf8string(address); } void SegmentationReworkView::OnSliderWidgetChanged(double value) { std::map::iterator it; unsigned int count = 0; for (it = m_ScoreMap.begin(); it != m_ScoreMap.end(); it++) { if (it->second < value) { count++; } } QString labelsToDelete = "slices to delete: " + QString::number(count); m_Controls.slicesToDeleteLabel->setText(labelsToDelete); std::map thresholdMap; for (it = m_ScoreMap.begin(); it != m_ScoreMap.end(); it++) { thresholdMap.insert(std::map::value_type(it->first, value)); } m_Controls.chartWidget->RemoveData(m_thresholdLabel); m_Controls.chartWidget->AddData2D(thresholdMap, m_thresholdLabel); m_Controls.chartWidget->SetChartType(m_thresholdLabel, QmitkChartWidget::ChartType::line); m_Controls.chartWidget->Show(); } void SegmentationReworkView::AddProgress(int progress, QString status) { auto futureValue = m_Controls.progressBar->value() + progress; if (futureValue >= 100) { m_Controls.progressBar->setValue(0); m_Controls.progressBar->setFormat(""); } else { m_Controls.progressBar->setFormat(status.append(" %p%")); m_Controls.progressBar->setValue(futureValue); } } void SegmentationReworkView::InitializeDcmMeta(ReworkRequestHandler::DicomDTO dto) { m_CurrentStudyUID = mitk::RESTUtil::convertToUtf8(dto.studyUID); m_SRUID = mitk::RESTUtil::convertToUtf8(dto.srSeriesUID); m_GroundTruth = mitk::RESTUtil::convertToUtf8(dto.groundTruth); } pplx::task SegmentationReworkView::TestConnection() { mitk::RESTUtil::ParamMap seriesInstancesParams; seriesInstancesParams.insert(mitk::RESTUtil::ParamMap::value_type(U("limit"), U("1"))); m_Controls.connectionStatus->setText(QString("Testing connection ...")); return m_DicomWeb.SendQIDO(seriesInstancesParams).then([=](pplx::task resultTask) { try { auto result = resultTask.get(); if (!result.is_null()) { m_Controls.connectionStatus->setText(QString("Connection works!")); return true; } else { m_Controls.connectionStatus->setText(QString("Trouble with connection. Not valid!")); return false; } } catch (mitk::Exception &e) { MITK_WARN << e.what(); m_Controls.connectionStatus->setText(QString("No connection possible.")); return false; } }); } void SegmentationReworkView::OnRestartConnection() { RestartConnection(mitk::RESTUtil::convertToTString(m_Controls.dcm4cheeHostValue->text().toStdString())); } void SegmentationReworkView::RestartConnection(utility::string_t newHost) { utility::string_t host; if (newHost.empty()) { MITK_INFO << "Host was empty"; m_Controls.connectionStatus->setText(QString("Host must not be empty!")); return; } - utility::string_t url = newHost + _XPLATSTR("/dcm4chee-arc/aets/DCM4CHEE/"); + utility::string_t url = newHost + U("/dcm4chee-arc/aets/DCM4CHEE/"); MITK_INFO << "Restarting connection to " << mitk::RESTUtil::convertToUtf8(url) << " ..."; m_Controls.connectionStatus->setText(QString("Restarting connection...")); m_Controls.dcm4cheeURL->setText({(utility::conversions::to_utf8string(url).c_str())}); m_DicomWeb = DICOMweb(url); if (!TestConnection().get()) { MITK_INFO << "Restart did not work.."; m_Controls.connectionStatus->setText(QString("No PACS server available under given host!")); } else { MITK_INFO << "requests to pacs are sent to: " << mitk::RESTUtil::convertToUtf8(url); } } void SegmentationReworkView::OnIndividualCheckChange(int state) { if (state == Qt::Unchecked) { m_Controls.individualWidget_2->setVisible(false); } else if (state == Qt::Checked) { m_Controls.individualWidget_2->setVisible(true); } } std::string SegmentationReworkView::GetAlgorithmOfSegByPath(std::string path) { auto scanner = mitk::DICOMDCMTKTagScanner::New(); mitk::DICOMTagPath algorithmName; algorithmName.AddAnySelection(0x0062, 0x0002).AddElement(0x0062, 0x0009); mitk::StringList files; files.push_back(path); scanner->SetInputFiles(files); scanner->AddTagPath(algorithmName); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); auto findings = frames.front()->GetTagValueAsString(algorithmName); if (findings.size() != 0) MITK_INFO << findings.front().value; return findings.front().value; } mitk::DataStorage::SetOfObjects::Pointer SegmentationReworkView::LoadData(std::vector filePathList) { MITK_INFO << "Pushing data to data storage ..."; auto ds = GetDataStorage(); auto dataNodes = mitk::IOUtil::Load(filePathList, *ds); // reinit view mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(ds); return dataNodes; } void SegmentationReworkView::LoadDataSegRework(std::vector filePathList) { auto dataNodes = LoadData(filePathList); // find data nodes m_Image = dataNodes->at(0); m_Image->SetName("image data"); m_SegA = dataNodes->at(1); m_SegB = dataNodes->at(2); auto algorithmNameA = GetAlgorithmOfSegByPath(filePathList[1]); auto algorithmNameB = GetAlgorithmOfSegByPath(filePathList[2]); m_SegA->SetName(algorithmNameA); m_SegB->SetName(algorithmNameB); m_Controls.labelSegAValue->setText(algorithmNameA.c_str()); m_Controls.labelSegBValue->setText(algorithmNameB.c_str()); m_Controls.labelGroundTruthValue->setText(m_GroundTruth.c_str()); emit InvokeProgress(20, {""}); } void SegmentationReworkView::SetSimilarityGraph(std::vector simScoreArray, int sliceMinStart) { std::string label = "similarity graph"; m_thresholdLabel = "threshold"; - // m_Controls.chartWidget->Clear(); double sliceIndex = sliceMinStart; for (double score : simScoreArray) { m_ScoreMap.insert(std::map::value_type(sliceIndex, score)); sliceIndex++; } std::map thresholdMap; m_Controls.chartWidget->AddData2D(m_ScoreMap, label); m_Controls.chartWidget->AddData2D(thresholdMap, m_thresholdLabel); m_Controls.chartWidget->SetChartType(label, QmitkChartWidget::ChartType::line); m_Controls.chartWidget->SetChartType(m_thresholdLabel, QmitkChartWidget::ChartType::line); m_Controls.chartWidget->SetXAxisLabel("slice number"); m_Controls.chartWidget->SetYAxisLabel("similarity in percent"); m_Controls.chartWidget->SetTitle("Similartiy Score for Segmentation Comparison"); m_Controls.chartWidget->Show(); } void SegmentationReworkView::UploadNewSegmentation() { AddProgress(10, {"save SEG to temp folder"}); std::string folderPathSeg = mitk::IOUtil::CreateTemporaryDirectory("XXXXXX", m_UploadBaseDir) + "/"; const std::string savePath = folderPathSeg + m_SegC->GetName() + ".dcm"; const std::string mimeType = mitk::MitkDICOMQIIOMimeTypes::DICOMSEG_MIMETYPE_NAME(); mitk::IOUtil::Save(m_SegC->GetData(), mimeType, savePath); // get Series Instance UID from new SEG auto scanner = mitk::DICOMDCMTKTagScanner::New(); mitk::DICOMTagPath seriesUID(0x0020, 0x000E); mitk::StringList files; files.push_back(savePath); scanner->SetInputFiles(files); scanner->AddTagPath(seriesUID); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); auto findings = frames.front()->GetTagValueAsString(seriesUID); auto segSeriesUID = findings.front().value; AddProgress(20, {"push SEG to PACS"}); auto filePath = utility::conversions::to_string_t(savePath); try { m_DicomWeb.SendSTOW(filePath, mitk::RESTUtil::convertToTString(m_CurrentStudyUID)).then([=] { emit InvokeProgress(50, {"persist reworked SEG to evaluation database"}); - m_SRUID = "1.2.276.0.7230010.3.1.3.296485632.12.1540282379.476412"; - std::string segSeriesUID = ""; DICOMweb::MitkUriBuilder queryBuilder(mitk::RESTUtil::convertToTString(m_Host) + U("/tasks/evaluations/")); queryBuilder.append_query(U("srUID"), utility::conversions::to_string_t(m_SRUID)); auto content = web::json::value(); m_ManagerService->SendRequest(queryBuilder.to_uri(), mitk::IRESTManager::RequestType::Get, &content) .then([=](web::json::value result) { MITK_INFO << "after GET"; MITK_INFO << utility::conversions::to_utf8string(result.serialize()); auto updatedContent = result.as_array()[0]; updatedContent[U("reworkedSegmentationUID")] = web::json::value::string(utility::conversions::to_string_t(segSeriesUID)); auto id = updatedContent.at(U("id")).as_integer(); MITK_INFO << id; auto idParam = std::to_string(id).append("/"); DICOMweb::MitkUriBuilder queryBuilder(mitk::RESTUtil::convertToTString(m_Host) + U("/tasks/evaluations")); queryBuilder.append_path(utility::conversions::to_string_t(idParam)); m_ManagerService->SendRequest(queryBuilder.to_uri(), mitk::IRESTManager::RequestType::Put, &updatedContent) .then([=](web::json::value result) { MITK_INFO << utility::conversions::to_utf8string(result.serialize()); if (result[U("reworkedSegmentationUID")].as_string() == utility::conversions::to_string_t(segSeriesUID)) { MITK_INFO << "successfully stored"; emit InvokeProgress(30, {"successfully stored"}); } }); }); }); } catch (const std::exception &exception) { std::cout << exception.what() << std::endl; } } std::vector SegmentationReworkView::CreateSegmentation(mitk::Image::Pointer baseSegmentation, double threshold) { MITK_INFO << "handle individual segmentation creation"; std::map::iterator it; std::vector sliceIndices; unsigned int count = 0; for (it = m_ScoreMap.begin(); it != m_ScoreMap.end(); it++) { if (it->second < threshold) { auto index = it->first; try { mitk::ImagePixelWriteAccessor imageAccessor(baseSegmentation); for (unsigned int x = 0; x < baseSegmentation->GetDimension(0); x++) { for (unsigned int y = 0; y < baseSegmentation->GetDimension(1); y++) { imageAccessor.SetPixelByIndex({{x, y, int(index)}}, 0); } } } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } count++; sliceIndices.push_back(index); MITK_INFO << "slice " << it->first << " removed "; } } MITK_INFO << "slices deleted " << count; return sliceIndices; } void SegmentationReworkView::CreateNewSegmentationC() { mitk::ToolManager *toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); toolManager->InitializeTools(); toolManager->SetReferenceData(m_Image); mitk::Image::Pointer baseImage; if (m_Controls.radioA->isChecked()) { baseImage = dynamic_cast(m_SegA->GetData())->Clone(); } else if (m_Controls.radioB->isChecked()) { baseImage = dynamic_cast(m_SegB->GetData())->Clone(); } if (m_Controls.checkIndiv->isChecked()) { auto sliceIndices = CreateSegmentation(baseImage, m_Controls.sliderWidget->value()); } - QmitkNewSegmentationDialog *dialog = - new QmitkNewSegmentationDialog(m_Parent); // needs a QWidget as parent, "this" is not QWidget + QmitkNewSegmentationDialog *dialog = new QmitkNewSegmentationDialog(m_Parent); int dialogReturnValue = dialog->exec(); if (dialogReturnValue == QDialog::Rejected) { // user clicked cancel or pressed Esc or something similar return; } // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::Tool *firstTool = toolManager->GetToolById(0); if (firstTool) { try { std::string newNodeName = dialog->GetSegmentationName().toStdString(); if (newNodeName.empty()) { newNodeName = "no_name"; } mitk::DataNode::Pointer newSegmentation = firstTool->CreateSegmentationNode(baseImage, newNodeName, dialog->GetColor()); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation newSegmentation->SetProperty("showVolume", mitk::BoolProperty::New(false)); if (!newSegmentation) { return; // could be aborted by user } if (mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)) { mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)->SetSelected(false); } newSegmentation->SetSelected(true); this->GetDataStorage()->Add( newSegmentation, toolManager->GetReferenceData(0)); // add as a child, because the segmentation "derives" from the original m_SegC = newSegmentation; auto referencedImages = m_Image->GetData()->GetProperty("files"); m_SegC->GetData()->SetProperty("referenceFiles", referencedImages); } catch (std::bad_alloc) { QMessageBox::warning( nullptr, tr("Create new segmentation"), tr("Could not allocate memory for new segmentation")); } } else { MITK_INFO << "no tools..."; } } void SegmentationReworkView::CleanDicomFolder() { if (m_SegA || m_SegB || m_SegC) { QMessageBox::warning(nullptr, tr("Clean dicom folder"), tr("Please remove the data in data storage before cleaning the download folder")); return; } auto downloadDir = Poco::File(m_DownloadBaseDir); downloadDir.remove(true); }