Mercurial > hg > orthanc
changeset 6794:2ec15c5ef4bd
reorganization
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Sun, 17 May 2026 19:22:11 +0200 |
| parents | 4514943d9239 |
| children | 7872e2d293bb |
| files | OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake OrthancFramework/Sources/DataSource/DicomDataSource.cpp OrthancFramework/Sources/DataSource/DicomDataSource.h OrthancFramework/UnitTestsSources/JobsTests.cpp |
| diffstat | 4 files changed, 320 insertions(+), 219 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Sun May 17 19:00:37 2026 +0200 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Sun May 17 19:22:11 2026 +0200 @@ -549,6 +549,7 @@ endif() set(ORTHANC_DICOM_SOURCES_INTERNAL + ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DataSource/DicomDataSource.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomNetworking/DicomFindAnswers.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomParsing/DicomModification.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/DicomParsing/DicomWebJsonVisitor.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/DataSource/DicomDataSource.cpp Sun May 17 19:22:11 2026 +0200 @@ -0,0 +1,214 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2024-2026 Orthanc Team SRL, Belgium + * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#include "../PrecompiledHeaders.h" +#include "DicomDataSource.h" + +#include "../DicomParsing/ParsedDicomFile.h" +#include "../OrthancException.h" +#include "DataSourceReader.h" +#include "StorageAreaDataSource.h" + +#include <boost/lexical_cast.hpp> + + +namespace Orthanc +{ + class DicomDataSource::BaseIdentifier : public IDataIdentifier + { + public: + virtual StorageAreaDataSource::Range* ReadRange(DataSourceReader& reader) const = 0; + }; + + + class DicomDataSource::WholeIdentifier : public DicomDataSource::BaseIdentifier + { + private: + FileInfo attachment_; + bool checkMD5_; + + public: + explicit WholeIdentifier(const FileInfo& attachment, + bool checkMD5) : + attachment_(attachment), + checkMD5_(checkMD5) + { + } + + virtual bool GetCacheKey(std::string& key) const ORTHANC_OVERRIDE + { + key = (attachment_.GetUuid() + "|" + + boost::lexical_cast<std::string>(attachment_.GetContentType())); + return true; + } + + virtual bool EstimateValueSize(size_t& target) const ORTHANC_OVERRIDE + { + target = attachment_.GetUncompressedSize(); + return true; + } + + virtual StorageAreaDataSource::Range* ReadRange(DataSourceReader& reader) const ORTHANC_OVERRIDE + { + return StorageAreaDataSource::ReadAttachment(reader, attachment_, true /* uncompress */, checkMD5_); + } + }; + + + class DicomDataSource::BeginningIdentifier : public DicomDataSource::BaseIdentifier + { + private: + FileInfo attachment_; + bool hasPixelDataOffset_; + size_t pixelDataOffset_; + + public: + BeginningIdentifier(const FileInfo& attachment, + size_t pixelDataOffset) : + attachment_(attachment), + hasPixelDataOffset_(true), + pixelDataOffset_(pixelDataOffset) + { + } + + virtual bool GetCacheKey(std::string& key) const ORTHANC_OVERRIDE + { + key = (attachment_.GetUuid() + "|" + + boost::lexical_cast<std::string>(attachment_.GetContentType()) + "|" + + boost::lexical_cast<std::string>(pixelDataOffset_)); + return true; + } + + virtual bool EstimateValueSize(size_t& target) const ORTHANC_OVERRIDE + { + target = pixelDataOffset_; + return true; + } + + virtual StorageAreaDataSource::Range* ReadRange(DataSourceReader& reader) const ORTHANC_OVERRIDE + { + return StorageAreaDataSource::ReadBeginning(reader, attachment_, pixelDataOffset_); + } + }; + + + class DicomDataSource::Value : public IDynamicObject + { + private: + std::unique_ptr<ParsedDicomFile> dicom_; + size_t size_; + + public: + Value(ParsedDicomFile* dicom, + size_t size) : + dicom_(dicom), + size_(size) + { + if (dicom == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + } + + ParsedDicomFile& GetDicom() const + { + return *dicom_; + } + + size_t GetSize() const + { + return size_; + } + }; + + + DicomDataSource::DicomDataSource(const boost::shared_ptr<DataSourceReader>& storageAreaReader) : + storageAreaReader_(storageAreaReader) + { + if (storageAreaReader == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + } + + + IDynamicObject* DicomDataSource::Load(const IDataIdentifier& obj) + { + const BaseIdentifier& id = dynamic_cast<const BaseIdentifier&>(obj); + + std::unique_ptr<StorageAreaDataSource::Range> range(id.ReadRange(*storageAreaReader_)); + + // Sanity check + size_t size; + if (!obj.EstimateValueSize(size) || + size != range->GetSize()) + { + throw OrthancException(ErrorCode_InternalError); + } + + return new Value(new ParsedDicomFile(range->GetData(), range->GetSize()), range->GetSize()); + } + + size_t DicomDataSource::GetValueSize(const IDynamicObject& obj) const + { + const Value& value = dynamic_cast<const Value&>(obj); + return value.GetSize(); + } + + + DicomDataSource::Dicom::Dicom(const boost::shared_ptr<IDynamicObject>& value) : + value_(value) + { + if (value.get() == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + } + + + ParsedDicomFile& DicomDataSource::Dicom::Lock::GetContent() const + { + return dynamic_cast<const Value&>(*that_.value_).GetDicom(); + } + + + DicomDataSource::Dicom* DicomDataSource::ReadWhole(DataSourceReader& reader, + const FileInfo& attachment, + bool checkMD5) + { + std::unique_ptr<IDataIdentifier> id(new WholeIdentifier(attachment, checkMD5)); + boost::shared_ptr<IDynamicObject> value = reader.ReadSingle(id.release()); + return new Dicom(value); + } + + + DicomDataSource::Dicom* DicomDataSource::ReadUntilPixelData(DataSourceReader& reader, + const FileInfo& attachment, + size_t pixelDataOffset) + { + std::unique_ptr<IDataIdentifier> id(new BeginningIdentifier(attachment, pixelDataOffset)); + boost::shared_ptr<IDynamicObject> value = reader.ReadSingle(id.release()); + return new Dicom(value); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/DataSource/DicomDataSource.h Sun May 17 19:22:11 2026 +0200 @@ -0,0 +1,105 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2024-2026 Orthanc Team SRL, Belgium + * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if !defined(ORTHANC_ENABLE_DCMTK) +# error The macro ORTHANC_ENABLE_DCMTK must be defined +#endif + +#if ORTHANC_ENABLE_DCMTK != 1 +# error The macro ORTHANC_ENABLE_DCMTK must be set to 1 to use this file +#endif + +#include "../Compatibility.h" +#include "../FileStorage/FileInfo.h" +#include "IDataIdentifier.h" +#include "IDataSource.h" + +#include <boost/shared_ptr.hpp> +#include <boost/thread/mutex.hpp> + + +namespace Orthanc +{ + class DataSourceReader; + class ParsedDicomFile; + + class DicomDataSource : public IDataSource + { + private: + class BaseIdentifier; + class WholeIdentifier; + class BeginningIdentifier; + class Value; + + boost::shared_ptr<DataSourceReader> storageAreaReader_; + + public: + explicit DicomDataSource(const boost::shared_ptr<DataSourceReader>& storageAreaReader); + + virtual IDynamicObject* Load(const IDataIdentifier& obj) ORTHANC_OVERRIDE; + + virtual size_t GetValueSize(const IDynamicObject& obj) const ORTHANC_OVERRIDE; + + class Dicom : public boost::noncopyable + { + private: + boost::mutex mutex_; + boost::shared_ptr<IDynamicObject> value_; + + public: + explicit Dicom(const boost::shared_ptr<IDynamicObject>& value); + + /** + * Access to the DICOM value must be protected by a mutex, as it + * could be shared by multiple threads if caching is enabled in + * the DataSourceReader. + **/ + class Lock : public boost::noncopyable + { + private: + Dicom& that_; + boost::mutex::scoped_lock lock_; + + public: + explicit Lock(Dicom& that) : + that_(that), + lock_(that.mutex_) + { + } + + ParsedDicomFile& GetContent() const; + }; + }; + + static Dicom* ReadWhole(DataSourceReader& reader, + const FileInfo& attachment, + bool checkMD5); + + static Dicom* ReadUntilPixelData(DataSourceReader& reader, + const FileInfo& attachment, + size_t pixelDataOffset); + }; +}
--- a/OrthancFramework/UnitTestsSources/JobsTests.cpp Sun May 17 19:00:37 2026 +0200 +++ b/OrthancFramework/UnitTestsSources/JobsTests.cpp Sun May 17 19:22:11 2026 +0200 @@ -1903,222 +1903,3 @@ ASSERT_EQ(11u, counter_); } - - - -#include "../Sources/DataSource/DataSourceReader.h" -#include "../Sources/DataSource/StorageAreaDataSource.h" -#include "../Sources/FileStorage/FilesystemStorage.h" -#include "../Sources/FileStorage/PluginStorageAreaAdapter.h" -#include "../Sources/MultiThreading/ThreadPool.h" - -#include <dcmtk/dcmdata/dcfilefo.h> - - -class DicomDataSource : public IDataSource -{ -private: - boost::shared_ptr<DataSourceReader> storageAreaReader_; - bool checkMD5_; - - class BaseIdentifier : public IDataIdentifier - { - public: - virtual StorageAreaDataSource::Range* ReadRange(DataSourceReader& reader) const = 0; - }; - - class WholeIdentifier : public BaseIdentifier - { - private: - FileInfo attachment_; - - public: - explicit WholeIdentifier(const FileInfo& attachment) : - attachment_(attachment) - { - } - - virtual bool GetCacheKey(std::string& key) const ORTHANC_OVERRIDE - { - key = (attachment_.GetUuid() + "|" + - boost::lexical_cast<std::string>(attachment_.GetContentType())); - return true; - } - - virtual bool EstimateValueSize(size_t& target) const ORTHANC_OVERRIDE - { - target = attachment_.GetUncompressedSize(); - return true; - } - - virtual StorageAreaDataSource::Range* ReadRange(DataSourceReader& reader) const ORTHANC_OVERRIDE - { - return StorageAreaDataSource::ReadAttachment(reader, attachment_, true /* uncompress */, true /* check MD5 */); - } - }; - - - class BeginningIdentifier : public BaseIdentifier - { - private: - FileInfo attachment_; - bool hasPixelDataOffset_; - size_t pixelDataOffset_; - - public: - BeginningIdentifier(const FileInfo& attachment, - size_t pixelDataOffset) : - attachment_(attachment), - hasPixelDataOffset_(true), - pixelDataOffset_(pixelDataOffset) - { - } - - virtual bool GetCacheKey(std::string& key) const ORTHANC_OVERRIDE - { - key = (attachment_.GetUuid() + "|" + - boost::lexical_cast<std::string>(attachment_.GetContentType()) + "|" + - boost::lexical_cast<std::string>(pixelDataOffset_)); - return true; - } - - virtual bool EstimateValueSize(size_t& target) const ORTHANC_OVERRIDE - { - target = pixelDataOffset_; - return true; - } - - virtual StorageAreaDataSource::Range* ReadRange(DataSourceReader& reader) const ORTHANC_OVERRIDE - { - return StorageAreaDataSource::ReadBeginning(reader, attachment_, pixelDataOffset_); - } - }; - - - class Value : public IDynamicObject - { - private: - std::unique_ptr<ParsedDicomFile> dicom_; - size_t size_; - - public: - Value(ParsedDicomFile* dicom, - size_t size) : - dicom_(dicom), - size_(size) - { - if (dicom == NULL) - { - throw OrthancException(ErrorCode_NullPointer); - } - } - - ParsedDicomFile& GetDicom() const - { - return *dicom_; - } - - size_t GetSize() const - { - return size_; - } - }; - -public: - explicit DicomDataSource(const boost::shared_ptr<DataSourceReader>& storageAreaReader) : - storageAreaReader_(storageAreaReader), - checkMD5_(false) - { - if (storageAreaReader == NULL) - { - throw OrthancException(ErrorCode_NullPointer); - } - } - - void SetCheckMD5(bool check) - { - checkMD5_ = check; - } - - virtual IDynamicObject* Load(const IDataIdentifier& obj) ORTHANC_OVERRIDE - { - const BaseIdentifier& id = dynamic_cast<const BaseIdentifier&>(obj); - - std::unique_ptr<StorageAreaDataSource::Range> range(id.ReadRange(*storageAreaReader_)); - - // Sanity check - size_t size; - if (!obj.EstimateValueSize(size) || - size != range->GetSize()) - { - throw OrthancException(ErrorCode_InternalError); - } - - return new Value(new ParsedDicomFile(range->GetData(), range->GetSize()), range->GetSize()); - } - - virtual size_t GetValueSize(const IDynamicObject& obj) const ORTHANC_OVERRIDE - { - const Value& value = dynamic_cast<const Value&>(obj); - return value.GetSize(); - } - - - class Dicom : public boost::noncopyable - { - private: - boost::mutex mutex_; - boost::shared_ptr<IDynamicObject> value_; - - public: - explicit Dicom(const boost::shared_ptr<IDynamicObject>& value) : - value_(value) - { - if (value.get() == NULL) - { - throw OrthancException(ErrorCode_NullPointer); - } - } - - /** - * Access to the DICOM value must be protected by a mutex, as it - * could be shared by multiple threads if caching is enabled in - * the DataSourceReader. - **/ - class Lock : public boost::noncopyable - { - private: - Dicom& that_; - boost::mutex::scoped_lock lock_; - - public: - explicit Lock(Dicom& that) : - that_(that), - lock_(that.mutex_) - { - } - - ParsedDicomFile& GetContent() const - { - return dynamic_cast<const Value&>(*that_.value_).GetDicom(); - } - }; - }; - - static Dicom* ReadWhole(DataSourceReader& reader, - const FileInfo& attachment) - { - std::unique_ptr<IDataIdentifier> id(new WholeIdentifier(attachment)); - boost::shared_ptr<IDynamicObject> value = reader.ReadSingle(id.release()); - return new Dicom(value); - } - - static Dicom* ReadUntilPixelData(DataSourceReader& reader, - const FileInfo& attachment, - size_t pixelDataOffset) - { - std::unique_ptr<IDataIdentifier> id(new BeginningIdentifier(attachment, pixelDataOffset)); - boost::shared_ptr<IDynamicObject> value = reader.ReadSingle(id.release()); - return new Dicom(value); - } -};
