changeset 15:0ed8bbf35577

start RetrieveBulkdata
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 07 May 2015 17:56:15 +0200
parents 1b383403c080
children a90389070513
files Plugin/Plugin.cpp Plugin/WadoRs.cpp Plugin/WadoRs.h Status.txt
diffstat 4 files changed, 190 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/Plugin/Plugin.cpp	Thu May 07 17:07:25 2015 +0200
+++ b/Plugin/Plugin.cpp	Thu May 07 17:56:15 2015 +0200
@@ -77,6 +77,7 @@
     OrthancPluginRegisterRestCallback(context, "/wado-rs/studies/([^/]*)/metadata", RetrieveStudyMetadata);
     OrthancPluginRegisterRestCallback(context, "/wado-rs/studies/([^/]*)/series/([^/]*)/metadata", RetrieveSeriesMetadata);
     OrthancPluginRegisterRestCallback(context, "/wado-rs/studies/([^/]*)/series/([^/]*)/instances/([^/]*)/metadata", RetrieveInstanceMetadata);
+    OrthancPluginRegisterRestCallback(context, "/wado-rs/studies/([^/]*)/series/([^/]*)/instances/([^/]*)/bulk/(.*)", RetrieveBulkData);
 
     // STOW-RS callbacks
     OrthancPluginRegisterRestCallback(context, "/stow-rs/studies", StowCallback);
--- a/Plugin/WadoRs.cpp	Thu May 07 17:07:25 2015 +0200
+++ b/Plugin/WadoRs.cpp	Thu May 07 17:56:15 2015 +0200
@@ -20,6 +20,8 @@
 
 #include "Plugin.h"
 
+#include <boost/lexical_cast.hpp>
+
 #include "../Core/Configuration.h"
 #include "../Core/Dicom.h"
 #include "../Core/DicomResults.h"
@@ -126,6 +128,50 @@
 
 
 
+static bool AcceptBulkData(const OrthancPluginHttpRequest* request)
+{
+  std::string accept;
+
+  if (!OrthancPlugins::LookupHttpHeader(accept, request, "accept"))
+  {
+    return true;   // By default, return "multipart/related; type=application/octet-stream;"
+  }
+
+  std::string application;
+  std::map<std::string, std::string> attributes;
+  OrthancPlugins::ParseContentType(application, attributes, accept);
+
+  if (application != "multipart/related" &&
+      application != "*/*")
+  {
+    std::string s = "This WADO-RS plugin cannot generate the following bulk data type: " + accept;
+    OrthancPluginLogError(context_, s.c_str());
+    return false;
+  }
+
+  if (attributes.find("type") != attributes.end())
+  {
+    std::string s = attributes["type"];
+    OrthancPlugins::ToLowerCase(s);
+    if (s != "application/octet-stream")
+    {
+      std::string s = "This WADO-RS plugin only supports application/octet-stream return type for bulk data retrieval (" + accept + ")";
+      OrthancPluginLogError(context_, s.c_str());
+      return false;
+    }
+  }
+
+  if (attributes.find("ra,ge") != attributes.end())
+  {
+    std::string s = "This WADO-RS plugin does not support Range retrieval, it can only return entire bulk data object";
+    OrthancPluginLogError(context_, s.c_str());
+    return false;
+  }
+
+  return true;
+}
+
+
 static int32_t AnswerListOfDicomInstances(OrthancPluginRestOutput* output,
                                           const std::string& resource)
 {
@@ -336,7 +382,7 @@
     if (!AcceptMultipartDicom(request))
     {
       OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
-      return false;
+      return 0;
     }
 
     std::string uri;
@@ -364,7 +410,7 @@
     if (!AcceptMultipartDicom(request))
     {
       OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
-      return false;
+      return 0;
     }
 
     std::string uri;
@@ -393,7 +439,7 @@
     if (!AcceptMultipartDicom(request))
     {
       OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
-      return false;
+      return 0;
     }
 
     std::string uri;
@@ -430,7 +476,7 @@
     if (!AcceptMetadata(request, isXml))
     {
       OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
-      return false;
+      return 0;
     }
 
     std::string uri;
@@ -459,7 +505,7 @@
     if (!AcceptMetadata(request, isXml))
     {
       OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
-      return false;
+      return 0;
     }
 
     std::string uri;
@@ -488,7 +534,7 @@
     if (!AcceptMetadata(request, isXml))
     {
       OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
-      return false;
+      return 0;
     }
 
     std::string uri;
@@ -505,3 +551,117 @@
     return -1;
   }
 }
