changeset 5903:63ea301075ef get-scu

RetrievMethod
author Alain Mazy <am@orthanc.team>
date Thu, 05 Dec 2024 09:30:05 +0100
parents dfd5effec064
children bed6a8ba5431
files NEWS OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h OrthancFramework/Sources/Enumerations.cpp OrthancFramework/Sources/Enumerations.h OrthancServer/Resources/Configuration.json OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerContext.h
diffstat 9 files changed, 264 insertions(+), 124 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Dec 03 15:20:55 2024 +0100
+++ b/NEWS	Thu Dec 05 09:30:05 2024 +0100
@@ -8,7 +8,10 @@
   - Added support for C-GET SCU.
   - Added a configuration "AcceptedSopClasses" and "RejectedSopClasses" to limit 
     the SOP classes accepted by Orthanc when acting as C-STORE SCP.
-
+  - New config option "DicomDefaultRetrieveMethod" to define wheter Orthanc uses C-MOVE or C-GET 
+    to retrieve a resource after a C-Find (when calling /queries/.../retrieve).
+    This configuration can be overriden for each modality in "DicomModalities->..->RetrieveMethod".
+    Default value: "C-MOVE" to keep the backward compatibility.
 
 REST API
 --------
@@ -22,7 +25,8 @@
   standard https://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_F.2.3.html.
   This has no impact on StoneViewer and OHIF.
   https://discourse.orthanc-server.org/t/dicomwebplugin-does-not-return-series-metadata-properly/5195
-
+* /queries/../retrieve now accepts a new field in the payload: "RetrieveMethod" to define wheter 
+  Orthanc uses C-MOVE or C-GET to retrieve the resource.
 
 Maintenance
 -----------
--- a/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp	Tue Dec 03 15:20:55 2024 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp	Thu Dec 05 09:30:05 2024 +0100
@@ -50,6 +50,7 @@
 static const char* KEY_USE_DICOM_TLS = "UseDicomTls";
 static const char* KEY_LOCAL_AET = "LocalAet";
 static const char* KEY_TIMEOUT = "Timeout";
+static const char* KEY_RETRIEVE_METHOD = "RetrieveMethod";
 
 
 namespace Orthanc
@@ -72,6 +73,7 @@
     useDicomTls_ = false;
     localAet_.clear();
     timeout_ = 0;
+    retrieveMethod_ = RetrieveMethod_SystemDefault;
   }
 
 
@@ -308,6 +310,17 @@
     {
       timeout_ = SerializationToolbox::ReadUnsignedInteger(serialized, KEY_TIMEOUT);
     }
+
+    if (serialized.isMember(KEY_RETRIEVE_METHOD))
+    {
+      retrieveMethod_ = StringToRetrieveMethod
+        (SerializationToolbox::ReadString(serialized, KEY_RETRIEVE_METHOD));
+    }   
+    else
+    {
+      retrieveMethod_ = RetrieveMethod_SystemDefault;
+    }
+
   }
 
 
@@ -427,6 +440,7 @@
       target[KEY_USE_DICOM_TLS] = useDicomTls_;
       target[KEY_LOCAL_AET] = localAet_;
       target[KEY_TIMEOUT] = timeout_;
+      target[KEY_RETRIEVE_METHOD] = EnumerationToString(retrieveMethod_);
     }
     else
     {
@@ -521,4 +535,14 @@
   {
     return timeout_ != 0;
   }
+
+  RetrieveMethod RemoteModalityParameters::GetRetrieveMethod() const
+  {
+    return retrieveMethod_;
+  }
+
+  void RemoteModalityParameters::SetRetrieveMethod(RetrieveMethod retrieveMethod)
+  {
+    retrieveMethod_ = retrieveMethod;
+  }
 }
--- a/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h	Tue Dec 03 15:20:55 2024 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h	Thu Dec 05 09:30:05 2024 +0100
@@ -51,6 +51,7 @@
     bool                  useDicomTls_;
     std::string           localAet_;
     uint32_t              timeout_;
+    RetrieveMethod        retrieveMethod_;   // New in Orthanc 1.12.6
     
     void Clear();
 
@@ -118,5 +119,10 @@
     uint32_t GetTimeout() const;
 
     bool HasTimeout() const;    
