changeset 2825:8aa6aef11b70

New configuration option "OverwriteInstances" to choose how duplicate SOPInstanceUID are handled
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 19 Sep 2018 15:24:01 +0200
parents 0e1b79bc4a2d
children c277e0421200
files NEWS OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp OrthancServer/ServerContext.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h OrthancServer/ServerJobs/ResourceModificationJob.cpp OrthancServer/main.cpp Resources/Configuration.json
diffstat 8 files changed, 73 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Sep 18 16:31:42 2018 +0200
+++ b/NEWS	Wed Sep 19 15:24:01 2018 +0200
@@ -6,7 +6,9 @@
 
 * "OrthancPeers" configuration option now allows to specify HTTP headers
 * New main DICOM tag: "ImageOrientationPatient" at the instance level
-* New configuration option: "HttpVerbose" to debug outgoing HTTP connections
+* New configuration options:
+  - "HttpVerbose" to debug outgoing HTTP connections
+  - "OverwriteInstances" to choose how duplicate SOPInstanceUID are handled
 
 Orthanc Explorer
 ----------------
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Tue Sep 18 16:31:42 2018 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp	Wed Sep 19 15:24:01 2018 +0200
@@ -130,9 +130,13 @@
   {
     std::string id = call.GetUriComponent("id", "");
 
-    ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), id);
+    std::auto_ptr<ParsedDicomFile> modified;
 
-    std::auto_ptr<ParsedDicomFile> modified(locker.GetDicom().Clone(true));
+    {
+      ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), id);
+      modified.reset(locker.GetDicom().Clone(true));
+    }
+    
     modification.Apply(*modified);
     modified->Answer(call.GetOutput());
   }
--- a/OrthancServer/ServerContext.cpp	Tue Sep 18 16:31:42 2018 +0200
+++ b/OrthancServer/ServerContext.cpp	Wed Sep 19 15:24:01 2018 +0200
@@ -354,6 +354,13 @@
         return StoreStatus_FilteredOut;
       }
 
+      {
+        // Remove the file from the DicomCache (useful if
+        // "OverwriteInstances" is set to "true")
+        boost::mutex::scoped_lock lock(dicomCacheMutex_);
+        dicomCache_.Invalidate(resultPublicId);
+      }
+
       // TODO Should we use "gzip" instead?
       CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None);
 
--- a/OrthancServer/ServerIndex.cpp	Tue Sep 18 16:31:42 2018 +0200
+++ b/OrthancServer/ServerIndex.cpp	Wed Sep 19 15:24:01 2018 +0200
@@ -626,15 +626,26 @@
     {
       Transaction t(*this);
 
-      // Do nothing if the instance already exists
+      // Check whether this instance is already stored
       {
         ResourceType type;
         int64_t tmp;
         if (db_.LookupResource(tmp, type, hasher.HashInstance()))
         {
           assert(type == ResourceType_Instance);
-          db_.GetAllMetadata(instanceMetadata, tmp);
-          return StoreStatus_AlreadyStored;
+
+          if (overwrite_)
+          {
+            // Overwrite the old instance
+            LOG(INFO) << "Overwriting instance: " << hasher.HashInstance();
+            db_.DeleteResource(tmp);
+          }
+          else
+          {
+            // Do nothing if the instance already exists
+            db_.GetAllMetadata(instanceMetadata, tmp);
+            return StoreStatus_AlreadyStored;
+          }
         }
       }
 
@@ -1461,6 +1472,13 @@
     StandaloneRecycling();
   }
 
