changeset 290:77d6374435dd refactoring

multipart implementation of STOW-RS
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sat, 08 Jun 2019 17:21:53 +0200
parents d7a831acaa16
children 05b4f440a961
files Plugin/Plugin.cpp Plugin/StowRs.cpp Plugin/StowRs.h
diffstat 3 files changed, 159 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/Plugin/Plugin.cpp	Sat Jun 08 11:58:49 2019 +0200
+++ b/Plugin/Plugin.cpp	Sat Jun 08 17:21:53 2019 +0200
@@ -175,6 +175,7 @@
 
 
 
+#include "DicomWebFormatter.h"
 
 class StowServer : public OrthancPlugins::MultipartRestCallback
 {
@@ -182,14 +183,29 @@
   class Handler : public IHandler
   {
   private:
-    unsigned int count_;
-    unsigned int part_;
+    OrthancPluginContext*  context_;
+    bool                   xml_;
+    std::string            wadoBase_;
+    std::string            expectedStudy_;
+    bool                   isFirst_;
+    Json::Value            result_;
+    Json::Value            success_;
+    Json::Value            failed_;
 
   public:
-    Handler(unsigned int count) :
-    count_(count), part_(0)
+    Handler(OrthancPluginContext* context,
+            bool xml,
+            const std::string& wadoBase,
+            const std::string& expectedStudy) :
+      context_(context),
+      xml_(xml),
+      wadoBase_(wadoBase),
+      expectedStudy_(expectedStudy),
+      isFirst_(true),
+      result_(Json::objectValue),
+      success_(Json::arrayValue),
+      failed_(Json::arrayValue)
     {
-      printf("  created handler: %d\n", count_);
     }
 
     virtual OrthancPluginErrorCode AddPart(const std::string& contentType,
@@ -197,34 +213,119 @@
                                            const void* data,
                                            size_t size)
     {
-      printf("  %d - %d - part received: [%s] %d\n", count_, part_++, contentType.c_str(), size);
+      if (contentType != "application/dicom")
+      {
+        throw Orthanc::OrthancException(
+          Orthanc::ErrorCode_UnsupportedMediaType,
+          "The STOW-RS request contains a part that is not "
+          "\"application/dicom\" (it is: \"" + contentType + "\")");
+      }
+
+      Json::Value dicom;
+
+      try
+      {
+        OrthancPlugins::OrthancString s;
+        s.Assign(OrthancPluginDicomBufferToJson(context_, data, size,
+                                                OrthancPluginDicomToJsonFormat_Short,
+                                                OrthancPluginDicomToJsonFlags_None, 256));
+        s.ToJson(dicom);
+      }
+      catch (Orthanc::OrthancException&)
+      {
+        // Bad DICOM file => TODO add to error
+        OrthancPlugins::LogWarning("STOW-RS cannot parse an incoming DICOM file");
+        return OrthancPluginErrorCode_Success;
+      }           
+
+      if (dicom.type() != Json::objectValue ||
+          !dicom.isMember(Orthanc::DICOM_TAG_SERIES_INSTANCE_UID.Format()) ||
+          !dicom.isMember(Orthanc::DICOM_TAG_SOP_CLASS_UID.Format()) ||
+          !dicom.isMember(Orthanc::DICOM_TAG_SOP_INSTANCE_UID.Format()) ||
+          !dicom.isMember(Orthanc::DICOM_TAG_STUDY_INSTANCE_UID.Format()) ||
+          dicom[Orthanc::DICOM_TAG_SERIES_INSTANCE_UID.Format()].type() != Json::stringValue ||
+          dicom[Orthanc::DICOM_TAG_SOP_CLASS_UID.Format()].type() != Json::stringValue ||
+          dicom[Orthanc::DICOM_TAG_SOP_INSTANCE_UID.Format()].type() != Json::stringValue ||
+          dicom[Orthanc::DICOM_TAG_STUDY_INSTANCE_UID.Format()].type() != Json::stringValue)
+      {
+        OrthancPlugins::LogWarning("STOW-RS: Missing a mandatory tag in incoming DICOM file");
+        return OrthancPluginErrorCode_Success;
+      }
+
+      const std::string seriesInstanceUid = dicom[Orthanc::DICOM_TAG_SERIES_INSTANCE_UID.Format()].asString();
+      const std::string sopClassUid = dicom[Orthanc::DICOM_TAG_SOP_CLASS_UID.Format()].asString();
+      const std::string sopInstanceUid = dicom[Orthanc::DICOM_TAG_SOP_INSTANCE_UID.Format()].asString();
+      const std::string studyInstanceUid = dicom[Orthanc::DICOM_TAG_STUDY_INSTANCE_UID.Format()].asString();
 
-      Json::Value v;
-      OrthancPlugins::RestApiPost(v, "/instances", data, size, false);
-      std::cout << v << std::endl;
+      Json::Value item = Json::objectValue;
+      item[OrthancPlugins::DICOM_TAG_REFERENCED_SOP_CLASS_UID.Format()] = sopClassUid;
+      item[OrthancPlugins::DICOM_TAG_REFERENCED_SOP_INSTANCE_UID.Format()] = sopInstanceUid;
+      
+      if (!expectedStudy_.empty() &&
+          studyInstanceUid != expectedStudy_)
+      {
+        OrthancPlugins::LogInfo("STOW-RS request restricted to study [" + expectedStudy_ + 
+                                "]: Ignoring instance from study [" + studyInstanceUid + "]");
+
+        /*item[OrthancPlugins::DICOM_TAG_WARNING_REASON.Format()] =
+          boost::lexical_cast<std::string>(0xB006);  // Elements discarded
+          success.append(item);*/
+      }
+      else
+      {
+        if (isFirst_)
+        {
+          std::string url = wadoBase_ + "studies/" + studyInstanceUid;
+          result_[OrthancPlugins::DICOM_TAG_RETRIEVE_URL.Format()] = url;
+          isFirst_ = false;
+        }
 
+        OrthancPlugins::MemoryBuffer tmp;
+        bool ok = tmp.RestApiPost("/instances", data, size, false);
+        tmp.Clear();
+
+        if (ok)
+        {
+          std::string url = (wadoBase_ + 
+                             "studies/" + studyInstanceUid +
+                             "/series/" + seriesInstanceUid +
+                             "/instances/" + sopInstanceUid);
+
+          item[OrthancPlugins::DICOM_TAG_RETRIEVE_URL.Format()] = url;
+          success_.append(item);      
+        }
+        else
+        {
+          OrthancPlugins::LogError("Orthanc was unable to store one instance in a STOW-RS request");
+          item[OrthancPlugins::DICOM_TAG_FAILURE_REASON.Format()] =
+            boost::lexical_cast<std::string>(0x0110);  // Processing failure
+          failed_.append(item);
+        }
+      }
+      
       return OrthancPluginErrorCode_Success;
     }
 
     virtual OrthancPluginErrorCode Execute(OrthancPluginRestOutput* output)
     {
-      printf("  %d - execute (total = %d)\n", count_, part_);
-      //throw Orthanc::OrthancException(Orthanc::ErrorCode_CanceledJob);
+      result_[OrthancPlugins::DICOM_TAG_FAILED_SOP_SEQUENCE.Format()] = failed_;
+      result_[OrthancPlugins::DICOM_TAG_REFERENCED_SOP_SEQUENCE.Format()] = success_;
+
+      std::string answer;
+  
+      {
+        OrthancPlugins::DicomWebFormatter::Locker locker(OrthancPluginDicomWebBinaryMode_Ignore, "");
+        locker.Apply(answer, context_, result_, xml_);
+      }
       
-      std::string s = "{}\n";
+      OrthancPluginAnswerBuffer(context_, output, answer.c_str(), answer.size(),
+                                xml_ ? "application/dicom+xml" : "application/dicom+json");
 
-      OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), "application/json");
       return OrthancPluginErrorCode_Success;
     }
   };
 