+
+    RetrieveMethod GetRetrieveMethod() const;
+
+    void SetRetrieveMethod(RetrieveMethod retrieveMethod);
+
   };
 }
--- a/OrthancFramework/Sources/Enumerations.cpp	Tue Dec 03 15:20:55 2024 +0100
+++ b/OrthancFramework/Sources/Enumerations.cpp	Thu Dec 05 09:30:05 2024 +0100
@@ -2493,6 +2493,44 @@
     }
   }
 
+  RetrieveMethod StringToRetrieveMethod(const std::string& str)
+  {
+    if (str == "C-MOVE")
+    {
+      return RetrieveMethod_Move;
+    }
+    else if (str == "C-GET")
+    {
+      return RetrieveMethod_Get;
+    }
+    else if (str == "SystemDefault")
+    {
+      return RetrieveMethod_SystemDefault;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange,
+                             "RetrieveMethod can be \"C-MOVE\", \"C-GET\" or \"SystemDefault\": " + str);
+    }    
+  }
+
+  const char* EnumerationToString(RetrieveMethod method)
+  {
+    switch (method)
+    {
+      case RetrieveMethod_Get:
+        return "C-GET";
+
+      case RetrieveMethod_Move:
+        return "C-MOVE";
+
+      case RetrieveMethod_SystemDefault:
+        return "SystemDefault";
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
 }
 
 
--- a/OrthancFramework/Sources/Enumerations.h	Tue Dec 03 15:20:55 2024 +0100
+++ b/OrthancFramework/Sources/Enumerations.h	Thu Dec 05 09:30:05 2024 +0100
@@ -793,6 +793,14 @@
     ResourceType_Instance = 4
   };
 
+  enum RetrieveMethod                         // new in Orthanc 1.12.6
+  {
+    RetrieveMethod_Move = 1,
+    RetrieveMethod_Get = 2,
+
+    RetrieveMethod_SystemDefault = 65535
+  };
+
 
   ORTHANC_PUBLIC
   const char* EnumerationToString(ErrorCode code);
@@ -849,6 +857,9 @@
   const char* EnumerationToString(DicomToJsonFormat format);
 
   ORTHANC_PUBLIC
+  const char* EnumerationToString(RetrieveMethod method);
+
+  ORTHANC_PUBLIC
   Encoding StringToEncoding(const char* encoding);
 
   ORTHANC_PUBLIC
@@ -947,4 +958,7 @@
 
   ORTHANC_PUBLIC
   void GetAllDicomTransferSyntaxes(std::set<DicomTransferSyntax>& target);
+
+  ORTHANC_PUBLIC 
+  RetrieveMethod StringToRetrieveMethod(const std::string& str);
 }
--- a/OrthancServer/Resources/Configuration.json	Tue Dec 03 15:20:55 2024 +0100
+++ b/OrthancServer/Resources/Configuration.json	Thu Dec 05 09:30:05 2024 +0100
@@ -485,6 +485,10 @@
      * for Orthanc when initiating an SCU to this very specific
      * modality. Similarly, "Timeout" allows one to overwrite the
      * global value "DicomScuTimeout" on a per-modality basis.
+     *
+     * The "RetrieveMethod" option allows one to overwrite the global
+     * "DicomDefaultRetrieveMethod" configuration option for this
+     * specific modality. (Allowed values: "C-MOVE" or "C-GET").
      **/
     //"untrusted" : {
     //  "AET" : "ORTHANC",
@@ -501,7 +505,8 @@
     //  "AllowTranscoding" : true,         // new in 1.7.0
     //  "UseDicomTls" : false,             // new in 1.9.0
     //  "LocalAet" : "HELLO",              // new in 1.9.0
-    //  "Timeout" : 60                     // new in 1.9.1
+    //  "Timeout" : 60,                    // new in 1.9.1
+    //  "RetrieveMethod": "C-MOVE"         // new in 1.12.6
     //}
   },
 
@@ -515,6 +520,13 @@
   // accept C-FIND requests from Orthanc (new in Orthanc 1.8.1).
   "DicomEchoChecksFind" : false,
 
