# HG changeset patch # User Sebastien Jodogne # Date 1769515240 -3600 # Node ID 0000864c1f631c0907aa2207bbdc398db0da56b9 # Parent dfb5748c820976bd0f1e323d43263d8d9a2dd45a# Parent bd866774d56d6d3d6554eaa7a1eae2c5d0a7c919 merge diff -r dfb5748c8209 -r 0000864c1f63 NEWS --- a/NEWS Tue Jan 27 13:00:23 2026 +0100 +++ b/NEWS Tue Jan 27 13:00:40 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 -------- diff -r dfb5748c8209 -r 0000864c1f63 OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp --- a/OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp Tue Jan 27 13:00:23 2026 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp Tue Jan 27 13:00:40 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()) diff -r dfb5748c8209 -r 0000864c1f63 OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp --- a/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp Tue Jan 27 13:00:23 2026 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.cpp Tue Jan 27 13:00:40 2026 +0100 @@ -28,8 +28,10 @@ #include "../Logging.h" #include "../OrthancException.h" #include "../SerializationToolbox.h" +#include "../Toolbox.h" #include +#include #include @@ -51,6 +53,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 +77,7 @@ localAet_.clear(); timeout_ = 0; retrieveMethod_ = RetrieveMethod_SystemDefault; + permissiveStoreSopClasses_.clear(); } @@ -321,6 +325,11 @@ retrieveMethod_ = RetrieveMethod_SystemDefault; } + if (serialized.isMember(KEY_PERMISSIVE_STORE_SOP_CLASSES)) + { + SerializationToolbox::ReadSetOfStrings(permissiveStoreSopClasses_, serialized[KEY_PERMISSIVE_STORE_SOP_CLASSES]); + } + } @@ -413,6 +422,7 @@ !allowNEventReport_ || !allowTranscoding_ || useDicomTls_ || + permissiveStoreSopClasses_.size() > 0 || HasLocalAet()); } @@ -441,6 +451,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 +556,26 @@ { retrieveMethod_ = retrieveMethod; } + + bool RemoteModalityParameters::IsPermissiveStoreSopClassUid(const std::string& sopClassUid) const + { + for (std::set::const_iterator it = permissiveStoreSopClasses_.begin(); it != permissiveStoreSopClasses_.end(); ++it) + { + if (it->find('*') != std::string::npos || it->find('?') != std::string::npos) + { + boost::regex pattern(Toolbox::WildcardToRegularExpression(*it)); + + if (boost::regex_match(sopClassUid, pattern)) + { + return true; + } + } + else if (sopClassUid == *it) + { + return true; + } + } + + return false; + } } diff -r dfb5748c8209 -r 0000864c1f63 OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h --- a/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h Tue Jan 27 13:00:23 2026 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/RemoteModalityParameters.h Tue Jan 27 13:00:40 2026 +0100 @@ -28,6 +28,7 @@ #include #include +#include #include namespace Orthanc @@ -52,7 +53,8 @@ std::string localAet_; uint32_t timeout_; RetrieveMethod retrieveMethod_; // New in Orthanc 1.12.6 - + std::set 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; }; } diff -r dfb5748c8209 -r 0000864c1f63 OrthancServer/Resources/Configuration.json --- a/OrthancServer/Resources/Configuration.json Tue Jan 27 13:00:23 2026 +0100 +++ b/OrthancServer/Resources/Configuration.json Tue Jan 27 13:00:40 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. Each entry can contain wildcards ("?" or "*"). + * 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 //} },