changeset 3737:f29843323daf storage-commitment

accessing storage commitment reports from REST API
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 10 Mar 2020 20:33:01 +0100
parents 0540b54324f1
children bff4da769f6f
files Core/Enumerations.cpp Core/Enumerations.h OrthancServer/OrthancRestApi/OrthancRestModalities.cpp OrthancServer/ServerJobs/DicomModalityStoreJob.cpp OrthancServer/StorageCommitmentReports.cpp OrthancServer/StorageCommitmentReports.h
diffstat 6 files changed, 160 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/Core/Enumerations.cpp	Tue Mar 10 17:43:49 2020 +0100
+++ b/Core/Enumerations.cpp	Tue Mar 10 20:33:01 2020 +0100
@@ -1168,6 +1168,41 @@
   }
 
 
+  const char* EnumerationToString(StorageCommitmentFailureReason reason)
+  {
+    switch (reason)
+    {
+      case StorageCommitmentFailureReason_Success:
+        return "Success";
+
+      case StorageCommitmentFailureReason_ProcessingFailure:
+        return "A general failure in processing the operation was encountered";
+
+      case StorageCommitmentFailureReason_NoSuchObjectInstance:
+        return "One or more of the elements in the Referenced SOP "
+          "Instance Sequence was not available";
+        
+      case StorageCommitmentFailureReason_ResourceLimitation:
+        return "The SCP does not currently have enough resources to "
+          "store the requested SOP Instance(s)";
+
+      case StorageCommitmentFailureReason_ReferencedSOPClassNotSupported:
+        return "Storage Commitment has been requested for a SOP Instance "
+          "with a SOP Class that is not supported by the SCP";
+
+      case StorageCommitmentFailureReason_ClassInstanceConflict:
+        return "The SOP Class of an element in the Referenced SOP Instance Sequence "
+          "did not correspond to the SOP class registered for this SOP Instance at the SCP";
+
+      case StorageCommitmentFailureReason_DuplicateTransactionUID:
+        return "The Transaction UID of the Storage Commitment Request is already in use";
+
+      default:
+        return "Unknown failure reason";
+    }
+  }
+
+
   Encoding StringToEncoding(const char* encoding)
   {
     std::string s(encoding);
--- a/Core/Enumerations.h	Tue Mar 10 17:43:49 2020 +0100
+++ b/Core/Enumerations.h	Tue Mar 10 20:33:01 2020 +0100
@@ -742,7 +742,7 @@
     StorageCommitmentFailureReason_ClassInstanceConflict = 0x0119,
 
     // The Transaction UID of the Storage Commitment Request is already in use
-    StorageCommitmentFailureReason_DuplicateTransactionUID = 0x0131    
+    StorageCommitmentFailureReason_DuplicateTransactionUID = 0x0131
   };
 
 
@@ -831,6 +831,8 @@
 
   const char* EnumerationToString(Endianness endianness);
 
+  const char* EnumerationToString(StorageCommitmentFailureReason reason);
+
   Encoding StringToEncoding(const char* encoding);
 
   ResourceType StringToResourceType(const char* type);
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Tue Mar 10 17:43:49 2020 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Tue Mar 10 20:33:01 2020 +0100
@@ -46,6 +46,7 @@
 #include "../ServerJobs/DicomMoveScuJob.h"
 #include "../ServerJobs/OrthancPeerStoreJob.h"
 #include "../ServerToolbox.h"
+#include "../StorageCommitmentReports.h"
 
 
 namespace Orthanc
@@ -1305,7 +1306,9 @@
   }
 
 
-  static void StorageCommitment(RestApiPostCall& call)
+  // Storage commitment SCU ---------------------------------------------------
+
+  static void StorageCommitmentScu(RestApiPostCall& call)
   {
     ServerContext& context = OrthancRestApi::GetContext(call);
 
@@ -1364,20 +1367,27 @@
         }
       }
 
-      const std::string transaction = Toolbox::GenerateDicomPrivateUniqueIdentifier();
+      const std::string transactionUid = Toolbox::GenerateDicomPrivateUniqueIdentifier();
 
       {
-        const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
         const RemoteModalityParameters remote =
           MyGetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
 
+        const std::string& remoteAet = remote.GetApplicationEntityTitle();
+        const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
+        
+        // Create a "pending" storage commitment report BEFORE the
+        // actual SCU call in order to avoid race conditions
+        context.GetStorageCommitmentReports().Store(
+          transactionUid, new StorageCommitmentReports::Report(remoteAet));
+
         DicomUserConnection scu(localAet, remote);
-        scu.RequestStorageCommitment(transaction, sopClassUids, sopInstanceUids);
+        scu.RequestStorageCommitment(transactionUid, sopClassUids, sopInstanceUids);
       }
 
       Json::Value result = Json::objectValue;
-      result["ID"] = transaction;
-      result["Path"] = "/storage-commitment/" + transaction;
+      result["ID"] = transactionUid;
+      result["Path"] = "/storage-commitment/" + transactionUid;
       call.GetOutput().AnswerJson(result);
     }
     else
@@ -1388,6 +1398,31 @@
   }
 
 
