diff Framework/Oracle/GenericOracleRunner.cpp @ 1151:48befc2bf66d broker

ParseDicomFromWadoCommand
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 15 Nov 2019 17:34:19 +0100
parents 7aad0984d38a
children 113131100638
line wrap: on
line diff
--- a/Framework/Oracle/GenericOracleRunner.cpp	Fri Nov 15 12:38:15 2019 +0100
+++ b/Framework/Oracle/GenericOracleRunner.cpp	Fri Nov 15 17:34:19 2019 +0100
@@ -30,11 +30,15 @@
 #include "HttpCommand.h"
 #include "OracleCommandExceptionMessage.h"
 #include "OrthancRestApiCommand.h"
+#include "ParseDicomFromFileCommand.h"
+#include "ParseDicomFromWadoCommand.h"
 #include "ReadFileCommand.h"
 
 #if ORTHANC_ENABLE_DCMTK == 1
-#  include "ParseDicomFileCommand.h"
+#  include "ParseDicomSuccessMessage.h"
 #  include <dcmtk/dcmdata/dcdeftag.h>
+static unsigned int BUCKET_DICOMDIR = 0;
+static unsigned int BUCKET_SOP = 1;
 #endif
 
 #include <Core/Compression/GzipCompressor.h>
@@ -45,6 +49,8 @@
 
 #include <boost/filesystem.hpp>
 
+#include <dcmtk/dcmdata/dcfilefo.h>
+
 
 namespace OrthancStone
 {
@@ -100,9 +106,9 @@
   }
 
 
-  static void RunInternal(boost::weak_ptr<IObserver> receiver,
-                          IMessageEmitter& emitter,
-                          const HttpCommand& command)
+  static void RunHttpCommand(std::string& answer,
+                             Orthanc::HttpClient::HttpHeaders& answerHeaders,
+                             const HttpCommand& command)
   {
     Orthanc::HttpClient client;
     client.SetUrl(command.GetUrl());
@@ -122,21 +128,28 @@
       client.SetBody(command.GetBody());
     }
 
-    std::string answer;
-    Orthanc::HttpClient::HttpHeaders answerHeaders;
     client.ApplyAndThrowException(answer, answerHeaders);
-
     DecodeAnswer(answer, answerHeaders);
-
-    HttpCommand::SuccessMessage message(command, answerHeaders, answer);
-    emitter.EmitMessage(receiver, message);
   }
 
 
   static void RunInternal(boost::weak_ptr<IObserver> receiver,
                           IMessageEmitter& emitter,
-                          const Orthanc::WebServiceParameters& orthanc,
-                          const OrthancRestApiCommand& command)
+                          const HttpCommand& command)
+  {
+    std::string answer;
+    Orthanc::HttpClient::HttpHeaders answerHeaders;
+    RunHttpCommand(answer, answerHeaders, command);
+    
+    HttpCommand::SuccessMessage message(command, answerHeaders, answer);
+    emitter.EmitMessage(receiver, message);
+  }
+
+  
+  static void RunOrthancRestApiCommand(std::string& answer,
+                                       Orthanc::HttpClient::HttpHeaders& answerHeaders,
+                                       const Orthanc::WebServiceParameters& orthanc,
+                                       const OrthancRestApiCommand& command)
   {
     Orthanc::HttpClient client(orthanc, command.GetUri());
     client.SetMethod(command.GetMethod());
@@ -150,11 +163,19 @@
       client.SetBody(command.GetBody());
     }
 
+    client.ApplyAndThrowException(answer, answerHeaders);
+    DecodeAnswer(answer, answerHeaders);
+  }
+
+  
+  static void RunInternal(boost::weak_ptr<IObserver> receiver,
+                          IMessageEmitter& emitter,
+                          const Orthanc::WebServiceParameters& orthanc,
+                          const OrthancRestApiCommand& command)
+  {
     std::string answer;
     Orthanc::HttpClient::HttpHeaders answerHeaders;
-    client.ApplyAndThrowException(answer, answerHeaders);
-
-    DecodeAnswer(answer, answerHeaders);
+    RunOrthancRestApiCommand(answer, answerHeaders, orthanc, command);
 
     OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer);
     emitter.EmitMessage(receiver, message);
@@ -238,160 +259,70 @@
 
 
 #if ORTHANC_ENABLE_DCMTK == 1