+  // Wheter Orthanc uses C-MOVE or C-GET to retrieve a resource after
+  // a C-Find (when calling /queries/.../retrieve).
+  // This configuration can be overriden for each modality by providing
+  // "RetrieveMethod" in the "DicomModalities" entry.
+  // (new in Orthanc 1.12.6)
+  "DicomDefaultRetrieveMethod" : "C-MOVE",
+
   // The timeout (in seconds) after which the DICOM associations are
   // considered as closed by the Orthanc SCU (client) if the remote
   // DICOM SCP (server) does not answer.
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp	Tue Dec 03 15:20:55 2024 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp	Thu Dec 05 09:30:05 2024 +0100
@@ -57,6 +57,7 @@
   static const char* const KEY_CHECK_FIND = "CheckFind";
   static const char* const SOP_CLASS_UID = "SOPClassUID";
   static const char* const SOP_INSTANCE_UID = "SOPInstanceUID";
+  static const char* const KEY_RETRIEVE_METHOD = "RetrieveMethod";
   
   static RemoteModalityParameters MyGetModalityUsingSymbolicName(const std::string& name)
   {
@@ -914,57 +915,57 @@
   }
 
 
-  static void SubmitGetScuJob(RestApiPostCall& call,
-                              bool allAnswers,
-                              size_t index)
-  {
-    ServerContext& context = OrthancRestApi::GetContext(call);
-
-    int timeout = -1;
-    Json::Value body;
-
-    if (call.ParseJsonRequest(body))
-    {
-      timeout = Toolbox::GetJsonIntegerField(body, KEY_TIMEOUT, -1);
-    }
+  // static void SubmitGetScuJob(RestApiPostCall& call,
+  //                             bool allAnswers,
+  //                             size_t index)
+  // {
+  //   ServerContext& context = OrthancRestApi::GetContext(call);
+
+  //   int timeout = -1;
+  //   Json::Value body;
+
+  //   if (call.ParseJsonRequest(body))
+  //   {
+  //     timeout = Toolbox::GetJsonIntegerField(body, KEY_TIMEOUT, -1);
+  //   }
     
-    std::unique_ptr<DicomGetScuJob> job(new DicomGetScuJob(context));
-    job->SetQueryFormat(OrthancRestApi::GetDicomFormat(body, DicomToJsonFormat_Short));
+  //   std::unique_ptr<DicomGetScuJob> job(new DicomGetScuJob(context));
+  //   job->SetQueryFormat(OrthancRestApi::GetDicomFormat(body, DicomToJsonFormat_Short));
     
-    {
-      QueryAccessor query(call);
-      job->SetRemoteModality(query.GetHandler().GetRemoteModality());
-
-      if (timeout >= 0)
-      {
-        // New in Orthanc 1.7.0
-        job->SetTimeout(static_cast<uint32_t>(timeout));
-      }
-      else if (query.GetHandler().HasTimeout())
-      {
-        // New in Orthanc 1.9.1
-        job->SetTimeout(query.GetHandler().GetTimeout());
-      }
-
-      LOG(WARNING) << "Driving C-Get SCU on remote modality "
-                   << query.GetHandler().GetRemoteModality().GetApplicationEntityTitle();
-
-      if (allAnswers)
-      {
-        for (size_t i = 0; i < query.GetHandler().GetAnswersCount(); i++)
-        {
-          job->AddFindAnswer(query.GetHandler(), i);
-        }
-      }
-      else
-      {
-        job->AddFindAnswer(query.GetHandler(), index);
-      }
-    }
-
-    OrthancRestApi::GetApi(call).SubmitCommandsJob
-      (call, job.release(), true /* synchronous by default */, body);
-  }
+  //   {
+  //     QueryAccessor query(call);
+  //     job->SetRemoteModality(query.GetHandler().GetRemoteModality());
+
+  //     if (timeout >= 0)
+  //     {
+  //       // New in Orthanc 1.7.0
+  //       job->SetTimeout(static_cast<uint32_t>(timeout));
+  //     }
+  //     else if (query.GetHandler().HasTimeout())
+  //     {
+  //       // New in Orthanc 1.9.1
+  //       job->SetTimeout(query.GetHandler().GetTimeout());
+  //     }
+
+  //     LOG(WARNING) << "Driving C-Get SCU on remote modality "
+  //                  << query.GetHandler().GetRemoteModality().GetApplicationEntityTitle();
+
+  //     if (allAnswers)
+  //     {
+  //       for (size_t i = 0; i < query.GetHandler().GetAnswersCount(); i++)
+  //       {
+  //         job->AddFindAnswer(query.GetHandler(), i);
+  //       }
+  //     }
+  //     else
+  //     {
+  //       job->AddFindAnswer(query.GetHandler(), index);
+  //     }
+  //   }
+
+  //   OrthancRestApi::GetApi(call).SubmitCommandsJob
+  //     (call, job.release(), true /* synchronous by default */, body);
+  // }
 
 
   static void SubmitRetrieveJob(RestApiPostCall& call,
@@ -975,12 +976,25 @@
 
     std::string targetAet;
     int timeout = -1;
-    
+
+    QueryAccessor query(call);
+
+    RetrieveMethod retrieveMethod = query.GetHandler().GetRemoteModality().GetRetrieveMethod();
+
     Json::Value body;
     if (call.ParseJsonRequest(body))
     {
+      OrthancConfiguration::ReaderLock lock;
+
       targetAet = Toolbox::GetJsonStringField(body, KEY_TARGET_AET, context.GetDefaultLocalApplicationEntityTitle());
       timeout = Toolbox::GetJsonIntegerField(body, KEY_TIMEOUT, -1);
+      
+      std::string strRetrieveMethod = SerializationToolbox::ReadString(body, KEY_RETRIEVE_METHOD, "");
+      
+      if (!strRetrieveMethod.empty())
+      {
+        retrieveMethod = StringToRetrieveMethod(strRetrieveMethod);
+      }
     }
     else
     {
@@ -995,45 +1009,96 @@
       }
     }
     
