# HG changeset patch # User Sebastien Jodogne # Date 1481621240 -3600 # Node ID 3eefb84ac0bd82603f645fe8990d3ab048993381 # Parent 4f39ab2cb45332723230afb5c127bb7dd6d73579 dynamically fix outgoing C-Find requests using "OutgoingFindRequestFilter()" diff -r 4f39ab2cb453 -r 3eefb84ac0bd Core/Lua/LuaFunctionCall.cpp --- 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); + } } diff -r 4f39ab2cb453 -r 3eefb84ac0bd Core/Lua/LuaFunctionCall.h --- 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 namespace Orthanc @@ -64,6 +67,10 @@ void PushStringMap(const std::map& value); + void PushDicom(const DicomMap& dicom); + + void PushDicom(const DicomArray& dicom); + void Execute() { ExecuteInternal(0); diff -r 4f39ab2cb453 -r 3eefb84ac0bd NEWS --- 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 diff -r 4f39ab2cb453 -r 3eefb84ac0bd OrthancExplorer/query-retrieve.js --- 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; diff -r 4f39ab2cb453 -r 3eefb84ac0bd OrthancServer/FromDcmtkBridge.cpp --- 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); + } + } } diff -r 4f39ab2cb453 -r 3eefb84ac0bd OrthancServer/FromDcmtkBridge.h --- 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 #include @@ -202,5 +203,8 @@ static bool LookupTransferSyntax(std::string& result, DcmFileFormat& dicom); + + static void ExecuteToDicom(DicomMap& target, + LuaFunctionCall& call); }; } diff -r 4f39ab2cb453 -r 3eefb84ac0bd OrthancServer/OrthancFindRequestHandler.cpp --- 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 counters(ComputeCounters(context_, instances[i], level, input)); + std::auto_ptr counters(ComputeCounters(context_, instances[i], level, *filteredInput)); AddAnswer(answers, dicom, query, sequencesToReturn, counters.get()); } } diff -r 4f39ab2cb453 -r 3eefb84ac0bd OrthancServer/QueryRetrieveHandler.cpp --- 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; } }