-  namespace
+  static Orthanc::ParsedDicomFile* ParseDicom(uint64_t& fileSize,  /* OUT */
+                                              const std::string& path,
+                                              bool isPixelData)
   {
-    class IDicomHandler : public boost::noncopyable
+    if (!Orthanc::SystemToolbox::IsRegularFile(path))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentFile);
+    }
+    
+    LOG(TRACE) << "Parsing DICOM file, " << (isPixelData ? "with" : "without")
+               << " pixel data: " << path;
+    
+    boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
+    
+    fileSize = Orthanc::SystemToolbox::GetFileSize(path);
+    
+    // Check for 32bit systems
+    if (fileSize != static_cast<uint64_t>(static_cast<size_t>(fileSize)))
     {
-    public:
-      virtual ~IDicomHandler()
-      {
-      }
-
-      virtual void Handle(Orthanc::ParsedDicomFile* dicom,
-                          const ParseDicomFileCommand& command,
-                          const std::string& path,
-                          uint64_t fileSize) = 0;
-
-      static void Apply(IDicomHandler& handler,
-                        const std::string& path,
-                        const ParseDicomFileCommand& command)
-      {
-        if (!Orthanc::SystemToolbox::IsRegularFile(path))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentFile);
-        }
-
-        LOG(TRACE) << "Parsing DICOM file, " 
-                   << (command.IsPixelDataIncluded() ? "with" : "without")
-                   << " pixel data: " << path;
-
-        boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time();
-
-        uint64_t fileSize = Orthanc::SystemToolbox::GetFileSize(path);
-
-        // Check for 32bit systems
-        if (fileSize != static_cast<uint64_t>(static_cast<size_t>(fileSize)))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
-        }
-
-        DcmFileFormat dicom;
-        bool ok;
-
-        if (command.IsPixelDataIncluded())
-        {
-          ok = dicom.loadFile(path.c_str()).good();
-        }
-        else
-        {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
+    }
+    
+    DcmFileFormat dicom;
+    bool ok;
+    
+    if (isPixelData)
+    {
+      ok = dicom.loadFile(path.c_str()).good();
+    }
+    else
+    {
 #if DCMTK_VERSION_NUMBER >= 362
-          /**
-           * NB : We could stop at (0x3007, 0x0000) instead of
-           * DCM_PixelData as the Stone framework does not use further
-           * tags (cf. the Orthanc::DICOM_TAG_* constants), but we
-           * still use "PixelData" as this does not change the runtime
-           * much, and as it is more explicit.
-           **/
-          static const DcmTagKey STOP = DCM_PixelData;
-          //static const DcmTagKey STOP(0x3007, 0x0000);
-
-          ok = dicom.loadFileUntilTag(path.c_str(), EXS_Unknown, EGL_noChange,
-                                      DCM_MaxReadLength, ERM_autoDetect, STOP).good();
-#else
-          // The primitive "loadFileUntilTag" was introduced in DCMTK 3.6.2
-          ok = dicom.loadFile(path.c_str()).good();
-#endif
-        }
-
-        if (ok)
-        {
-          handler.Handle(new Orthanc::ParsedDicomFile(dicom), command, path, fileSize);
+      /**
+       * NB : We could stop at (0x3007, 0x0000) instead of
+       * DCM_PixelData as the Stone framework does not use further
+       * tags (cf. the Orthanc::DICOM_TAG_* constants), but we still
+       * use "PixelData" as this does not change the runtime much, and
+       * as it is more explicit.
+       **/
+      static const DcmTagKey STOP = DCM_PixelData;
+      //static const DcmTagKey STOP(0x3007, 0x0000);
 
-          boost::posix_time::ptime end = boost::posix_time::microsec_clock::local_time();
-          LOG(TRACE) << path << ": parsed in " << (end-start).total_milliseconds() << " ms";
-        }
-        else
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
-                                          "Cannot parse file: " + path);
-        }
-      }
-    };
-
+      ok = dicom.loadFileUntilTag(path.c_str(), EXS_Unknown, EGL_noChange,
+                                  DCM_MaxReadLength, ERM_autoDetect, STOP).good();
+#else
+      // The primitive "loadFileUntilTag" was introduced in DCMTK 3.6.2
+      ok = dicom.loadFile(path.c_str()).good();
+#endif
+    }
 
