changeset 3862:594263db316a transcoding

DicomModalityStoreJob now uses DicomStoreUserConnection
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 27 Apr 2020 17:28:55 +0200
parents eb8280b30031
children 9973d10bc5c4
files Core/DicomNetworking/DicomStoreUserConnection.cpp Core/DicomNetworking/DicomStoreUserConnection.h OrthancServer/ServerJobs/DicomModalityStoreJob.cpp OrthancServer/ServerJobs/DicomModalityStoreJob.h UnitTestsSources/FromDcmtkTests.cpp
diffstat 5 files changed, 122 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- 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<DcmFileFormat> dicom(
       FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size));
 
+    if (dicom.get() == NULL ||
+        dicom->getDataset() == NULL)
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+    
     Store(sopClassUid, sopInstanceUid, *dicom->getDataset(),
           moveOriginatorAET, moveOriginatorID);
   }
--- 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<std::string, std::set<DicomTransferSyntax> > StorageClasses;
     
     DicomAssociationParameters           parameters_;
-    boost::shared_ptr<DicomAssociation>  association_;
+    boost::shared_ptr<DicomAssociation>  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<DicomTransferSyntax>& 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
+    }
   };
 }
--- 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<std::string> a(sopClassUids_.begin(), sopClassUids_.end());
         std::vector<std::string> b(sopInstanceUids_.begin(), sopInstanceUids_.end());
-        connection_->RequestStorageCommitment(transactionUid_, a, b);
+
+        DicomAssociationParameters parameters(localAet_, remote_);
+        DicomAssociation::RequestStorageCommitment(parameters, transactionUid_, a, b);
       }
     }
 
--- 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 <list>
 
 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<DicomUserConnection>  connection_;
-    bool                                  storageCommitment_;
+    ServerContext&                             context_;
+    std::string                                localAet_;
+    RemoteModalityParameters                   remote_;
+    std::string                                moveOriginatorAet_;
+    uint16_t                                   moveOriginatorId_;
+    std::unique_ptr<DicomStoreUserConnection>  connection_;
+    bool                                       storageCommitment_;
 
     // For storage commitment
     std::string             transactionUid_;
--- 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