+
+
+
+static uint32_t Hex2Dec(char c)
+{
+  return (c >= '0' && c <= '9') ? c - '0' : c - 'a' + 10;
+}
+
+
+static bool ParseBulkTag(gdcm::Tag& tag,
+                         const std::string& s)
+{
+  if (s.size() != 8)
+  {
+    return false;
+  }
+
+  for (size_t i = 0; i < 8; i++)
+  {
+    if ((s[i] < '0' || s[i] > '9') &&
+        (s[i] < 'a' || s[i] > 'f'))
+    {
+      return false;
+    }
+  }
+
+  uint32_t g = ((Hex2Dec(s[0]) << 12) +
+                (Hex2Dec(s[1]) << 8) +
+                (Hex2Dec(s[2]) << 4) +
+                Hex2Dec(s[3]));
+
+  uint32_t e = ((Hex2Dec(s[4]) << 12) +
+                (Hex2Dec(s[5]) << 8) +
+                (Hex2Dec(s[6]) << 4) +
+                Hex2Dec(s[7]));
+
+  tag = gdcm::Tag(g, e);
+  return true;
+}
+
+
+static bool ExploreBulkData(std::string& content,
+                            const std::vector<std::string>& path,
+                            size_t position,
+                            const gdcm::DataSet& dataset)
+{
+  gdcm::Tag tag;
+  if (!ParseBulkTag(tag, path[position]) ||
+      !dataset.FindDataElement(tag))
+  {
+    return false;
+  }
+
+  const gdcm::DataElement& element = dataset.GetDataElement(tag);
+
+  if (position + 1 == path.size())
+  {
+    const gdcm::ByteValue* data = element.GetByteValue();
+    if (!data)
+    {
+      printf("AIE\n");
+      return false;
+    }
+
+    content.assign(data->GetPointer(), data->GetLength());
+    return true;
+  }
+
+  return false;
+}
+
+int32_t RetrieveBulkData(OrthancPluginRestOutput* output,
+                         const char* url,
+                         const OrthancPluginHttpRequest* request)
+{
+  try
+  {
+    if (!AcceptBulkData(request))
+    {
+      OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+      return 0;
+    }
+
+    std::string uri, content;
+    if (LocateInstance(output, uri, request) &&
+        OrthancPlugins::RestApiGetString(content, context_, uri + "/file"))
+    {
+      OrthancPlugins::ParsedDicomFile dicom(content);
+
+      std::vector<std::string> path;
+      OrthancPlugins::TokenizeString(path, request->groups[3], '/');
+      
+      std::string result;
+      if (path.size() % 2 == 1 &&
+          ExploreBulkData(result, path, 0, dicom.GetDataSet()))
+      {
+        OrthancPlugins::MultipartWriter writer("application/octet-stream");
+        writer.AddPart(result);
+        writer.Answer(context_, output);
+      }
+      else
+      {
+        OrthancPluginSendHttpStatusCode(context_, output, 400 /* Bad request */);
+      }      
+    }
+
+    return 0;
+  }
+  catch (std::runtime_error& e)
+  {
+    OrthancPluginLogError(context_, e.what());
+    return -1;
+  }
+}
--- a/Plugin/WadoRs.h	Thu May 07 17:07:25 2015 +0200
+++ b/Plugin/WadoRs.h	Thu May 07 17:56:15 2015 +0200
@@ -46,3 +46,7 @@
 int32_t RetrieveInstanceMetadata(OrthancPluginRestOutput* output,
                                  const char* url,
                                  const OrthancPluginHttpRequest* request);
+
+int32_t RetrieveBulkData(OrthancPluginRestOutput* output,
+                         const char* url,
+                         const OrthancPluginHttpRequest* request);
--- a/Status.txt	Thu May 07 17:07:25 2015 +0200
+++ b/Status.txt	Thu May 07 17:56:15 2015 +0200
@@ -23,13 +23,30 @@
 
 
 
+================================
+6.5.4 WADO-RS / RetrieveFrames
+================================
+
+Not supported.
+
+
 
 ================================
-6.5.4 WADO-RS / RetrieveFrames
 6.5.5 WADO-RS / RetrieveBulkdata
 ================================
 
-Not supported.
+Supported
+---------
+
+* application/octet-stream response
+
+
+Not supported
+-------------
+
+* MediaType data response
+* Range query (only entire bulk data object can be returned)
+
 
 
 ================================