-    std::unique_ptr<DicomMoveScuJob> job(new DicomMoveScuJob(context));
-    job->SetQueryFormat(OrthancRestApi::GetDicomFormat(body, DicomToJsonFormat_Short));
-    
+    if (retrieveMethod == RetrieveMethod_SystemDefault)
     {
-      QueryAccessor query(call);
-      job->SetTargetAet(targetAet);
-      job->SetLocalAet(query.GetHandler().GetLocalAet());
-      job->SetRemoteModality(query.GetHandler().GetRemoteModality());
-
-      if (timeout >= 0)
-      {
-        // New in Orthanc 1.7.0
-        job->SetTimeout(static_cast<uint32_t>(timeout));
-      }
-      else if (query.GetHandler().HasTimeout())
+      retrieveMethod = context.GetDefaultDicomRetrieveMethod();
+    }
+
+    switch (retrieveMethod)
+    {
+      case RetrieveMethod_Move:
       {
-        // New in Orthanc 1.9.1
-        job->SetTimeout(query.GetHandler().GetTimeout());
-      }
-
-      LOG(WARNING) << "Driving C-Move SCU on remote modality "
-                   << query.GetHandler().GetRemoteModality().GetApplicationEntityTitle()
-                   << " to target modality " << targetAet;
-
-      if (allAnswers)
-      {
-        for (size_t i = 0; i < query.GetHandler().GetAnswersCount(); i++)
+        std::unique_ptr<DicomMoveScuJob> job(new DicomMoveScuJob(context));
+        job->SetQueryFormat(OrthancRestApi::GetDicomFormat(body, DicomToJsonFormat_Short));
+
+        job->SetTargetAet(targetAet);
+        job->SetLocalAet(query.GetHandler().GetLocalAet());
+        job->SetRemoteModality(query.GetHandler().GetRemoteModality());
+
+        // TODO: refactor in a base class for DicomGetScuJob and DicomMoveScuJob
+        if (timeout >= 0)
+        {
+          // New in Orthanc 1.7.0
+          job->SetTimeout(static_cast<uint32_t>(timeout));
+        }
+        else if (query.GetHandler().HasTimeout())
+        {
+          // New in Orthanc 1.9.1
+          job->SetTimeout(query.GetHandler().GetTimeout());
+        }
+
+        LOG(WARNING) << "Driving C-Move SCU on remote modality "
+                    << query.GetHandler().GetRemoteModality().GetApplicationEntityTitle()
+                    << " to target modality " << targetAet;
+
+        // TODO: refactor in a base class for DicomGetScuJob and DicomMoveScuJob
+        if (allAnswers)
+        {
+          for (size_t i = 0; i < query.GetHandler().GetAnswersCount(); i++)
+          {
+            job->AddFindAnswer(query.GetHandler(), i);
+          }
+        }
+        else
         {
-          job->AddFindAnswer(query.GetHandler(), i);
+          job->AddFindAnswer(query.GetHandler(), index);
         }
-      }
-      else
+
+        OrthancRestApi::GetApi(call).SubmitCommandsJob
+          (call, job.release(), true /* synchronous by default */, body);
+
+      }; break;
+      case RetrieveMethod_Get:
       {
-        job->AddFindAnswer(query.GetHandler(), index);
-      }
+        std::unique_ptr<DicomGetScuJob> job(new DicomGetScuJob(context));
+        job->SetQueryFormat(OrthancRestApi::GetDicomFormat(body, DicomToJsonFormat_Short));
+        job->SetRemoteModality(query.GetHandler().GetRemoteModality());
+
+        // TODO: refactor in a base class for DicomGetScuJob and DicomMoveScuJob
+        if (timeout >= 0)
+        {
+          // New in Orthanc 1.7.0
+          job->SetTimeout(static_cast<uint32_t>(timeout));
+        }
+        else if (query.GetHandler().HasTimeout())
+        {
+          // New in Orthanc 1.9.1
+          job->SetTimeout(query.GetHandler().GetTimeout());
+        }
+
+        LOG(WARNING) << "Driving C-Get SCU on remote modality "
+                    << query.GetHandler().GetRemoteModality().GetApplicationEntityTitle();
+
+        // TODO: refactor in a base class for DicomGetScuJob and DicomMoveScuJob
+        if (allAnswers)
+        {
+          for (size_t i = 0; i < query.GetHandler().GetAnswersCount(); i++)
+          {
+            job->AddFindAnswer(query.GetHandler(), i);
+          }
+        }
+        else
+        {
+          job->AddFindAnswer(query.GetHandler(), index);
+        }
+
+        OrthancRestApi::GetApi(call).SubmitCommandsJob
+          (call, job.release(), true /* synchronous by default */, body);
+
+      }; break;
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
     }
