changeset 6571:3eafc8b4db4e

PermissiveStoreSopClasses
author Alain Mazy <am@orthanc.team>
date Tue, 27 Jan 2026 12:14:45 +0100
parents bb56a9cccf75
children 43ff4470024d
files NEWS OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h OrthancServer/Resources/Configuration.json
diffstat 5 files changed, 45 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Jan 20 09:19:56 2026 +0100
+++ b/NEWS	Tue Jan 27 12:14:45 2026 +0100
@@ -4,6 +4,13 @@
 General
 -------
 
+General
+-------
+
+* New configuration "PermissiveStoreSopClasses" in "DicomModalities" to ignore errors when 
+  a modality does not accept to receive the given SOP Classes.
+
+
 REST API
 --------
 
--- a/OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp	Tue Jan 20 09:19:56 2026 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp	Tue Jan 27 12:14:45 2026 +0100
@@ -547,8 +547,16 @@
 
     if (accepted.size() == 0)
     {
-      throw OrthancException(ErrorCode_NoPresentationContext, "Cannot C-Store an instance of SOPClassUID " + 
-                             sopClassUid + ", the destination has not accepted any TransferSyntax for this SOPClassUID.");
+      if (parameters_.GetRemoteModality().IsPermissiveStoreSopClassUid(sopClassUid))
+      {
+        LOG(INFO) << "Permissive SopClassUid '" << sopClassUid << "' is not accepted by '" << parameters_.GetRemoteModality().GetApplicationEntityTitle() << "'";
+        return;
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_NoPresentationContext, "Cannot C-Store an instance of SOPClassUID " + 
+                              sopClassUid + ", the destination has not accepted any TransferSyntax for this SOPClassUID.");
+      }
     }
 
     if (accepted.find(sourceSyntax) != accepted.end())
--- a/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp	Tue Jan 20 09:19:56 2026 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp	Tue Jan 27 12:14:45 2026 +0100
@@ -51,6 +51,7 @@
 static const char* KEY_LOCAL_AET = "LocalAet";
 static const char* KEY_TIMEOUT = "Timeout";
 static const char* KEY_RETRIEVE_METHOD = "RetrieveMethod";
+static const char* KEY_PERMISSIVE_STORE_SOP_CLASSES = "PermissiveStoreSopClasses";
 
 
 namespace Orthanc
@@ -74,6 +75,7 @@
     localAet_.clear();
     timeout_ = 0;
     retrieveMethod_ = RetrieveMethod_SystemDefault;
+    permissiveStoreSopClasses_.clear();
   }
 
 
@@ -321,6 +323,11 @@
       retrieveMethod_ = RetrieveMethod_SystemDefault;
     }
 
+    if (serialized.isMember(KEY_PERMISSIVE_STORE_SOP_CLASSES))
+    {
+      SerializationToolbox::ReadSetOfStrings(permissiveStoreSopClasses_, serialized[KEY_PERMISSIVE_STORE_SOP_CLASSES]);
+    }   
+
   }
 
 
@@ -413,6 +420,7 @@
             !allowNEventReport_ ||
             !allowTranscoding_ ||
             useDicomTls_ ||
+            permissiveStoreSopClasses_.size() > 0 ||
             HasLocalAet());
   }
 
@@ -441,6 +449,7 @@
       target[KEY_LOCAL_AET] = localAet_;
       target[KEY_TIMEOUT] = timeout_;
       target[KEY_RETRIEVE_METHOD] = EnumerationToString(retrieveMethod_);
+      SerializationToolbox::WriteSetOfStrings(target, permissiveStoreSopClasses_, KEY_PERMISSIVE_STORE_SOP_CLASSES);
     }
     else
     {
@@ -545,4 +554,9 @@
   {
     retrieveMethod_ = retrieveMethod;
   }
+
+  bool RemoteModalityParameters::IsPermissiveStoreSopClassUid(const std::string& sopClassUid) const
+  {
+    return permissiveStoreSopClasses_.find(sopClassUid) != permissiveStoreSopClasses_.end();
+  }
 }
--- a/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h	Tue Jan 20 09:19:56 2026 +0100
+++ b/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h	Tue Jan 27 12:14:45 2026 +0100
@@ -28,6 +28,7 @@
 
 #include <stdint.h>
 #include <string>
+#include <set>
 #include <json/value.h>
 
 namespace Orthanc
@@ -52,7 +53,8 @@
     std::string           localAet_;
     uint32_t              timeout_;
     RetrieveMethod        retrieveMethod_;   // New in Orthanc 1.12.6
-    
+    std::set<std::string> permissiveStoreSopClasses_;  // New in 1.12.11: a list of sop classes that will not generate an error if they are not accepted by the remote modality
+
     void Clear();
 
     void UnserializeArray(const Json::Value& serialized);
@@ -124,5 +126,6 @@
 
     void SetRetrieveMethod(RetrieveMethod retrieveMethod);
 
+    bool IsPermissiveStoreSopClassUid(const std::string& sopClassUid) const;
   };
 }
--- a/OrthancServer/Resources/Configuration.json	Tue Jan 20 09:19:56 2026 +0100
+++ b/OrthancServer/Resources/Configuration.json	Tue Jan 27 12:14:45 2026 +0100
@@ -513,6 +513,14 @@
      * The "RetrieveMethod" option allows one to overwrite the global
      * "DicomDefaultRetrieveMethod" configuration option for this
      * specific modality. (Allowed values: "C-MOVE" or "C-GET").
+     *
+     * The "PermissiveStoreSopClasses" option allows one to ignore
+     * errors when a modality does not accept to receive the given 
+     * SOP Classes.
+     * E.g. CVI42 does not accept Encapsulated PDF files; by setting
+     * "PermissiveStoreSopClasses": ["1.2.840.10008.5.1.4.1.1.104.1"],
+     * a C-Store to CVI42 will be able to complete even if the study
+     * contains an encapsulated PDF.
      **/
     //"untrusted" : {
     //  "AET" : "ORTHANC",
@@ -530,7 +538,8 @@
     //  "UseDicomTls" : false,             // new in 1.9.0
     //  "LocalAet" : "HELLO",              // new in 1.9.0
     //  "Timeout" : 60,                    // new in 1.9.1
-    //  "RetrieveMethod": "C-MOVE"         // new in 1.12.6
+    //  "RetrieveMethod": "C-MOVE",        // new in 1.12.6
+    //  "PermissiveStoreSopClasses": []    // new in 1.12.11
     //}
   },