# HG changeset patch # User Sebastien Jodogne # Date 1537363441 -7200 # Node ID 8aa6aef11b703e191e4c956c1bd32263dbe1f249 # Parent 0e1b79bc4a2dbd65407eb8f731152f81a57bb819 New configuration option "OverwriteInstances" to choose how duplicate SOPInstanceUID are handled diff -r 0e1b79bc4a2d -r 8aa6aef11b70 NEWS --- 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 ---------------- diff -r 0e1b79bc4a2d -r 8aa6aef11b70 OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp --- 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 modified; - std::auto_ptr 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()); } diff -r 0e1b79bc4a2d -r 8aa6aef11b70 OrthancServer/ServerContext.cpp --- 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); diff -r 0e1b79bc4a2d -r 8aa6aef11b70 OrthancServer/ServerIndex.cpp --- 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 diff -r 0e1b79bc4a2d -r 8aa6aef11b70 OrthancServer/ServerIndex.h --- 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 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& instanceMetadata, DicomInstanceToStore& instance, const Attachments& attachments); diff -r 0e1b79bc4a2d -r 8aa6aef11b70 OrthancServer/ServerJobs/ResourceModificationJob.cpp --- 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 locker; + + /** + * Retrieve the original instance from the DICOM cache. + **/ + + std::auto_ptr originalHasher; + std::auto_ptr 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 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); diff -r 0e1b79bc4a2d -r 8aa6aef11b70 OrthancServer/main.cpp --- 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)); diff -r 0e1b79bc4a2d -r 8aa6aef11b70 Resources/Configuration.json --- 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 }