+  void ServerIndex::SetOverwriteInstances(bool overwrite)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+    overwrite_ = overwrite;
+  }
+
+
   void ServerIndex::StandaloneRecycling()
   {
     // WARNING: No mutex here, do not include this as a public method
--- a/OrthancServer/ServerIndex.h	Tue Sep 18 16:31:42 2018 +0200
+++ b/OrthancServer/ServerIndex.h	Wed Sep 19 15:24:01 2018 +0200
@@ -70,9 +70,10 @@
     IDatabaseWrapper& db_;
     LeastRecentlyUsedIndex<int64_t, UnstableResourcePayload>  unstableResources_;
 
-    uint64_t currentStorageSize_;
-    uint64_t maximumStorageSize_;
+    uint64_t     currentStorageSize_;
+    uint64_t     maximumStorageSize_;
     unsigned int maximumPatients_;
+    bool         overwrite_;
 
     static void FlushThread(ServerIndex* that,
                             unsigned int threadSleep);
@@ -149,6 +150,8 @@
     // "count == 0" means no limit on the number of patients
     void SetMaximumPatientCount(unsigned int count);
 
+    void SetOverwriteInstances(bool overwrite);
+
     StoreStatus Store(std::map<MetadataType, std::string>& instanceMetadata,
                       DicomInstanceToStore& instance,
                       const Attachments& attachments);
--- a/OrthancServer/ServerJobs/ResourceModificationJob.cpp	Tue Sep 18 16:31:42 2018 +0200
+++ b/OrthancServer/ServerJobs/ResourceModificationJob.cpp	Wed Sep 19 15:24:01 2018 +0200
@@ -129,11 +129,21 @@
       
     LOG(INFO) << "Modifying instance in a job: " << instance;
 
-    std::auto_ptr<ServerContext::DicomCacheLocker> locker;
+
+    /**
+     * Retrieve the original instance from the DICOM cache.
+     **/
+    
+    std::auto_ptr<DicomInstanceHasher> originalHasher;
+    std::auto_ptr<ParsedDicomFile> modified;
 
     try
     {
-      locker.reset(new ServerContext::DicomCacheLocker(context_, instance));
+      ServerContext::DicomCacheLocker locker(context_, instance);
+      ParsedDicomFile& original = locker.GetDicom();
+
+      originalHasher.reset(new DicomInstanceHasher(original.GetHasher()));
+      modified.reset(original.Clone(true));
     }
     catch (OrthancException&)
     {
@@ -142,15 +152,10 @@
     }
 
 
-    ParsedDicomFile& original = locker->GetDicom();
-    DicomInstanceHasher originalHasher = original.GetHasher();
-
-
     /**
      * Compute the resulting DICOM instance.
      **/
 
-    std::auto_ptr<ParsedDicomFile> modified(original.Clone(true));
     modification_->Apply(*modified);
 
     DicomInstanceToStore toStore;
@@ -169,22 +174,22 @@
                                  MetadataType_AnonymizedFrom :
                                  MetadataType_ModifiedFrom);
 
-    if (originalHasher.HashSeries() != modifiedHasher.HashSeries())
+    if (originalHasher->HashSeries() != modifiedHasher.HashSeries())
     {
-      toStore.AddMetadata(ResourceType_Series, metadataType, originalHasher.HashSeries());
+      toStore.AddMetadata(ResourceType_Series, metadataType, originalHasher->HashSeries());
     }
 
-    if (originalHasher.HashStudy() != modifiedHasher.HashStudy())
+    if (originalHasher->HashStudy() != modifiedHasher.HashStudy())
     {
-      toStore.AddMetadata(ResourceType_Study, metadataType, originalHasher.HashStudy());
+      toStore.AddMetadata(ResourceType_Study, metadataType, originalHasher->HashStudy());
     }
 
-    if (originalHasher.HashPatient() != modifiedHasher.HashPatient())
+    if (originalHasher->HashPatient() != modifiedHasher.HashPatient())
     {
-      toStore.AddMetadata(ResourceType_Patient, metadataType, originalHasher.HashPatient());
+      toStore.AddMetadata(ResourceType_Patient, metadataType, originalHasher->HashPatient());
     }
 
-    assert(instance == originalHasher.HashInstance());
+    assert(instance == originalHasher->HashInstance());
     toStore.AddMetadata(ResourceType_Instance, metadataType, instance);
 
 
--- a/OrthancServer/main.cpp	Tue Sep 18 16:31:42 2018 +0200
+++ b/OrthancServer/main.cpp	Wed Sep 19 15:24:01 2018 +0200
@@ -984,6 +984,9 @@
   context.SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false));
   context.SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true));
 
+  // New option in Orthanc 1.4.2
+  context.GetIndex().SetOverwriteInstances(Configuration::GetGlobalBoolParameter("OverwriteInstances", false));
+
   try
   {
     context.GetIndex().SetMaximumPatientCount(Configuration::GetGlobalUnsignedIntegerParameter("MaximumPatientCount", 0));
--- a/Resources/Configuration.json	Tue Sep 18 16:31:42 2018 +0200
+++ b/Resources/Configuration.json	Wed Sep 19 15:24:01 2018 +0200
@@ -389,12 +389,19 @@
 
   // Whether to run DICOM C-Move operations synchronously. If set to
   // "false" (the default), each incoming C-Move request results in
-  // creating a new background job. Until Orthanc 1.3.2, the default
+  // creating a new background job. Up to Orthanc 1.3.2, the implicit
   // behavior was to use synchronous C-Move.
   "SynchronousCMove" : false,
 
   // Maximum number of completed jobs that are kept in memory. A
   // processing job is considered as complete once it is tagged as
   // "Success" or "Failure".
-  "JobsHistorySize" : 10
+  "JobsHistorySize" : 10,
+
+  // Specifies how Orthanc reacts when it receives a DICOM instance
+  // whose SOPInstanceUID is already stored. If set to "true", the new
+  // instance replaces the old one. If set to "false", the new
+  // instance is discarded and the old one is kept. Up to Orthanc
+  // 1.4.1, the implicit behavior corresponded to "false".
+  "OverwriteInstances" : false
 }