# HG changeset patch # User Sebastien Jodogne # Date 1588623055 -7200 # Node ID cdd0cb5ec4e49a538fd2a9850267df0233db4f10 # Parent a18b34dec94ab34ef7ec42e1aacd464e56dea1a3 DicomStoreUserConnection::LookupTranscoding() diff -r a18b34dec94a -r cdd0cb5ec4e4 Core/DicomNetworking/DicomStoreUserConnection.cpp --- a/Core/DicomNetworking/DicomStoreUserConnection.cpp Mon May 04 19:17:07 2020 +0200 +++ b/Core/DicomNetworking/DicomStoreUserConnection.cpp Mon May 04 22:10:55 2020 +0200 @@ -84,13 +84,13 @@ it = syntaxes.begin(); it != syntaxes.end(); ++it) { association_->ProposePresentationContext(sopClassUid, *it); + proposedOriginalClasses_.insert(std::make_pair(sopClassUid, *it)); } if (addLittleEndianImplicit) { - std::set uncompressed; - uncompressed.insert(DicomTransferSyntax_LittleEndianImplicit); - association_->ProposePresentationContext(sopClassUid, uncompressed); + association_->ProposePresentationContext(sopClassUid, DicomTransferSyntax_LittleEndianImplicit); + proposedOriginalClasses_.insert(std::make_pair(sopClassUid, DicomTransferSyntax_LittleEndianImplicit)); } if (addLittleEndianExplicit || @@ -109,6 +109,14 @@ } association_->ProposePresentationContext(sopClassUid, uncompressed); + + assert(!uncompressed.empty()); + if (addLittleEndianExplicit ^ addBigEndianExplicit) + { + // Only one transfer syntax was proposed for this presentation context + assert(uncompressed.size() == 1); + proposedOriginalClasses_.insert(std::make_pair(sopClassUid, *uncompressed.begin())); + } } return true; @@ -201,7 +209,7 @@ { /** * Step 1: Check whether this presentation context is already - * available in the previously negociated assocation. + * available in the previously negotiated assocation. **/ if (LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax)) @@ -212,11 +220,21 @@ // The association must be re-negotiated if (association_->IsOpen()) { - LOG(INFO) << "Re-negociating DICOM association with " + LOG(INFO) << "Re-negotiating DICOM association with " << parameters_.GetRemoteModality().GetApplicationEntityTitle(); + + if (proposedOriginalClasses_.find(std::make_pair(sopClassUid, transferSyntax)) != + proposedOriginalClasses_.end()) + { + LOG(INFO) << "The remote modality has already rejected SOP class UID \"" + << sopClassUid << "\" with transfer syntax \"" + << GetTransferSyntaxUid(transferSyntax) << "\", don't renegotiate"; + return false; + } } - + association_->ClearPresentationContexts(); + proposedOriginalClasses_.clear(); RegisterStorageClass(sopClassUid, transferSyntax); // (*) @@ -304,7 +322,7 @@ { DicomTransferSyntax transferSyntax; LookupParameters(sopClassUid, sopInstanceUid, transferSyntax, dataset); - + uint8_t presID; if (!NegotiatePresentationContext(presID, sopClassUid, transferSyntax)) { @@ -390,4 +408,34 @@ StoreInternal(sopClassUid, sopInstanceUid, *dicom->getDataset(), moveOriginatorAET, moveOriginatorID); } + + + bool DicomStoreUserConnection::LookupTranscoding(std::set& acceptedSyntaxes, + const std::string& sopClassUid, + DicomTransferSyntax sourceSyntax) + { + acceptedSyntaxes.clear(); + + // Make sure a negotiation has already occurred for this transfer + // syntax. We don't use the return code: Transcoding is possible + // even if the "sourceSyntax" is not supported. + uint8_t presID; + NegotiatePresentationContext(presID, sopClassUid, sourceSyntax); + + std::map contexts; + if (association_->LookupAcceptedPresentationContext(contexts, sopClassUid)) + { + for (std::map::const_iterator + it = contexts.begin(); it != contexts.end(); ++it) + { + acceptedSyntaxes.insert(it->first); + } + + return true; + } + else + { + return false; + } + } } diff -r a18b34dec94a -r cdd0cb5ec4e4 Core/DicomNetworking/DicomStoreUserConnection.h --- a/Core/DicomNetworking/DicomStoreUserConnection.h Mon May 04 19:17:07 2020 +0200 +++ b/Core/DicomNetworking/DicomStoreUserConnection.h Mon May 04 22:10:55 2020 +0200 @@ -69,10 +69,15 @@ { private: typedef std::map > RegisteredClasses; + + // "ProposedOriginalClasses" keeps track of the storage classes + // that were proposed with a single transfer syntax + typedef std::set< std::pair > ProposedOriginalClasses; DicomAssociationParameters parameters_; boost::shared_ptr association_; // "shared_ptr" is for PImpl RegisteredClasses registeredClasses_; + ProposedOriginalClasses proposedOriginalClasses_; bool proposeCommonClasses_; bool proposeUncompressedSyntaxes_; bool proposeRetiredBigEndian_; @@ -86,6 +91,14 @@ DicomTransferSyntax& transferSyntax, DcmDataset& dataset); + bool LookupPresentationContext(uint8_t& presentationContextId, + const std::string& sopClassUid, + DicomTransferSyntax transferSyntax); + + bool NegotiatePresentationContext(uint8_t& presentationContextId, + const std::string& sopClassUid, + DicomTransferSyntax transferSyntax); + void StoreInternal(std::string& sopClassUid, std::string& sopInstanceUid, DcmDataset& dataset, @@ -133,17 +146,6 @@ void RegisterStorageClass(const std::string& sopClassUid, DicomTransferSyntax syntax); - // Should only be used if transcoding - // TODO => to private - bool LookupPresentationContext(uint8_t& presentationContextId, - const std::string& sopClassUid, - DicomTransferSyntax transferSyntax); - - // TODO => to private - bool NegotiatePresentationContext(uint8_t& presentationContextId, - const std::string& sopClassUid, - DicomTransferSyntax transferSyntax); - void Store(std::string& sopClassUid, std::string& sopInstanceUid, const void* buffer, @@ -158,5 +160,9 @@ { Store(sopClassUid, sopInstanceUid, buffer, size, "", 0); // Not a C-Move } + + bool LookupTranscoding(std::set& acceptedSyntaxes, + const std::string& sopClassUid, + DicomTransferSyntax sourceSyntax); }; } diff -r a18b34dec94a -r cdd0cb5ec4e4 UnitTestsSources/FromDcmtkTests.cpp --- a/UnitTestsSources/FromDcmtkTests.cpp Mon May 04 19:17:07 2020 +0200 +++ b/UnitTestsSources/FromDcmtkTests.cpp Mon May 04 22:10:55 2020 +0200 @@ -2875,11 +2875,27 @@ const std::string& sopClassUid, DicomTransferSyntax transferSyntax) { - uint8_t id; - - if (scu.NegotiatePresentationContext(id, sopClassUid, transferSyntax)) + std::set accepted; + + if (!scu.LookupTranscoding(accepted, sopClassUid, transferSyntax)) + { + throw OrthancException(ErrorCode_NetworkProtocol, + "The SOP class is not supported by the remote modality"); + } + { - printf("**** OK, without transcoding !! %d\n", id); + unsigned int count = 0; + for (std::set::const_iterator + it = accepted.begin(); it != accepted.end(); ++it) + { + LOG(INFO) << "available for transcoding " << (count++) << ": " << sopClassUid + << " / " << GetTransferSyntaxUid(*it); + } + } + + if (accepted.find(transferSyntax) != accepted.end()) + { + printf("**** OK, without transcoding !! [%s]\n", GetTransferSyntaxUid(transferSyntax)); } else { @@ -2894,10 +2910,9 @@ bool found = false; for (size_t i = 0; i < 3; i++) { - if (scu.LookupPresentationContext(id, sopClassUid, uncompressed[i])) + if (accepted.find(uncompressed[i]) != accepted.end()) { - printf("**** TRANSCODING to %s => %d\n", - GetTransferSyntaxUid(uncompressed[i]), id); + printf("**** TRANSCODING to %s\n", GetTransferSyntaxUid(uncompressed[i])); found = true; break; } @@ -2924,9 +2939,11 @@ //assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); //assoc.SetUncompressedSyntaxesProposed(false); // Necessary for transcoding - //assoc.SetCommonClassesProposed(false); + assoc.SetCommonClassesProposed(false); + assoc.SetRetiredBigEndianProposed(true); + TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000); - TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); + TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000); }