+  static void GetStorageCommitmentReport(RestApiGetCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    const std::string& transactionUid = call.GetUriComponent("id", "");
+
+    {
+      StorageCommitmentReports::Accessor accessor(
+        context.GetStorageCommitmentReports(), transactionUid);
+
+      if (accessor.IsValid())
+      {
+        Json::Value json;
+        accessor.GetReport().Format(json);
+        call.GetOutput().AnswerJson(json);
+      }
+      else
+      {
+        throw OrthancException(ErrorCode_InexistentItem,
+                               "No storage commitment transaction with UID: " + transactionUid);
+      }
+    }
+  }
+  
+
   void OrthancRestApi::RegisterModalities()
   {
     Register("/modalities", ListModalities);
@@ -1432,6 +1467,7 @@
 
     Register("/modalities/{id}/find-worklist", DicomFindWorklist);
 
-    Register("/modalities/{id}/storage-commitment", StorageCommitment);
+    Register("/modalities/{id}/storage-commitment", StorageCommitmentScu);
+    Register("/storage-commitment/{id}", GetStorageCommitmentReport);
   }
 }
--- a/OrthancServer/ServerJobs/DicomModalityStoreJob.cpp	Tue Mar 10 17:43:49 2020 +0100
+++ b/OrthancServer/ServerJobs/DicomModalityStoreJob.cpp	Tue Mar 10 20:33:01 2020 +0100
@@ -38,6 +38,7 @@
 #include "../../Core/Logging.h"
 #include "../../Core/SerializationToolbox.h"
 #include "../ServerContext.h"
+#include "../StorageCommitmentReports.h"
 
 
 namespace Orthanc
@@ -96,9 +97,15 @@
       }
       
       if (sopClassUids_.size() == GetInstancesCount())
-      {      
-        LOG(INFO) << "Sending storage commitment request to modality: "
-                  << remote_.GetApplicationEntityTitle();
+      {
+        const std::string& remoteAet = remote_.GetApplicationEntityTitle();
+        
+        LOG(INFO) << "Sending storage commitment request to modality: " << remoteAet;
+
+        // Create a "pending" storage commitment report BEFORE the
+        // actual SCU call in order to avoid race conditions
+        context_.GetStorageCommitmentReports().Store(
+          transactionUid_, new StorageCommitmentReports::Report(remoteAet));
         
         assert(IsStarted());
         OpenConnection();
--- a/OrthancServer/StorageCommitmentReports.cpp	Tue Mar 10 17:43:49 2020 +0100
+++ b/OrthancServer/StorageCommitmentReports.cpp	Tue Mar 10 20:33:01 2020 +0100
@@ -62,7 +62,7 @@
       Success success;
       success.sopClassUid_ = sopClassUid;
       success.sopInstanceUid_ = sopInstanceUid;
-      successes_.push_back(success);
+      success_.push_back(success);
     }
   }
 
@@ -102,6 +102,71 @@
   }
 
 
+  void StorageCommitmentReports::Report::Format(Json::Value& json) const
+  {
+    static const char* const FIELD_STATUS = "Status";
+    static const char* const FIELD_SOP_CLASS_UID = "SOPClassUID";
+    static const char* const FIELD_SOP_INSTANCE_UID = "SOPInstanceUID";
+    static const char* const FIELD_FAILURE_REASON = "FailureReason";
+    static const char* const FIELD_DESCRIPTION = "Description";
+    static const char* const FIELD_REMOTE_AET = "RemoteAET";
+    static const char* const FIELD_SUCCESS = "Success";
+    static const char* const FIELD_FAILURES = "Failures";
+
+    
+    json = Json::objectValue;
+    json[FIELD_REMOTE_AET] = remoteAet_;
+    
+    switch (GetStatus())
+    {
+      case Status_Pending:
+        json[FIELD_STATUS] = "Pending";
+        break;
+
+      case Status_Success:
+        json[FIELD_STATUS] = "Success";
+        break;
+
+      case Status_Failure:
+        json[FIELD_STATUS] = "Failure";
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+
+    {
+      Json::Value success = Json::arrayValue;
+      for (std::list<Success>::const_iterator
+             it = success_.begin(); it != success_.end(); ++it)
+      {
+        Json::Value item = Json::objectValue;
+        item[FIELD_SOP_CLASS_UID] = it->sopClassUid_;
+        item[FIELD_SOP_INSTANCE_UID] = it->sopInstanceUid_;
+        success.append(item);
+      }
+
+      json[FIELD_SUCCESS] = success;
+    }
+
+    {
+      Json::Value failures = Json::arrayValue;
+      for (std::list<Failure>::const_iterator
+             it = failures_.begin(); it != failures_.end(); ++it)
+      {
+        Json::Value item = Json::objectValue;
+        item[FIELD_SOP_CLASS_UID] = it->sopClassUid_;
+        item[FIELD_SOP_INSTANCE_UID] = it->sopInstanceUid_;
+        item[FIELD_FAILURE_REASON] = it->reason_;
+        item[FIELD_DESCRIPTION] = EnumerationToString(it->reason_);
+        failures.append(item);
+      }
+
+      json[FIELD_FAILURES] = failures;
+    }
+  }
+
+
   StorageCommitmentReports::~StorageCommitmentReports()
   {
     while (!content_.IsEmpty())
--- a/OrthancServer/StorageCommitmentReports.h	Tue Mar 10 17:43:49 2020 +0100
+++ b/OrthancServer/StorageCommitmentReports.h	Tue Mar 10 20:33:01 2020 +0100
@@ -65,7 +65,7 @@
       };
       
       bool                isComplete_;
-      std::list<Success>  successes_;
+      std::list<Success>  success_;
       std::list<Failure>  failures_;
       std::string         remoteAet_;
 
@@ -91,6 +91,8 @@
                       StorageCommitmentFailureReason reason);
 
       Status GetStatus() const;
+
+      void Format(Json::Value& json) const;
     };
 
   private: