changeset 2218:3eefb84ac0bd

dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 13 Dec 2016 10:27:20 +0100
parents 4f39ab2cb453
children 262fcab24024
files Core/Lua/LuaFunctionCall.cpp Core/Lua/LuaFunctionCall.h NEWS OrthancExplorer/query-retrieve.js OrthancServer/FromDcmtkBridge.cpp OrthancServer/FromDcmtkBridge.h OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/QueryRetrieveHandler.cpp
diffstat 8 files changed, 132 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Lua/LuaFunctionCall.cpp	Mon Dec 12 11:56:26 2016 +0100
+++ b/Core/Lua/LuaFunctionCall.cpp	Tue Dec 13 10:27:20 2016 +0100
@@ -166,4 +166,26 @@
 
     PushJson(json);
   }
+
+
+  void LuaFunctionCall::PushDicom(const DicomMap& dicom)
+  {
+    DicomArray a(dicom);
+    PushDicom(a);
+  }
+
+
+  void LuaFunctionCall::PushDicom(const DicomArray& dicom)
+  {
+    Json::Value value = Json::objectValue;
+
+    for (size_t i = 0; i < dicom.GetSize(); i++)
+    {
+      const DicomValue& v = dicom.GetElement(i).GetValue();
+      std::string s = (v.IsNull() || v.IsBinary()) ? "" : v.GetContent();
+      value[dicom.GetElement(i).GetTag().Format()] = s;
+    }
+
+    PushJson(value);
+  }
 }
--- a/Core/Lua/LuaFunctionCall.h	Mon Dec 12 11:56:26 2016 +0100
+++ b/Core/Lua/LuaFunctionCall.h	Tue Dec 13 10:27:20 2016 +0100
@@ -34,6 +34,9 @@
 
 #include "LuaContext.h"
 
+#include "../DicomFormat/DicomArray.h"
+#include "../DicomFormat/DicomMap.h"
+
 #include <json/json.h>
 
 namespace Orthanc
@@ -64,6 +67,10 @@
 
     void PushStringMap(const std::map<std::string, std::string>& value);
 
+    void PushDicom(const DicomMap& dicom);
+
+    void PushDicom(const DicomArray& dicom);
+
     void Execute()
     {
       ExecuteInternal(0);
--- a/NEWS	Mon Dec 12 11:56:26 2016 +0100
+++ b/NEWS	Tue Dec 13 10:27:20 2016 +0100
@@ -87,6 +87,7 @@
 Lua
 ---
 
+* Possibility to dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()"
 * Access to the HTTP headers in the "IncomingHttpRequestFilter()" callback
 
 Image decoding
--- a/OrthancExplorer/query-retrieve.js	Mon Dec 12 11:56:26 2016 +0100
+++ b/OrthancExplorer/query-retrieve.js	Tue Dec 13 10:27:20 2016 +0100
@@ -83,7 +83,6 @@
       'PatientID' : '*',
       'PatientName' : '*',
       'PatientSex' : '*',
-      'SpecificCharacterSet' : 'ISO_IR 192',  // UTF-8
       'StudyDate' : $('#qr-date').val(),
       'StudyDescription' : '*'
     }
@@ -95,7 +94,7 @@
   var modalities = '';
   $('#qr-modalities input:checked').each(function() {
     var s = $(this).attr('name');
-    if (modalities == '*')
+    if (modalities == '')
       modalities = s;
     else
       modalities += '\\' + s;
--- a/OrthancServer/FromDcmtkBridge.cpp	Mon Dec 12 11:56:26 2016 +0100
+++ b/OrthancServer/FromDcmtkBridge.cpp	Tue Dec 13 10:27:20 2016 +0100
@@ -1975,4 +1975,41 @@
       return false;
     }
   }
+
+
+  void FromDcmtkBridge::ExecuteToDicom(DicomMap& target,
+                                       LuaFunctionCall& call)
+  {
+    Json::Value output;
+    call.ExecuteToJson(output, true /* keep strings */);
+
+    target.Clear();
+
+    if (output.type() == Json::arrayValue &&
+        output.size() == 0)
+    {
+      // This case happens for empty tables
+      return;
+    }
+
+    if (output.type() != Json::objectValue)
+    {
+      LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table";
+      throw OrthancException(ErrorCode_LuaBadOutput);
+    }
+
+    Json::Value::Members members = output.getMemberNames();
+
+    for (size_t i = 0; i < members.size(); i++)
+    {
+      if (output[members[i]].type() != Json::stringValue)
+      {
+        LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table mapping names of DICOM tags to strings";
+        throw OrthancException(ErrorCode_LuaBadOutput);
+      }
+
+      DicomTag tag(ParseTag(members[i]));
+      target.SetValue(tag, output[members[i]].asString(), false);
+    }
+  }
 }
--- a/OrthancServer/FromDcmtkBridge.h	Mon Dec 12 11:56:26 2016 +0100
+++ b/OrthancServer/FromDcmtkBridge.h	Tue Dec 13 10:27:20 2016 +0100
@@ -36,6 +36,7 @@
 
 #include "../Core/DicomFormat/DicomElement.h"
 #include "../Core/DicomFormat/DicomMap.h"
+#include "../Core/Lua/LuaFunctionCall.h"
 
 #include <dcmtk/dcmdata/dcdatset.h>
 #include <dcmtk/dcmdata/dcmetinf.h>
