# HG changeset patch # User Sebastien Jodogne # Date 1588001335 -7200 # Node ID 594263db316af0f426f52876d0a8a9475521c1ab # Parent eb8280b30031353794f6694348776e700a18ca25 DicomModalityStoreJob now uses DicomStoreUserConnection diff -r eb8280b30031 -r 594263db316a Core/DicomNetworking/DicomStoreUserConnection.cpp --- a/Core/DicomNetworking/DicomStoreUserConnection.cpp Mon Apr 27 15:56:20 2020 +0200 +++ b/Core/DicomNetworking/DicomStoreUserConnection.cpp Mon Apr 27 17:28:55 2020 +0200 @@ -116,16 +116,31 @@ return false; } + + + void DicomStoreUserConnection::Setup() + { + association_.reset(new DicomAssociation); + proposeCommonClasses_ = true; + proposeUncompressedSyntaxes_ = true; + proposeRetiredBigEndian_ = false; + } DicomStoreUserConnection::DicomStoreUserConnection( + const std::string& localAet, + const RemoteModalityParameters& remote) : + parameters_(localAet, remote) + { + Setup(); + } + + + DicomStoreUserConnection::DicomStoreUserConnection( const DicomAssociationParameters& params) : - parameters_(params), - association_(new DicomAssociation), - proposeCommonClasses_(true), - proposeUncompressedSyntaxes_(true), - proposeRetiredBigEndian_(false) + parameters_(params) { + Setup(); } @@ -147,6 +162,32 @@ } + void DicomStoreUserConnection::LookupParameters(std::string& sopClassUid, + std::string& sopInstanceUid, + DicomTransferSyntax& transferSyntax, + DcmDataset& dataset) + { + OFString a, b; + if (!dataset.findAndGetOFString(DCM_SOPClassUID, a).good() || + !dataset.findAndGetOFString(DCM_SOPInstanceUID, b).good()) + { + throw OrthancException(ErrorCode_NoSopClassOrInstance, + "Unable to determine the SOP class/instance for C-STORE with AET " + + parameters_.GetRemoteApplicationEntityTitle()); + } + + sopClassUid.assign(a.c_str()); + sopInstanceUid.assign(b.c_str()); + + if (!FromDcmtkBridge::LookupOrthancTransferSyntax( + transferSyntax, dataset.getOriginalXfer())) + { + throw OrthancException(ErrorCode_InternalError, + "Unknown transfer syntax from DCMTK"); + } + } + + bool DicomStoreUserConnection::NegotiatePresentationContext( uint8_t& presentationContextId, const std::string& sopClassUid, @@ -163,8 +204,12 @@ } // The association must be re-negotiated - LOG(INFO) << "Re-negociating DICOM association with " - << parameters_.GetRemoteApplicationEntityTitle(); + if (association_->IsOpen()) + { + LOG(INFO) << "Re-negociating DICOM association with " + << parameters_.GetRemoteApplicationEntityTitle(); + } + association_->ClearPresentationContexts(); PrepareStorageClass(sopClassUid, transferSyntax); @@ -249,32 +294,18 @@ const std::string& moveOriginatorAET, uint16_t moveOriginatorID) { - OFString a, b; - if (!dataset.findAndGetOFString(DCM_SOPClassUID, a).good() || - !dataset.findAndGetOFString(DCM_SOPInstanceUID, b).good()) + DicomTransferSyntax transferSyntax; + LookupParameters(sopClassUid, sopInstanceUid, transferSyntax, dataset); + + uint8_t presID; + if (!NegotiatePresentationContext(presID, sopClassUid, transferSyntax)) { - throw OrthancException(ErrorCode_NoSopClassOrInstance, - "Unable to determine the SOP class/instance for C-STORE with AET " + - parameters_.GetRemoteApplicationEntityTitle()); - } - - sopClassUid.assign(a.c_str()); - sopInstanceUid.assign(b.c_str()); - - DicomTransferSyntax transferSyntax; - if (!FromDcmtkBridge::LookupOrthancTransferSyntax( - transferSyntax, dataset.getOriginalXfer())) - { - throw OrthancException(ErrorCode_InternalError, - "Unknown transfer syntax from DCMTK"); - } - - // Figure out which accepted presentation context should be used - uint8_t presID; - if (!NegotiatePresentationContext(presID, sopClassUid.c_str(), transferSyntax)) - { - throw OrthancException(ErrorCode_InternalError, - "No valid presentation context was negotiated upfront"); + throw OrthancException(ErrorCode_NetworkProtocol, + "No valid presentation context was negotiated for " + "SOP class UID [" + sopClassUid + "] and transfer " + "syntax [" + GetTransferSyntaxUid(transferSyntax) + "] " + "while sending to modality [" + + parameters_.GetRemoteApplicationEntityTitle() + "]"); } // Prepare the transmission of data @@ -338,6 +369,11 @@ const std::string& moveOriginatorAET, uint16_t moveOriginatorID) { + if (parsed.GetDcmtkObject().getDataset() == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + Store(sopClassUid, sopInstanceUid, *parsed.GetDcmtkObject().getDataset(), moveOriginatorAET, moveOriginatorID); } @@ -353,6 +389,12 @@ std::unique_ptr dicom( FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size)); + if (dicom.get() == NULL || + dicom->getDataset() == NULL) + { + throw OrthancException(ErrorCode_InternalError); + } + Store(sopClassUid, sopInstanceUid, *dicom->getDataset(), moveOriginatorAET, moveOriginatorID); } diff -r eb8280b30031 -r 594263db316a Core/DicomNetworking/DicomStoreUserConnection.h --- a/Core/DicomNetworking/DicomStoreUserConnection.h Mon Apr 27 15:56:20 2020 +0200 +++ b/Core/DicomNetworking/DicomStoreUserConnection.h Mon Apr 27 17:28:55 2020 +0200 @@ -72,17 +72,22 @@ typedef std::map > StorageClasses; DicomAssociationParameters parameters_; - boost::shared_ptr association_; + boost::shared_ptr association_; // "shared_ptr" is for PImpl StorageClasses storageClasses_; bool proposeCommonClasses_; bool proposeUncompressedSyntaxes_; bool proposeRetiredBigEndian_; + void Setup(); + // Return "false" if there is not enough room remaining in the association bool ProposeStorageClass(const std::string& sopClassUid, const std::set& syntaxes); public: + DicomStoreUserConnection(const std::string& localAet, + const RemoteModalityParameters& remote); + DicomStoreUserConnection(const DicomAssociationParameters& params); const DicomAssociationParameters& GetParameters() const @@ -134,6 +139,13 @@ const std::string& sopClassUid, DicomTransferSyntax transferSyntax); + // TODO => to private + void LookupParameters(std::string& sopClassUid, + std::string& sopInstanceUid, + DicomTransferSyntax& transferSyntax, + DcmDataset& dataset); + + private: void Store(std::string& sopClassUid, std::string& sopInstanceUid, DcmDataset& dataset, @@ -146,11 +158,20 @@ const std::string& moveOriginatorAET, uint16_t moveOriginatorID); + public: void Store(std::string& sopClassUid, std::string& sopInstanceUid, const void* buffer, size_t size, const std::string& moveOriginatorAET, uint16_t moveOriginatorID); + + void Store(std::string& sopClassUid, + std::string& sopInstanceUid, + const void* buffer, + size_t size) + { + Store(sopClassUid, sopInstanceUid, buffer, size, "", 0); // Not a C-Move + } }; } diff -r eb8280b30031 -r 594263db316a OrthancServer/ServerJobs/DicomModalityStoreJob.cpp --- a/OrthancServer/ServerJobs/DicomModalityStoreJob.cpp Mon Apr 27 15:56:20 2020 +0200 +++ b/OrthancServer/ServerJobs/DicomModalityStoreJob.cpp Mon Apr 27 17:28:55 2020 +0200 @@ -35,6 +35,7 @@ #include "DicomModalityStoreJob.h" #include "../../Core/Compatibility.h" +#include "../../Core/DicomNetworking/DicomAssociation.h" #include "../../Core/Logging.h" #include "../../Core/SerializationToolbox.h" #include "../ServerContext.h" @@ -47,7 +48,7 @@ { if (connection_.get() == NULL) { - connection_.reset(new DicomUserConnection(localAet_, remote_)); + connection_.reset(new DicomStoreUserConnection(localAet_, remote_)); } } @@ -74,13 +75,16 @@ std::string sopClassUid, sopInstanceUid; + const void* data = dicom.empty() ? NULL : dicom.c_str(); + if (HasMoveOriginator()) { - connection_->Store(sopClassUid, sopInstanceUid, dicom, moveOriginatorAet_, moveOriginatorId_); + connection_->Store(sopClassUid, sopInstanceUid, data, dicom.size(), + moveOriginatorAet_, moveOriginatorId_); } else { - connection_->Store(sopClassUid, sopInstanceUid, dicom); + connection_->Store(sopClassUid, sopInstanceUid, data, dicom.size()); } if (storageCommitment_) @@ -96,6 +100,9 @@ if (sopClassUids_.size() == GetInstancesCount()) { + assert(IsStarted()); + connection_.reset(NULL); + const std::string& remoteAet = remote_.GetApplicationEntityTitle(); LOG(INFO) << "Sending storage commitment request to modality: " << remoteAet; @@ -105,12 +112,11 @@ context_.GetStorageCommitmentReports().Store( transactionUid_, new StorageCommitmentReports::Report(remoteAet)); - assert(IsStarted()); - OpenConnection(); - std::vector a(sopClassUids_.begin(), sopClassUids_.end()); std::vector b(sopInstanceUids_.begin(), sopInstanceUids_.end()); - connection_->RequestStorageCommitment(transactionUid_, a, b); + + DicomAssociationParameters parameters(localAet_, remote_); + DicomAssociation::RequestStorageCommitment(parameters, transactionUid_, a, b); } } diff -r eb8280b30031 -r 594263db316a OrthancServer/ServerJobs/DicomModalityStoreJob.h --- a/OrthancServer/ServerJobs/DicomModalityStoreJob.h Mon Apr 27 15:56:20 2020 +0200 +++ b/OrthancServer/ServerJobs/DicomModalityStoreJob.h Mon Apr 27 17:28:55 2020 +0200 @@ -35,7 +35,9 @@ #include "../../Core/Compatibility.h" #include "../../Core/JobsEngine/SetOfInstancesJob.h" -#include "../../Core/DicomNetworking/DicomUserConnection.h" +#include "../../Core/DicomNetworking/DicomStoreUserConnection.h" + +#include namespace Orthanc { @@ -44,13 +46,13 @@ class DicomModalityStoreJob : public SetOfInstancesJob { private: - ServerContext& context_; - std::string localAet_; - RemoteModalityParameters remote_; - std::string moveOriginatorAet_; - uint16_t moveOriginatorId_; - std::unique_ptr connection_; - bool storageCommitment_; + ServerContext& context_; + std::string localAet_; + RemoteModalityParameters remote_; + std::string moveOriginatorAet_; + uint16_t moveOriginatorId_; + std::unique_ptr connection_; + bool storageCommitment_; // For storage commitment std::string transactionUid_; diff -r eb8280b30031 -r 594263db316a UnitTestsSources/FromDcmtkTests.cpp --- a/UnitTestsSources/FromDcmtkTests.cpp Mon Apr 27 15:56:20 2020 +0200 +++ b/UnitTestsSources/FromDcmtkTests.cpp Mon Apr 27 17:28:55 2020 +0200 @@ -2516,7 +2516,7 @@ { DicomAssociationParameters params; params.SetLocalApplicationEntityTitle("ORTHANC"); - params.SetRemoteApplicationEntityTitle("PACS"); + params.SetRemoteApplicationEntityTitle("STORESCP"); params.SetRemotePort(2000); DicomStoreUserConnection assoc(params); @@ -2524,10 +2524,10 @@ assoc.PrepareStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess2_4); //assoc.PrepareStorageClass(UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); - //assoc.SetUncompressedSyntaxesProposed(false); + //assoc.SetUncompressedSyntaxesProposed(false); // Necessary for transcoding //assoc.SetCommonClassesProposed(false); TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000); - //TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); + TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); } #endif