changeset 3662:d8371b4302ff storage-commitment

OrthancPluginRegisterStorageCommitmentScpCallback()
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 11 Feb 2020 19:59:53 +0100
parents 25117919a36b
children 85acfcc15829
files NEWS OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h OrthancServer/ServerJobs/IStorageCommitmentFactory.h OrthancServer/ServerJobs/StorageCommitmentScpJob.cpp Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Plugins/Engine/PluginsEnumerations.cpp Plugins/Engine/PluginsEnumerations.h Plugins/Include/orthanc/OrthancCPlugin.h
diffstat 10 files changed, 297 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Mon Feb 10 17:54:40 2020 +0100
+++ b/NEWS	Tue Feb 11 19:59:53 2020 +0100
@@ -19,6 +19,7 @@
 -------
 
 * New sample plugin: "ConnectivityChecks"
+* New primitives to handle storage commitment SCP by plugins
 
 Maintenance
 -----------
--- a/OrthancServer/ServerContext.cpp	Mon Feb 10 17:54:40 2020 +0100
+++ b/OrthancServer/ServerContext.cpp	Tue Feb 11 19:59:53 2020 +0100
@@ -1058,13 +1058,15 @@
   ServerContext::CreateStorageCommitment(const std::string& jobId,
                                          const std::string& transactionUid,
                                          const std::vector<std::string>& sopClassUids,
-                                         const std::vector<std::string>& sopInstanceUids)
+                                         const std::vector<std::string>& sopInstanceUids,
+                                         const std::string& remoteAet,
+                                         const std::string& calledAet)
   {
 #if ORTHANC_ENABLE_PLUGINS == 1
     if (HasPlugins())
     {
-      // TODO
-      return NULL;
+      return GetPlugins().CreateStorageCommitment(
+        jobId, transactionUid, sopClassUids, sopInstanceUids, remoteAet, calledAet);
     }
 #endif
 
--- a/OrthancServer/ServerContext.h	Mon Feb 10 17:54:40 2020 +0100
+++ b/OrthancServer/ServerContext.h	Tue Feb 11 19:59:53 2020 +0100
@@ -432,6 +432,8 @@
     CreateStorageCommitment(const std::string& jobId,
                             const std::string& transactionUid,
                             const std::vector<std::string>& sopClassUids,
-                            const std::vector<std::string>& sopInstanceUids) ORTHANC_OVERRIDE;
+                            const std::vector<std::string>& sopInstanceUids,
+                            const std::string& remoteAet,
+                            const std::string& calledAet) ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancServer/ServerJobs/IStorageCommitmentFactory.h	Mon Feb 10 17:54:40 2020 +0100
+++ b/OrthancServer/ServerJobs/IStorageCommitmentFactory.h	Tue Feb 11 19:59:53 2020 +0100
@@ -59,6 +59,8 @@
     virtual ILookupHandler* CreateStorageCommitment(const std::string& jobId,
                                                     const std::string& transactionUid,
                                                     const std::vector<std::string>& sopClassUids,
-                                                    const std::vector<std::string>& sopInstanceUids) = 0;
+                                                    const std::vector<std::string>& sopInstanceUids,
+                                                    const std::string& remoteAet,
+                                                    const std::string& calledAet) = 0;
   };
 }
--- a/OrthancServer/ServerJobs/StorageCommitmentScpJob.cpp	Mon Feb 10 17:54:40 2020 +0100
+++ b/OrthancServer/ServerJobs/StorageCommitmentScpJob.cpp	Tue Feb 11 19:59:53 2020 +0100
@@ -265,7 +265,10 @@
   void StorageCommitmentScpJob::Setup(const std::string& jobId)
   {
     CheckInvariants();
-    lookupHandler_.reset(context_.CreateStorageCommitment(jobId, transactionUid_, sopClassUids_, sopInstanceUids_));
+
+    const std::string& remoteAet = remoteModality_.GetApplicationEntityTitle();
+    lookupHandler_.reset(context_.CreateStorageCommitment(jobId, transactionUid_, sopClassUids_,
+                                                          sopInstanceUids_, remoteAet, calledAet_));
   }
 
 
--- a/Plugins/Engine/OrthancPlugins.cpp	Mon Feb 10 17:54:40 2020 +0100
+++ b/Plugins/Engine/OrthancPlugins.cpp	Tue Feb 11 19:59:53 2020 +0100
@@ -682,6 +682,102 @@
     };
 
 