@@ -202,5 +203,8 @@
 
     static bool LookupTransferSyntax(std::string& result,
                                      DcmFileFormat& dicom);
+
+    static void ExecuteToDicom(DicomMap& target,
+                               LuaFunctionCall& call);
   };
 }
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Mon Dec 12 11:56:26 2016 +0100
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Tue Dec 13 10:27:20 2016 +0100
@@ -486,62 +486,27 @@
                                                  const std::string& remoteAet,
                                                  const std::string& calledAet)
   {
-    Json::Value output;
-
-    {
-      LuaScripting::Locker locker(context_.GetLua());
-      static const char* NAME = "IncomingFindRequestFilter";
+    LuaScripting::Locker locker(context_.GetLua());
+    static const char* CALLBACK = "IncomingFindRequestFilter";
       
-      if (!locker.GetLua().IsExistingFunction(NAME))
-      {
-        return false;
-      }
-
-      Json::Value tmp = Json::objectValue;
-      DicomArray a(source);
-
-      for (size_t i = 0; i < a.GetSize(); i++)
-      {
-        const DicomValue& v = a.GetElement(i).GetValue();
-        std::string s = (v.IsNull() || v.IsBinary()) ? "" : v.GetContent();
-        tmp[a.GetElement(i).GetTag().Format()] = s;
-      }
-
+    if (!locker.GetLua().IsExistingFunction(CALLBACK))
+    {
+      return false;
+    }
+    else
+    {
       Json::Value origin = Json::objectValue;
       origin["RemoteIp"] = remoteIp;
       origin["RemoteAet"] = remoteAet;
       origin["CalledAet"] = calledAet;
 
-      LuaFunctionCall call(locker.GetLua(), NAME);
-      call.PushJson(tmp);
+      LuaFunctionCall call(locker.GetLua(), CALLBACK);
+      call.PushDicom(source);
       call.PushJson(origin);
-
-      call.ExecuteToJson(output, true);
-    }
-
-    // The Lua context is released at this point
-
-    if (output.type() != Json::objectValue)
-    {
-      LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table";
-      throw OrthancException(ErrorCode_LuaBadOutput);
-    }
+      FromDcmtkBridge::ExecuteToDicom(target, call);
 
-    Json::Value::Members members = output.getMemberNames();
-
-    for (size_t i = 0; i < members.size(); i++)
-    {
-      if (output[members[i]].type() != Json::stringValue)
-      {
-        LOG(ERROR) << "Lua: IncomingFindRequestFilter must return a table mapping names of DICOM tags to strings";
-        throw OrthancException(ErrorCode_LuaBadOutput);
-      }
-
-      DicomTag tag(FromDcmtkBridge::ParseTag(members[i]));
-      target.SetValue(tag, output[members[i]].asString(), false);
+      return true;
     }
-
-    return true;
   }
 
 
@@ -702,7 +667,7 @@
         }
         else
         {
-          std::auto_ptr<DicomMap> counters(ComputeCounters(context_, instances[i], level, input));
+          std::auto_ptr<DicomMap> counters(ComputeCounters(context_, instances[i], level, *filteredInput));
           AddAnswer(answers, dicom, query, sequencesToReturn, counters.get());
         }
       }
--- a/OrthancServer/QueryRetrieveHandler.cpp	Mon Dec 12 11:56:26 2016 +0100
+++ b/OrthancServer/QueryRetrieveHandler.cpp	Tue Dec 13 10:27:20 2016 +0100
@@ -34,10 +34,43 @@
 #include "QueryRetrieveHandler.h"
 
 #include "OrthancInitialization.h"
+#include "FromDcmtkBridge.h"
 
 
 namespace Orthanc
 {
+  static void FixQuery(DicomMap& query,
+                       ServerContext& context,
+                       const std::string& modality)
+  {
+    LuaScripting::Locker locker(context.GetLua());
+    static const char* CALLBACK = "OutgoingFindRequestFilter";
+        
+    if (locker.GetLua().IsExistingFunction(CALLBACK))
+    {
+      LuaFunctionCall call(locker.GetLua(), CALLBACK);
+      call.PushDicom(query);
+      call.PushJson(modality);
+      FromDcmtkBridge::ExecuteToDicom(query, call);
+    }
+  }
+
+
+  static void FixQuery(DicomMap& query,
+                       ModalityManufacturer manufacturer)
+  {
+    /**
+     * Introduce patches for specific manufacturers below.
+     **/
+
+    switch (manufacturer)
+    {
+      default:
+        break;
+    }
+  }
+
+
   void QueryRetrieveHandler::Invalidate()
   {
     done_ = false;
@@ -49,8 +82,20 @@
   {
     if (!done_)
     {
-      ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_);
-      locker.GetConnection().Find(answers_, level_, query_);
+      // Firstly, fix the content of the query for specific manufacturers
+      DicomMap fixed;
+      fixed.Assign(query_);
+      FixQuery(fixed, modality_.GetManufacturer());
+
+      // Secondly, possibly fix the query with the user-provider Lua callback
+      FixQuery(fixed, context_, modality_.GetApplicationEntityTitle()); 
+
+      {
+        // Finally, run the C-FIND SCU against the fixed query
+        ReusableDicomUserConnection::Locker locker(context_.GetReusableDicomUserConnection(), localAet_, modality_);
+        locker.GetConnection().Find(answers_, level_, fixed);
+      }
+
       done_ = true;
     }
   }