-    class DicomHandlerWithoutCache : public IDicomHandler
-    {
-    private:
-      boost::weak_ptr<IObserver> receiver_;
-      IMessageEmitter&           emitter_;
-      
-    public:
-      DicomHandlerWithoutCache(boost::weak_ptr<IObserver> receiver,
-                               IMessageEmitter& emitter) :
-        receiver_(receiver),
-        emitter_(emitter)
-      {
-      }        
-      
-      virtual void Handle(Orthanc::ParsedDicomFile* dicom,
-                          const ParseDicomFileCommand& command,
-                          const std::string& path,
-                          uint64_t fileSize)
-      {
-        std::auto_ptr<Orthanc::ParsedDicomFile> parsed(dicom);
-
-        ParseDicomFileCommand::SuccessMessage message
-          (command, *parsed, static_cast<size_t>(fileSize), command.IsPixelDataIncluded());
-        emitter_.EmitMessage(receiver_, message);
-      }
-    };
-
-
-    class DicomHandlerWithCache : public IDicomHandler
+    if (ok)
     {
-    private:
-      boost::weak_ptr<IObserver> receiver_;
-      IMessageEmitter&           emitter_;
-      boost::shared_ptr<ParsedDicomCache>  cache_;
+      std::auto_ptr<Orthanc::ParsedDicomFile> result(new Orthanc::ParsedDicomFile(dicom));
+
+      boost::posix_time::ptime end = boost::posix_time::microsec_clock::local_time();
+      LOG(TRACE) << path << ": parsed in " << (end-start).total_milliseconds() << " ms";
 
-    public:
-      DicomHandlerWithCache(boost::weak_ptr<IObserver> receiver,
-                            IMessageEmitter& emitter,
-                            boost::shared_ptr<ParsedDicomCache> cache) :
-        receiver_(receiver),
-        emitter_(emitter),
-        cache_(cache)
-      {
-        if (!cache)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-        }
-      }
-      
-      virtual void Handle(Orthanc::ParsedDicomFile* dicom,
-                          const ParseDicomFileCommand& command,
-                          const std::string& path,
-                          uint64_t fileSize)
-      {
-        std::auto_ptr<Orthanc::ParsedDicomFile> parsed(dicom);
-
-        {
-          ParseDicomFileCommand::SuccessMessage message
-            (command, *parsed, static_cast<size_t>(fileSize), command.IsPixelDataIncluded());
-          emitter_.EmitMessage(receiver_, message);
-        }
-
-        // Store it into the cache for future use
-        assert(cache_);
-
-        // Invalidate to overwrite DICOM instance that would already
-        // be stored without pixel data
-        cache_->Invalidate(path);
-
-        cache_->Acquire(path, parsed.release(),
-                        static_cast<size_t>(fileSize), command.IsPixelDataIncluded());
-      }
-    };
+      return result.release();
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat,
+                                      "Cannot parse file: " + path);
+    }
   }
 
   
@@ -399,42 +330,105 @@
                           IMessageEmitter& emitter,
                           boost::shared_ptr<ParsedDicomCache> cache,
                           const std::string& root,
