diff Plugins/Engine/OrthancPlugins.cpp @ 1995:f0acfa753973

New callback to handle non-worklists C-Find requests: OrthancPluginRegisterCFindCallback()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 24 May 2016 17:45:56 +0200
parents ce90d109bb64
children 364cc624eb65
line wrap: on
line diff
--- a/Plugins/Engine/OrthancPlugins.cpp	Tue May 17 15:45:49 2016 +0200
+++ b/Plugins/Engine/OrthancPlugins.cpp	Tue May 24 17:45:56 2016 +0200
@@ -39,6 +39,7 @@
 
 
 #include "../../Core/ChunkedBuffer.h"
+#include "../../Core/DicomFormat/DicomArray.h"
 #include "../../Core/HttpServer/HttpToolbox.h"
 #include "../../Core/Logging.h"
 #include "../../Core/OrthancException.h"
@@ -107,6 +108,27 @@
   }
 
 
+  static char* CopyString(const std::string& str)
+  {
+    char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
+    if (result == NULL)
+    {
+      throw OrthancException(ErrorCode_NotEnoughMemory);
+    }
+
+    if (str.size() == 0)
+    {
+      result[0] = '\0';
+    }
+    else
+    {
+      memcpy(result, &str[0], str.size() + 1);
+    }
+
+    return result;
+  }
+
+
   namespace
   {
     class PluginStorageArea : public IStorageArea
@@ -291,6 +313,7 @@
     RestCallbacks restCallbacks_;
     OnStoredCallbacks  onStoredCallbacks_;
     OnChangeCallbacks  onChangeCallbacks_;
+    OrthancPluginFindCallback  findCallback_;
     OrthancPluginWorklistCallback  worklistCallback_;
     OrthancPluginDecodeImageCallback  decodeImageCallback_;
     IncomingHttpRequestFilters  incomingHttpRequestFilters_;
@@ -298,6 +321,7 @@
     boost::recursive_mutex restCallbackMutex_;
     boost::recursive_mutex storedCallbackMutex_;
     boost::recursive_mutex changeCallbackMutex_;
+    boost::mutex findCallbackMutex_;
     boost::mutex worklistCallbackMutex_;
     boost::mutex decodeImageCallbackMutex_;
     boost::recursive_mutex invokeServiceMutex_;
@@ -309,6 +333,7 @@
 
     PImpl() : 
       context_(NULL), 
+      findCallback_(NULL),
       worklistCallback_(NULL),
       decodeImageCallback_(NULL),
       argc_(1),
@@ -345,12 +370,13 @@
                         const std::string& calledAet)
     {
       bool caseSensitivePN = Configuration::GetGlobalBoolParameter("CaseSensitivePN", false);
-      matcher_.reset(new HierarchicalMatcher(query, caseSensitivePN));
-      currentQuery_ = &query;
 
       {
         boost::mutex::scoped_lock lock(that_.pimpl_->worklistCallbackMutex_);
 
+        matcher_.reset(new HierarchicalMatcher(query, caseSensitivePN));
+        currentQuery_ = &query;
+
         if (that_.pimpl_->worklistCallback_)
         {
           OrthancPluginErrorCode error = that_.pimpl_->worklistCallback_
@@ -366,14 +392,18 @@
             throw OrthancException(static_cast<ErrorCode>(error));
           }
         }
+
+        Reset();
       }
-
-      Reset();
     }
 
     void GetDicomQuery(OrthancPluginMemoryBuffer& target) const
     {
-      assert(currentQuery_ != NULL);
+      if (currentQuery_ == NULL)
+      {
+        throw OrthancException(ErrorCode_Plugin);
+      }
+
       std::string dicom;
       currentQuery_->SaveToMemoryBuffer(dicom);
       CopyToMemoryBuffer(target, dicom.c_str(), dicom.size());
@@ -382,7 +412,11 @@
     bool IsMatch(const void* dicom,
                  size_t size) const
     {
-      assert(matcher_.get() != NULL);
+      if (matcher_.get() == NULL)
+      {
+        throw OrthancException(ErrorCode_Plugin);
+      }
+
       ParsedDicomFile f(dicom, size);
       return matcher_->Match(f);
     }
@@ -391,7 +425,11 @@
                    const void* dicom,
                    size_t size) const
     {
-      assert(matcher_.get() != NULL);
+      if (matcher_.get() == NULL)
+      {
+        throw OrthancException(ErrorCode_Plugin);
+      }
+
       ParsedDicomFile f(dicom, size);
       std::auto_ptr<ParsedDicomFile> summary(matcher_->Extract(f));
       reinterpret_cast<DicomFindAnswers*>(answers)->Add(*summary);
@@ -399,25 +437,107 @@
   };
 
   