-  unsigned int count_;
-
 public:
-  StowServer() : count_(0)
-  {
-  }
-
   virtual IHandler* CreateHandler(OrthancPluginHttpMethod method,
                                   const std::string& url,
                                   const std::string& contentType,
@@ -268,19 +369,7 @@
                                       "The STOW-RS plugin currently only supports \"application/dicom\" subtype");
     }
 
-
-
-
-
-    printf("new handler: [%s] [%s]\n", contentType.c_str(), subType.c_str());
-
-    for (std::map<std::string, std::string>::const_iterator
-           it = headers.begin(); it != headers.end(); ++it)
-    {
-      printf("  header: [%s] = [%s]\n", it->first.c_str(), it->second.c_str());
-    }
-
-    return new Handler(count_++);
+    return new Handler(context, IsXmlExpected(headers), wadoBase, expectedStudy);
   }
 };
 
--- a/Plugin/StowRs.cpp	Sat Jun 08 11:58:49 2019 +0200
+++ b/Plugin/StowRs.cpp	Sat Jun 08 17:21:53 2019 +0200
@@ -27,16 +27,12 @@
 #include <Core/Toolbox.h>
 #include <Plugins/Samples/Common/OrthancPluginCppWrapper.h>
 
-bool IsXmlExpected(const OrthancPluginHttpRequest* request)
+
+static bool IsXmlExpected(const std::string& acceptHeader)
 {
   std::string accept;
-
-  if (!OrthancPlugins::LookupHttpHeader(accept, request, "accept"))
-  {
-    return false;   // By default, return DICOM+JSON
-  }
-
-  Orthanc::Toolbox::ToLowerCase(accept);
+  Orthanc::Toolbox::ToLowerCase(accept, acceptHeader);
+  
   if (accept == "application/dicom+json" ||
       accept == "application/json" ||
       accept == "*/*")
@@ -58,6 +54,37 @@
 }
 
 
+bool IsXmlExpected(const std::map<std::string, std::string>& headers)
+{
+  std::map<std::string, std::string>::const_iterator found = headers.find("accept");
+
+  if (found == headers.end())
+  {
+    return false;   // By default, return DICOM+JSON
+  }
+  else
+  {
+    return IsXmlExpected(found->second);
+  }
+}
+
+
+  // TODO => REMOVE
+bool IsXmlExpected(const OrthancPluginHttpRequest* request)
+{
+  std::string accept;
+
+  if (OrthancPlugins::LookupHttpHeader(accept, request, "accept"))
+  {
+    return IsXmlExpected(accept);
+  }
+  else
+  {
+    return false;   // By default, return DICOM+JSON
+  }
+}
+
+
 void StowCallback(OrthancPluginRestOutput* output,
                   const char* url,
                   const OrthancPluginHttpRequest* request)
--- a/Plugin/StowRs.h	Sat Jun 08 11:58:49 2019 +0200
+++ b/Plugin/StowRs.h	Sat Jun 08 17:21:53 2019 +0200
@@ -23,7 +23,9 @@
 
 #include "Configuration.h"
 
-bool IsXmlExpected(const OrthancPluginHttpRequest* request);
+bool IsXmlExpected(const std::map<std::string, std::string>& headers);
+
+  bool IsXmlExpected(const OrthancPluginHttpRequest* request);
 
 void StowCallback(OrthancPluginRestOutput* output,
                   const char* url,