+
+    class StorageCommitmentScp : public IStorageCommitmentFactory
+    {
+    private:
+      class Handler : public IStorageCommitmentFactory::ILookupHandler
+      {
+      private:
+        _OrthancPluginRegisterStorageCommitmentScpCallback  parameters_;
+        void*    handler_;
+
+      public:
+        Handler(_OrthancPluginRegisterStorageCommitmentScpCallback  parameters,
+                void* handler) :
+          parameters_(parameters)
+        {
+          if (handler == NULL)
+          {
+            throw OrthancException(ErrorCode_NullPointer);
+          }
+        }
+
+        virtual ~Handler()
+        {
+          assert(handler_ != NULL);
+          parameters_.destructor(handler_);
+          handler_ = NULL;
+        }
+
+        virtual StorageCommitmentFailureReason Lookup(const std::string& sopClassUid,
+                                                      const std::string& sopInstanceUid)
+        {
+          assert(handler_ != NULL);
+          OrthancPluginStorageCommitmentFailureReason reason =
+            OrthancPluginStorageCommitmentFailureReason_Success;
+          OrthancPluginErrorCode error = parameters_.lookup(
+            &reason, handler_, sopClassUid.c_str(), sopInstanceUid.c_str());
+          if (error == OrthancPluginErrorCode_Success)
+          {
+            return Plugins::Convert(reason);
+          }
+          else
+          {
+            throw OrthancException(static_cast<ErrorCode>(error));
+          }
+        }
+      };
+      
+      _OrthancPluginRegisterStorageCommitmentScpCallback  parameters_;
+
+    public:
+      StorageCommitmentScp(_OrthancPluginRegisterStorageCommitmentScpCallback parameters) :
+        parameters_(parameters)
+      {
+      }
+
+      virtual ILookupHandler* CreateStorageCommitment(
+        const std::string& jobId,
+        const std::string& transactionUid,
+        const std::vector<std::string>& sopClassUids,
+        const std::vector<std::string>& sopInstanceUids,
+        const std::string& remoteAet,
+        const std::string& calledAet) ORTHANC_OVERRIDE
+      {
+        const size_t n = sopClassUids.size();
+        
+        if (sopInstanceUids.size() != n)
+        {
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
+        
+        std::vector<const char*> a, b;
+        a.resize(n);
+        b.resize(n);
+
+        for (size_t i = 0; i < n; i++)
+        {
+          a[i] = sopClassUids[i].c_str();
+          b[i] = sopInstanceUids[i].c_str();
+        }
+
+        void* handler = parameters_.factory(jobId.c_str(), transactionUid.c_str(),
+                                            a.empty() ? NULL : &a[0], b.empty() ? NULL : &b[0],
+                                            static_cast<uint32_t>(n),
+                                            remoteAet.c_str(), calledAet.c_str());
+        if (handler == NULL)
+        {
+          return NULL;
+        }
+        else
+        {
+          return new Handler(parameters_, handler);
+        }
+      }
+    };
+
+
     class ServerContextLock
     {
     private:
@@ -724,6 +820,7 @@
     typedef std::list<OrthancPluginDecodeImageCallback>  DecodeImageCallbacks;
     typedef std::list<OrthancPluginJobsUnserializer>  JobsUnserializers;
     typedef std::list<OrthancPluginRefreshMetricsCallback>  RefreshMetricsCallbacks;
+    typedef std::list<StorageCommitmentScp*>  StorageCommitmentScpCallbacks;
     typedef std::map<Property, std::string>  Properties;
 
     PluginsManager manager_;
@@ -740,6 +837,7 @@
     IncomingHttpRequestFilters  incomingHttpRequestFilters_;
     IncomingHttpRequestFilters2 incomingHttpRequestFilters2_;
     RefreshMetricsCallbacks refreshMetricsCallbacks_;
+    StorageCommitmentScpCallbacks storageCommitmentScpCallbacks_;
     std::auto_ptr<StorageAreaFactory>  storageArea_;
 
     boost::recursive_mutex restCallbackMutex_;
@@ -750,6 +848,7 @@
     boost::mutex decodeImageCallbackMutex_;
     boost::mutex jobsUnserializersMutex_;
     boost::mutex refreshMetricsMutex_;
+    boost::mutex storageCommitmentScpMutex_;
     boost::recursive_mutex invokeServiceMutex_;
 
     Properties properties_;
@@ -1260,6 +1359,7 @@
         sizeof(int32_t) != sizeof(OrthancPluginConstraintType) ||
         sizeof(int32_t) != sizeof(OrthancPluginMetricsType) ||
         sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode) ||
+        sizeof(int32_t) != sizeof(OrthancPluginStorageCommitmentFailureReason) ||
         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeBinary) != static_cast<int>(DicomToJsonFlags_IncludeBinary) ||
         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludePrivateTags) != static_cast<int>(DicomToJsonFlags_IncludePrivateTags) ||
         static_cast<int>(OrthancPluginDicomToJsonFlags_IncludeUnknownTags) != static_cast<int>(DicomToJsonFlags_IncludeUnknownTags) ||