-
-    OrthancRestApi::GetApi(call).SubmitCommandsJob
-      (call, job.release(), true /* synchronous by default */, body);
   }
 
 
@@ -1050,6 +1115,10 @@
                        "`DicomAet` configuration option.", false)
       .SetRequestField(KEY_TIMEOUT, RestApiCallDocumentation::Type_Number,
                        "Timeout for the C-MOVE command, in seconds", false)
+      .SetRequestField(KEY_RETRIEVE_METHOD, RestApiCallDocumentation::Type_String,
+                        "Force usage of C-MOVE or C-GET to retrieve the resource.  If note defined in the payload, "
+                        "the retrieve method is defined in the DicomDefaultRetrieveMethod configuration or in "
+                        "DicomModalities->..->RetrieveMethod", false)
       .AddRequestType(MimeType_PlainText, "AET of the target modality");
   }
   
@@ -1060,8 +1129,8 @@
     {
       DocumentRetrieveShared(call);
       call.GetDocumentation()
-        .SetSummary("Retrieve one answer with a C-MOVE SCU")
-        .SetDescription("Start a C-MOVE SCU command as a job, in order to retrieve one answer associated with the "
+        .SetSummary("Retrieve one answer with a C-MOVE or a C-GET SCU")
+        .SetDescription("Start a C-MOVE or a C-GET SCU command as a job, in order to retrieve one answer associated with the "
                         "query/retrieve operation whose identifiers are provided in the URL: "
                         "https://orthanc.uclouvain.be/book/users/rest.html#performing-retrieve-c-move")
         .SetUriArgument("index", "Index of the answer");
@@ -1073,40 +1142,6 @@
   }
 
 
-  static void RetrieveOneAnswerWithGet(RestApiPostCall& call)
-  {
-    if (call.IsDocumentation())
-    {
-      DocumentRetrieveShared(call);
-      call.GetDocumentation()
-        .SetSummary("Retrieve one answer with a C-GET SCU")
-        .SetDescription("Start a C-GET SCU command as a job, in order to retrieve one answer associated with the "
-                        "query/retrieve operation whose identifiers are provided in the URL: "
-                        "https://orthanc.uclouvain.be/book/users/rest.html#performing-retrieve-c-get")  // TODO-GET: write doc
-        .SetUriArgument("index", "Index of the answer");
-      return;
-    }
-
-    size_t index = boost::lexical_cast<size_t>(call.GetUriComponent("index", ""));
-    SubmitRetrieveJob(call, false, index);
-  }
-
-
-  static void RetrieveAllAnswersWithGet(RestApiPostCall& call)
-  {
-    if (call.IsDocumentation())
-    {
-      DocumentRetrieveShared(call);
-      call.GetDocumentation()
-        .SetSummary("Retrieve all answers with C-GET SCU")
-        .SetDescription("Start a C-GET SCU command as a job, in order to retrieve all the answers associated with the "
-                        "query/retrieve operation whose identifier is provided in the URL: "
-                        "https://orthanc.uclouvain.be/book/users/rest.html#performing-retrieve-c-get");
-      return;
-    }
-
-    SubmitGetScuJob(call, true, 0);
-  }
 
 
   static void RetrieveAllAnswers(RestApiPostCall& call)
@@ -2727,7 +2762,6 @@
     Register("/queries/{id}/answers/{index}", ListQueryAnswerOperations);
     Register("/queries/{id}/answers/{index}/content", GetQueryOneAnswer);
     Register("/queries/{id}/answers/{index}/retrieve", RetrieveOneAnswer);
-    Register("/queries/{id}/answers/{index}/get", RetrieveOneAnswerWithGet);
     Register("/queries/{id}/answers/{index}/query-instances",
              QueryAnswerChildren<ResourceType_Instance>);
     Register("/queries/{id}/answers/{index}/query-series",
@@ -2738,7 +2772,6 @@
     Register("/queries/{id}/modality", GetQueryModality);
     Register("/queries/{id}/query", GetQueryArguments);
     Register("/queries/{id}/retrieve", RetrieveAllAnswers);
-    Register("/queries/{id}/get", RetrieveAllAnswersWithGet);
 
     Register("/peers", ListPeers);
     Register("/peers/{id}", ListPeerOperations);
--- a/OrthancServer/Sources/ServerContext.cpp	Tue Dec 03 15:20:55 2024 +0100
+++ b/OrthancServer/Sources/ServerContext.cpp	Thu Dec 05 09:30:05 2024 +0100
@@ -489,11 +489,14 @@
 
         isUnknownSopClassAccepted_ = lock.GetConfiguration().GetBooleanParameter("UnknownSopClassAccepted", false);
 
+        // New options in Orthanc 1.12.6
         std::list<std::string> acceptedSopClasses;
         std::set<std::string> rejectedSopClasses;
         lock.GetConfiguration().GetListOfStringsParameter(acceptedSopClasses, "AcceptedSopClasses");
         lock.GetConfiguration().GetSetOfStringsParameter(rejectedSopClasses, "RejectSopClasses");
         SetAcceptedSopClasses(acceptedSopClasses, rejectedSopClasses);
+
+        defaultDicomRetrieveMethod_ = StringToRetrieveMethod(lock.GetConfiguration().GetStringParameter("DicomDefaultRetrieveMethod", "C-MOVE"));
       }
 
       jobsEngine_.SetThreadSleep(unitTesting ? 20 : 200);
--- a/OrthancServer/Sources/ServerContext.h	Tue Dec 03 15:20:55 2024 +0100
+++ b/OrthancServer/Sources/ServerContext.h	Thu Dec 05 09:30:05 2024 +0100
@@ -250,6 +250,7 @@
         
     std::unique_ptr<SharedArchive>  queryRetrieveArchive_;
     std::string defaultLocalAet_;
+    RetrieveMethod defaultDicomRetrieveMethod_;
     OrthancHttpHandler  httpHandler_;
     bool saveJobs_;
     FindStorageAccessMode findStorageAccessMode_;
@@ -434,6 +435,11 @@
       return defaultLocalAet_;
     }
 
+    RetrieveMethod GetDefaultDicomRetrieveMethod() const
+    {
+      return defaultDicomRetrieveMethod_;
+    }
+
     LuaScripting& GetLuaScripting()
     {
       return mainLua_;