# HG changeset patch # User Alain Mazy # Date 1733235595 -3600 # Node ID f622e5964cfa0b0cf4d6afa91eb0ea08082ab289 # Parent ed74c56db02fcc9bc01e4b268acf3e97c2a6c880 Get-SCU: proposed TS + Get-SCP: accept more transcoding diff -r ed74c56db02f -r f622e5964cfa NEWS --- a/NEWS Mon Dec 02 15:49:03 2024 +0100 +++ b/NEWS Tue Dec 03 15:19:55 2024 +0100 @@ -33,6 +33,8 @@ - When opening a DICOM SCU connection, Orthanc now only proposes the contexts that it is going to use in the connection and not all contexts as in previous versions. E.g, when performing a C-ECHO, Orthanc will not propose C-MOVE or C-FIND. +* DICOM Get-SCP: Orthanc won't refuse anymore to send e.g. a LittleEndianExplicit file when + the accepted transfer syntax is a compressed one. * Introduced a new thread to update the statistics at regular interval for the DB plugins that are implementing the UpdateAndGetStatistics function (currently only PostgreSQL). This avoids very long update times in case you don't call /statistics diff -r ed74c56db02f -r f622e5964cfa OrthancFramework/Sources/DicomNetworking/DicomAssociation.cpp --- a/OrthancFramework/Sources/DicomNetworking/DicomAssociation.cpp Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomAssociation.cpp Tue Dec 03 15:19:55 2024 +0100 @@ -346,12 +346,12 @@ assert(presentationContextId <= 255); const char* abstractSyntax = proposed_[i].abstractSyntax_.c_str(); - const std::set& source = proposed_[i].transferSyntaxes_; + const std::list& source = proposed_[i].transferSyntaxes_; std::vector transferSyntaxes; transferSyntaxes.reserve(source.size()); - for (std::set::const_iterator + for (std::list::const_iterator it = source.begin(); it != source.end(); ++it) { transferSyntaxes.push_back(GetTransferSyntaxUid(*it)); @@ -454,10 +454,10 @@ void DicomAssociation::ProposeGenericPresentationContext(const std::string& abstractSyntax, DicomAssociationRole role) { - std::set ts; - ts.insert(DicomTransferSyntax_LittleEndianImplicit); - ts.insert(DicomTransferSyntax_LittleEndianExplicit); - ts.insert(DicomTransferSyntax_BigEndianExplicit); // Retired + std::list ts; + ts.push_back(DicomTransferSyntax_LittleEndianExplicit); // the most standard one first ! + ts.push_back(DicomTransferSyntax_LittleEndianImplicit); + ts.push_back(DicomTransferSyntax_BigEndianExplicit); // Retired but was historicaly proposed by Orthanc ProposePresentationContext(abstractSyntax, ts, role); } @@ -478,8 +478,8 @@ DicomTransferSyntax transferSyntax, DicomAssociationRole role) { - std::set ts; - ts.insert(transferSyntax); + std::list ts; + ts.push_back(transferSyntax); ProposePresentationContext(abstractSyntax, ts, role); } @@ -491,7 +491,7 @@ void DicomAssociation::ProposePresentationContext( const std::string& abstractSyntax, - const std::set& transferSyntaxes) + const std::list& transferSyntaxes) { ProposePresentationContext(abstractSyntax, transferSyntaxes, DicomAssociationRole_Default); } @@ -499,7 +499,7 @@ void DicomAssociation::ProposePresentationContext( const std::string& abstractSyntax, - const std::set& transferSyntaxes, + const std::list& transferSyntaxes, DicomAssociationRole role) { if (transferSyntaxes.empty()) @@ -694,9 +694,9 @@ DicomAssociation association; { - std::set transferSyntaxes; - transferSyntaxes.insert(DicomTransferSyntax_LittleEndianExplicit); - transferSyntaxes.insert(DicomTransferSyntax_LittleEndianImplicit); + std::list transferSyntaxes; + transferSyntaxes.push_back(DicomTransferSyntax_LittleEndianExplicit); + transferSyntaxes.push_back(DicomTransferSyntax_LittleEndianImplicit); association.ProposePresentationContext(UID_StorageCommitmentPushModelSOPClass, transferSyntaxes, DicomAssociationRole_Scp); @@ -872,9 +872,9 @@ DicomAssociation association; { - std::set transferSyntaxes; - transferSyntaxes.insert(DicomTransferSyntax_LittleEndianExplicit); - transferSyntaxes.insert(DicomTransferSyntax_LittleEndianImplicit); + std::list transferSyntaxes; + transferSyntaxes.push_back(DicomTransferSyntax_LittleEndianExplicit); + transferSyntaxes.push_back(DicomTransferSyntax_LittleEndianImplicit); // association.SetRole(DicomAssociationRole_Default); association.ProposePresentationContext(UID_StorageCommitmentPushModelSOPClass, diff -r ed74c56db02f -r f622e5964cfa OrthancFramework/Sources/DicomNetworking/DicomAssociation.h --- a/OrthancFramework/Sources/DicomNetworking/DicomAssociation.h Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomAssociation.h Tue Dec 03 15:19:55 2024 +0100 @@ -44,6 +44,7 @@ #include // For uint8_t #include #include +#include namespace Orthanc { @@ -58,7 +59,7 @@ struct ProposedPresentationContext { std::string abstractSyntax_; - std::set transferSyntaxes_; + std::list transferSyntaxes_; DicomAssociationRole role_; }; @@ -121,11 +122,11 @@ void ProposePresentationContext( const std::string& abstractSyntax, - const std::set& transferSyntaxes); + const std::list& transferSyntaxes); void ProposePresentationContext( const std::string& abstractSyntax, - const std::set& transferSyntaxes, + const std::list& transferSyntaxes, DicomAssociationRole role); T_ASC_Association& GetDcmtkAssociation() const; diff -r ed74c56db02f -r f622e5964cfa OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp --- a/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp Tue Dec 03 15:19:55 2024 +0100 @@ -237,7 +237,7 @@ void DicomControlUserConnection::SetupPresentationContexts( ScuOperationFlags scuOperation, const std::set& acceptedStorageSopClasses, - const std::set& acceptedTransferSyntaxes) + const std::list& proposedStorageTransferSyntaxes) { assert(association_.get() != NULL); @@ -283,7 +283,7 @@ for (std::set::const_iterator it = acceptedStorageSopClasses.begin(); it != acceptedStorageSopClasses.end(); ++it) { - association_->ProposePresentationContext(*it, acceptedTransferSyntaxes, DicomAssociationRole_Scp); + association_->ProposePresentationContext(*it, proposedStorageTransferSyntaxes, DicomAssociationRole_Scp); } } } @@ -708,7 +708,7 @@ { assert((scuOperation & ScuOperationFlags_Get) == 0); // you must provide acceptedStorageSopClassUids for Get SCU std::set emptyStorageSopClasses; - std::set emptyStorageTransferSyntaxes; + std::list emptyStorageTransferSyntaxes; SetupPresentationContexts(scuOperation, emptyStorageSopClasses, emptyStorageTransferSyntaxes); } @@ -716,11 +716,11 @@ DicomControlUserConnection::DicomControlUserConnection(const DicomAssociationParameters& params, ScuOperationFlags scuOperation, const std::set& acceptedStorageSopClasses, - const std::set& acceptedTransferSyntaxes) : + const std::list& proposedStorageTransferSyntaxes) : parameters_(params), association_(new DicomAssociation) { - SetupPresentationContexts(scuOperation, acceptedStorageSopClasses, acceptedTransferSyntaxes); + SetupPresentationContexts(scuOperation, acceptedStorageSopClasses, proposedStorageTransferSyntaxes); } diff -r ed74c56db02f -r f622e5964cfa OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.h --- a/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.h Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.h Tue Dec 03 15:19:55 2024 +0100 @@ -69,7 +69,7 @@ void SetupPresentationContexts(ScuOperationFlags scuOperation, const std::set& acceptedStorageSopClasses, - const std::set& acceptedTransferSyntaxes); // TODO-GET: in order of preference ? + const std::list& proposedStorageTransferSyntaxes); void FindInternal(DicomFindAnswers& answers, DcmDataset* dataset, @@ -88,7 +88,7 @@ explicit DicomControlUserConnection(const DicomAssociationParameters& params, ScuOperationFlags scuOperation, const std::set& acceptedStorageSopClasses, - const std::set& acceptedTransferSyntaxes); // TODO-GET: in order of preference ? + const std::list& proposedStorageTransferSyntaxes); const DicomAssociationParameters& GetParameters() const { diff -r ed74c56db02f -r f622e5964cfa OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp --- a/OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp Tue Dec 03 15:19:55 2024 +0100 @@ -57,7 +57,7 @@ bool hasPreferred, DicomTransferSyntax preferred) { - typedef std::list< std::set > GroupsOfSyntaxes; + typedef std::list< std::list > GroupsOfSyntaxes; GroupsOfSyntaxes groups; @@ -65,8 +65,8 @@ for (std::set::const_iterator it = sourceSyntaxes.begin(); it != sourceSyntaxes.end(); ++it) { - std::set group; - group.insert(*it); + std::list group; + group.push_back(*it); groups.push_back(group); } @@ -74,8 +74,8 @@ if (hasPreferred && sourceSyntaxes.find(preferred) == sourceSyntaxes.end()) { - std::set group; - group.insert(preferred); + std::list group; + group.push_back(preferred); groups.push_back(group); } @@ -89,7 +89,7 @@ DicomTransferSyntax_BigEndianExplicit }; - std::set group; + std::list group; for (size_t i = 0; i < N; i++) { @@ -97,7 +97,7 @@ if (sourceSyntaxes.find(syntax) == sourceSyntaxes.end() && (!hasPreferred || preferred != syntax)) { - group.insert(syntax); + group.push_back(syntax); } } diff -r ed74c56db02f -r f622e5964cfa OrthancFramework/Sources/DicomNetworking/IApplicationEntityFilter.h --- a/OrthancFramework/Sources/DicomNetworking/IApplicationEntityFilter.h Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancFramework/Sources/DicomNetworking/IApplicationEntityFilter.h Tue Dec 03 15:19:55 2024 +0100 @@ -28,6 +28,7 @@ #include #include +#include namespace Orthanc { @@ -47,11 +48,18 @@ const std::string& calledAet, DicomRequestType type) = 0; + // Get the set of TransferSyntaxes that are accepted when negotiation a C-Store association, acting as SCP when it has been initiated by the C-Store SCU. virtual void GetAcceptedTransferSyntaxes(std::set& target, const std::string& remoteIp, const std::string& remoteAet, const std::string& calledAet) = 0; - + + // Get the list of TransferSyntaxes that are proposed when initiating a C-Store SCP which actually only happens in a C-Get SCU + virtual void GetProposedStorageTransferSyntaxes(std::list& target, + const std::string& remoteIp, + const std::string& remoteAet, + const std::string& calledAet) = 0; + virtual bool IsUnknownSopClassAccepted(const std::string& remoteIp, const std::string& remoteAet, const std::string& calledAet) = 0; diff -r ed74c56db02f -r f622e5964cfa OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.cpp --- a/OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.cpp Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.cpp Tue Dec 03 15:19:55 2024 +0100 @@ -201,6 +201,18 @@ } +void DicomFilter::GetProposedStorageTransferSyntaxes(std::list& target, + const std::string& remoteIp, + const std::string& remoteAet, + const std::string& calledAet) +{ + // default TS + target.push_back(Orthanc::DicomTransferSyntax_LittleEndianExplicit); + target.push_back(Orthanc::DicomTransferSyntax_LittleEndianImplicit); + target.push_back(Orthanc::DicomTransferSyntax_BigEndianExplicit); +} + + bool DicomFilter::IsUnknownSopClassAccepted(const std::string& remoteIp, const std::string& remoteAet, const std::string& calledAet) diff -r ed74c56db02f -r f622e5964cfa OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.h --- a/OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.h Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/DicomFilter.h Tue Dec 03 15:19:55 2024 +0100 @@ -64,6 +64,12 @@ const std::string& remoteAet, const std::string& calledAet) ORTHANC_OVERRIDE; + virtual void GetProposedStorageTransferSyntaxes(std::list& target, + const std::string& remoteIp, + const std::string& remoteAet, + const std::string& calledAet) ORTHANC_OVERRIDE; + + virtual bool IsUnknownSopClassAccepted(const std::string& remoteIp, const std::string& remoteAet, const std::string& calledAet) ORTHANC_OVERRIDE; diff -r ed74c56db02f -r f622e5964cfa OrthancServer/Sources/QueryRetrieveHandler.cpp --- a/OrthancServer/Sources/QueryRetrieveHandler.cpp Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancServer/Sources/QueryRetrieveHandler.cpp Tue Dec 03 15:19:55 2024 +0100 @@ -79,13 +79,7 @@ params.SetTimeout(timeout_); } - std::set acceptedStorageClasses; - std::set acceptedTransferSyntaxes; - - context_.GetAcceptedSopClasses(acceptedStorageClasses, 120); // limit to 120 SOP Classes since there are 128 presentation contexts available. - context_.GetAcceptedTransferSyntaxes(acceptedTransferSyntaxes); - - DicomControlUserConnection connection(params, static_cast(ScuOperationFlags_Find | ScuOperationFlags_Move | ScuOperationFlags_Get), acceptedStorageClasses, acceptedTransferSyntaxes); + DicomControlUserConnection connection(params, static_cast(ScuOperationFlags_Find)); connection.Find(answers_, level_, fixed, findNormalized_); } diff -r ed74c56db02f -r f622e5964cfa OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancServer/Sources/ServerContext.cpp Tue Dec 03 15:19:55 2024 +0100 @@ -2241,6 +2241,24 @@ } + void ServerContext::GetProposedStorageTransferSyntaxes(std::list& syntaxes) const + { + boost::mutex::scoped_lock lock(dynamicOptionsMutex_); + + // // TODO: investigate: actually, neither Orthanc 1.12.4 nor DCM4CHEE will accept to send a LittleEndianExplicit file + // // while e.g., Jpeg-LS has been presented (and accepted) as the preferred TS for the C-Store SCP. + // // if we have defined IngestTranscoding, let's propose this TS first to avoid any unnecessary transcoding + // if (isIngestTranscoding_) + // { + // syntaxes.push_back(ingestTransferSyntax_); + // } + + // then, propose the default ones + syntaxes.push_back(DicomTransferSyntax_LittleEndianExplicit); + syntaxes.push_back(DicomTransferSyntax_LittleEndianImplicit); + } + + bool ServerContext::IsUnknownSopClassAccepted() const { boost::mutex::scoped_lock lock(dynamicOptionsMutex_); diff -r ed74c56db02f -r f622e5964cfa OrthancServer/Sources/ServerContext.h --- a/OrthancServer/Sources/ServerContext.h Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancServer/Sources/ServerContext.h Tue Dec 03 15:19:55 2024 +0100 @@ -597,6 +597,8 @@ void SetAcceptedTransferSyntaxes(const std::set& syntaxes); + void GetProposedStorageTransferSyntaxes(std::list& syntaxes) const; + void SetAcceptedSopClasses(const std::list& acceptedSopClasses, const std::set& rejectedSopClasses); diff -r ed74c56db02f -r f622e5964cfa OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp --- a/OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp Tue Dec 03 15:19:55 2024 +0100 @@ -116,7 +116,7 @@ std::set sopClassesToPropose; std::set sopClassesInStudy; std::set acceptedSopClasses; - std::set storageAcceptedTransferSyntaxes; + std::list proposedTransferSyntaxes; if (findAnswer.HasTag(DICOM_TAG_SOP_CLASSES_IN_STUDY) && findAnswer.LookupStringValues(sopClassesInStudy, DICOM_TAG_SOP_CLASSES_IN_STUDY, false)) @@ -138,12 +138,12 @@ throw OrthancException(ErrorCode_NoPresentationContext, "Cannot perform C-Get, no SOPClassUID have been accepted by Orthanc."); } - context_.GetAcceptedTransferSyntaxes(storageAcceptedTransferSyntaxes); + context_.GetProposedStorageTransferSyntaxes(proposedTransferSyntaxes); connection_.reset(new DicomControlUserConnection(parameters_, ScuOperationFlags_Get, sopClassesToPropose, - storageAcceptedTransferSyntaxes)); + proposedTransferSyntaxes)); } connection_->Get(findAnswer, InstanceReceivedHandler, &context_); diff -r ed74c56db02f -r f622e5964cfa OrthancServer/Sources/main.cpp --- a/OrthancServer/Sources/main.cpp Mon Dec 02 15:49:03 2024 +0100 +++ b/OrthancServer/Sources/main.cpp Tue Dec 03 15:19:55 2024 +0100 @@ -494,6 +494,13 @@ context_.GetAcceptedTransferSyntaxes(target); } + virtual void GetProposedStorageTransferSyntaxes(std::list& target, + const std::string& remoteIp, + const std::string& remoteAet, + const std::string& calledAet) ORTHANC_OVERRIDE + { + context_.GetProposedStorageTransferSyntaxes(target); + } virtual bool IsUnknownSopClassAccepted(const std::string& remoteIp, const std::string& remoteAet,