@@ -1303,6 +1403,13 @@
     {
       delete *it;
     }
+
+    for (PImpl::StorageCommitmentScpCallbacks::iterator
+           it = pimpl_->storageCommitmentScpCallbacks_.begin(); 
+         it != pimpl_->storageCommitmentScpCallbacks_.end(); ++it)
+    {
+      delete *it;
+    } 
   }
 
 
@@ -1863,6 +1970,18 @@
   }
 
 
+  void OrthancPlugins::RegisterStorageCommitmentScpCallback(const void* parameters)
+  {
+    const _OrthancPluginRegisterStorageCommitmentScpCallback& p = 
+      *reinterpret_cast<const _OrthancPluginRegisterStorageCommitmentScpCallback*>(parameters);
+
+    boost::mutex::scoped_lock lock(pimpl_->storageCommitmentScpMutex_);
+    LOG(INFO) << "Plugin has registered a storage commitment callback";
+
+    pimpl_->storageCommitmentScpCallbacks_.push_back(new PImpl::StorageCommitmentScp(p));
+  }
+
+
   void OrthancPlugins::AnswerBuffer(const void* parameters)
   {
     const _OrthancPluginAnswerBuffer& p = 
@@ -3882,6 +4001,10 @@
         RegisterRefreshMetricsCallback(parameters);
         return true;
 
+      case _OrthancPluginService_RegisterStorageCommitmentScpCallback:
+        RegisterStorageCommitmentScpCallback(parameters);
+        return true;
+
       case _OrthancPluginService_RegisterStorageArea:
       {
         LOG(INFO) << "Plugin has registered a custom storage area";
@@ -4539,4 +4662,32 @@
       }
     }
   }
+
+
+  IStorageCommitmentFactory::ILookupHandler* OrthancPlugins::CreateStorageCommitment(
+    const std::string& jobId,
+    const std::string& transactionUid,
+    const std::vector<std::string>& sopClassUids,
+    const std::vector<std::string>& sopInstanceUids,
+    const std::string& remoteAet,
+    const std::string& calledAet)
+  {
+    boost::mutex::scoped_lock lock(pimpl_->storageCommitmentScpMutex_);
+
+    for (PImpl::StorageCommitmentScpCallbacks::iterator
+           it = pimpl_->storageCommitmentScpCallbacks_.begin(); 
+         it != pimpl_->storageCommitmentScpCallbacks_.end(); ++it)
+    {
+      assert(*it != NULL);
+      IStorageCommitmentFactory::ILookupHandler* handler = (*it)->CreateStorageCommitment
+        (jobId, transactionUid, sopClassUids, sopInstanceUids, remoteAet, calledAet);
+
+      if (handler != NULL)
+      {
+        return handler;
+      }
+    } 
+    
+    return NULL;
+  }
 }
--- a/Plugins/Engine/OrthancPlugins.h	Mon Feb 10 17:54:40 2020 +0100
+++ b/Plugins/Engine/OrthancPlugins.h	Tue Feb 11 19:59:53 2020 +0100
@@ -62,6 +62,7 @@
 #include "../../Core/JobsEngine/IJob.h"
 #include "../../OrthancServer/IDicomImageDecoder.h"
 #include "../../OrthancServer/IServerListener.h"
+#include "../../OrthancServer/ServerJobs/IStorageCommitmentFactory.h"
 #include "OrthancPluginDatabase.h"
 #include "PluginsManager.h"
 
@@ -80,7 +81,8 @@
     public IDicomImageDecoder,
     public IIncomingHttpRequestFilter,
     public IFindRequestHandlerFactory,
-    public IMoveRequestHandlerFactory
+    public IMoveRequestHandlerFactory,
+    public IStorageCommitmentFactory
   {
   private:
     class PImpl;
@@ -124,6 +126,8 @@
 
     void RegisterRefreshMetricsCallback(const void* parameters);
 
+    void RegisterStorageCommitmentScpCallback(const void* parameters);
+
     void AnswerBuffer(const void* parameters);
 
     void Redirect(const void* parameters);
@@ -341,6 +345,15 @@
                                             HttpMethod method,
                                             const UriComponents& uri,
                                             const Arguments& headers);
