# HG changeset patch # User Sebastien Jodogne # Date 1404821165 -7200 # Node ID a226e0959d8b25f316ee02daa7e113a0589585bd # Parent 1d35281d967ceb492c26c94b33263fa7c5841c0d DicomInstanceToStore diff -r 1d35281d967c -r a226e0959d8b OrthancServer/DicomInstanceToStore.cpp --- a/OrthancServer/DicomInstanceToStore.cpp Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/DicomInstanceToStore.cpp Tue Jul 08 14:06:05 2014 +0200 @@ -32,42 +32,21 @@ #include "DicomInstanceToStore.h" +#include "FromDcmtkBridge.h" + +#include +#include + + namespace Orthanc { - DicomInstanceToStore::DicomInstanceToStore() : - hasBuffer_(false), - parsed_(NULL), - summary_(NULL), - json_(NULL) - { - } - - void DicomInstanceToStore::SetBuffer(const std::string& dicom) + static DcmDataset& GetDataset(ParsedDicomFile& file) { - hasBuffer_ = true; - bufferSize_ = dicom.size(); - - if (dicom.size() == 0) - { - buffer_ = NULL; - } - else - { - buffer_ = &dicom[0]; - } + return *reinterpret_cast(file.GetDcmtkObject())->getDataset(); } - void DicomInstanceToStore::SetBuffer(const char* buffer, - size_t size) - { - hasBuffer_ = true; - buffer_ = buffer; - bufferSize_ = size; - } - - - void DicomInstanceToStore::SetMetadata(ResourceType level, + void DicomInstanceToStore::AddMetadata(ResourceType level, MetadataType metadata, const std::string& value) { @@ -77,23 +56,119 @@ void DicomInstanceToStore::ComputeMissingInformation() { - // TODO + if (buffer_.HasContent() && + summary_.HasContent() && + json_.HasContent()) + { + // Fine, everything is available + return; + } + + if (!buffer_.HasContent()) + { + if (!parsed_.HasContent()) + { + throw OrthancException(ErrorCode_NotImplemented); + } + else + { + // Serialize the parsed DICOM file + buffer_.Allocate(); + if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer_.GetContent(), GetDataset(parsed_.GetContent()))) + { + LOG(ERROR) << "Unable to serialize a DICOM file to a memory buffer"; + throw OrthancException(ErrorCode_InternalError); + } + } + } - assert(hasBuffer_ && (buffer_ != NULL || bufferSize_ == 0)); + if (summary_.HasContent() && + json_.HasContent()) + { + return; + } + + // At this point, we know that the DICOM file is available as a + // memory buffer, but that its summary or its JSON version is + // missing + + if (!parsed_.HasContent()) + { + parsed_.TakeOwnership(new ParsedDicomFile(buffer_.GetContent())); + } + + // At this point, we have parsed the DICOM file + + if (!summary_.HasContent()) + { + summary_.Allocate(); + FromDcmtkBridge::Convert(summary_.GetContent(), GetDataset(parsed_.GetContent())); + } + + if (!json_.HasContent()) + { + json_.Allocate(); + FromDcmtkBridge::ToJson(json_.GetContent(), GetDataset(parsed_.GetContent())); + } } - const char* DicomInstanceToStore::GetBuffer() + const char* DicomInstanceToStore::GetBufferData() { ComputeMissingInformation(); - return buffer_; + + if (!buffer_.HasContent()) + { + throw OrthancException(ErrorCode_InternalError); + } + + if (buffer_.GetConstContent().size() == 0) + { + return NULL; + } + else + { + return buffer_.GetConstContent().c_str(); + } } size_t DicomInstanceToStore::GetBufferSize() { ComputeMissingInformation(); - return bufferSize_; + + if (!buffer_.HasContent()) + { + throw OrthancException(ErrorCode_InternalError); + } + + return buffer_.GetConstContent().size(); + } + + + const DicomMap& DicomInstanceToStore::GetSummary() + { + ComputeMissingInformation(); + + if (!summary_.HasContent()) + { + throw OrthancException(ErrorCode_InternalError); + } + + return summary_.GetConstContent(); + } + + + const Json::Value& DicomInstanceToStore::GetJson() + { + ComputeMissingInformation(); + + if (!json_.HasContent()) + { + throw OrthancException(ErrorCode_InternalError); + } + + return json_.GetConstContent(); } } diff -r 1d35281d967c -r a226e0959d8b OrthancServer/DicomInstanceToStore.h --- a/OrthancServer/DicomInstanceToStore.h Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/DicomInstanceToStore.h Tue Jul 08 14:06:05 2014 +0200 @@ -34,19 +34,149 @@ #include "ParsedDicomFile.h" #include "ServerIndex.h" +#include "../Core/OrthancException.h" namespace Orthanc { class DicomInstanceToStore { private: - bool hasBuffer_; - const char* buffer_; - size_t bufferSize_; + template + class SmartContainer + { + private: + T* content_; + bool toDelete_; + bool isReadOnly_; + + void Deallocate() + { + if (content_ && toDelete_) + { + delete content_; + toDelete_ = false; + content_ = NULL; + } + } + + public: + SmartContainer() : content_(NULL), toDelete_(false) + { + } + + ~SmartContainer() + { + Deallocate(); + } + + void Allocate() + { + Deallocate(); + content_ = new T; + toDelete_ = true; + isReadOnly_ = false; + } + + void TakeOwnership(T* content) + { + if (content == NULL) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + Deallocate(); + content_ = content; + toDelete_ = true; + isReadOnly_ = true; + } + + void SetReference(const T& content) // Read-only assign, without transfering ownership + { + Deallocate(); + content_ = &const_cast(content); + toDelete_ = false; + isReadOnly_ = true; + } + + bool HasContent() const + { + return content_ != NULL; + } - ParsedDicomFile* parsed_; - const DicomMap* summary_; - const Json::Value* json_; + T& GetContent() + { + if (content_ == NULL) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + if (isReadOnly_) + { + throw OrthancException(ErrorCode_ReadOnly); + } + + return *content_; + } + + const T& GetConstContent() const + { + if (content_ == NULL) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + return *content_; + } + }; + + + /*class MemoryBuffer + { + private: + const char* buffer_; + size_t size_; + + public: + MemoryBuffer() : buffer_(NULL), size_(0) + { + } + + const char* GetBuffer() const + { + return buffer_; + } + + size_t GetSize() const + { + return size_; + } + + void Assign(const char* buffer, size_t size) + { + buffer_ = buffer; + size_ = size; + } + + void Assign(const std::string& buffer) + { + size_ = buffer.size(); + + if (size_ == 0) + { + buffer_ = NULL; + } + else + { + buffer_ = &buffer[0]; + } + } + };*/ + + + SmartContainer buffer_; + SmartContainer parsed_; + SmartContainer summary_; + SmartContainer json_; std::string remoteAet_; ServerIndex::MetadataMap metadata_; @@ -54,26 +184,24 @@ void ComputeMissingInformation(); public: - DicomInstanceToStore(); - - void SetBuffer(const std::string& dicom); - - void SetBuffer(const char* buffer, - size_t size); + void SetBuffer(const std::string& dicom) + { + buffer_.SetReference(dicom); + } void SetParsedDicomFile(ParsedDicomFile& parsed) { - parsed_ = &parsed; + parsed_.SetReference(parsed); } void SetSummary(const DicomMap& summary) { - summary_ = &summary; + summary_.SetReference(summary); } void SetJson(const Json::Value& json) { - json_ = &json; + json_.SetReference(json); } const std::string GetRemoteAet() const @@ -86,7 +214,7 @@ remoteAet_ = aet; } - void SetMetadata(ResourceType level, + void AddMetadata(ResourceType level, MetadataType metadata, const std::string& value); @@ -95,8 +223,17 @@ return metadata_; } - const char* GetBuffer(); + ServerIndex::MetadataMap& GetMetadata() + { + return metadata_; + } + + const char* GetBufferData(); size_t GetBufferSize(); + + const DicomMap& GetSummary(); + + const Json::Value& GetJson(); }; } diff -r 1d35281d967c -r a226e0959d8b OrthancServer/FromDcmtkBridge.cpp --- a/OrthancServer/FromDcmtkBridge.cpp Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/FromDcmtkBridge.cpp Tue Jul 08 14:06:05 2014 +0200 @@ -634,7 +634,7 @@ } bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer, - DcmDataset* dataSet) + DcmDataset& dataSet) { // Determine the transfer syntax which shall be used to write the // information to the file. We always switch to the Little Endian @@ -649,7 +649,7 @@ * dataset into memory. We now keep the original transfer syntax * (if available). **/ - E_TransferSyntax xfer = dataSet->getOriginalXfer(); + E_TransferSyntax xfer = dataSet.getOriginalXfer(); if (xfer == EXS_Unknown) { // No information about the original transfer syntax: This is @@ -660,7 +660,7 @@ E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength; // Create the meta-header information - DcmFileFormat ff(dataSet); + DcmFileFormat ff(&dataSet); ff.validateMetaInfo(xfer); // Create a memory buffer with the proper size diff -r 1d35281d967c -r a226e0959d8b OrthancServer/FromDcmtkBridge.h --- a/OrthancServer/FromDcmtkBridge.h Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/FromDcmtkBridge.h Tue Jul 08 14:06:05 2014 +0200 @@ -104,6 +104,6 @@ static std::string GenerateUniqueIdentifier(ResourceType level); static bool SaveToMemoryBuffer(std::string& buffer, - DcmDataset* dataSet); + DcmDataset& dataSet); }; } diff -r 1d35281d967c -r a226e0959d8b OrthancServer/Internals/StoreScp.cpp --- a/OrthancServer/Internals/StoreScp.cpp Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/Internals/StoreScp.cpp Tue Jul 08 14:06:05 2014 +0200 @@ -168,7 +168,7 @@ FromDcmtkBridge::Convert(summary, **imageDataSet); FromDcmtkBridge::ToJson(dicomJson, **imageDataSet); - if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, *imageDataSet)) + if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, **imageDataSet)) { LOG(ERROR) << "cannot write DICOM file to memory"; rsp->DimseStatus = STATUS_STORE_Refused_OutOfResources; diff -r 1d35281d967c -r a226e0959d8b OrthancServer/ParsedDicomFile.cpp --- a/OrthancServer/ParsedDicomFile.cpp Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/ParsedDicomFile.cpp Tue Jul 08 14:06:05 2014 +0200 @@ -873,7 +873,7 @@ void ParsedDicomFile::Answer(RestApiOutput& output) { std::string serialized; - if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, pimpl_->file_->getDataset())) + if (FromDcmtkBridge::SaveToMemoryBuffer(serialized, *pimpl_->file_->getDataset())) { output.AnswerBuffer(serialized, CONTENT_TYPE_OCTET_STREAM); } @@ -956,7 +956,7 @@ void ParsedDicomFile::SaveToMemoryBuffer(std::string& buffer) { - FromDcmtkBridge::SaveToMemoryBuffer(buffer, pimpl_->file_->getDataset()); + FromDcmtkBridge::SaveToMemoryBuffer(buffer, *pimpl_->file_->getDataset()); } diff -r 1d35281d967c -r a226e0959d8b OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/ServerContext.cpp Tue Jul 08 14:06:05 2014 +0200 @@ -159,6 +159,106 @@ } + StoreStatus ServerContext::Store(std::string& resultPublicId, + DicomInstanceToStore& dicom) + { + try + { + DicomInstanceHasher hasher(dicom.GetSummary()); + resultPublicId = hasher.HashInstance(); + + Json::Value simplified; + SimplifyTags(simplified, dicom.GetJson()); + + // Test if the instance must be filtered out + if (!ApplyReceivedInstanceFilter(simplified, dicom.GetRemoteAet())) + { + LOG(INFO) << "An incoming instance has been discarded by the filter"; + return StoreStatus_FilteredOut; + } + + if (compressionEnabled_) + { + accessor_.SetCompressionForNextOperations(CompressionType_Zlib); + } + else + { + accessor_.SetCompressionForNextOperations(CompressionType_None); + } + + FileInfo dicomInfo = accessor_.Write(dicom.GetBufferData(), dicom.GetBufferSize(), FileContentType_Dicom); + FileInfo jsonInfo = accessor_.Write(dicom.GetJson().toStyledString(), FileContentType_DicomAsJson); + + ServerIndex::Attachments attachments; + attachments.push_back(dicomInfo); + attachments.push_back(jsonInfo); + + StoreStatus status = index_.Store(dicom.GetSummary(), attachments, dicom.GetRemoteAet(), dicom.GetMetadata()); + + if (status != StoreStatus_Success) + { + storage_.Remove(dicomInfo.GetUuid()); + storage_.Remove(jsonInfo.GetUuid()); + } + + switch (status) + { + case StoreStatus_Success: + LOG(INFO) << "New instance stored"; + break; + + case StoreStatus_AlreadyStored: + LOG(INFO) << "Already stored"; + break; + + case StoreStatus_Failure: + LOG(ERROR) << "Store failure"; + break; + + default: + // This should never happen + break; + } + + if (status == StoreStatus_Success || + status == StoreStatus_AlreadyStored) + { + try + { + Json::Value metadata = Json::objectValue; + for (ServerIndex::MetadataMap::const_iterator + it = dicom.GetMetadata().begin(); + it != dicom.GetMetadata().end(); ++it) + { + if (it->first.first == ResourceType_Instance) + { + metadata[EnumerationToString(it->first.second)] = it->second; + } + } + + ApplyOnStoredInstance(resultPublicId, simplified, metadata); + } + catch (OrthancException&) + { + LOG(ERROR) << "Error when dealing with OnStoredInstance"; + } + } + + return status; + } + catch (OrthancException& e) + { + if (e.GetErrorCode() == ErrorCode_InexistentTag) + { + LogMissingRequiredTag(dicom.GetSummary()); + } + + throw; + } + } + + + StoreStatus ServerContext::Store(const char* dicomInstance, size_t dicomSize, const DicomMap& dicomSummary, @@ -192,7 +292,8 @@ attachments.push_back(dicomInfo); attachments.push_back(jsonInfo); - StoreStatus status = index_.Store(dicomSummary, attachments, remoteAet, metadata); + // TODO REMOVE CONST_CAST !!!! + StoreStatus status = index_.Store(dicomSummary, attachments, remoteAet, const_cast(metadata)); if (status != StoreStatus_Success) { @@ -379,7 +480,7 @@ const ServerIndex::MetadataMap& metadata) { std::string buffer; - if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, GetDicom(dicomInstance).getDataset())) + if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer, *GetDicom(dicomInstance).getDataset())) { throw OrthancException(ErrorCode_InternalError); } diff -r 1d35281d967c -r a226e0959d8b OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/ServerContext.h Tue Jul 08 14:06:05 2014 +0200 @@ -41,6 +41,7 @@ #include "ParsedDicomFile.h" #include "DicomProtocol/ReusableDicomUserConnection.h" #include "Scheduler/ServerScheduler.h" +#include "DicomInstanceToStore.h" namespace Orthanc { @@ -86,6 +87,11 @@ boost::mutex luaMutex_; LuaContext lua_; + StoreStatus Store(std::string& resultPublicId, + const char* dicomBuffer, + size_t dicomSize, + const ServerIndex::MetadataMap& metadata = ServerIndex::MetadataMap()); + public: class DicomCacheLocker : public boost::noncopyable { @@ -152,6 +158,9 @@ // TODO SIMPLIFY THESE MANY "Store" methods! + StoreStatus Store(std::string& resultPublicId, + DicomInstanceToStore& dicom); + StoreStatus Store(const char* dicomInstance, size_t dicomSize, const DicomMap& dicomSummary, @@ -170,11 +179,6 @@ const ServerIndex::MetadataMap& metadata = ServerIndex::MetadataMap()); StoreStatus Store(std::string& resultPublicId, - const char* dicomBuffer, - size_t dicomSize, - const ServerIndex::MetadataMap& metadata = ServerIndex::MetadataMap()); - - StoreStatus Store(std::string& resultPublicId, const std::string& dicomContent, const ServerIndex::MetadataMap& metadata = ServerIndex::MetadataMap()); diff -r 1d35281d967c -r a226e0959d8b OrthancServer/ServerIndex.cpp --- a/OrthancServer/ServerIndex.cpp Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/ServerIndex.cpp Tue Jul 08 14:06:05 2014 +0200 @@ -385,7 +385,7 @@ StoreStatus ServerIndex::Store(const DicomMap& dicomSummary, const Attachments& attachments, const std::string& remoteAet, - const MetadataMap* metadata) + MetadataMap* metadata) { boost::mutex::scoped_lock lock(mutex_); listener_->Reset(); @@ -550,27 +550,41 @@ } } - // Attach the auto-computer metadata + // Attach the auto-computed metadata for the patient/study/series levels std::string now = Toolbox::GetNowIsoString(); - db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, now); db_->SetMetadata(series, MetadataType_LastUpdate, now); db_->SetMetadata(study, MetadataType_LastUpdate, now); db_->SetMetadata(patient, MetadataType_LastUpdate, now); + + // Attach the auto-computed metadata for the instance level, + // reflecting these additions into the input metadata map + db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, now); db_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet); + if (metadata) + { + (*metadata) [std::make_pair(ResourceType_Instance, MetadataType_Instance_ReceptionDate)] = now; + (*metadata) [std::make_pair(ResourceType_Instance, MetadataType_Instance_RemoteAet)] = remoteAet; + } + const DicomValue* value; if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) { db_->SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString()); + + if (metadata) + { + (*metadata) [std::make_pair(ResourceType_Instance, MetadataType_Instance_IndexInSeries)] = value->AsString(); + } } + // Check whether the series of this new instance is now completed if (isNewSeries) { ComputeExpectedNumberOfInstances(*db_, series, dicomSummary); } - // Check whether the series of this new instance is now completed SeriesStatus seriesStatus = GetSeriesStatus(series); if (seriesStatus == SeriesStatus_Complete) { diff -r 1d35281d967c -r a226e0959d8b OrthancServer/ServerIndex.h --- a/OrthancServer/ServerIndex.h Fri Jul 04 16:51:33 2014 +0200 +++ b/OrthancServer/ServerIndex.h Tue Jul 08 14:06:05 2014 +0200 @@ -105,7 +105,7 @@ StoreStatus Store(const DicomMap& dicomSummary, const Attachments& attachments, const std::string& remoteAet, - const MetadataMap* metadata); + MetadataMap* metadata); public: ServerIndex(ServerContext& context, @@ -139,7 +139,7 @@ StoreStatus Store(const DicomMap& dicomSummary, const Attachments& attachments, const std::string& remoteAet, - const MetadataMap& metadata) + MetadataMap& metadata) { return Store(dicomSummary, attachments, remoteAet, &metadata); }