-  static char* CopyString(const std::string& str)
+  class OrthancPlugins::FindHandler : public IFindRequestHandler
   {
-    char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
-    if (result == NULL)
+  private:
+    OrthancPlugins&            that_;
+    std::auto_ptr<DicomArray>  currentQuery_;
+
+    void Reset()
     {
-      throw OrthancException(ErrorCode_NotEnoughMemory);
+      currentQuery_.reset(NULL);
+    }
+
+  public:
+    FindHandler(OrthancPlugins& that) : that_(that)
+    {
+      Reset();
     }
 
-    if (str.size() == 0)
+    virtual void Handle(DicomFindAnswers& answers,
+                        const DicomMap& input,
+                        const std::list<DicomTag>& sequencesToReturn,
+                        const std::string& remoteIp,
+                        const std::string& remoteAet,
+                        const std::string& calledAet)
     {
-      result[0] = '\0';
+      DicomMap tmp;
+      tmp.Assign(input);
+
+      for (std::list<DicomTag>::const_iterator it = sequencesToReturn.begin(); 
+           it != sequencesToReturn.end(); ++it)
+      {
+        if (!input.HasTag(*it))
+        {
+          tmp.SetValue(*it, "");
+        }
+      }      
+
+      {
+        boost::mutex::scoped_lock lock(that_.pimpl_->findCallbackMutex_);
+        currentQuery_.reset(new DicomArray(tmp));
+
+        if (that_.pimpl_->findCallback_)
+        {
+          OrthancPluginErrorCode error = that_.pimpl_->findCallback_
+            (reinterpret_cast<OrthancPluginFindAnswers*>(&answers),
+             reinterpret_cast<const OrthancPluginFindQuery*>(this),
+             remoteAet.c_str(),
+             calledAet.c_str());
+
+          if (error != OrthancPluginErrorCode_Success)
+          {
+            Reset();
+            that_.GetErrorDictionary().LogError(error, true);
+            throw OrthancException(static_cast<ErrorCode>(error));
+          }
+        }
+
+        Reset();
+      }
     }
-    else
+
+    void Invoke(_OrthancPluginService service,
+                const _OrthancPluginFindOperation& operation) const
     {
-      memcpy(result, &str[0], str.size() + 1);
+      if (currentQuery_.get() == NULL)
+      {
+        throw OrthancException(ErrorCode_Plugin);
+      }
+
+      switch (service)
+      {
+        case _OrthancPluginService_GetFindQuerySize:
+          *operation.resultUint32 = currentQuery_->GetSize();
+          break;
+
+        case _OrthancPluginService_GetFindQueryTag:
+        {
+          const DicomTag& tag = currentQuery_->GetElement(operation.index).GetTag();
+          *operation.resultGroup = tag.GetGroup();
+          *operation.resultElement = tag.GetElement();
+          break;
+        }
+
+        case _OrthancPluginService_GetFindQueryTagName:
+        {
+          const DicomTag& tag = currentQuery_->GetElement(operation.index).GetTag();
+          *operation.resultString = CopyString(FromDcmtkBridge::GetName(tag));
+          break;
+        }
+
+        case _OrthancPluginService_GetFindQueryValue:
+        {
+          *operation.resultString = CopyString(currentQuery_->GetElement(operation.index).GetValue().GetContent());
+          break;
+        }
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
     }
-
-    return result;
-  }
+  };
+  
 
 
   OrthancPlugins::OrthancPlugins()
@@ -741,6 +861,26 @@
   }
 
 
+  void OrthancPlugins::RegisterFindCallback(const void* parameters)
+  {
+    const _OrthancPluginFindCallback& p = 
+      *reinterpret_cast<const _OrthancPluginFindCallback*>(parameters);
+
+    boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
+
+    if (pimpl_->findCallback_ != NULL)
+    {
+      LOG(ERROR) << "Can only register one plugin to handle C-FIND requests";
+      throw OrthancException(ErrorCode_Plugin);
+    }
+    else
+    {
+      LOG(INFO) << "Plugin has registered a callback to handle C-FIND requests";
+      pimpl_->findCallback_ = p.callback;
+    }
+  }
+
+
   void OrthancPlugins::RegisterDecodeImageCallback(const void* parameters)
   {
     const _OrthancPluginDecodeImageCallback& p = 
@@ -1838,6 +1978,10 @@
         RegisterWorklistCallback(parameters);
         return true;
 
+      case _OrthancPluginService_RegisterFindCallback:
+        RegisterFindCallback(parameters);
+        return true;
+
       case _OrthancPluginService_RegisterDecodeImageCallback:
         RegisterDecodeImageCallback(parameters);
         return true;
@@ -2314,6 +2458,33 @@
         return true;
       }
 
+      case _OrthancPluginService_FindAddAnswer:
+      {
+        const _OrthancPluginFindOperation& p =
+          *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
+        reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(p.dicom, p.size);
+        return true;
+      }
+
+      case _OrthancPluginService_FindMarkIncomplete:
+      {
+        const _OrthancPluginFindOperation& p =
+          *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
+        reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
+        return true;
+      }
+
+      case _OrthancPluginService_GetFindQuerySize:
+      case _OrthancPluginService_GetFindQueryTag:
+      case _OrthancPluginService_GetFindQueryTagName:
+      case _OrthancPluginService_GetFindQueryValue:
+      {
+        const _OrthancPluginFindOperation& p =
+          *reinterpret_cast<const _OrthancPluginFindOperation*>(parameters);
+        reinterpret_cast<const FindHandler*>(p.query)->Invoke(service, p);
+        return true;
+      }
+
       case _OrthancPluginService_CreateImage:
       case _OrthancPluginService_CreateImageAccessor:
       case _OrthancPluginService_DecodeDicomImage:
@@ -2475,6 +2646,26 @@
   }
 
 
+  IFindRequestHandler* OrthancPlugins::ConstructFindRequestHandler()
+  {
+    if (HasFindHandler())
+    {
+      return new FindHandler(*this);
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+
+
+  bool OrthancPlugins::HasFindHandler()
+  {
+    boost::mutex::scoped_lock lock(pimpl_->findCallbackMutex_);
+    return pimpl_->findCallback_ != NULL;
+  }
+
+
   bool OrthancPlugins::HasCustomImageDecoder()
   {
     boost::mutex::scoped_lock lock(pimpl_->decodeImageCallbackMutex_);