+
+    // New in Orthanc 1.6.0
+    IStorageCommitmentFactory::ILookupHandler* CreateStorageCommitment(
+      const std::string& jobId,
+      const std::string& transactionUid,
+      const std::vector<std::string>& sopClassUids,
+      const std::vector<std::string>& sopInstanceUids,
+      const std::string& remoteAet,
+      const std::string& calledAet) ORTHANC_OVERRIDE;
   };
 }
 
--- a/Plugins/Engine/PluginsEnumerations.cpp	Mon Feb 10 17:54:40 2020 +0100
+++ b/Plugins/Engine/PluginsEnumerations.cpp	Tue Feb 11 19:59:53 2020 +0100
@@ -549,5 +549,36 @@
           throw OrthancException(ErrorCode_ParameterOutOfRange);
       }
     }
+
+
+    StorageCommitmentFailureReason Convert(OrthancPluginStorageCommitmentFailureReason reason)
+    {
+      switch (reason)
+      {
+        case OrthancPluginStorageCommitmentFailureReason_Success:
+          return StorageCommitmentFailureReason_Success;
+          
+        case OrthancPluginStorageCommitmentFailureReason_ProcessingFailure:
+          return StorageCommitmentFailureReason_ProcessingFailure;
+
+        case OrthancPluginStorageCommitmentFailureReason_NoSuchObjectInstance:
+          return StorageCommitmentFailureReason_NoSuchObjectInstance;
+
+        case OrthancPluginStorageCommitmentFailureReason_ResourceLimitation:
+          return StorageCommitmentFailureReason_ResourceLimitation;
+
+        case OrthancPluginStorageCommitmentFailureReason_ReferencedSOPClassNotSupported:
+          return StorageCommitmentFailureReason_ReferencedSOPClassNotSupported;
+
+        case OrthancPluginStorageCommitmentFailureReason_ClassInstanceConflict:
+          return StorageCommitmentFailureReason_ClassInstanceConflict;
+
+        case OrthancPluginStorageCommitmentFailureReason_DuplicateTransactionUID:
+          return StorageCommitmentFailureReason_DuplicateTransactionUID;
+             
+        default:
+          throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
   }
 }
--- a/Plugins/Engine/PluginsEnumerations.h	Mon Feb 10 17:54:40 2020 +0100
+++ b/Plugins/Engine/PluginsEnumerations.h	Tue Feb 11 19:59:53 2020 +0100
@@ -79,6 +79,8 @@
     OrthancPluginJobStepStatus Convert(JobStepCode step);
 
     JobStepCode Convert(OrthancPluginJobStepStatus step);
+
+    StorageCommitmentFailureReason Convert(OrthancPluginStorageCommitmentFailureReason reason);
   }
 }
 
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Mon Feb 10 17:54:40 2020 +0100
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Tue Feb 11 19:59:53 2020 +0100
@@ -26,6 +26,7 @@
  *    - Possibly register a callback to unserialize jobs using OrthancPluginRegisterJobsUnserializer().
  *    - Possibly register a callback to refresh its metrics using OrthancPluginRegisterRefreshMetricsCallback().
  *    - Possibly register a callback to answer chunked HTTP transfers using ::OrthancPluginRegisterChunkedRestCallback().
+ *    - Possibly register a callback for Storage Commitment SCP using ::OrthancPluginRegisterStorageCommitmentScpCallback().
  * -# <tt>void OrthancPluginFinalize()</tt>:
  *    This function is invoked by Orthanc during its shutdown. The plugin
  *    must free all its memory.
@@ -451,6 +452,7 @@
     _OrthancPluginService_RegisterIncomingHttpRequestFilter2 = 1010,
     _OrthancPluginService_RegisterRefreshMetricsCallback = 1011,
     _OrthancPluginService_RegisterChunkedRestCallback = 1012,  /* New in Orthanc 1.5.7 */
+    _OrthancPluginService_RegisterStorageCommitmentScpCallback = 1013,
 
     /* Sending answers to REST calls */
     _OrthancPluginService_AnswerBuffer = 2000,
@@ -910,14 +912,14 @@
    **/
   typedef enum
   {
-    OrthancPluginMetricsType_Default,   /*!< Default metrics */
+    OrthancPluginMetricsType_Default = 0,   /*!< Default metrics */
 
     /**
      * This metrics represents a time duration. Orthanc will keep the
      * maximum value of the metrics over a sliding window of ten
      * seconds, which is useful if the metrics is sampled frequently.
      **/
-    OrthancPluginMetricsType_Timer
+    OrthancPluginMetricsType_Timer = 1
   } OrthancPluginMetricsType;
   
 