-                          const ParseDicomFileCommand& command)
+                          const ParseDicomFromFileCommand& command)
   {
     const std::string path = GetPath(root, command.GetPath());
 
-#if 1
-    if (cache.get())
+    if (cache)
     {
+      ParsedDicomCache::Reader reader(*cache, BUCKET_DICOMDIR, path);
+      if (reader.IsValid() &&
+          (!command.IsPixelDataIncluded() ||
+           reader.HasPixelData()))
       {
-        ParsedDicomCache::Reader reader(*cache, path);
-        if (reader.IsValid() &&
-            (!command.IsPixelDataIncluded() ||
-             reader.HasPixelData()))
-        {
-          // Reuse the DICOM file from the cache
-          ParseDicomFileCommand::SuccessMessage message(
-            command, reader.GetDicom(), reader.GetFileSize(), reader.HasPixelData());
-          emitter.EmitMessage(receiver, message);
-          return;
-        }
+        // Reuse the DICOM file from the cache
+        ParseDicomSuccessMessage message(command, reader.GetDicom(), reader.GetFileSize(), reader.HasPixelData());
+        emitter.EmitMessage(receiver, message);
+        return;
       }
+    }
+
+    uint64_t fileSize;
+    std::auto_ptr<Orthanc::ParsedDicomFile> parsed(ParseDicom(fileSize, path, command.IsPixelDataIncluded()));
+
+    if (fileSize != static_cast<size_t>(fileSize))
+    {
+      // Cannot load such a large file on 32-bit architecture
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
+    }
+    
+    {
+      ParseDicomSuccessMessage message
+        (command, *parsed, static_cast<size_t>(fileSize), command.IsPixelDataIncluded());
+      emitter.EmitMessage(receiver, message);
+    }
+
+    if (cache)
+    {
+      // Store it into the cache for future use
       
-      DicomHandlerWithCache handler(receiver, emitter, cache);
-      IDicomHandler::Apply(handler, path, command);
+      // Invalidate to overwrite DICOM instance that would already
+      // be stored without pixel data
+      cache->Invalidate(BUCKET_DICOMDIR, path);
+      
+      cache->Acquire(BUCKET_DICOMDIR, path, parsed.release(),
+                     static_cast<size_t>(fileSize), command.IsPixelDataIncluded());
     }
-    else
+  }
+
+  
+  static void RunInternal(boost::weak_ptr<IObserver> receiver,
+                          IMessageEmitter& emitter,
+                          boost::shared_ptr<ParsedDicomCache> cache,
+                          const Orthanc::WebServiceParameters& orthanc,
+                          const ParseDicomFromWadoCommand& command)
+  {
+    if (cache)
     {
-      // No cache available
-      DicomHandlerWithoutCache handler(receiver, emitter);
-      IDicomHandler::Apply(handler, path, command);
+      ParsedDicomCache::Reader reader(*cache, BUCKET_SOP, command.GetSopInstanceUid());
+      if (reader.IsValid() &&
+          reader.HasPixelData())
+      {
+        // Reuse the DICOM file from the cache
+        ParseDicomSuccessMessage message(command, reader.GetDicom(), reader.GetFileSize(), reader.HasPixelData());
+        emitter.EmitMessage(receiver, message);
+        return;
+      }
     }
-#else
-    DicomHandlerWithoutCache handler(receiver, emitter);
-    IDicomHandler::Apply(handler, path, command);
-#endif
+
+    std::string answer;
+    Orthanc::HttpClient::HttpHeaders answerHeaders;
+
+    switch (command.GetRestCommand().GetType())
+    {
+      case IOracleCommand::Type_Http:
+        RunHttpCommand(answer, answerHeaders, dynamic_cast<const HttpCommand&>(command.GetRestCommand()));
+        break;
+        
+      case IOracleCommand::Type_OrthancRestApi:
+        RunOrthancRestApiCommand(answer, answerHeaders, orthanc,
+                                 dynamic_cast<const OrthancRestApiCommand&>(command.GetRestCommand()));
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+    size_t fileSize;
+    std::auto_ptr<Orthanc::ParsedDicomFile> parsed(ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, answerHeaders));
+
+    {
+      ParseDicomSuccessMessage message(command, *parsed, fileSize,
+                                       true /* pixel data always is included in WADO-RS */);
+      emitter.EmitMessage(receiver, message);
+    }
+
+    if (cache)
+    {
+      // Store it into the cache for future use
+      cache->Acquire(BUCKET_SOP, command.GetSopInstanceUid(), parsed.release(), fileSize, true);
+    }
   }
-  
 #endif
 
 
@@ -476,10 +470,24 @@
                       dynamic_cast<const ReadFileCommand&>(command));
           break;
 
-        case IOracleCommand::Type_ParseDicomFile:
+        case IOracleCommand::Type_ParseDicomFromFile:
+        case IOracleCommand::Type_ParseDicomFromWado:
 #if ORTHANC_ENABLE_DCMTK == 1
-          RunInternal(receiver, emitter, dicomCache_, rootDirectory_,
-                      dynamic_cast<const ParseDicomFileCommand&>(command));
+          switch (command.GetType())
+          {
+            case IOracleCommand::Type_ParseDicomFromFile:
+              RunInternal(receiver, emitter, dicomCache_, rootDirectory_,
+                          dynamic_cast<const ParseDicomFromFileCommand&>(command));
+              break;
+
+            case IOracleCommand::Type_ParseDicomFromWado:
+              RunInternal(receiver, emitter, dicomCache_, orthanc_,
+                          dynamic_cast<const ParseDicomFromWadoCommand&>(command));
+              break;
+
+            default:
+              throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+          }            
           break;
 #else
           throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented,