Mercurial > hg > orthanc
changeset 2627:00b6a7f935fc jobs
refactoring of archive creation
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 25 May 2018 14:07:52 +0200 |
parents | e09021ddc00d |
children | 7ba7d5806911 |
files | OrthancServer/OrthancRestApi/OrthancRestArchive.cpp |
diffstat | 1 files changed, 306 insertions(+), 122 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp Fri May 25 11:25:33 2018 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestArchive.cpp Fri May 25 14:07:52 2018 +0200 @@ -58,7 +58,8 @@ static bool IsZip64Required(uint64_t uncompressedSize, unsigned int countInstances) { - static const uint64_t SAFETY_MARGIN = 64 * MEGA_BYTES; + static const uint64_t SAFETY_MARGIN = 64 * MEGA_BYTES; // Should be large enough to hold DICOMDIR + static const unsigned int FILES_MARGIN = 10; /** * Determine whether ZIP64 is required. Original ZIP format can @@ -68,7 +69,7 @@ **/ const bool isZip64 = (uncompressedSize >= 2 * GIGA_BYTES - SAFETY_MARGIN || - countInstances >= 65535); + countInstances >= 65535 - FILES_MARGIN); LOG(INFO) << "Creating a ZIP file with " << countInstances << " files of size " << (uncompressedSize / MEGA_BYTES) << "MB using the " @@ -80,7 +81,7 @@ namespace { - class ResourceIdentifiers + class ResourceIdentifiers : public boost::noncopyable { private: ResourceType level_; @@ -193,7 +194,7 @@ }; - class ArchiveIndex + class ArchiveIndex : public boost::noncopyable { private: struct Instance @@ -347,90 +348,200 @@ }; - class StatisticsVisitor : public IArchiveVisitor + + class ArchiveCommands : public boost::noncopyable { private: - uint64_t size_; - unsigned int instances_; + enum Type + { + Type_OpenDirectory, + Type_CloseDirectory, + Type_WriteInstance + }; + + class Command : public boost::noncopyable + { + private: + Type type_; + std::string filename_; + std::string instanceId_; + FileInfo info_; + + public: + explicit Command(Type type) : + type_(type) + { + assert(type_ == Type_CloseDirectory); + } + + Command(Type type, + const std::string& filename) : + type_(type), + filename_(filename) + { + assert(type_ == Type_OpenDirectory); + } + + Command(Type type, + const std::string& filename, + const std::string& instanceId, + const FileInfo& info) : + type_(type), + filename_(filename), + instanceId_(instanceId), + info_(info) + { + assert(type_ == Type_WriteInstance); + } + + void Apply(HierarchicalZipWriter& writer, + ServerContext& context, + DicomDirWriter* dicomDir, + const std::string& dicomDirFolder) const + { + switch (type_) + { + case Type_OpenDirectory: + writer.OpenDirectory(filename_.c_str()); + break; + + case Type_CloseDirectory: + writer.CloseDirectory(); + break; + + case Type_WriteInstance: + { + std::string content; + + try + { + context.ReadAttachment(content, info_); + } + catch (OrthancException& e) + { + LOG(WARNING) << "An instance was removed after the job was issued: " << instanceId_; + return; + } + + writer.OpenFile(filename_.c_str()); + writer.Write(content); + + if (dicomDir != NULL) + { + ParsedDicomFile parsed(content); + dicomDir->Add(dicomDirFolder, filename_, parsed); + } + + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + }; + + std::deque<Command*> commands_; + uint64_t uncompressedSize_; + unsigned int instancesCount_; + + + void ApplyInternal(HierarchicalZipWriter& writer, + ServerContext& context, + size_t index, + DicomDirWriter* dicomDir, + const std::string& dicomDirFolder) const + { + if (index >= commands_.size()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + commands_[index]->Apply(writer, context, dicomDir, dicomDirFolder); + } public: - StatisticsVisitor() : size_(0), instances_(0) + ArchiveCommands() : + uncompressedSize_(0), + instancesCount_(0) { } + + ~ArchiveCommands() + { + for (std::deque<Command*>::iterator it = commands_.begin(); + it != commands_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + } - uint64_t GetUncompressedSize() const + size_t GetSize() const { - return size_; + return commands_.size(); } unsigned int GetInstancesCount() const { - return instances_; + return instancesCount_; + } + + uint64_t GetUncompressedSize() const + { + return uncompressedSize_; } - virtual void Open(ResourceType level, - const std::string& publicId) + void Apply(HierarchicalZipWriter& writer, + ServerContext& context, + size_t index, + DicomDirWriter& dicomDir, + const std::string& dicomDirFolder) const { + ApplyInternal(writer, context, index, &dicomDir, dicomDirFolder); } - virtual void Close() + void Apply(HierarchicalZipWriter& writer, + ServerContext& context, + size_t index) const { + ApplyInternal(writer, context, index, NULL, ""); + } + + void AddOpenDirectory(const std::string& filename) + { + commands_.push_back(new Command(Type_OpenDirectory, filename)); } - virtual void AddInstance(const std::string& instanceId, - const FileInfo& dicom) + void AddCloseDirectory() + { + commands_.push_back(new Command(Type_CloseDirectory)); + } + + void AddWriteInstance(const std::string& filename, + const std::string& instanceId, + const FileInfo& info) { - instances_ ++; - size_ += dicom.GetUncompressedSize(); + commands_.push_back(new Command(Type_WriteInstance, filename, instanceId, info)); + instancesCount_ ++; + uncompressedSize_ += info.GetUncompressedSize(); + } + + bool IsZip64() const + { + return IsZip64Required(GetUncompressedSize(), GetInstancesCount()); } }; - - - class PrintVisitor : public IArchiveVisitor - { - private: - std::ostream& out_; - std::string indent_; - - public: - PrintVisitor(std::ostream& out) : out_(out) - { - } - - virtual void Open(ResourceType level, - const std::string& publicId) - { - switch (level) - { - case ResourceType_Patient: indent_ = ""; break; - case ResourceType_Study: indent_ = " "; break; - case ResourceType_Series: indent_ = " "; break; - default: - throw OrthancException(ErrorCode_InternalError); - } - - out_ << indent_ << publicId << std::endl; - } - - virtual void Close() - { - } - - virtual void AddInstance(const std::string& instanceId, - const FileInfo& dicom) - { - out_ << " " << instanceId << std::endl; - } - }; - + + class ArchiveWriterVisitor : public IArchiveVisitor { private: - HierarchicalZipWriter& writer_; - ServerContext& context_; - char instanceFormat_[24]; - unsigned int countInstances_; + ArchiveCommands& commands_; + ServerContext& context_; + char instanceFormat_[24]; + unsigned int counter_; static std::string GetTag(const DicomMap& tags, const DicomTag& tag) @@ -449,12 +560,17 @@ } public: - ArchiveWriterVisitor(HierarchicalZipWriter& writer, + ArchiveWriterVisitor(ArchiveCommands& commands, ServerContext& context) : - writer_(writer), + commands_(commands), context_(context), - countInstances_(0) + counter_(0) { + if (commands.GetSize() != 0) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + snprintf(instanceFormat_, sizeof(instanceFormat_) - 1, "%%08d.dcm"); } @@ -496,7 +612,7 @@ toupper(modality[0]), toupper(modality[1])); } - countInstances_ = 0; + counter_ = 0; break; } @@ -513,26 +629,22 @@ path = std::string("Unknown ") + EnumerationToString(level); } - writer_.OpenDirectory(path.c_str()); + commands_.AddOpenDirectory(path.c_str()); } virtual void Close() { - writer_.CloseDirectory(); + commands_.AddCloseDirectory(); } virtual void AddInstance(const std::string& instanceId, const FileInfo& dicom) { - std::string content; - context_.ReadAttachment(content, dicom); + char filename[24]; + snprintf(filename, sizeof(filename) - 1, instanceFormat_, counter_); + counter_ ++; - char filename[24]; - snprintf(filename, sizeof(filename) - 1, instanceFormat_, countInstances_); - countInstances_ ++; - - writer_.OpenFile(filename); - writer_.Write(content); + commands_.AddWriteInstance(filename, instanceId, dicom); } static void Apply(RestApiOutput& output, @@ -540,23 +652,25 @@ ArchiveIndex& archive, const std::string& filename) { - archive.Expand(context.GetIndex()); + ArchiveCommands commands; - StatisticsVisitor stats; - archive.Apply(stats); - - const bool isZip64 = IsZip64Required(stats.GetUncompressedSize(), stats.GetInstancesCount()); + { + ArchiveWriterVisitor visitor(commands, context); + archive.Expand(context.GetIndex()); + archive.Apply(visitor); + } // Create a RAII for the temporary file to manage the ZIP file TemporaryFile tmp; { - // Create a ZIP writer HierarchicalZipWriter writer(tmp.GetPath().c_str()); - writer.SetZip64(isZip64); + writer.SetZip64(commands.IsZip64()); - ArchiveWriterVisitor v(writer, context); - archive.Apply(v); + for (size_t i = 0; i < commands.GetSize(); i++) + { + commands.Apply(writer, context, i); + } } // Prepare the sending of the ZIP file @@ -575,25 +689,19 @@ class MediaWriterVisitor : public IArchiveVisitor { private: - HierarchicalZipWriter& writer_; - DicomDirWriter dicomDir_; - ServerContext& context_; - unsigned int countInstances_; + ArchiveCommands& commands_; + ServerContext& context_; + unsigned int counter_; public: - MediaWriterVisitor(HierarchicalZipWriter& writer, + MediaWriterVisitor(ArchiveCommands& commands, ServerContext& context) : - writer_(writer), + commands_(commands), context_(context), - countInstances_(0) + counter_(0) { } - void EncodeDicomDir(std::string& result) - { - dicomDir_.Encode(result); - } - virtual void Open(ResourceType level, const std::string& publicId) { @@ -609,17 +717,10 @@ // "DICOM restricts the filenames on DICOM media to 8 // characters (some systems wrongly use 8.3, but this does not // conform to the standard)." - std::string filename = "IM" + boost::lexical_cast<std::string>(countInstances_); - writer_.OpenFile(filename.c_str()); + std::string filename = "IM" + boost::lexical_cast<std::string>(counter_); + commands_.AddWriteInstance(filename, instanceId, dicom); - std::string content; - context_.ReadAttachment(content, dicom); - writer_.Write(content); - - ParsedDicomFile parsed(content); - dicomDir_.Add("IMAGES", filename, parsed); - - countInstances_ ++; + counter_ ++; } static void Apply(RestApiOutput& output, @@ -628,38 +729,42 @@ const std::string& filename, bool enableExtendedSopClass) { - archive.Expand(context.GetIndex()); + static const char* IMAGES_FOLDER = "IMAGES"; + + ArchiveCommands commands; - StatisticsVisitor stats; - archive.Apply(stats); + { + MediaWriterVisitor visitor(commands, context); + archive.Expand(context.GetIndex()); - const bool isZip64 = IsZip64Required(stats.GetUncompressedSize(), stats.GetInstancesCount()); + commands.AddOpenDirectory(IMAGES_FOLDER); + archive.Apply(visitor); + commands.AddCloseDirectory(); + } // Create a RAII for the temporary file to manage the ZIP file TemporaryFile tmp; { - // Create a ZIP writer - HierarchicalZipWriter writer(tmp.GetPath().c_str()); - writer.SetZip64(isZip64); - writer.OpenDirectory("IMAGES"); + DicomDirWriter dicomDir; + dicomDir.EnableExtendedSopClass(enableExtendedSopClass); - // Create a DICOMDIR writer - MediaWriterVisitor v(writer, context); + HierarchicalZipWriter writer(tmp.GetPath().c_str()); + writer.SetZip64(commands.IsZip64()); - // Request type-3 arguments to be added to the DICOMDIR - v.dicomDir_.EnableExtendedSopClass(enableExtendedSopClass); - - archive.Apply(v); + for (size_t i = 0; i < commands.GetSize(); i++) + { + commands.Apply(writer, context, i, dicomDir, IMAGES_FOLDER); + } // Add the DICOMDIR - writer.CloseDirectory(); writer.OpenFile("DICOMDIR"); std::string s; - v.EncodeDicomDir(s); + dicomDir.Encode(s); writer.Write(s); } + // Prepare the sending of the ZIP file FilesystemHttpSender sender(tmp.GetPath()); sender.SetContentType("application/zip"); @@ -671,6 +776,85 @@ // The temporary file is automatically removed thanks to the RAII } }; + + +#if 0 + class ArchiveJob : public IJob + { + private: + ServerContext& context_; + ArchiveIndex archive_; + bool isMedia_; + std::auto_ptr<TemporaryFile> file_; + std::auto_ptr<HierarchicalZipWriter> writer_; + std::auto_ptr<IArchiveVisitor> visitor_; + std::string description_; + bool started_; + unsigned int currentInstance_; + + public: + ArchiveJob(ServerContext& context, + ResourceType level, + bool isMedia) : + context_(context), + archive_(level), + isMedia_(isMedia), + started_(false), + currentInstance_(0) + { + } + + void SetDescription(const std::string& description) + { + description_ = description; + } + + const std::string& GetDescription() const + { + return description_; + } + + void AddResource(const std::string& publicId) + { + if (started_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + ResourceIdentifiers resource(context_.GetIndex(), publicId); + archive_.Add(context_.GetIndex(), resource); + } + + virtual void SignalResubmit() + { + LOG(ERROR) << "Cannot resubmit the creation of an archive"; + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + virtual void Start() + { + if (started_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + + started_ = true; + + archive_.Expand(context_.GetIndex()); + + const bool isZip64 = IsZip64Required(stats_.GetUncompressedSize(), stats_.GetInstancesCount()); + + file_.reset(new TemporaryFile); + writer_.reset(new HierarchicalZipWriter(file_->GetPath().c_str())); + writer_->SetZip64(isZip64); + + if (isMedia_) + { + //visitor_.reset(new + } + } + }; +#endif }