@@ -927,11 +929,47 @@
    **/
   typedef enum
   {
-    OrthancPluginDicomWebBinaryMode_Ignore,        /*!< Don't include binary tags */
-    OrthancPluginDicomWebBinaryMode_InlineBinary,  /*!< Inline encoding using Base64 */
-    OrthancPluginDicomWebBinaryMode_BulkDataUri    /*!< Use a bulk data URI field */
+    OrthancPluginDicomWebBinaryMode_Ignore = 0,        /*!< Don't include binary tags */
+    OrthancPluginDicomWebBinaryMode_InlineBinary = 1,  /*!< Inline encoding using Base64 */
+    OrthancPluginDicomWebBinaryMode_BulkDataUri = 2    /*!< Use a bulk data URI field */
   } OrthancPluginDicomWebBinaryMode;
 
+
+  /**
+   * The available values for the Failure Reason (0008,1197) during
+   * storage commitment.
+   * http://dicom.nema.org/medical/dicom/2019e/output/chtml/part03/sect_C.14.html#sect_C.14.1.1
+   **/
+  typedef enum
+  {
+    OrthancPluginStorageCommitmentFailureReason_Success = 0,
+
+    /* 0110H: A general failure in processing the operation was
+     * encountered */
+    OrthancPluginStorageCommitmentFailureReason_ProcessingFailure = 1,
+
+    /* 0112H: One or more of the elements in the Referenced SOP
+       Instance Sequence was not available */
+    OrthancPluginStorageCommitmentFailureReason_NoSuchObjectInstance = 2,
+
+    /* 0213H: The SCP does not currently have enough resources to
+       store the requested SOP Instance(s) */
+    OrthancPluginStorageCommitmentFailureReason_ResourceLimitation = 3,
+
+    /* 0122H: Storage Commitment has been requested for a SOP Instance
+       with a SOP Class that is not supported by the SCP */
+    OrthancPluginStorageCommitmentFailureReason_ReferencedSOPClassNotSupported = 4,
+
+    /* 0119H: 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 */
+    OrthancPluginStorageCommitmentFailureReason_ClassInstanceConflict = 5,
+
+    /* 0131H: The Transaction UID of the Storage Commitment Request is
+       already in use */
+    OrthancPluginStorageCommitmentFailureReason_DuplicateTransactionUID = 6
+  } OrthancPluginStorageCommitmentFailureReason;
+
   
 
   /**
@@ -1659,7 +1697,8 @@
         sizeof(int32_t) != sizeof(OrthancPluginJobStepStatus) ||
         sizeof(int32_t) != sizeof(OrthancPluginConstraintType) ||
         sizeof(int32_t) != sizeof(OrthancPluginMetricsType) ||
-        sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode))
+        sizeof(int32_t) != sizeof(OrthancPluginDicomWebBinaryMode) ||
+        sizeof(int32_t) != sizeof(OrthancPluginStorageCommitmentFailureReason))
     {
       /* Mismatch in the size of the enumerations */
       return 0;
@@ -7260,6 +7299,44 @@
   }
 
 
+
+  typedef void* (*OrthancPluginStorageCommitmentFactory) (
+    const char*         jobId,
+    const char*         transactionUid,
+    const char* const*  sopClassUids,
+    const char* const*  sopInstancesUids,
+    uint32_t            countInstances,
+    const char*         remoteAet,
+    const char*         calledAet);
+
+  typedef void (*OrthancPluginStorageCommitmentDestructor) (void* handler);
+    
+  typedef OrthancPluginErrorCode (*OrthancPluginStorageCommitmentLookup) (
+    OrthancPluginStorageCommitmentFailureReason* target,
+    void* handler,
+    const char* sopClassUid,
+    const char* sopInstanceUid);
+    
+    
+  typedef struct
+  {
+    OrthancPluginStorageCommitmentFactory     factory;
+    OrthancPluginStorageCommitmentDestructor  destructor;
+    OrthancPluginStorageCommitmentLookup      lookup;
+  } _OrthancPluginRegisterStorageCommitmentScpCallback;
+
+  ORTHANC_PLUGIN_INLINE void OrthancPluginRegisterStorageCommitmentScpCallback(
+    OrthancPluginContext*                     context,
+    OrthancPluginStorageCommitmentFactory     factory,
+    OrthancPluginStorageCommitmentDestructor  destructor,
+    OrthancPluginStorageCommitmentLookup      lookup)
+  {
+    _OrthancPluginRegisterStorageCommitmentScpCallback params;
+    params.factory = factory;
+    params.destructor = destructor;
+    params.lookup = lookup;
+    context->InvokeService(context, _OrthancPluginService_RegisterStorageCommitmentScpCallback, &params);
+  }
   
 #ifdef  __cplusplus
 }