# HG changeset patch # User Alain Mazy # Date 1728464471 -7200 # Node ID 58c549b881ae44283820a72b4451f5d841601ddf # Parent f75596b224e06421f965a93f96d5b139e7c7bf12# Parent dd2af8692cbc322afe7c7d3596cd91392c3972cf merged find-refactoring -> find-refactoring-clean diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/EclipseCodingStyle.xml --- a/OrthancFramework/Resources/Graveyard/EclipseCodingStyle.xml Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/FromDcmtkBridge.cpp --- a/OrthancFramework/Resources/Graveyard/FromDcmtkBridge.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,192 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - - DcmElement* FromDcmtkBridge::CreateElementForTag(const DicomTag& tag) - { - DcmTag key(tag.GetGroup(), tag.GetElement()); - - if (tag.IsPrivate()) - { - // This raises BitBucket issue 140 (Modifying private tags with - // REST API changes VR from LO to UN) - // https://orthanc.uclouvain.be/bugs/show_bug.cgi?id=140 - LOG(WARNING) << "You are using DCMTK < 3.6.1: All the private tags " - "are considered as having a binary value representation"; - return new DcmOtherByteOtherWord(key); - } - else if (IsBinaryTag(key)) - { - return new DcmOtherByteOtherWord(key); - } - - switch (key.getEVR()) - { - // http://support.dcmtk.org/docs/dcvr_8h-source.html - - /** - * Binary types, handled above - **/ - -#if DCMTK_VERSION_NUMBER >= 361 - case EVR_OD: -#endif - -#if DCMTK_VERSION_NUMBER >= 362 - case EVR_OL: -#endif - - case EVR_OB: // other byte - case EVR_OF: // other float - case EVR_OW: // other word - case EVR_UN: // unknown value representation - case EVR_ox: // OB or OW depending on context - throw OrthancException(ErrorCode_InternalError); - - - /** - * String types. - * http://support.dcmtk.org/docs/classDcmByteString.html - **/ - - case EVR_AS: // age string - return new DcmAgeString(key); - - case EVR_AE: // application entity title - return new DcmApplicationEntity(key); - - case EVR_CS: // code string - return new DcmCodeString(key); - - case EVR_DA: // date string - return new DcmDate(key); - - case EVR_DT: // date time string - return new DcmDateTime(key); - - case EVR_DS: // decimal string - return new DcmDecimalString(key); - - case EVR_IS: // integer string - return new DcmIntegerString(key); - - case EVR_TM: // time string - return new DcmTime(key); - - case EVR_UI: // unique identifier - return new DcmUniqueIdentifier(key); - - case EVR_ST: // short text - return new DcmShortText(key); - - case EVR_LO: // long string - return new DcmLongString(key); - - case EVR_LT: // long text - return new DcmLongText(key); - - case EVR_UT: // unlimited text - return new DcmUnlimitedText(key); - - case EVR_SH: // short string - return new DcmShortString(key); - - case EVR_PN: // person name - return new DcmPersonName(key); - -#if DCMTK_VERSION_NUMBER >= 361 - case EVR_UC: // unlimited characters - return new DcmUnlimitedCharacters(key); -#endif - -#if DCMTK_VERSION_NUMBER >= 361 - case EVR_UR: // URI/URL - return new DcmUniversalResourceIdentifierOrLocator(key); -#endif - - - /** - * Numerical types - **/ - - case EVR_SL: // signed long - return new DcmSignedLong(key); - - case EVR_SS: // signed short - return new DcmSignedShort(key); - - case EVR_UL: // unsigned long - return new DcmUnsignedLong(key); - - case EVR_US: // unsigned short - return new DcmUnsignedShort(key); - - case EVR_FL: // float single-precision - return new DcmFloatingPointSingle(key); - - case EVR_FD: // float double-precision - return new DcmFloatingPointDouble(key); - - - /** - * Sequence types, should never occur at this point. - **/ - - case EVR_SQ: // sequence of items - throw OrthancException(ErrorCode_ParameterOutOfRange); - - - /** - * TODO - **/ - - case EVR_AT: // attribute tag - throw OrthancException(ErrorCode_NotImplemented); - - - /** - * Internal to DCMTK. - **/ - - case EVR_xs: // SS or US depending on context - case EVR_lt: // US, SS or OW depending on context, used for LUT Data (thus the name) - case EVR_na: // na="not applicable", for data which has no VR - case EVR_up: // up="unsigned pointer", used internally for DICOMDIR suppor - case EVR_item: // used internally for items - case EVR_metainfo: // used internally for meta info datasets - case EVR_dataset: // used internally for datasets - case EVR_fileFormat: // used internally for DICOM files - case EVR_dicomDir: // used internally for DICOMDIR objects - case EVR_dirRecord: // used internally for DICOMDIR records - case EVR_pixelSQ: // used internally for pixel sequences in a compressed image - case EVR_pixelItem: // used internally for pixel items in a compressed image - case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR) - case EVR_PixelData: // used internally for uncompressed pixeld data - case EVR_OverlayData: // used internally for overlay data - case EVR_UNKNOWN2B: // used internally for elements with unknown VR with 2-byte length field in explicit VR - default: - break; - } - - throw OrthancException(ErrorCode_InternalError); - } diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/BagOfTasks.h --- a/OrthancFramework/Resources/Graveyard/Multithreading/BagOfTasks.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#pragma once - -#include "../ICommand.h" - -#include -#include - -namespace Orthanc -{ - class BagOfTasks : public boost::noncopyable - { - private: - typedef std::list Tasks; - - Tasks tasks_; - - public: - ~BagOfTasks() - { - for (Tasks::iterator it = tasks_.begin(); it != tasks_.end(); ++it) - { - delete *it; - } - } - - ICommand* Pop() - { - ICommand* task = tasks_.front(); - tasks_.pop_front(); - return task; - } - - void Push(ICommand* task) // Takes ownership - { - if (task != NULL) - { - tasks_.push_back(task); - } - } - - size_t GetSize() const - { - return tasks_.size(); - } - - bool IsEmpty() const - { - return tasks_.empty(); - } - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/BagOfTasksProcessor.cpp --- a/OrthancFramework/Resources/Graveyard/Multithreading/BagOfTasksProcessor.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,268 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#include "../PrecompiledHeaders.h" -#include "BagOfTasksProcessor.h" - -#include "../Logging.h" -#include "../OrthancException.h" - -#include - -namespace Orthanc -{ - class BagOfTasksProcessor::Task : public IDynamicObject - { - private: - uint64_t bag_; - std::auto_ptr command_; - - public: - Task(uint64_t bag, - ICommand* command) : - bag_(bag), - command_(command) - { - } - - bool Execute() - { - try - { - return command_->Execute(); - } - catch (OrthancException& e) - { - LOG(ERROR) << "Exception while processing a bag of tasks: " << e.What(); - return false; - } - catch (std::runtime_error& e) - { - LOG(ERROR) << "Runtime exception while processing a bag of tasks: " << e.what(); - return false; - } - catch (...) - { - LOG(ERROR) << "Native exception while processing a bag of tasks"; - return false; - } - } - - uint64_t GetBag() - { - return bag_; - } - }; - - - void BagOfTasksProcessor::SignalProgress(Task& task, - Bag& bag) - { - assert(bag.done_ < bag.size_); - - bag.done_ += 1; - - if (bag.done_ == bag.size_) - { - exitStatus_[task.GetBag()] = (bag.status_ == BagStatus_Running); - bagFinished_.notify_all(); - } - } - - void BagOfTasksProcessor::Worker(BagOfTasksProcessor* that) - { - while (that->continue_) - { - std::auto_ptr obj(that->queue_.Dequeue(100)); - if (obj.get() != NULL) - { - Task& task = *dynamic_cast(obj.get()); - - { - boost::mutex::scoped_lock lock(that->mutex_); - - Bags::iterator bag = that->bags_.find(task.GetBag()); - assert(bag != that->bags_.end()); - assert(bag->second.done_ < bag->second.size_); - - if (bag->second.status_ != BagStatus_Running) - { - // Do not execute this task, as its parent bag of tasks - // has failed or is tagged as canceled - that->SignalProgress(task, bag->second); - continue; - } - } - - bool success = task.Execute(); - - { - boost::mutex::scoped_lock lock(that->mutex_); - - Bags::iterator bag = that->bags_.find(task.GetBag()); - assert(bag != that->bags_.end()); - - if (!success) - { - bag->second.status_ = BagStatus_Failed; - } - - that->SignalProgress(task, bag->second); - } - } - } - } - - - void BagOfTasksProcessor::Cancel(int64_t bag) - { - boost::mutex::scoped_lock lock(mutex_); - - Bags::iterator it = bags_.find(bag); - if (it != bags_.end()) - { - it->second.status_ = BagStatus_Canceled; - } - } - - - bool BagOfTasksProcessor::Join(int64_t bag) - { - boost::mutex::scoped_lock lock(mutex_); - - while (continue_) - { - ExitStatus::iterator it = exitStatus_.find(bag); - if (it == exitStatus_.end()) // The bag is still running - { - bagFinished_.wait(lock); - } - else - { - bool status = it->second; - exitStatus_.erase(it); - return status; - } - } - - return false; // The processor is stopping - } - - - float BagOfTasksProcessor::GetProgress(int64_t bag) - { - boost::mutex::scoped_lock lock(mutex_); - - Bags::const_iterator it = bags_.find(bag); - if (it == bags_.end()) - { - // The bag of tasks has finished - return 1.0f; - } - else - { - return (static_cast(it->second.done_) / - static_cast(it->second.size_)); - } - } - - - bool BagOfTasksProcessor::Handle::Join() - { - if (hasJoined_) - { - return status_; - } - else - { - status_ = that_.Join(bag_); - hasJoined_ = true; - return status_; - } - } - - - BagOfTasksProcessor::BagOfTasksProcessor(size_t countThreads) : - countBags_(0), - continue_(true) - { - if (countThreads == 0) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - - threads_.resize(countThreads); - - for (size_t i = 0; i < threads_.size(); i++) - { - threads_[i] = new boost::thread(Worker, this); - } - } - - - BagOfTasksProcessor::~BagOfTasksProcessor() - { - continue_ = false; - - bagFinished_.notify_all(); // Wakes up all the pending "Join()" - - for (size_t i = 0; i < threads_.size(); i++) - { - if (threads_[i]) - { - if (threads_[i]->joinable()) - { - threads_[i]->join(); - } - - delete threads_[i]; - threads_[i] = NULL; - } - } - } - - - BagOfTasksProcessor::Handle* BagOfTasksProcessor::Submit(BagOfTasks& tasks) - { - if (tasks.GetSize() == 0) - { - return new Handle(*this, 0, true); - } - - boost::mutex::scoped_lock lock(mutex_); - - uint64_t id = countBags_; - countBags_ += 1; - - Bag bag(tasks.GetSize()); - bags_[id] = bag; - - while (!tasks.IsEmpty()) - { - queue_.Enqueue(new Task(id, tasks.Pop())); - } - - return new Handle(*this, id, false); - } -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/BagOfTasksProcessor.h --- a/OrthancFramework/Resources/Graveyard/Multithreading/BagOfTasksProcessor.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#pragma once - -#include "BagOfTasks.h" -#include "SharedMessageQueue.h" - -#include -#include - -namespace Orthanc -{ - class BagOfTasksProcessor : public boost::noncopyable - { - private: - enum BagStatus - { - BagStatus_Running, - BagStatus_Canceled, - BagStatus_Failed - }; - - - struct Bag - { - size_t size_; - size_t done_; - BagStatus status_; - - Bag() : - size_(0), - done_(0), - status_(BagStatus_Failed) - { - } - - explicit Bag(size_t size) : - size_(size), - done_(0), - status_(BagStatus_Running) - { - } - }; - - class Task; - - - typedef std::map Bags; - typedef std::map ExitStatus; - - SharedMessageQueue queue_; - - boost::mutex mutex_; - uint64_t countBags_; - Bags bags_; - std::vector threads_; - ExitStatus exitStatus_; - bool continue_; - - boost::condition_variable bagFinished_; - - static void Worker(BagOfTasksProcessor* that); - - void Cancel(int64_t bag); - - bool Join(int64_t bag); - - float GetProgress(int64_t bag); - - void SignalProgress(Task& task, - Bag& bag); - - public: - class Handle : public boost::noncopyable - { - friend class BagOfTasksProcessor; - - private: - BagOfTasksProcessor& that_; - uint64_t bag_; - bool hasJoined_; - bool status_; - - Handle(BagOfTasksProcessor& that, - uint64_t bag, - bool empty) : - that_(that), - bag_(bag), - hasJoined_(empty) - { - } - - public: - ~Handle() - { - Join(); - } - - void Cancel() - { - that_.Cancel(bag_); - } - - bool Join(); - - float GetProgress() - { - return that_.GetProgress(bag_); - } - }; - - - explicit BagOfTasksProcessor(size_t countThreads); - - ~BagOfTasksProcessor(); - - Handle* Submit(BagOfTasks& tasks); - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/ICommand.h --- a/OrthancFramework/Resources/Graveyard/Multithreading/ICommand.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#pragma once - -#include "IDynamicObject.h" - -namespace Orthanc -{ - /** - * This class is the base class for the "Command" design pattern. - * http://en.wikipedia.org/wiki/Command_pattern - **/ - class ICommand : public IDynamicObject - { - public: - virtual bool Execute() = 0; - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/ILockable.h --- a/OrthancFramework/Resources/Graveyard/Multithreading/ILockable.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#pragma once - -#include - -namespace Orthanc -{ - class ILockable : public boost::noncopyable - { - friend class Locker; - - protected: - virtual void Lock() = 0; - - virtual void Unlock() = 0; - - public: - virtual ~ILockable() - { - } - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/Locker.h --- a/OrthancFramework/Resources/Graveyard/Multithreading/Locker.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#pragma once - -#include "ILockable.h" - -namespace Orthanc -{ - class Locker : public boost::noncopyable - { - private: - ILockable& lockable_; - - public: - Locker(ILockable& lockable) : lockable_(lockable) - { - lockable_.Lock(); - } - - virtual ~Locker() - { - lockable_.Unlock(); - } - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/Mutex.cpp --- a/OrthancFramework/Resources/Graveyard/Multithreading/Mutex.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#include "../PrecompiledHeaders.h" -#include "Mutex.h" - -#include "../OrthancException.h" - -#if defined(_WIN32) -#include -#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) -#include -#else -#error Support your platform here -#endif - -namespace Orthanc -{ -#if defined (_WIN32) - - struct Mutex::PImpl - { - CRITICAL_SECTION criticalSection_; - }; - - Mutex::Mutex() - { - pimpl_ = new PImpl; - ::InitializeCriticalSection(&pimpl_->criticalSection_); - } - - Mutex::~Mutex() - { - ::DeleteCriticalSection(&pimpl_->criticalSection_); - delete pimpl_; - } - - void Mutex::Lock() - { - ::EnterCriticalSection(&pimpl_->criticalSection_); - } - - void Mutex::Unlock() - { - ::LeaveCriticalSection(&pimpl_->criticalSection_); - } - - -#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) - - struct Mutex::PImpl - { - pthread_mutex_t mutex_; - }; - - Mutex::Mutex() - { - pimpl_ = new PImpl; - - if (pthread_mutex_init(&pimpl_->mutex_, NULL) != 0) - { - delete pimpl_; - throw OrthancException(ErrorCode_InternalError); - } - } - - Mutex::~Mutex() - { - pthread_mutex_destroy(&pimpl_->mutex_); - delete pimpl_; - } - - void Mutex::Lock() - { - if (pthread_mutex_lock(&pimpl_->mutex_) != 0) - { - throw OrthancException(ErrorCode_InternalError); - } - } - - void Mutex::Unlock() - { - if (pthread_mutex_unlock(&pimpl_->mutex_) != 0) - { - throw OrthancException(ErrorCode_InternalError); - } - } - -#else -#error Support your plateform here -#endif -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/Mutex.h --- a/OrthancFramework/Resources/Graveyard/Multithreading/Mutex.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#pragma once - -#include "ILockable.h" - -namespace Orthanc -{ - class Mutex : public ILockable - { - private: - struct PImpl; - - PImpl *pimpl_; - - protected: - virtual void Lock(); - - virtual void Unlock(); - - public: - Mutex(); - - ~Mutex(); - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/ReaderWriterLock.cpp --- a/OrthancFramework/Resources/Graveyard/Multithreading/ReaderWriterLock.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#include "../PrecompiledHeaders.h" -#include "ReaderWriterLock.h" - -#include - -namespace Orthanc -{ - namespace - { - // Anonymous namespace to avoid clashes between compilation - // modules. - - class ReaderLockable : public ILockable - { - private: - boost::shared_mutex& lock_; - - protected: - virtual void Lock() - { - lock_.lock_shared(); - } - - virtual void Unlock() - { - lock_.unlock_shared(); - } - - public: - explicit ReaderLockable(boost::shared_mutex& lock) : lock_(lock) - { - } - }; - - - class WriterLockable : public ILockable - { - private: - boost::shared_mutex& lock_; - - protected: - virtual void Lock() - { - lock_.lock(); - } - - virtual void Unlock() - { - lock_.unlock(); - } - - public: - explicit WriterLockable(boost::shared_mutex& lock) : lock_(lock) - { - } - }; - } - - struct ReaderWriterLock::PImpl - { - boost::shared_mutex lock_; - ReaderLockable reader_; - WriterLockable writer_; - - PImpl() : reader_(lock_), writer_(lock_) - { - } - }; - - - ReaderWriterLock::ReaderWriterLock() - { - pimpl_ = new PImpl; - } - - - ReaderWriterLock::~ReaderWriterLock() - { - delete pimpl_; - } - - - ILockable& ReaderWriterLock::ForReader() - { - return pimpl_->reader_; - } - - - ILockable& ReaderWriterLock::ForWriter() - { - return pimpl_->writer_; - } -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/Multithreading/ReaderWriterLock.h --- a/OrthancFramework/Resources/Graveyard/Multithreading/ReaderWriterLock.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - -#pragma once - -#include "ILockable.h" - -#include - -namespace Orthanc -{ - class ReaderWriterLock : public boost::noncopyable - { - private: - struct PImpl; - - PImpl *pimpl_; - - public: - ReaderWriterLock(); - - virtual ~ReaderWriterLock(); - - ILockable& ForReader(); - - ILockable& ForWriter(); - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancFramework/Resources/Graveyard/TestTranscoding.cpp --- a/OrthancFramework/Resources/Graveyard/TestTranscoding.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,991 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 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 - * . - **/ - - - bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer, - DcmFileFormat& dicom, - DicomTransferSyntax syntax) - { - E_TransferSyntax xfer; - if (!LookupDcmtkTransferSyntax(xfer, syntax)) - { - return false; - } - else if (!dicom.validateMetaInfo(xfer).good()) - { - throw OrthancException(ErrorCode_InternalError, - "Cannot setup the transfer syntax to write a DICOM instance"); - } - else - { - return SaveToMemoryBufferInternal(buffer, dicom, xfer); - } - } - - - bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer, - DcmFileFormat& dicom) - { - E_TransferSyntax xfer = dicom.getDataset()->getCurrentXfer(); - if (xfer == EXS_Unknown) - { - throw OrthancException(ErrorCode_InternalError, - "Cannot write a DICOM instance with unknown transfer syntax"); - } - else if (!dicom.validateMetaInfo(xfer).good()) - { - throw OrthancException(ErrorCode_InternalError, - "Cannot setup the transfer syntax to write a DICOM instance"); - } - else - { - return SaveToMemoryBufferInternal(buffer, dicom, xfer); - } - } - - - - - -#include -#include -#include - -#include "../Core/DicomParsing/Internals/DicomFrameIndex.h" - -namespace Orthanc -{ - class IParsedDicomImage : public boost::noncopyable - { - public: - virtual ~IParsedDicomImage() - { - } - - virtual DicomTransferSyntax GetTransferSyntax() = 0; - - virtual std::string GetSopClassUid() = 0; - - virtual std::string GetSopInstanceUid() = 0; - - virtual unsigned int GetFramesCount() = 0; - - // Can return NULL, for compressed transfer syntaxes - virtual ImageAccessor* GetUncompressedFrame(unsigned int frame) = 0; - - virtual void GetCompressedFrame(std::string& target, - unsigned int frame) = 0; - - virtual void WriteToMemoryBuffer(std::string& target) = 0; - }; - - - class IDicomImageReader : public boost::noncopyable - { - public: - virtual ~IDicomImageReader() - { - } - - virtual IParsedDicomImage* Read(const void* data, - size_t size) = 0; - - virtual IParsedDicomImage* Transcode(const void* data, - size_t size, - DicomTransferSyntax syntax, - bool allowNewSopInstanceUid) = 0; - }; - - - class DcmtkImageReader : public IDicomImageReader - { - private: - class Image : public IParsedDicomImage - { - private: - std::unique_ptr dicom_; - std::unique_ptr index_; - DicomTransferSyntax transferSyntax_; - std::string sopClassUid_; - std::string sopInstanceUid_; - - static std::string GetStringTag(DcmDataset& dataset, - const DcmTagKey& tag) - { - const char* value = NULL; - - if (!dataset.findAndGetString(tag, value).good() || - value == NULL) - { - throw OrthancException(ErrorCode_BadFileFormat, - "Missing SOP class/instance UID in DICOM instance"); - } - else - { - return std::string(value); - } - } - - public: - Image(DcmFileFormat* dicom, - DicomTransferSyntax syntax) : - dicom_(dicom), - transferSyntax_(syntax) - { - if (dicom == NULL || - dicom_->getDataset() == NULL) - { - throw OrthancException(ErrorCode_NullPointer); - } - - DcmDataset& dataset = *dicom_->getDataset(); - index_.reset(new DicomFrameIndex(dataset)); - - sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID); - sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID); - } - - virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE - { - return transferSyntax_; - } - - virtual std::string GetSopClassUid() ORTHANC_OVERRIDE - { - return sopClassUid_; - } - - virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE - { - return sopInstanceUid_; - } - - virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE - { - return index_->GetFramesCount(); - } - - virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE - { - assert(dicom_.get() != NULL); - if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, transferSyntax_)) - { - throw OrthancException(ErrorCode_InternalError, - "Cannot write the DICOM instance to a memory buffer"); - } - } - - virtual ImageAccessor* GetUncompressedFrame(unsigned int frame) ORTHANC_OVERRIDE - { - assert(dicom_.get() != NULL && - dicom_->getDataset() != NULL); - return DicomImageDecoder::Decode(*dicom_->getDataset(), frame); - } - - virtual void GetCompressedFrame(std::string& target, - unsigned int frame) ORTHANC_OVERRIDE - { - assert(index_.get() != NULL); - index_->GetRawFrame(target, frame); - } - }; - - unsigned int lossyQuality_; - - static DicomTransferSyntax DetectTransferSyntax(DcmFileFormat& dicom) - { - if (dicom.getDataset() == NULL) - { - throw OrthancException(ErrorCode_InternalError); - } - - DcmDataset& dataset = *dicom.getDataset(); - - E_TransferSyntax xfer = dataset.getCurrentXfer(); - if (xfer == EXS_Unknown) - { - dataset.updateOriginalXfer(); - xfer = dataset.getCurrentXfer(); - if (xfer == EXS_Unknown) - { - throw OrthancException(ErrorCode_BadFileFormat, - "Cannot determine the transfer syntax of the DICOM instance"); - } - } - - DicomTransferSyntax syntax; - if (FromDcmtkBridge::LookupOrthancTransferSyntax(syntax, xfer)) - { - return syntax; - } - else - { - throw OrthancException( - ErrorCode_BadFileFormat, - "Unsupported transfer syntax: " + boost::lexical_cast(xfer)); - } - } - - - static uint16_t GetBitsStored(DcmFileFormat& dicom) - { - if (dicom.getDataset() == NULL) - { - throw OrthancException(ErrorCode_InternalError); - } - - uint16_t bitsStored; - if (dicom.getDataset()->findAndGetUint16(DCM_BitsStored, bitsStored).good()) - { - return bitsStored; - } - else - { - throw OrthancException(ErrorCode_BadFileFormat, - "Missing \"Bits Stored\" tag in DICOM instance"); - } - } - - - public: - DcmtkImageReader() : - lossyQuality_(90) - { - } - - void SetLossyQuality(unsigned int quality) - { - if (quality <= 0 || - quality > 100) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - else - { - lossyQuality_ = quality; - } - } - - unsigned int GetLossyQuality() const - { - return lossyQuality_; - } - - virtual IParsedDicomImage* Read(const void* data, - size_t size) - { - std::unique_ptr dicom(FromDcmtkBridge::LoadFromMemoryBuffer(data, size)); - if (dicom.get() == NULL) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - DicomTransferSyntax transferSyntax = DetectTransferSyntax(*dicom); - - return new Image(dicom.release(), transferSyntax); - } - - virtual IParsedDicomImage* Transcode(const void* data, - size_t size, - DicomTransferSyntax syntax, - bool allowNewSopInstanceUid) - { - std::unique_ptr dicom(FromDcmtkBridge::LoadFromMemoryBuffer(data, size)); - if (dicom.get() == NULL) - { - throw OrthancException(ErrorCode_BadFileFormat); - } - - const uint16_t bitsStored = GetBitsStored(*dicom); - - if (syntax == DetectTransferSyntax(*dicom)) - { - // No transcoding is needed - return new Image(dicom.release(), syntax); - } - - if (syntax == DicomTransferSyntax_LittleEndianImplicit && - FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_LittleEndianImplicit, NULL)) - { - return new Image(dicom.release(), syntax); - } - - if (syntax == DicomTransferSyntax_LittleEndianExplicit && - FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_LittleEndianExplicit, NULL)) - { - return new Image(dicom.release(), syntax); - } - - if (syntax == DicomTransferSyntax_BigEndianExplicit && - FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_BigEndianExplicit, NULL)) - { - return new Image(dicom.release(), syntax); - } - - if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit && - FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_DeflatedLittleEndianExplicit, NULL)) - { - return new Image(dicom.release(), syntax); - } - -#if ORTHANC_ENABLE_JPEG == 1 - if (syntax == DicomTransferSyntax_JPEGProcess1 && - allowNewSopInstanceUid && - bitsStored == 8) - { - DJ_RPLossy rpLossy(lossyQuality_); - - if (FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_JPEGProcess1, &rpLossy)) - { - return new Image(dicom.release(), syntax); - } - } -#endif - -#if ORTHANC_ENABLE_JPEG == 1 - if (syntax == DicomTransferSyntax_JPEGProcess2_4 && - allowNewSopInstanceUid && - bitsStored <= 12) - { - DJ_RPLossy rpLossy(lossyQuality_); - if (FromDcmtkBridge::Transcode(*dicom, DicomTransferSyntax_JPEGProcess2_4, &rpLossy)) - { - return new Image(dicom.release(), syntax); - } - } -#endif - - //LOG(INFO) << "Unable to transcode DICOM image using the built-in reader"; - return NULL; - } - }; - - - - class IDicomTranscoder1 : public boost::noncopyable - { - public: - virtual ~IDicomTranscoder1() - { - } - - virtual DcmFileFormat& GetDicom() = 0; - - virtual DicomTransferSyntax GetTransferSyntax() = 0; - - virtual std::string GetSopClassUid() = 0; - - virtual std::string GetSopInstanceUid() = 0; - - virtual unsigned int GetFramesCount() = 0; - - virtual ImageAccessor* DecodeFrame(unsigned int frame) = 0; - - virtual void GetCompressedFrame(std::string& target, - unsigned int frame) = 0; - - // NB: Transcoding can change the value of "GetSopInstanceUid()" - // and "GetTransferSyntax()" if lossy compression is applied - virtual bool Transcode(std::string& target, - DicomTransferSyntax syntax, - bool allowNewSopInstanceUid) = 0; - - virtual void WriteToMemoryBuffer(std::string& target) = 0; - }; - - - class DcmtkTranscoder2 : public IDicomTranscoder1 - { - private: - std::unique_ptr dicom_; - std::unique_ptr index_; - DicomTransferSyntax transferSyntax_; - std::string sopClassUid_; - std::string sopInstanceUid_; - uint16_t bitsStored_; - unsigned int lossyQuality_; - - static std::string GetStringTag(DcmDataset& dataset, - const DcmTagKey& tag) - { - const char* value = NULL; - - if (!dataset.findAndGetString(tag, value).good() || - value == NULL) - { - throw OrthancException(ErrorCode_BadFileFormat, - "Missing SOP class/instance UID in DICOM instance"); - } - else - { - return std::string(value); - } - } - - void Setup(DcmFileFormat* dicom) - { - lossyQuality_ = 90; - - dicom_.reset(dicom); - - if (dicom == NULL || - dicom_->getDataset() == NULL) - { - throw OrthancException(ErrorCode_NullPointer); - } - - DcmDataset& dataset = *dicom_->getDataset(); - index_.reset(new DicomFrameIndex(dataset)); - - E_TransferSyntax xfer = dataset.getCurrentXfer(); - if (xfer == EXS_Unknown) - { - dataset.updateOriginalXfer(); - xfer = dataset.getCurrentXfer(); - if (xfer == EXS_Unknown) - { - throw OrthancException(ErrorCode_BadFileFormat, - "Cannot determine the transfer syntax of the DICOM instance"); - } - } - - if (!FromDcmtkBridge::LookupOrthancTransferSyntax(transferSyntax_, xfer)) - { - throw OrthancException( - ErrorCode_BadFileFormat, - "Unsupported transfer syntax: " + boost::lexical_cast(xfer)); - } - - if (!dataset.findAndGetUint16(DCM_BitsStored, bitsStored_).good()) - { - throw OrthancException(ErrorCode_BadFileFormat, - "Missing \"Bits Stored\" tag in DICOM instance"); - } - - sopClassUid_ = GetStringTag(dataset, DCM_SOPClassUID); - sopInstanceUid_ = GetStringTag(dataset, DCM_SOPInstanceUID); - } - - public: - DcmtkTranscoder2(DcmFileFormat* dicom) // Takes ownership - { - Setup(dicom); - } - - DcmtkTranscoder2(const void* dicom, - size_t size) - { - Setup(FromDcmtkBridge::LoadFromMemoryBuffer(dicom, size)); - } - - void SetLossyQuality(unsigned int quality) - { - if (quality <= 0 || - quality > 100) - { - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - else - { - lossyQuality_ = quality; - } - } - - unsigned int GetLossyQuality() const - { - return lossyQuality_; - } - - unsigned int GetBitsStored() const - { - return bitsStored_; - } - - virtual DcmFileFormat& GetDicom() - { - assert(dicom_ != NULL); - return *dicom_; - } - - virtual DicomTransferSyntax GetTransferSyntax() ORTHANC_OVERRIDE - { - return transferSyntax_; - } - - virtual std::string GetSopClassUid() ORTHANC_OVERRIDE - { - return sopClassUid_; - } - - virtual std::string GetSopInstanceUid() ORTHANC_OVERRIDE - { - return sopInstanceUid_; - } - - virtual unsigned int GetFramesCount() ORTHANC_OVERRIDE - { - return index_->GetFramesCount(); - } - - virtual void WriteToMemoryBuffer(std::string& target) ORTHANC_OVERRIDE - { - if (!FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_)) - { - throw OrthancException(ErrorCode_InternalError, - "Cannot write the DICOM instance to a memory buffer"); - } - } - - virtual ImageAccessor* DecodeFrame(unsigned int frame) ORTHANC_OVERRIDE - { - assert(dicom_->getDataset() != NULL); - return DicomImageDecoder::Decode(*dicom_->getDataset(), frame); - } - - virtual void GetCompressedFrame(std::string& target, - unsigned int frame) ORTHANC_OVERRIDE - { - index_->GetRawFrame(target, frame); - } - - virtual bool Transcode(std::string& target, - DicomTransferSyntax syntax, - bool allowNewSopInstanceUid) ORTHANC_OVERRIDE - { - assert(dicom_ != NULL && - dicom_->getDataset() != NULL); - - if (syntax == GetTransferSyntax()) - { - printf("NO TRANSCODING\n"); - - // No change in the transfer syntax => simply serialize the current dataset - WriteToMemoryBuffer(target); - return true; - } - - printf(">> %d\n", bitsStored_); - - if (syntax == DicomTransferSyntax_LittleEndianImplicit && - FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && - FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) - { - transferSyntax_ = DicomTransferSyntax_LittleEndianImplicit; - return true; - } - - if (syntax == DicomTransferSyntax_LittleEndianExplicit && - FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && - FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) - { - transferSyntax_ = DicomTransferSyntax_LittleEndianExplicit; - return true; - } - - if (syntax == DicomTransferSyntax_BigEndianExplicit && - FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && - FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) - { - transferSyntax_ = DicomTransferSyntax_BigEndianExplicit; - return true; - } - - if (syntax == DicomTransferSyntax_DeflatedLittleEndianExplicit && - FromDcmtkBridge::Transcode(*dicom_, syntax, NULL) && - FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) - { - transferSyntax_ = DicomTransferSyntax_DeflatedLittleEndianExplicit; - return true; - } - -#if ORTHANC_ENABLE_JPEG == 1 - if (syntax == DicomTransferSyntax_JPEGProcess1 && - allowNewSopInstanceUid && - GetBitsStored() == 8) - { - DJ_RPLossy rpLossy(lossyQuality_); - - if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) && - FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) - { - transferSyntax_ = DicomTransferSyntax_JPEGProcess1; - sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID); - return true; - } - } -#endif - -#if ORTHANC_ENABLE_JPEG == 1 - if (syntax == DicomTransferSyntax_JPEGProcess2_4 && - allowNewSopInstanceUid && - GetBitsStored() <= 12) - { - DJ_RPLossy rpLossy(lossyQuality_); - if (FromDcmtkBridge::Transcode(*dicom_, syntax, &rpLossy) && - FromDcmtkBridge::SaveToMemoryBuffer(target, *dicom_, syntax)) - { - transferSyntax_ = DicomTransferSyntax_JPEGProcess2_4; - sopInstanceUid_ = GetStringTag(*dicom_->getDataset(), DCM_SOPInstanceUID); - return true; - } - } -#endif - - return false; - } - }; -} - - - - -#include - - -static void TestFile(const std::string& path) -{ - static unsigned int count = 0; - count++; - - - printf("** %s\n", path.c_str()); - - std::string s; - SystemToolbox::ReadFile(s, path); - - Orthanc::DcmtkTranscoder2 transcoder(s.c_str(), s.size()); - - /*if (transcoder.GetBitsStored() != 8) // TODO - return; */ - - { - char buf[1024]; - sprintf(buf, "/tmp/source-%06d.dcm", count); - printf(">> %s\n", buf); - Orthanc::SystemToolbox::WriteFile(s, buf); - } - - printf("[%s] [%s] [%s] %d %d\n", GetTransferSyntaxUid(transcoder.GetTransferSyntax()), - transcoder.GetSopClassUid().c_str(), transcoder.GetSopInstanceUid().c_str(), - transcoder.GetFramesCount(), transcoder.GetTransferSyntax()); - - for (size_t i = 0; i < transcoder.GetFramesCount(); i++) - { - std::string f; - transcoder.GetCompressedFrame(f, i); - - if (i == 0) - { - char buf[1024]; - sprintf(buf, "/tmp/frame-%06d.raw", count); - printf(">> %s\n", buf); - Orthanc::SystemToolbox::WriteFile(f, buf); - } - } - - { - std::string t; - transcoder.WriteToMemoryBuffer(t); - - Orthanc::DcmtkTranscoder2 transcoder2(t.c_str(), t.size()); - printf(">> %d %d ; %lu bytes\n", transcoder.GetTransferSyntax(), transcoder2.GetTransferSyntax(), t.size()); - } - - { - std::string a = transcoder.GetSopInstanceUid(); - DicomTransferSyntax b = transcoder.GetTransferSyntax(); - - DicomTransferSyntax syntax = DicomTransferSyntax_JPEGProcess2_4; - //DicomTransferSyntax syntax = DicomTransferSyntax_LittleEndianExplicit; - - std::string t; - bool ok = transcoder.Transcode(t, syntax, true); - printf("Transcoding: %d\n", ok); - - if (ok) - { - printf("[%s] => [%s]\n", a.c_str(), transcoder.GetSopInstanceUid().c_str()); - printf("[%s] => [%s]\n", GetTransferSyntaxUid(b), - GetTransferSyntaxUid(transcoder.GetTransferSyntax())); - - { - char buf[1024]; - sprintf(buf, "/tmp/transcoded-%06d.dcm", count); - printf(">> %s\n", buf); - Orthanc::SystemToolbox::WriteFile(t, buf); - } - - Orthanc::DcmtkTranscoder2 transcoder2(t.c_str(), t.size()); - printf(" => transcoded transfer syntax %d ; %lu bytes\n", transcoder2.GetTransferSyntax(), t.size()); - } - } - - printf("\n"); -} - -TEST(Toto, DISABLED_Transcode) -{ - //OFLog::configure(OFLogger::DEBUG_LOG_LEVEL); - - if (1) - { - const char* const PATH = "/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes"; - - for (boost::filesystem::directory_iterator it(PATH); - it != boost::filesystem::directory_iterator(); ++it) - { - if (boost::filesystem::is_regular_file(it->status())) - { - TestFile(it->path().string()); - } - } - } - - if (0) - { - TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Multiframe.dcm"); - TestFile("/home/jodogne/Subversion/orthanc-tests/Database/Issue44/Monochrome1-Jpeg.dcm"); - } - - if (0) - { - TestFile("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/1.2.840.10008.1.2.1.dcm"); - } -} - - -TEST(Toto, DISABLED_Transcode2) -{ - for (int i = 0; i <= DicomTransferSyntax_XML; i++) - { - DicomTransferSyntax a = (DicomTransferSyntax) i; - - std::string path = ("/home/jodogne/Subversion/orthanc-tests/Database/TransferSyntaxes/" + - std::string(GetTransferSyntaxUid(a)) + ".dcm"); - if (Orthanc::SystemToolbox::IsRegularFile(path)) - { - printf("\n======= %s\n", GetTransferSyntaxUid(a)); - - std::string source; - Orthanc::SystemToolbox::ReadFile(source, path); - - DcmtkImageReader reader; - - { - std::unique_ptr image( - reader.Read(source.c_str(), source.size())); - ASSERT_TRUE(image.get() != NULL); - ASSERT_EQ(a, image->GetTransferSyntax()); - - std::string target; - image->WriteToMemoryBuffer(target); - } - - for (int j = 0; j <= DicomTransferSyntax_XML; j++) - { - DicomTransferSyntax b = (DicomTransferSyntax) j; - //if (a == b) continue; - - std::unique_ptr image( - reader.Transcode(source.c_str(), source.size(), b, true)); - if (image.get() != NULL) - { - printf("[%s] -> [%s]\n", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b)); - - std::string target; - image->WriteToMemoryBuffer(target); - - char buf[1024]; - sprintf(buf, "/tmp/%s-%s.dcm", GetTransferSyntaxUid(a), GetTransferSyntaxUid(b)); - - SystemToolbox::WriteFile(target, buf); - } - else if (a != DicomTransferSyntax_JPEG2000 && - a != DicomTransferSyntax_JPEG2000LosslessOnly) - { - ASSERT_TRUE(b != DicomTransferSyntax_LittleEndianImplicit && - b != DicomTransferSyntax_LittleEndianExplicit && - b != DicomTransferSyntax_BigEndianExplicit && - b != DicomTransferSyntax_DeflatedLittleEndianExplicit); - } - } - } - } -} - - -#include "../Core/DicomNetworking/DicomAssociation.h" -#include "../Core/DicomNetworking/DicomControlUserConnection.h" -#include "../Core/DicomNetworking/DicomStoreUserConnection.h" - -TEST(Toto, DISABLED_DicomAssociation) -{ - DicomAssociationParameters params; - params.SetLocalApplicationEntityTitle("ORTHANC"); - params.SetRemoteApplicationEntityTitle("PACS"); - params.SetRemotePort(2001); - -#if 0 - DicomAssociation assoc; - assoc.ProposeGenericPresentationContext(UID_StorageCommitmentPushModelSOPClass); - assoc.ProposeGenericPresentationContext(UID_VerificationSOPClass); - assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage, - DicomTransferSyntax_JPEGProcess1); - assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage, - DicomTransferSyntax_JPEGProcess2_4); - assoc.ProposePresentationContext(UID_ComputedRadiographyImageStorage, - DicomTransferSyntax_JPEG2000); - - assoc.Open(params); - - int presID = ASC_findAcceptedPresentationContextID(&assoc.GetDcmtkAssociation(), UID_ComputedRadiographyImageStorage); - printf(">> %d\n", presID); - - std::map pc; - printf(">> %d\n", assoc.LookupAcceptedPresentationContext(pc, UID_ComputedRadiographyImageStorage)); - - for (std::map::const_iterator - it = pc.begin(); it != pc.end(); ++it) - { - printf("[%s] => %d\n", GetTransferSyntaxUid(it->first), it->second); - } -#else - { - DicomControlUserConnection assoc(params); - - try - { - printf(">> %d\n", assoc.Echo()); - } - catch (OrthancException&) - { - } - } - - params.SetRemoteApplicationEntityTitle("PACS"); - params.SetRemotePort(2000); - - { - DicomControlUserConnection assoc(params); - printf(">> %d\n", assoc.Echo()); - } - -#endif -} - -static void TestTranscode(DicomStoreUserConnection& scu, - const std::string& sopClassUid, - DicomTransferSyntax transferSyntax) -{ - std::set accepted; - - scu.LookupTranscoding(accepted, sopClassUid, transferSyntax); - if (accepted.empty()) - { - throw OrthancException(ErrorCode_NetworkProtocol, - "The SOP class is not supported by the remote modality"); - } - - { - unsigned int count = 0; - for (std::set::const_iterator - it = accepted.begin(); it != accepted.end(); ++it) - { - LOG(INFO) << "available for transcoding " << (count++) << ": " << sopClassUid - << " / " << GetTransferSyntaxUid(*it); - } - } - - if (accepted.find(transferSyntax) != accepted.end()) - { - printf("**** OK, without transcoding !! [%s]\n", GetTransferSyntaxUid(transferSyntax)); - } - else - { - // Transcoding - only in Orthanc >= 1.7.0 - - const DicomTransferSyntax uncompressed[] = { - DicomTransferSyntax_LittleEndianImplicit, // Default transfer syntax - DicomTransferSyntax_LittleEndianExplicit, - DicomTransferSyntax_BigEndianExplicit - }; - - bool found = false; - for (size_t i = 0; i < 3; i++) - { - if (accepted.find(uncompressed[i]) != accepted.end()) - { - printf("**** TRANSCODING to %s\n", GetTransferSyntaxUid(uncompressed[i])); - found = true; - break; - } - } - - if (!found) - { - printf("**** KO KO KO\n"); - } - } -} - - -TEST(Toto, DISABLED_Store) -{ - DicomAssociationParameters params; - params.SetLocalApplicationEntityTitle("ORTHANC"); - params.SetRemoteApplicationEntityTitle("STORESCP"); - params.SetRemotePort(2000); - - DicomStoreUserConnection assoc(params); - assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess1); - assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_JPEGProcess2_4); - //assoc.RegisterStorageClass(UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); - - //assoc.SetUncompressedSyntaxesProposed(false); // Necessary for transcoding - assoc.SetCommonClassesProposed(false); - assoc.SetRetiredBigEndianProposed(true); - TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_LittleEndianExplicit); - TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000); - TestTranscode(assoc, UID_MRImageStorage, DicomTransferSyntax_JPEG2000); -} - - -TEST(Toto, DISABLED_Store2) -{ - DicomAssociationParameters params; - params.SetLocalApplicationEntityTitle("ORTHANC"); - params.SetRemoteApplicationEntityTitle("STORESCP"); - params.SetRemotePort(2000); - - DicomStoreUserConnection assoc(params); - //assoc.SetCommonClassesProposed(false); - assoc.SetRetiredBigEndianProposed(true); - - std::string s; - Orthanc::SystemToolbox::ReadFile(s, "/tmp/i/" + std::string(GetTransferSyntaxUid(DicomTransferSyntax_BigEndianExplicit)) +".dcm"); - - std::string c, i; - assoc.Store(c, i, s.c_str(), s.size()); - printf("[%s] [%s]\n", c.c_str(), i.c_str()); -} - diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabaseOptimizations/LookupIdentifierQuery.cpp --- a/OrthancServer/Resources/Graveyard/DatabaseOptimizations/LookupIdentifierQuery.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,205 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeadersServer.h" -#include "LookupIdentifierQuery.h" - -#include "../../Core/DicomParsing/FromDcmtkBridge.h" -#include "../../Core/OrthancException.h" -#include "../ServerToolbox.h" -#include "SetOfResources.h" - -#include - - - -namespace Orthanc -{ - LookupIdentifierQuery::SingleConstraint:: - SingleConstraint(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value) : - tag_(tag), - type_(type), - value_(ServerToolbox::NormalizeIdentifier(value)) - { - } - - - LookupIdentifierQuery::RangeConstraint:: - RangeConstraint(const DicomTag& tag, - const std::string& start, - const std::string& end) : - tag_(tag), - start_(ServerToolbox::NormalizeIdentifier(start)), - end_(ServerToolbox::NormalizeIdentifier(end)) - { - } - - - LookupIdentifierQuery::Disjunction::~Disjunction() - { - for (size_t i = 0; i < singleConstraints_.size(); i++) - { - delete singleConstraints_[i]; - } - - for (size_t i = 0; i < rangeConstraints_.size(); i++) - { - delete rangeConstraints_[i]; - } - } - - - void LookupIdentifierQuery::Disjunction::Add(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value) - { - singleConstraints_.push_back(new SingleConstraint(tag, type, value)); - } - - - void LookupIdentifierQuery::Disjunction::AddRange(const DicomTag& tag, - const std::string& start, - const std::string& end) - { - rangeConstraints_.push_back(new RangeConstraint(tag, start, end)); - } - - - LookupIdentifierQuery::~LookupIdentifierQuery() - { - for (Disjunctions::iterator it = disjunctions_.begin(); - it != disjunctions_.end(); ++it) - { - delete *it; - } - } - - - bool LookupIdentifierQuery::IsIdentifier(const DicomTag& tag) - { - return ServerToolbox::IsIdentifier(tag, level_); - } - - - void LookupIdentifierQuery::AddConstraint(DicomTag tag, - IdentifierConstraintType type, - const std::string& value) - { - assert(IsIdentifier(tag)); - disjunctions_.push_back(new Disjunction); - disjunctions_.back()->Add(tag, type, value); - } - - - void LookupIdentifierQuery::AddRange(DicomTag tag, - const std::string& start, - const std::string& end) - { - assert(IsIdentifier(tag)); - disjunctions_.push_back(new Disjunction); - disjunctions_.back()->AddRange(tag, start, end); - } - - - LookupIdentifierQuery::Disjunction& LookupIdentifierQuery::AddDisjunction() - { - disjunctions_.push_back(new Disjunction); - return *disjunctions_.back(); - } - - - void LookupIdentifierQuery::Apply(std::list& result, - IDatabaseWrapper& database) - { - SetOfResources resources(database, level_); - Apply(resources, database); - - resources.Flatten(result); - } - - - void LookupIdentifierQuery::Apply(SetOfResources& result, - IDatabaseWrapper& database) - { - for (size_t i = 0; i < disjunctions_.size(); i++) - { - std::list a; - - for (size_t j = 0; j < disjunctions_[i]->GetSingleConstraintsCount(); j++) - { - const SingleConstraint& constraint = disjunctions_[i]->GetSingleConstraint(j); - std::list b; - database.LookupIdentifier(b, level_, constraint.GetTag(), - constraint.GetType(), constraint.GetValue()); - - a.splice(a.end(), b); - } - - for (size_t j = 0; j < disjunctions_[i]->GetRangeConstraintsCount(); j++) - { - const RangeConstraint& constraint = disjunctions_[i]->GetRangeConstraint(j); - std::list b; - database.LookupIdentifierRange(b, level_, constraint.GetTag(), - constraint.GetStart(), constraint.GetEnd()); - - a.splice(a.end(), b); - } - - result.Intersect(a); - } - } - - - void LookupIdentifierQuery::Print(std::ostream& s) const - { - s << "Constraint: " << std::endl; - for (Disjunctions::const_iterator - it = disjunctions_.begin(); it != disjunctions_.end(); ++it) - { - if (it == disjunctions_.begin()) - s << " "; - else - s << "OR "; - - for (size_t j = 0; j < (*it)->GetSingleConstraintsCount(); j++) - { - const SingleConstraint& c = (*it)->GetSingleConstraint(j); - s << FromDcmtkBridge::GetTagName(c.GetTag(), ""); - - switch (c.GetType()) - { - case IdentifierConstraintType_Equal: s << " == "; break; - case IdentifierConstraintType_SmallerOrEqual: s << " <= "; break; - case IdentifierConstraintType_GreaterOrEqual: s << " >= "; break; - case IdentifierConstraintType_Wildcard: s << " ~= "; break; - default: - s << " ? "; - } - - s << c.GetValue() << std::endl; - } - } - } -} diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabaseOptimizations/LookupIdentifierQuery.h --- a/OrthancServer/Resources/Graveyard/DatabaseOptimizations/LookupIdentifierQuery.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../IDatabaseWrapper.h" - -#include "SetOfResources.h" - -#include -#include - -namespace Orthanc -{ - /** - * Primitive for wildcard matching, as defined in DICOM: - * http://dicom.nema.org/dicom/2013/output/chtml/part04/sect_C.2.html#sect_C.2.2.2.4 - * - * "Any occurrence of an "*" or a "?", then "*" shall match any - * sequence of characters (including a zero length value) and "?" - * shall match any single character. This matching is case - * sensitive, except for Attributes with an PN Value - * Representation (e.g., Patient Name (0010,0010))." - * - * Pay attention to the fact that "*" (resp. "?") generally - * corresponds to "%" (resp. "_") in primitive LIKE of SQL. The - * values "%", "_", "\" should in the user request should - * respectively be escaped as "\%", "\_" and "\\". - * - * This matching must be case sensitive: The special case of PN VR - * is taken into consideration by normalizing the query string in - * method "NormalizeIdentifier()". - **/ - - class LookupIdentifierQuery : public boost::noncopyable - { - // This class encodes a conjunction ("AND") of disjunctions. Each - // disjunction represents an "OR" of several constraints. - - public: - class SingleConstraint - { - private: - DicomTag tag_; - IdentifierConstraintType type_; - std::string value_; - - public: - SingleConstraint(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value); - - const DicomTag& GetTag() const - { - return tag_; - } - - IdentifierConstraintType GetType() const - { - return type_; - } - - const std::string& GetValue() const - { - return value_; - } - }; - - - class RangeConstraint - { - private: - DicomTag tag_; - std::string start_; - std::string end_; - - public: - RangeConstraint(const DicomTag& tag, - const std::string& start, - const std::string& end); - - const DicomTag& GetTag() const - { - return tag_; - } - - const std::string& GetStart() const - { - return start_; - } - - const std::string& GetEnd() const - { - return end_; - } - }; - - - class Disjunction : public boost::noncopyable - { - private: - std::vector singleConstraints_; - std::vector rangeConstraints_; - - public: - ~Disjunction(); - - void Add(const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value); - - void AddRange(const DicomTag& tag, - const std::string& start, - const std::string& end); - - size_t GetSingleConstraintsCount() const - { - return singleConstraints_.size(); - } - - const SingleConstraint& GetSingleConstraint(size_t i) const - { - return *singleConstraints_[i]; - } - - size_t GetRangeConstraintsCount() const - { - return rangeConstraints_.size(); - } - - const RangeConstraint& GetRangeConstraint(size_t i) const - { - return *rangeConstraints_[i]; - } - }; - - - private: - typedef std::vector Disjunctions; - - ResourceType level_; - Disjunctions disjunctions_; - - public: - LookupIdentifierQuery(ResourceType level) : level_(level) - { - } - - ~LookupIdentifierQuery(); - - bool IsIdentifier(const DicomTag& tag); - - void AddConstraint(DicomTag tag, - IdentifierConstraintType type, - const std::string& value); - - void AddRange(DicomTag tag, - const std::string& start, - const std::string& end); - - Disjunction& AddDisjunction(); - - ResourceType GetLevel() const - { - return level_; - } - - // The database must be locked - void Apply(std::list& result, - IDatabaseWrapper& database); - - void Apply(SetOfResources& result, - IDatabaseWrapper& database); - - void Print(std::ostream& s) const; - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabaseOptimizations/LookupResource.cpp --- a/OrthancServer/Resources/Graveyard/DatabaseOptimizations/LookupResource.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,469 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeadersServer.h" -#include "LookupResource.h" - -#include "../../Core/OrthancException.h" -#include "../../Core/FileStorage/StorageAccessor.h" -#include "../ServerToolbox.h" -#include "../../Core/DicomParsing/FromDcmtkBridge.h" - - -namespace Orthanc -{ - static bool DoesDicomMapMatch(const DicomMap& dicom, - const DicomTag& tag, - const IFindConstraint& constraint) - { - const DicomValue* value = dicom.TestAndGetValue(tag); - - return (value != NULL && - !value->IsNull() && - !value->IsBinary() && - constraint.Match(value->GetContent())); - } - - - LookupResource::Level::Level(ResourceType level) : level_(level) - { - const DicomTag* tags = NULL; - size_t size; - - ServerToolbox::LoadIdentifiers(tags, size, level); - - for (size_t i = 0; i < size; i++) - { - identifiers_.insert(tags[i]); - } - - DicomMap::LoadMainDicomTags(tags, size, level); - - for (size_t i = 0; i < size; i++) - { - if (identifiers_.find(tags[i]) == identifiers_.end()) - { - mainTags_.insert(tags[i]); - } - } - } - - LookupResource::Level::~Level() - { - for (Constraints::iterator it = mainTagsConstraints_.begin(); - it != mainTagsConstraints_.end(); ++it) - { - delete it->second; - } - - for (Constraints::iterator it = identifiersConstraints_.begin(); - it != identifiersConstraints_.end(); ++it) - { - delete it->second; - } - } - - bool LookupResource::Level::Add(const DicomTag& tag, - std::auto_ptr& constraint) - { - if (identifiers_.find(tag) != identifiers_.end()) - { - if (level_ == ResourceType_Patient) - { - // The filters on the patient level must be cloned to the study level - identifiersConstraints_[tag] = constraint->Clone(); - } - else - { - identifiersConstraints_[tag] = constraint.release(); - } - - return true; - } - else if (mainTags_.find(tag) != mainTags_.end()) - { - if (level_ == ResourceType_Patient) - { - // The filters on the patient level must be cloned to the study level - mainTagsConstraints_[tag] = constraint->Clone(); - } - else - { - mainTagsConstraints_[tag] = constraint.release(); - } - - return true; - } - else - { - // This is not a main DICOM tag - return false; - } - } - - - bool LookupResource::Level::IsMatch(const DicomMap& dicom) const - { - for (Constraints::const_iterator it = identifiersConstraints_.begin(); - it != identifiersConstraints_.end(); ++it) - { - assert(it->second != NULL); - - if (!DoesDicomMapMatch(dicom, it->first, *it->second)) - { - return false; - } - } - - for (Constraints::const_iterator it = mainTagsConstraints_.begin(); - it != mainTagsConstraints_.end(); ++it) - { - assert(it->second != NULL); - - if (!DoesDicomMapMatch(dicom, it->first, *it->second)) - { - return false; - } - } - - return true; - } - - - LookupResource::LookupResource(ResourceType level) : level_(level) - { - switch (level) - { - case ResourceType_Patient: - levels_[ResourceType_Patient] = new Level(ResourceType_Patient); - break; - - case ResourceType_Instance: - levels_[ResourceType_Instance] = new Level(ResourceType_Instance); - // Do not add "break" here - - case ResourceType_Series: - levels_[ResourceType_Series] = new Level(ResourceType_Series); - // Do not add "break" here - - case ResourceType_Study: - levels_[ResourceType_Study] = new Level(ResourceType_Study); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - } - - - LookupResource::~LookupResource() - { - for (Levels::iterator it = levels_.begin(); - it != levels_.end(); ++it) - { - delete it->second; - } - - for (Constraints::iterator it = unoptimizedConstraints_.begin(); - it != unoptimizedConstraints_.end(); ++it) - { - delete it->second; - } - } - - - - bool LookupResource::AddInternal(ResourceType level, - const DicomTag& tag, - std::auto_ptr& constraint) - { - Levels::iterator it = levels_.find(level); - if (it != levels_.end()) - { - if (it->second->Add(tag, constraint)) - { - return true; - } - } - - return false; - } - - - void LookupResource::Add(const DicomTag& tag, - IFindConstraint* constraint) - { - std::auto_ptr c(constraint); - - if (!AddInternal(ResourceType_Patient, tag, c) && - !AddInternal(ResourceType_Study, tag, c) && - !AddInternal(ResourceType_Series, tag, c) && - !AddInternal(ResourceType_Instance, tag, c)) - { - unoptimizedConstraints_[tag] = c.release(); - } - } - - - static bool Match(const DicomMap& tags, - const DicomTag& tag, - const IFindConstraint& constraint) - { - const DicomValue* value = tags.TestAndGetValue(tag); - - if (value == NULL || - value->IsNull() || - value->IsBinary()) - { - return false; - } - else - { - return constraint.Match(value->GetContent()); - } - } - - - void LookupResource::Level::Apply(SetOfResources& candidates, - IDatabaseWrapper& database) const - { - // First, use the indexed identifiers - LookupIdentifierQuery query(level_); - - for (Constraints::const_iterator it = identifiersConstraints_.begin(); - it != identifiersConstraints_.end(); ++it) - { - it->second->Setup(query, it->first); - } - - query.Apply(candidates, database); - - /*{ - query.Print(std::cout); - std::list source; - candidates.Flatten(source); - printf("=> %d\n", source.size()); - }*/ - - // Secondly, filter using the main DICOM tags - if (!identifiersConstraints_.empty() || - !mainTagsConstraints_.empty()) - { - std::list source; - candidates.Flatten(source); - candidates.Clear(); - - std::list filtered; - for (std::list::const_iterator candidate = source.begin(); - candidate != source.end(); ++candidate) - { - DicomMap tags; - database.GetMainDicomTags(tags, *candidate); - - bool match = true; - - // Re-apply the identifier constraints, as their "Setup" - // method is less restrictive than their "Match" method - for (Constraints::const_iterator it = identifiersConstraints_.begin(); - match && it != identifiersConstraints_.end(); ++it) - { - if (!Match(tags, it->first, *it->second)) - { - match = false; - } - } - - for (Constraints::const_iterator it = mainTagsConstraints_.begin(); - match && it != mainTagsConstraints_.end(); ++it) - { - if (!Match(tags, it->first, *it->second)) - { - match = false; - } - } - - if (match) - { - filtered.push_back(*candidate); - } - } - - candidates.Intersect(filtered); - } - } - - - - bool LookupResource::IsMatch(const DicomMap& dicom) const - { - for (Levels::const_iterator it = levels_.begin(); it != levels_.end(); ++it) - { - if (!it->second->IsMatch(dicom)) - { - return false; - } - } - - for (Constraints::const_iterator it = unoptimizedConstraints_.begin(); - it != unoptimizedConstraints_.end(); ++it) - { - assert(it->second != NULL); - - if (!DoesDicomMapMatch(dicom, it->first, *it->second)) - { - return false; - } - } - - return true; - } - - - void LookupResource::ApplyLevel(SetOfResources& candidates, - ResourceType level, - IDatabaseWrapper& database) const - { - Levels::const_iterator it = levels_.find(level); - if (it != levels_.end()) - { - it->second->Apply(candidates, database); - } - - if (level == ResourceType_Study && - modalitiesInStudy_.get() != NULL) - { - // There is a constraint on the "ModalitiesInStudy" DICOM - // extension. Check out whether one child series has one of the - // allowed modalities - std::list allStudies, matchingStudies; - candidates.Flatten(allStudies); - - for (std::list::const_iterator - study = allStudies.begin(); study != allStudies.end(); ++study) - { - std::list childrenSeries; - database.GetChildrenInternalId(childrenSeries, *study); - - for (std::list::const_iterator - series = childrenSeries.begin(); series != childrenSeries.end(); ++series) - { - DicomMap tags; - database.GetMainDicomTags(tags, *series); - - const DicomValue* value = tags.TestAndGetValue(DICOM_TAG_MODALITY); - if (value != NULL && - !value->IsNull() && - !value->IsBinary()) - { - if (modalitiesInStudy_->Match(value->GetContent())) - { - matchingStudies.push_back(*study); - break; - } - } - } - } - - candidates.Intersect(matchingStudies); - } - } - - - void LookupResource::FindCandidates(std::list& result, - IDatabaseWrapper& database) const - { - ResourceType startingLevel; - if (level_ == ResourceType_Patient) - { - startingLevel = ResourceType_Patient; - } - else - { - startingLevel = ResourceType_Study; - } - - SetOfResources candidates(database, startingLevel); - - switch (level_) - { - case ResourceType_Patient: - ApplyLevel(candidates, ResourceType_Patient, database); - break; - - case ResourceType_Study: - ApplyLevel(candidates, ResourceType_Study, database); - break; - - case ResourceType_Series: - ApplyLevel(candidates, ResourceType_Study, database); - candidates.GoDown(); - ApplyLevel(candidates, ResourceType_Series, database); - break; - - case ResourceType_Instance: - ApplyLevel(candidates, ResourceType_Study, database); - candidates.GoDown(); - ApplyLevel(candidates, ResourceType_Series, database); - candidates.GoDown(); - ApplyLevel(candidates, ResourceType_Instance, database); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - candidates.Flatten(result); - } - - - void LookupResource::SetModalitiesInStudy(const std::string& modalities) - { - modalitiesInStudy_.reset(new ListConstraint(true /* case sensitive */)); - - std::vector items; - Toolbox::TokenizeString(items, modalities, '\\'); - - for (size_t i = 0; i < items.size(); i++) - { - modalitiesInStudy_->AddAllowedValue(items[i]); - } - } - - - void LookupResource::AddDicomConstraint(const DicomTag& tag, - const std::string& dicomQuery, - bool caseSensitive) - { - // http://www.itk.org/Wiki/DICOM_QueryRetrieve_Explained - // http://dicomiseasy.blogspot.be/2012/01/dicom-queryretrieve-part-i.html - if (tag == DICOM_TAG_MODALITIES_IN_STUDY) - { - SetModalitiesInStudy(dicomQuery); - } - else - { - Add(tag, IFindConstraint::ParseDicomConstraint(tag, dicomQuery, caseSensitive)); - } - } - -} diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabaseOptimizations/LookupResource.h --- a/OrthancServer/Resources/Graveyard/DatabaseOptimizations/LookupResource.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "ListConstraint.h" -#include "SetOfResources.h" - -#include - -namespace Orthanc -{ - class LookupResource : public boost::noncopyable - { - private: - typedef std::map Constraints; - - class Level - { - private: - ResourceType level_; - std::set identifiers_; - std::set mainTags_; - Constraints identifiersConstraints_; - Constraints mainTagsConstraints_; - - public: - Level(ResourceType level); - - ~Level(); - - bool Add(const DicomTag& tag, - std::auto_ptr& constraint); - - void Apply(SetOfResources& candidates, - IDatabaseWrapper& database) const; - - bool IsMatch(const DicomMap& dicom) const; - }; - - typedef std::map Levels; - - ResourceType level_; - Levels levels_; - Constraints unoptimizedConstraints_; // Constraints on non-main DICOM tags - std::auto_ptr modalitiesInStudy_; - - bool AddInternal(ResourceType level, - const DicomTag& tag, - std::auto_ptr& constraint); - - void ApplyLevel(SetOfResources& candidates, - ResourceType level, - IDatabaseWrapper& database) const; - - public: - LookupResource(ResourceType level); - - ~LookupResource(); - - ResourceType GetLevel() const - { - return level_; - } - - void SetModalitiesInStudy(const std::string& modalities); - - void Add(const DicomTag& tag, - IFindConstraint* constraint); // Takes ownership - - void AddDicomConstraint(const DicomTag& tag, - const std::string& dicomQuery, - bool caseSensitive); - - void FindCandidates(std::list& result, - IDatabaseWrapper& database) const; - - bool HasOnlyMainDicomTags() const - { - return unoptimizedConstraints_.empty(); - } - - bool IsMatch(const DicomMap& dicom) const; - }; -} diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabasePluginSample/CMakeLists.txt --- a/OrthancServer/Resources/Graveyard/DatabasePluginSample/CMakeLists.txt Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -project(SampleDatabasePlugin) - -# Parameters of the build -SET(SAMPLE_DATABASE_VERSION "0.0" CACHE STRING "Version of the plugin") -SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)") -SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages") -SET(STANDALONE_BUILD ON) - -# Advanced parameters to fine-tune linking against system libraries -SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of Boost") -SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp") -SET(USE_SYSTEM_SQLITE ON CACHE BOOL "Use the system version of SQLite") - -include(${CMAKE_SOURCE_DIR}/../../../Plugins/Samples/Common/OrthancPlugins.cmake) - -include(${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake) -set(ENABLE_SQLITE ON) -include(${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake) - -EmbedResources( - --system-exception # Use "std::runtime_error" instead of "OrthancException" for embedded resources - PREPARE_DATABASE ${CMAKE_SOURCE_DIR}/../../../Sources/Database/PrepareDatabase.sql - ) - -message("Setting the version of the plugin to ${SAMPLE_DATABASE_VERSION}") - -add_definitions( - -DORTHANC_SQLITE_STANDALONE=1 - -DORTHANC_ENABLE_PLUGINS=1 - -DORTHANC_SANDBOXED=0 - -DSAMPLE_DATABASE_VERSION="${SAMPLE_DATABASE_VERSION}" - ) - -add_library(SampleDatabase SHARED - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/DicomFormat/DicomArray.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/DicomFormat/DicomMap.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/DicomFormat/DicomTag.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/DicomFormat/DicomValue.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/Enumerations.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/SQLite/Connection.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/SQLite/FunctionContext.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/SQLite/Statement.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/SQLite/StatementId.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/SQLite/StatementReference.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/SQLite/Transaction.cpp - ${CMAKE_SOURCE_DIR}/../../../../OrthancFramework/Sources/Toolbox.cpp - ${CMAKE_SOURCE_DIR}/../../../Plugins/Engine/PluginsEnumerations.cpp - - Database.cpp - Plugin.cpp - DatabaseWrapperBase.cpp - - ${BOOST_SOURCES} - ${JSONCPP_SOURCES} - ${SQLITE_SOURCES} - ${AUTOGENERATED_SOURCES} - ) - -set_target_properties(SampleDatabase PROPERTIES - VERSION ${SAMPLE_DATABASE_VERSION} - SOVERSION ${SAMPLE_DATABASE_VERSION}) - -install( - TARGETS SampleDatabase - RUNTIME DESTINATION lib # Destination for Windows - LIBRARY DESTINATION share/orthanc/plugins # Destination for Linux - ) diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabasePluginSample/Database.cpp --- a/OrthancServer/Resources/Graveyard/DatabasePluginSample/Database.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,555 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "Database.h" - -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomArray.h" - -#include -#include - - -namespace Internals -{ - class SignalFileDeleted : public Orthanc::SQLite::IScalarFunction - { - private: - OrthancPlugins::DatabaseBackendOutput& output_; - - public: - SignalFileDeleted(OrthancPlugins::DatabaseBackendOutput& output) : output_(output) - { - } - - virtual const char* GetName() const - { - return "SignalFileDeleted"; - } - - virtual unsigned int GetCardinality() const - { - return 7; - } - - virtual void Compute(Orthanc::SQLite::FunctionContext& context) - { - std::string uncompressedMD5, compressedMD5; - - if (!context.IsNullValue(5)) - { - uncompressedMD5 = context.GetStringValue(5); - } - - if (!context.IsNullValue(6)) - { - compressedMD5 = context.GetStringValue(6); - } - - output_.SignalDeletedAttachment(context.GetStringValue(0), - context.GetIntValue(1), - context.GetInt64Value(2), - uncompressedMD5, - context.GetIntValue(3), - context.GetInt64Value(4), - compressedMD5); - } - }; - - - class SignalResourceDeleted : public Orthanc::SQLite::IScalarFunction - { - private: - OrthancPlugins::DatabaseBackendOutput& output_; - - public: - SignalResourceDeleted(OrthancPlugins::DatabaseBackendOutput& output) : output_(output) - { - } - - virtual const char* GetName() const - { - return "SignalResourceDeleted"; - } - - virtual unsigned int GetCardinality() const - { - return 2; - } - - virtual void Compute(Orthanc::SQLite::FunctionContext& context) - { - output_.SignalDeletedResource(context.GetStringValue(0), - Orthanc::Plugins::Convert(static_cast(context.GetIntValue(1)))); - } - }; -} - - -class Database::SignalRemainingAncestor : public Orthanc::SQLite::IScalarFunction -{ -private: - bool hasRemainingAncestor_; - std::string remainingPublicId_; - OrthancPluginResourceType remainingType_; - -public: - SignalRemainingAncestor() : - hasRemainingAncestor_(false), - remainingType_(OrthancPluginResourceType_Instance) // Some dummy value - { - } - - void Reset() - { - hasRemainingAncestor_ = false; - } - - virtual const char* GetName() const - { - return "SignalRemainingAncestor"; - } - - virtual unsigned int GetCardinality() const - { - return 2; - } - - virtual void Compute(Orthanc::SQLite::FunctionContext& context) - { - if (!hasRemainingAncestor_ || - remainingType_ >= context.GetIntValue(1)) - { - hasRemainingAncestor_ = true; - remainingPublicId_ = context.GetStringValue(0); - remainingType_ = Orthanc::Plugins::Convert(static_cast(context.GetIntValue(1))); - } - } - - bool HasRemainingAncestor() const - { - return hasRemainingAncestor_; - } - - const std::string& GetRemainingAncestorId() const - { - assert(hasRemainingAncestor_); - return remainingPublicId_; - } - - OrthancPluginResourceType GetRemainingAncestorType() const - { - assert(hasRemainingAncestor_); - return remainingType_; - } -}; - - - -Database::Database(const std::string& path) : - path_(path), - base_(db_), - signalRemainingAncestor_(NULL) -{ -} - - -void Database::Open() -{ - db_.Open(path_); - - db_.Execute("PRAGMA ENCODING=\"UTF-8\";"); - - // http://www.sqlite.org/pragma.html - db_.Execute("PRAGMA SYNCHRONOUS=NORMAL;"); - db_.Execute("PRAGMA JOURNAL_MODE=WAL;"); - db_.Execute("PRAGMA LOCKING_MODE=EXCLUSIVE;"); - db_.Execute("PRAGMA WAL_AUTOCHECKPOINT=1000;"); - //db_.Execute("PRAGMA TEMP_STORE=memory"); - - if (!db_.DoesTableExist("GlobalProperties")) - { - std::string query; - Orthanc::EmbeddedResources::GetFileResource(query, Orthanc::EmbeddedResources::PREPARE_DATABASE); - db_.Execute(query); - } - - signalRemainingAncestor_ = new SignalRemainingAncestor; - db_.Register(signalRemainingAncestor_); - db_.Register(new Internals::SignalFileDeleted(GetOutput())); - db_.Register(new Internals::SignalResourceDeleted(GetOutput())); -} - - -void Database::Close() -{ - db_.Close(); -} - - -void Database::AddAttachment(int64_t id, - const OrthancPluginAttachment& attachment) -{ - Orthanc::FileInfo info(attachment.uuid, - static_cast(attachment.contentType), - attachment.uncompressedSize, - attachment.uncompressedHash, - static_cast(attachment.compressionType), - attachment.compressedSize, - attachment.compressedHash); - base_.AddAttachment(id, info); -} - - -void Database::DeleteResource(int64_t id) -{ - signalRemainingAncestor_->Reset(); - - Orthanc::SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Resources WHERE internalId=?"); - s.BindInt64(0, id); - s.Run(); - - if (signalRemainingAncestor_->HasRemainingAncestor()) - { - GetOutput().SignalRemainingAncestor(signalRemainingAncestor_->GetRemainingAncestorId(), - signalRemainingAncestor_->GetRemainingAncestorType()); - } -} - - -static void Answer(OrthancPlugins::DatabaseBackendOutput& output, - const Orthanc::ServerIndexChange& change) -{ - output.AnswerChange(change.GetSeq(), - change.GetChangeType(), - Orthanc::Plugins::Convert(change.GetResourceType()), - change.GetPublicId(), - change.GetDate()); -} - - -static void Answer(OrthancPlugins::DatabaseBackendOutput& output, - const Orthanc::ExportedResource& resource) -{ - output.AnswerExportedResource(resource.GetSeq(), - Orthanc::Plugins::Convert(resource.GetResourceType()), - resource.GetPublicId(), - resource.GetModality(), - resource.GetDate(), - resource.GetPatientId(), - resource.GetStudyInstanceUid(), - resource.GetSeriesInstanceUid(), - resource.GetSopInstanceUid()); -} - - -void Database::GetChanges(bool& done /*out*/, - int64_t since, - uint32_t maxResults) -{ - typedef std::list Changes; - - Changes changes; - base_.GetChanges(changes, done, since, maxResults); - - for (Changes::const_iterator it = changes.begin(); it != changes.end(); ++it) - { - Answer(GetOutput(), *it); - } -} - - -void Database::GetExportedResources(bool& done /*out*/, - int64_t since, - uint32_t maxResults) -{ - typedef std::list Resources; - - Resources resources; - base_.GetExportedResources(resources, done, since, maxResults); - - for (Resources::const_iterator it = resources.begin(); it != resources.end(); ++it) - { - Answer(GetOutput(), *it); - } -} - - -void Database::GetLastChange() -{ - std::list change; - Orthanc::ErrorCode code = base_.GetLastChange(change); - - if (code != Orthanc::ErrorCode_Success) - { - throw OrthancPlugins::DatabaseException(static_cast(code)); - } - - if (!change.empty()) - { - Answer(GetOutput(), change.front()); - } -} - - -void Database::GetLastExportedResource() -{ - std::list resource; - base_.GetLastExportedResource(resource); - - if (!resource.empty()) - { - Answer(GetOutput(), resource.front()); - } -} - - -void Database::GetMainDicomTags(int64_t id) -{ - Orthanc::DicomMap tags; - base_.GetMainDicomTags(tags, id); - - Orthanc::DicomArray arr(tags); - for (size_t i = 0; i < arr.GetSize(); i++) - { - GetOutput().AnswerDicomTag(arr.GetElement(i).GetTag().GetGroup(), - arr.GetElement(i).GetTag().GetElement(), - arr.GetElement(i).GetValue().GetContent()); - } -} - - -std::string Database::GetPublicId(int64_t resourceId) -{ - std::string id; - if (base_.GetPublicId(id, resourceId)) - { - return id; - } - else - { - throw OrthancPlugins::DatabaseException(OrthancPluginErrorCode_UnknownResource); - } -} - - -OrthancPluginResourceType Database::GetResourceType(int64_t resourceId) -{ - Orthanc::ResourceType result; - Orthanc::ErrorCode code = base_.GetResourceType(result, resourceId); - - if (code == Orthanc::ErrorCode_Success) - { - return Orthanc::Plugins::Convert(result); - } - else - { - throw OrthancPlugins::DatabaseException(static_cast(code)); - } -} - - - -template -static void ConvertList(std::list& target, - const std::list& source) -{ - for (typename std::list::const_iterator - it = source.begin(); it != source.end(); ++it) - { - target.push_back(*it); - } -} - - -void Database::ListAvailableMetadata(std::list& target /*out*/, - int64_t id) -{ - std::list tmp; - base_.ListAvailableMetadata(tmp, id); - ConvertList(target, tmp); -} - - -void Database::ListAvailableAttachments(std::list& target /*out*/, - int64_t id) -{ - std::list tmp; - base_.ListAvailableAttachments(tmp, id); - ConvertList(target, tmp); -} - - -void Database::LogChange(const OrthancPluginChange& change) -{ - int64_t id; - OrthancPluginResourceType type; - if (!LookupResource(id, type, change.publicId) || - type != change.resourceType) - { - throw OrthancPlugins::DatabaseException(OrthancPluginErrorCode_DatabasePlugin); - } - - Orthanc::ServerIndexChange tmp(change.seq, - static_cast(change.changeType), - Orthanc::Plugins::Convert(change.resourceType), - change.publicId, - change.date); - - base_.LogChange(id, tmp); -} - - -void Database::LogExportedResource(const OrthancPluginExportedResource& resource) -{ - Orthanc::ExportedResource tmp(resource.seq, - Orthanc::Plugins::Convert(resource.resourceType), - resource.publicId, - resource.modality, - resource.date, - resource.patientId, - resource.studyInstanceUid, - resource.seriesInstanceUid, - resource.sopInstanceUid); - - base_.LogExportedResource(tmp); -} - - -bool Database::LookupAttachment(int64_t id, - int32_t contentType) -{ - Orthanc::FileInfo attachment; - if (base_.LookupAttachment(attachment, id, static_cast(contentType))) - { - GetOutput().AnswerAttachment(attachment.GetUuid(), - attachment.GetContentType(), - attachment.GetUncompressedSize(), - attachment.GetUncompressedMD5(), - attachment.GetCompressionType(), - attachment.GetCompressedSize(), - attachment.GetCompressedMD5()); - return true; - } - else - { - return false; - } -} - - -bool Database::LookupParent(int64_t& parentId /*out*/, - int64_t resourceId) -{ - bool found; - Orthanc::ErrorCode code = base_.LookupParent(found, parentId, resourceId); - - if (code == Orthanc::ErrorCode_Success) - { - return found; - } - else - { - throw OrthancPlugins::DatabaseException(static_cast(code)); - } -} - - -bool Database::LookupResource(int64_t& id /*out*/, - OrthancPluginResourceType& type /*out*/, - const char* publicId) -{ - Orthanc::ResourceType tmp; - if (base_.LookupResource(id, tmp, publicId)) - { - type = Orthanc::Plugins::Convert(tmp); - return true; - } - else - { - return false; - } -} - - -void Database::StartTransaction() -{ - transaction_.reset(new Orthanc::SQLite::Transaction(db_)); - transaction_->Begin(); -} - - -void Database::RollbackTransaction() -{ - transaction_->Rollback(); - transaction_.reset(NULL); -} - - -void Database::CommitTransaction() -{ - transaction_->Commit(); - transaction_.reset(NULL); -} - - -uint32_t Database::GetDatabaseVersion() -{ - std::string version; - - if (!LookupGlobalProperty(version, Orthanc::GlobalProperty_DatabaseSchemaVersion)) - { - throw OrthancPlugins::DatabaseException(OrthancPluginErrorCode_InternalError); - } - - try - { - return boost::lexical_cast(version); - } - catch (boost::bad_lexical_cast&) - { - throw OrthancPlugins::DatabaseException(OrthancPluginErrorCode_InternalError); - } -} - - -void Database::UpgradeDatabase(uint32_t targetVersion, - OrthancPluginStorageArea* storageArea) -{ - if (targetVersion == 6) - { - OrthancPluginErrorCode code = OrthancPluginReconstructMainDicomTags(GetOutput().GetContext(), storageArea, - OrthancPluginResourceType_Study); - if (code == OrthancPluginErrorCode_Success) - { - code = OrthancPluginReconstructMainDicomTags(GetOutput().GetContext(), storageArea, - OrthancPluginResourceType_Series); - } - - if (code != OrthancPluginErrorCode_Success) - { - throw OrthancPlugins::DatabaseException(code); - } - - base_.SetGlobalProperty(Orthanc::GlobalProperty_DatabaseSchemaVersion, "6"); - } -} diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabasePluginSample/Database.h --- a/OrthancServer/Resources/Graveyard/DatabasePluginSample/Database.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,275 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "OrthancCppDatabasePlugin.h" - -#include "../../../../OrthancFramework/Sources/SQLite/Connection.h" -#include "../../../../OrthancFramework/Sources/SQLite/Transaction.h" -#include "../../../Plugins/Engine/PluginsEnumerations.h" -#include "DatabaseWrapperBase.h" - -#include - -class Database : public OrthancPlugins::IDatabaseBackend -{ -private: - class SignalRemainingAncestor; - - std::string path_; - Orthanc::SQLite::Connection db_; - Orthanc::DatabaseWrapperBase base_; - SignalRemainingAncestor* signalRemainingAncestor_; - - std::auto_ptr transaction_; - -public: - Database(const std::string& path); - - virtual void Open(); - - virtual void Close(); - - virtual void AddAttachment(int64_t id, - const OrthancPluginAttachment& attachment); - - virtual void AttachChild(int64_t parent, - int64_t child) - { - base_.AttachChild(parent, child); - } - - virtual void ClearChanges() - { - db_.Execute("DELETE FROM Changes"); - } - - virtual void ClearExportedResources() - { - db_.Execute("DELETE FROM ExportedResources"); - } - - virtual int64_t CreateResource(const char* publicId, - OrthancPluginResourceType type) - { - return base_.CreateResource(publicId, Orthanc::Plugins::Convert(type)); - } - - virtual void DeleteAttachment(int64_t id, - int32_t attachment) - { - base_.DeleteAttachment(id, static_cast(attachment)); - } - - virtual void DeleteMetadata(int64_t id, - int32_t metadataType) - { - base_.DeleteMetadata(id, static_cast(metadataType)); - } - - virtual void DeleteResource(int64_t id); - - virtual void GetAllInternalIds(std::list& target, - OrthancPluginResourceType resourceType) - { - base_.GetAllInternalIds(target, Orthanc::Plugins::Convert(resourceType)); - } - - virtual void GetAllPublicIds(std::list& target, - OrthancPluginResourceType resourceType) - { - base_.GetAllPublicIds(target, Orthanc::Plugins::Convert(resourceType)); - } - - virtual void GetAllPublicIds(std::list& target, - OrthancPluginResourceType resourceType, - uint64_t since, - uint64_t limit) - { - base_.GetAllPublicIds(target, Orthanc::Plugins::Convert(resourceType), since, limit); - } - - virtual void GetChanges(bool& done /*out*/, - int64_t since, - uint32_t maxResults); - - virtual void GetChildrenInternalId(std::list& target /*out*/, - int64_t id) - { - base_.GetChildrenInternalId(target, id); - } - - virtual void GetChildrenPublicId(std::list& target /*out*/, - int64_t id) - { - base_.GetChildrenPublicId(target, id); - } - - virtual void GetExportedResources(bool& done /*out*/, - int64_t since, - uint32_t maxResults); - - virtual void GetLastChange(); - - virtual void GetLastExportedResource(); - - virtual void GetMainDicomTags(int64_t id); - - virtual std::string GetPublicId(int64_t resourceId); - - virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType) - { - return base_.GetResourceCount(Orthanc::Plugins::Convert(resourceType)); - } - - virtual OrthancPluginResourceType GetResourceType(int64_t resourceId); - - virtual uint64_t GetTotalCompressedSize() - { - return base_.GetTotalCompressedSize(); - } - - virtual uint64_t GetTotalUncompressedSize() - { - return base_.GetTotalUncompressedSize(); - } - - virtual bool IsExistingResource(int64_t internalId) - { - return base_.IsExistingResource(internalId); - } - - virtual bool IsProtectedPatient(int64_t internalId) - { - return base_.IsProtectedPatient(internalId); - } - - virtual void ListAvailableMetadata(std::list& target /*out*/, - int64_t id); - - virtual void ListAvailableAttachments(std::list& target /*out*/, - int64_t id); - - virtual void LogChange(const OrthancPluginChange& change); - - virtual void LogExportedResource(const OrthancPluginExportedResource& resource); - - virtual bool LookupAttachment(int64_t id, - int32_t contentType); - - virtual bool LookupGlobalProperty(std::string& target /*out*/, - int32_t property) - { - return base_.LookupGlobalProperty(target, static_cast(property)); - } - - virtual void LookupIdentifier(std::list& target /*out*/, - OrthancPluginResourceType level, - uint16_t group, - uint16_t element, - OrthancPluginIdentifierConstraint constraint, - const char* value) - { - base_.LookupIdentifier(target, Orthanc::Plugins::Convert(level), - Orthanc::DicomTag(group, element), - Orthanc::Plugins::Convert(constraint), value); - } - - virtual bool LookupMetadata(std::string& target /*out*/, - int64_t id, - int32_t metadataType) - { - return base_.LookupMetadata(target, id, static_cast(metadataType)); - } - - virtual bool LookupParent(int64_t& parentId /*out*/, - int64_t resourceId); - - virtual bool LookupResource(int64_t& id /*out*/, - OrthancPluginResourceType& type /*out*/, - const char* publicId); - - virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/) - { - return base_.SelectPatientToRecycle(internalId); - } - - virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/, - int64_t patientIdToAvoid) - { - return base_.SelectPatientToRecycle(internalId, patientIdToAvoid); - } - - - virtual void SetGlobalProperty(int32_t property, - const char* value) - { - base_.SetGlobalProperty(static_cast(property), value); - } - - virtual void SetMainDicomTag(int64_t id, - uint16_t group, - uint16_t element, - const char* value) - { - base_.SetMainDicomTag(id, Orthanc::DicomTag(group, element), value); - } - - virtual void SetIdentifierTag(int64_t id, - uint16_t group, - uint16_t element, - const char* value) - { - base_.SetIdentifierTag(id, Orthanc::DicomTag(group, element), value); - } - - virtual void SetMetadata(int64_t id, - int32_t metadataType, - const char* value) - { - base_.SetMetadata(id, static_cast(metadataType), value); - } - - virtual void SetProtectedPatient(int64_t internalId, - bool isProtected) - { - base_.SetProtectedPatient(internalId, isProtected); - } - - virtual void StartTransaction(); - - virtual void RollbackTransaction(); - - virtual void CommitTransaction(); - - virtual uint32_t GetDatabaseVersion(); - - virtual void UpgradeDatabase(uint32_t targetVersion, - OrthancPluginStorageArea* storageArea); - - virtual void ClearMainDicomTags(int64_t internalId) - { - base_.ClearMainDicomTags(internalId); - } -}; diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabasePluginSample/DatabaseWrapperBase.cpp --- a/OrthancServer/Resources/Graveyard/DatabasePluginSample/DatabaseWrapperBase.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,747 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../../../Sources/PrecompiledHeadersServer.h" -#include "DatabaseWrapperBase.h" - -#include -#include - -namespace Orthanc -{ - void DatabaseWrapperBase::SetGlobalProperty(GlobalProperty property, - const std::string& value) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO GlobalProperties VALUES(?, ?)"); - s.BindInt(0, property); - s.BindString(1, value); - s.Run(); - } - - bool DatabaseWrapperBase::LookupGlobalProperty(std::string& target, - GlobalProperty property) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT value FROM GlobalProperties WHERE property=?"); - s.BindInt(0, property); - - if (!s.Step()) - { - return false; - } - else - { - target = s.ColumnString(0); - return true; - } - } - - int64_t DatabaseWrapperBase::CreateResource(const std::string& publicId, - ResourceType type) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Resources VALUES(NULL, ?, ?, NULL)"); - s.BindInt(0, type); - s.BindString(1, publicId); - s.Run(); - return db_.GetLastInsertRowId(); - } - - bool DatabaseWrapperBase::LookupResource(int64_t& id, - ResourceType& type, - const std::string& publicId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT internalId, resourceType FROM Resources WHERE publicId=?"); - s.BindString(0, publicId); - - if (!s.Step()) - { - return false; - } - else - { - id = s.ColumnInt(0); - type = static_cast(s.ColumnInt(1)); - - // Check whether there is a single resource with this public id - assert(!s.Step()); - - return true; - } - } - - ErrorCode DatabaseWrapperBase::LookupParent(bool& found, - int64_t& parentId, - int64_t resourceId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT parentId FROM Resources WHERE internalId=?"); - s.BindInt64(0, resourceId); - - if (!s.Step()) - { - return ErrorCode_UnknownResource; - } - - if (s.ColumnIsNull(0)) - { - found = false; - } - else - { - found = true; - parentId = s.ColumnInt(0); - } - - return ErrorCode_Success; - } - - bool DatabaseWrapperBase::GetPublicId(std::string& result, - int64_t resourceId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT publicId FROM Resources WHERE internalId=?"); - s.BindInt64(0, resourceId); - - if (!s.Step()) - { - return false; - } - else - { - result = s.ColumnString(0); - return true; - } - } - - - ErrorCode DatabaseWrapperBase::GetResourceType(ResourceType& result, - int64_t resourceId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT resourceType FROM Resources WHERE internalId=?"); - s.BindInt64(0, resourceId); - - if (s.Step()) - { - result = static_cast(s.ColumnInt(0)); - return ErrorCode_Success; - } - else - { - return ErrorCode_UnknownResource; - } - } - - - void DatabaseWrapperBase::AttachChild(int64_t parent, - int64_t child) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "UPDATE Resources SET parentId = ? WHERE internalId = ?"); - s.BindInt64(0, parent); - s.BindInt64(1, child); - s.Run(); - } - - - void DatabaseWrapperBase::SetMetadata(int64_t id, - MetadataType type, - const std::string& value) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT OR REPLACE INTO Metadata VALUES(?, ?, ?)"); - s.BindInt64(0, id); - s.BindInt(1, type); - s.BindString(2, value); - s.Run(); - } - - void DatabaseWrapperBase::DeleteMetadata(int64_t id, - MetadataType type) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM Metadata WHERE id=? and type=?"); - s.BindInt64(0, id); - s.BindInt(1, type); - s.Run(); - } - - bool DatabaseWrapperBase::LookupMetadata(std::string& target, - int64_t id, - MetadataType type) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT value FROM Metadata WHERE id=? AND type=?"); - s.BindInt64(0, id); - s.BindInt(1, type); - - if (!s.Step()) - { - return false; - } - else - { - target = s.ColumnString(0); - return true; - } - } - - void DatabaseWrapperBase::ListAvailableMetadata(std::list& target, - int64_t id) - { - target.clear(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT type FROM Metadata WHERE id=?"); - s.BindInt64(0, id); - - while (s.Step()) - { - target.push_back(static_cast(s.ColumnInt(0))); - } - } - - - void DatabaseWrapperBase::AddAttachment(int64_t id, - const FileInfo& attachment) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO AttachedFiles VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); - s.BindInt64(0, id); - s.BindInt(1, attachment.GetContentType()); - s.BindString(2, attachment.GetUuid()); - s.BindInt64(3, attachment.GetCompressedSize()); - s.BindInt64(4, attachment.GetUncompressedSize()); - s.BindInt(5, attachment.GetCompressionType()); - s.BindString(6, attachment.GetUncompressedMD5()); - s.BindString(7, attachment.GetCompressedMD5()); - s.Run(); - } - - - void DatabaseWrapperBase::DeleteAttachment(int64_t id, - FileContentType attachment) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM AttachedFiles WHERE id=? AND fileType=?"); - s.BindInt64(0, id); - s.BindInt(1, attachment); - s.Run(); - } - - - - void DatabaseWrapperBase::ListAvailableAttachments(std::list& target, - int64_t id) - { - target.clear(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT fileType FROM AttachedFiles WHERE id=?"); - s.BindInt64(0, id); - - while (s.Step()) - { - target.push_back(static_cast(s.ColumnInt(0))); - } - } - - bool DatabaseWrapperBase::LookupAttachment(FileInfo& attachment, - int64_t id, - FileContentType contentType) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT uuid, uncompressedSize, compressionType, compressedSize, uncompressedMD5, compressedMD5 FROM AttachedFiles WHERE id=? AND fileType=?"); - s.BindInt64(0, id); - s.BindInt(1, contentType); - - if (!s.Step()) - { - return false; - } - else - { - attachment = FileInfo(s.ColumnString(0), - contentType, - s.ColumnInt64(1), - s.ColumnString(4), - static_cast(s.ColumnInt(2)), - s.ColumnInt64(3), - s.ColumnString(5)); - return true; - } - } - - - void DatabaseWrapperBase::ClearMainDicomTags(int64_t id) - { - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM DicomIdentifiers WHERE id=?"); - s.BindInt64(0, id); - s.Run(); - } - - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM MainDicomTags WHERE id=?"); - s.BindInt64(0, id); - s.Run(); - } - } - - - void DatabaseWrapperBase::SetMainDicomTag(int64_t id, - const DicomTag& tag, - const std::string& value) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO MainDicomTags VALUES(?, ?, ?, ?)"); - s.BindInt64(0, id); - s.BindInt(1, tag.GetGroup()); - s.BindInt(2, tag.GetElement()); - s.BindString(3, value); - s.Run(); - } - - - void DatabaseWrapperBase::SetIdentifierTag(int64_t id, - const DicomTag& tag, - const std::string& value) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO DicomIdentifiers VALUES(?, ?, ?, ?)"); - s.BindInt64(0, id); - s.BindInt(1, tag.GetGroup()); - s.BindInt(2, tag.GetElement()); - s.BindString(3, value); - s.Run(); - } - - - void DatabaseWrapperBase::GetMainDicomTags(DicomMap& map, - int64_t id) - { - map.Clear(); - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM MainDicomTags WHERE id=?"); - s.BindInt64(0, id); - while (s.Step()) - { - map.SetValue(s.ColumnInt(1), - s.ColumnInt(2), - s.ColumnString(3), false); - } - } - - - - void DatabaseWrapperBase::GetChildrenPublicId(std::list& target, - int64_t id) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.publicId FROM Resources AS a, Resources AS b " - "WHERE a.parentId = b.internalId AND b.internalId = ?"); - s.BindInt64(0, id); - - target.clear(); - - while (s.Step()) - { - target.push_back(s.ColumnString(0)); - } - } - - - void DatabaseWrapperBase::GetChildrenInternalId(std::list& target, - int64_t id) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT a.internalId FROM Resources AS a, Resources AS b " - "WHERE a.parentId = b.internalId AND b.internalId = ?"); - s.BindInt64(0, id); - - target.clear(); - - while (s.Step()) - { - target.push_back(s.ColumnInt64(0)); - } - } - - - void DatabaseWrapperBase::LogChange(int64_t internalId, - const ServerIndexChange& change) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO Changes VALUES(NULL, ?, ?, ?, ?)"); - s.BindInt(0, change.GetChangeType()); - s.BindInt64(1, internalId); - s.BindInt(2, change.GetResourceType()); - s.BindString(3, change.GetDate()); - s.Run(); - } - - - ErrorCode DatabaseWrapperBase::GetChangesInternal(std::list& target, - bool& done, - SQLite::Statement& s, - uint32_t maxResults) - { - target.clear(); - - while (target.size() < maxResults && s.Step()) - { - int64_t seq = s.ColumnInt64(0); - ChangeType changeType = static_cast(s.ColumnInt(1)); - ResourceType resourceType = static_cast(s.ColumnInt(3)); - const std::string& date = s.ColumnString(4); - - int64_t internalId = s.ColumnInt64(2); - std::string publicId; - if (!GetPublicId(publicId, internalId)) - { - return ErrorCode_UnknownResource; - } - - target.push_back(ServerIndexChange(seq, changeType, resourceType, publicId, date)); - } - - done = !(target.size() == maxResults && s.Step()); - return ErrorCode_Success; - } - - - ErrorCode DatabaseWrapperBase::GetChanges(std::list& target, - bool& done, - int64_t since, - uint32_t maxResults) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes WHERE seq>? ORDER BY seq LIMIT ?"); - s.BindInt64(0, since); - s.BindInt(1, maxResults + 1); - return GetChangesInternal(target, done, s, maxResults); - } - - ErrorCode DatabaseWrapperBase::GetLastChange(std::list& target) - { - bool done; // Ignored - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT * FROM Changes ORDER BY seq DESC LIMIT 1"); - return GetChangesInternal(target, done, s, 1); - } - - - void DatabaseWrapperBase::LogExportedResource(const ExportedResource& resource) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "INSERT INTO ExportedResources VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?)"); - - s.BindInt(0, resource.GetResourceType()); - s.BindString(1, resource.GetPublicId()); - s.BindString(2, resource.GetModality()); - s.BindString(3, resource.GetPatientId()); - s.BindString(4, resource.GetStudyInstanceUid()); - s.BindString(5, resource.GetSeriesInstanceUid()); - s.BindString(6, resource.GetSopInstanceUid()); - s.BindString(7, resource.GetDate()); - s.Run(); - } - - - void DatabaseWrapperBase::GetExportedResourcesInternal(std::list& target, - bool& done, - SQLite::Statement& s, - uint32_t maxResults) - { - target.clear(); - - while (target.size() < maxResults && s.Step()) - { - int64_t seq = s.ColumnInt64(0); - ResourceType resourceType = static_cast(s.ColumnInt(1)); - std::string publicId = s.ColumnString(2); - - ExportedResource resource(seq, - resourceType, - publicId, - s.ColumnString(3), // modality - s.ColumnString(8), // date - s.ColumnString(4), // patient ID - s.ColumnString(5), // study instance UID - s.ColumnString(6), // series instance UID - s.ColumnString(7)); // sop instance UID - - target.push_back(resource); - } - - done = !(target.size() == maxResults && s.Step()); - } - - - void DatabaseWrapperBase::GetExportedResources(std::list& target, - bool& done, - int64_t since, - uint32_t maxResults) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM ExportedResources WHERE seq>? ORDER BY seq LIMIT ?"); - s.BindInt64(0, since); - s.BindInt(1, maxResults + 1); - GetExportedResourcesInternal(target, done, s, maxResults); - } - - - void DatabaseWrapperBase::GetLastExportedResource(std::list& target) - { - bool done; // Ignored - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM ExportedResources ORDER BY seq DESC LIMIT 1"); - GetExportedResourcesInternal(target, done, s, 1); - } - - - - uint64_t DatabaseWrapperBase::GetTotalCompressedSize() - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(compressedSize) FROM AttachedFiles"); - s.Run(); - return static_cast(s.ColumnInt64(0)); - } - - - uint64_t DatabaseWrapperBase::GetTotalUncompressedSize() - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT SUM(uncompressedSize) FROM AttachedFiles"); - s.Run(); - return static_cast(s.ColumnInt64(0)); - } - - void DatabaseWrapperBase::GetAllInternalIds(std::list& target, - ResourceType resourceType) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT internalId FROM Resources WHERE resourceType=?"); - s.BindInt(0, resourceType); - - target.clear(); - while (s.Step()) - { - target.push_back(s.ColumnInt64(0)); - } - } - - - void DatabaseWrapperBase::GetAllPublicIds(std::list& target, - ResourceType resourceType) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=?"); - s.BindInt(0, resourceType); - - target.clear(); - while (s.Step()) - { - target.push_back(s.ColumnString(0)); - } - } - - void DatabaseWrapperBase::GetAllPublicIds(std::list& target, - ResourceType resourceType, - size_t since, - size_t limit) - { - if (limit == 0) - { - target.clear(); - return; - } - - SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT publicId FROM Resources WHERE resourceType=? LIMIT ? OFFSET ?"); - s.BindInt(0, resourceType); - s.BindInt64(1, limit); - s.BindInt64(2, since); - - target.clear(); - while (s.Step()) - { - target.push_back(s.ColumnString(0)); - } - } - - - uint64_t DatabaseWrapperBase::GetResourceCount(ResourceType resourceType) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT COUNT(*) FROM Resources WHERE resourceType=?"); - s.BindInt(0, resourceType); - - if (!s.Step()) - { - return 0; - } - else - { - int64_t c = s.ColumnInt(0); - assert(!s.Step()); - return c; - } - } - - - bool DatabaseWrapperBase::SelectPatientToRecycle(int64_t& internalId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT patientId FROM PatientRecyclingOrder ORDER BY seq ASC LIMIT 1"); - - if (!s.Step()) - { - // No patient remaining or all the patients are protected - return false; - } - else - { - internalId = s.ColumnInt(0); - return true; - } - } - - bool DatabaseWrapperBase::SelectPatientToRecycle(int64_t& internalId, - int64_t patientIdToAvoid) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT patientId FROM PatientRecyclingOrder " - "WHERE patientId != ? ORDER BY seq ASC LIMIT 1"); - s.BindInt64(0, patientIdToAvoid); - - if (!s.Step()) - { - // No patient remaining or all the patients are protected - return false; - } - else - { - internalId = s.ColumnInt(0); - return true; - } - } - - bool DatabaseWrapperBase::IsProtectedPatient(int64_t internalId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM PatientRecyclingOrder WHERE patientId = ?"); - s.BindInt64(0, internalId); - return !s.Step(); - } - - void DatabaseWrapperBase::SetProtectedPatient(int64_t internalId, - bool isProtected) - { - if (isProtected) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DELETE FROM PatientRecyclingOrder WHERE patientId=?"); - s.BindInt64(0, internalId); - s.Run(); - } - else if (IsProtectedPatient(internalId)) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "INSERT INTO PatientRecyclingOrder VALUES(NULL, ?)"); - s.BindInt64(0, internalId); - s.Run(); - } - else - { - // Nothing to do: The patient is already unprotected - } - } - - - - bool DatabaseWrapperBase::IsExistingResource(int64_t internalId) - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, - "SELECT * FROM Resources WHERE internalId=?"); - s.BindInt64(0, internalId); - return s.Step(); - } - - - - void DatabaseWrapperBase::LookupIdentifier(std::list& target, - ResourceType level, - const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value) - { - static const char* COMMON = ("SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE " - "d.id = r.internalId AND r.resourceType=? AND " - "d.tagGroup=? AND d.tagElement=? AND "); - - std::auto_ptr s; - - switch (type) - { - case IdentifierConstraintType_GreaterOrEqual: - s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value>=?")); - break; - - case IdentifierConstraintType_SmallerOrEqual: - s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value<=?")); - break; - - case IdentifierConstraintType_Wildcard: - s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value GLOB ?")); - break; - - case IdentifierConstraintType_Equal: - default: - s.reset(new SQLite::Statement(db_, std::string(COMMON) + "d.value=?")); - break; - } - - assert(s.get() != NULL); - - s->BindInt(0, level); - s->BindInt(1, tag.GetGroup()); - s->BindInt(2, tag.GetElement()); - s->BindString(3, value); - - target.clear(); - - while (s->Step()) - { - target.push_back(s->ColumnInt64(0)); - } - } - - - void DatabaseWrapperBase::LookupIdentifierRange(std::list& target, - ResourceType level, - const DicomTag& tag, - const std::string& start, - const std::string& end) - { - SQLite::Statement statement(db_, SQLITE_FROM_HERE, - "SELECT d.id FROM DicomIdentifiers AS d, Resources AS r WHERE " - "d.id = r.internalId AND r.resourceType=? AND " - "d.tagGroup=? AND d.tagElement=? AND d.value>=? AND d.value<=?"); - - statement.BindInt(0, level); - statement.BindInt(1, tag.GetGroup()); - statement.BindInt(2, tag.GetElement()); - statement.BindString(3, start); - statement.BindString(4, end); - - target.clear(); - - while (statement.Step()) - { - target.push_back(statement.ColumnInt64(0)); - } - } -} diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabasePluginSample/DatabaseWrapperBase.h --- a/OrthancServer/Resources/Graveyard/DatabasePluginSample/DatabaseWrapperBase.h Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,200 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomMap.h" -#include "../../../../OrthancFramework/Sources/DicomFormat/DicomTag.h" -#include "../../../../OrthancFramework/Sources/Enumerations.h" -#include "../../../../OrthancFramework/Sources/FileStorage/FileInfo.h" -#include "../../../../OrthancFramework/Sources/SQLite/Connection.h" -#include "../../../Sources/ExportedResource.h" -#include "../../../Sources/ServerIndexChange.h" -#include "../../../Sources/ServerEnumerations.h" - -#include - - -namespace Orthanc -{ - /** - * This class is shared between the Orthanc core and the sample - * database plugin whose code is in - * "../Plugins/Samples/DatabasePlugin". - **/ - class DatabaseWrapperBase - { - private: - SQLite::Connection& db_; - - ErrorCode GetChangesInternal(std::list& target, - bool& done, - SQLite::Statement& s, - uint32_t maxResults); - - void GetExportedResourcesInternal(std::list& target, - bool& done, - SQLite::Statement& s, - uint32_t maxResults); - - public: - DatabaseWrapperBase(SQLite::Connection& db) : db_(db) - { - } - - void SetGlobalProperty(GlobalProperty property, - const std::string& value); - - bool LookupGlobalProperty(std::string& target, - GlobalProperty property); - - int64_t CreateResource(const std::string& publicId, - ResourceType type); - - bool LookupResource(int64_t& id, - ResourceType& type, - const std::string& publicId); - - ErrorCode LookupParent(bool& found, - int64_t& parentId, - int64_t resourceId); - - bool GetPublicId(std::string& result, - int64_t resourceId); - - ErrorCode GetResourceType(ResourceType& result, - int64_t resourceId); - - void AttachChild(int64_t parent, - int64_t child); - - void SetMetadata(int64_t id, - MetadataType type, - const std::string& value); - - void DeleteMetadata(int64_t id, - MetadataType type); - - bool LookupMetadata(std::string& target, - int64_t id, - MetadataType type); - - void ListAvailableMetadata(std::list& target, - int64_t id); - - void AddAttachment(int64_t id, - const FileInfo& attachment); - - void DeleteAttachment(int64_t id, - FileContentType attachment); - - void ListAvailableAttachments(std::list& target, - int64_t id); - - bool LookupAttachment(FileInfo& attachment, - int64_t id, - FileContentType contentType); - - - void ClearMainDicomTags(int64_t id); - - - void SetMainDicomTag(int64_t id, - const DicomTag& tag, - const std::string& value); - - void SetIdentifierTag(int64_t id, - const DicomTag& tag, - const std::string& value); - - void GetMainDicomTags(DicomMap& map, - int64_t id); - - void GetChildrenPublicId(std::list& target, - int64_t id); - - void GetChildrenInternalId(std::list& target, - int64_t id); - - void LogChange(int64_t internalId, - const ServerIndexChange& change); - - ErrorCode GetChanges(std::list& target, - bool& done, - int64_t since, - uint32_t maxResults); - - ErrorCode GetLastChange(std::list& target); - - void LogExportedResource(const ExportedResource& resource); - - void GetExportedResources(std::list& target, - bool& done, - int64_t since, - uint32_t maxResults); - - void GetLastExportedResource(std::list& target); - - uint64_t GetTotalCompressedSize(); - - uint64_t GetTotalUncompressedSize(); - - void GetAllInternalIds(std::list& target, - ResourceType resourceType); - - void GetAllPublicIds(std::list& target, - ResourceType resourceType); - - void GetAllPublicIds(std::list& target, - ResourceType resourceType, - size_t since, - size_t limit); - - uint64_t GetResourceCount(ResourceType resourceType); - - bool SelectPatientToRecycle(int64_t& internalId); - - bool SelectPatientToRecycle(int64_t& internalId, - int64_t patientIdToAvoid); - - bool IsProtectedPatient(int64_t internalId); - - void SetProtectedPatient(int64_t internalId, - bool isProtected); - - bool IsExistingResource(int64_t internalId); - - void LookupIdentifier(std::list& result, - ResourceType level, - const DicomTag& tag, - IdentifierConstraintType type, - const std::string& value); - - void LookupIdentifierRange(std::list& result, - ResourceType level, - const DicomTag& tag, - const std::string& start, - const std::string& end); - }; -} - diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/DatabasePluginSample/Plugin.cpp --- a/OrthancServer/Resources/Graveyard/DatabasePluginSample/Plugin.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/** - * 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-2024 Orthanc Team SRL, Belgium - * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU 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 - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "Database.h" - -#include -#include -#include - -static OrthancPluginContext* context_ = NULL; -static std::auto_ptr backend_; - - -extern "C" -{ - ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c) - { - context_ = c; - OrthancPluginLogWarning(context_, "Sample plugin is initializing"); - - /* Check the version of the Orthanc core */ - if (OrthancPluginCheckVersion(c) == 0) - { - char info[256]; - sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin", - c->orthancVersion, - ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, - ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, - ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); - OrthancPluginLogError(context_, info); - return -1; - } - - std::string path = "SampleDatabase.sqlite"; - uint32_t argCount = OrthancPluginGetCommandLineArgumentsCount(context_); - for (uint32_t i = 0; i < argCount; i++) - { - char* tmp = OrthancPluginGetCommandLineArgument(context_, i); - std::string argument(tmp); - OrthancPluginFreeString(context_, tmp); - - if (boost::starts_with(argument, "--database=")) - { - path = argument.substr(11); - } - } - - std::string s = "Using the following SQLite database: " + path; - OrthancPluginLogWarning(context_, s.c_str()); - - backend_.reset(new Database(path)); - OrthancPlugins::DatabaseBackendAdapter::Register(context_, *backend_); - - return 0; - } - - ORTHANC_PLUGINS_API void OrthancPluginFinalize() - { - backend_.reset(NULL); - } - - ORTHANC_PLUGINS_API const char* OrthancPluginGetName() - { - return "sample-database"; - } - - - ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() - { - return "1.0"; - } -} diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/FindRefactoringForSQLite.cpp --- a/OrthancServer/Resources/Graveyard/FindRefactoringForSQLite.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -#if 0 - // TODO-FIND: Remove this implementation, as it should be done by - // the compatibility mode implemented by "GenericFind" - - virtual void ExecuteFind(FindResponse& response, - const FindRequest& request, - const std::vector& normalized) ORTHANC_OVERRIDE - { -#if 0 - Compatibility::GenericFind find(*this); - find.Execute(response, request); -#else - { - SQLite::Statement s(db_, SQLITE_FROM_HERE, "DROP TABLE IF EXISTS FilteredResourcesIds"); - s.Run(); - } - - { - - LookupFormatter formatter; - - std::string sqlLookup; - LookupFormatter::Apply(sqlLookup, - formatter, - normalized, - request.GetLevel(), - request.GetLabels(), - request.GetLabelsConstraint(), - (request.HasLimits() ? request.GetLimitsCount() : 0)); // TODO: handles since and count - - { - // first create a temporary table that with the filtered and ordered results - sqlLookup = "CREATE TEMPORARY TABLE FilteredResourcesIds AS " + sqlLookup; - - SQLite::Statement statement(db_, SQLITE_FROM_HERE_DYNAMIC(sqlLookup), sqlLookup); - formatter.Bind(statement); - statement.Run(); - } - - { - // create the response item with the public ids only - SQLite::Statement statement(db_, SQLITE_FROM_HERE, "SELECT publicId FROM FilteredResourcesIds"); - formatter.Bind(statement); - - while (statement.Step()) - { - const std::string resourceId = statement.ColumnString(0); - response.Add(new FindResponse::Resource(request.GetLevel(), resourceId)); - } - } - - // request Each response content through INNER JOIN with the temporary table - if (request.IsRetrieveMainDicomTags()) - { - // TODO-FIND: handle the case where we request tags from multiple levels - SQLite::Statement statement(db_, SQLITE_FROM_HERE, - "SELECT publicId, tagGroup, tagElement, value FROM MainDicomTags AS tags " - " INNER JOIN FilteredResourcesIds ON tags.id = FilteredResourcesIds.internalId"); - formatter.Bind(statement); - - while (statement.Step()) - { - const std::string& resourceId = statement.ColumnString(0); - assert(response.HasResource(resourceId)); - response.GetResource(resourceId).AddStringDicomTag(statement.ColumnInt(1), - statement.ColumnInt(2), - statement.ColumnString(3)); - } - } - - if (request.IsRetrieveChildrenIdentifiers()) - { - SQLite::Statement statement(db_, SQLITE_FROM_HERE, - "SELECT filtered.publicId, childLevel.publicId AS childPublicId " - "FROM Resources as currentLevel " - " INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = currentLevel.internalId " - " INNER JOIN Resources childLevel ON childLevel.parentId = currentLevel.internalId"); - formatter.Bind(statement); - - while (statement.Step()) - { - const std::string& resourceId = statement.ColumnString(0); - assert(response.HasResource(resourceId)); - response.GetResource(resourceId).AddChildIdentifier(GetChildResourceType(request.GetLevel()), statement.ColumnString(1)); - } - } - - if (request.IsRetrieveParentIdentifier()) - { - SQLite::Statement statement(db_, SQLITE_FROM_HERE, - "SELECT filtered.publicId, parentLevel.publicId AS parentPublicId " - "FROM Resources as currentLevel " - " INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = currentLevel.internalId " - " INNER JOIN Resources parentLevel ON currentLevel.parentId = parentLevel.internalId"); - - while (statement.Step()) - { - const std::string& resourceId = statement.ColumnString(0); - const std::string& parentId = statement.ColumnString(1); - assert(response.HasResource(resourceId)); - response.GetResource(resourceId).SetParentIdentifier(parentId); - } - } - - if (request.IsRetrieveMetadata()) - { - SQLite::Statement statement(db_, SQLITE_FROM_HERE, - "SELECT filtered.publicId, metadata.type, metadata.value " - "FROM Metadata " - " INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = Metadata.id"); - - while (statement.Step()) - { - const std::string& resourceId = statement.ColumnString(0); - assert(response.HasResource(resourceId)); - response.GetResource(resourceId).AddMetadata(static_cast(statement.ColumnInt(1)), - statement.ColumnString(2)); - } - } - - if (request.IsRetrieveLabels()) - { - SQLite::Statement statement(db_, SQLITE_FROM_HERE, - "SELECT filtered.publicId, label " - "FROM Labels " - " INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = Labels.id"); - - while (statement.Step()) - { - const std::string& resourceId = statement.ColumnString(0); - assert(response.HasResource(resourceId)); - response.GetResource(resourceId).AddLabel(statement.ColumnString(1)); - } - } - - if (request.IsRetrieveAttachments()) - { - SQLite::Statement statement(db_, SQLITE_FROM_HERE, - "SELECT filtered.publicId, uuid, fileType, uncompressedSize, compressionType, compressedSize, " - " uncompressedMD5, compressedMD5 " - "FROM AttachedFiles " - " INNER JOIN FilteredResourcesIds filtered ON filtered.internalId = AttachedFiles.id"); - - while (statement.Step()) - { - const std::string& resourceId = statement.ColumnString(0); - FileInfo attachment = FileInfo(statement.ColumnString(1), - static_cast(statement.ColumnInt(2)), - statement.ColumnInt64(3), - statement.ColumnString(6), - static_cast(statement.ColumnInt(4)), - statement.ColumnInt64(5), - statement.ColumnString(7)); - - assert(response.HasResource(resourceId)); - response.GetResource(resourceId).AddAttachment(attachment); - }; - } - - // TODO-FIND: implement other responseContent: ResponseContent_ChildInstanceId, ResponseContent_ChildrenMetadata (later: ResponseContent_IsStable) - - } - -#endif - } -#endif - diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Resources/Graveyard/SetupAnonymization2011.cpp --- a/OrthancServer/Resources/Graveyard/SetupAnonymization2011.cpp Wed Oct 09 10:23:04 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,258 +0,0 @@ - /** - * This is a manual implementation by Alain Mazy. Only kept for reference. - * https://bitbucket.org/sjodogne/orthanc/commits/c6defdc4c611fca2ab528ba2c6937a742e0329a8?at=issue-46-anonymization - **/ - - void DicomModification::SetupAnonymization2011() - { - // This is Table E.1-1 from PS 3.15-2011 - DICOM Part 15: Security and System Management Profiles - // https://raw.githubusercontent.com/jodogne/dicom-specification/master/2011/11_15pu.pdf - - removals_.insert(DicomTag(0x0000, 0x1000)); // Affected SOP Instance UID - removals_.insert(DicomTag(0x0000, 0x1001)); // Requested SOP Instance UID - removals_.insert(DicomTag(0x0002, 0x0003)); // Media Storage SOP Instance UID => TODO: replace with a non-zero length UID that is internally consistent within a set of Instances - removals_.insert(DicomTag(0x0004, 0x1511)); // Referenced SOP Instance UID in File - removals_.insert(DicomTag(0x0008, 0x0010)); // Irradiation Event UID - removals_.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID - //removals_.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set in Apply() - clearings_.insert(DicomTag(0x0008, 0x0020)); // Study Date - clearings_.insert(DicomTag(0x0008, 0x0021)); // Series Date - clearings_.insert(DicomTag(0x0008, 0x0030)); // Study Time - clearings_.insert(DicomTag(0x0008, 0x0031)); // Series Time - removals_.insert(DicomTag(0x0008, 0x0022)); // Acquisition Date - removals_.insert(DicomTag(0x0008, 0x0023)); // Content Date - removals_.insert(DicomTag(0x0008, 0x0024)); // Overlay Date - removals_.insert(DicomTag(0x0008, 0x0025)); // Curve Date - removals_.insert(DicomTag(0x0008, 0x002a)); // Acquisition DateTime - removals_.insert(DicomTag(0x0008, 0x0032)); // Acquisition Time - removals_.insert(DicomTag(0x0008, 0x0033)); // Content Time - removals_.insert(DicomTag(0x0008, 0x0034)); // Overlay Time - removals_.insert(DicomTag(0x0008, 0x0035)); // Curve Time - removals_.insert(DicomTag(0x0008, 0x0050)); // Accession Number - removals_.insert(DicomTag(0x0008, 0x0058)); // Failed SOP Instance UID List - removals_.insert(DicomTag(0x0008, 0x0080)); // Institution Name - removals_.insert(DicomTag(0x0008, 0x0081)); // Institution Address - removals_.insert(DicomTag(0x0008, 0x0082)); // Institution Code Sequence - removals_.insert(DicomTag(0x0008, 0x0090)); // Referring Physician's Name - removals_.insert(DicomTag(0x0008, 0x0092)); // Referring Physician's Address - removals_.insert(DicomTag(0x0008, 0x0094)); // Referring Physician's Telephone Numbers - removals_.insert(DicomTag(0x0008, 0x0096)); // Referring Physician's Identification Sequence - removals_.insert(DicomTag(0x0008, 0x010d)); // Context Group Extension Creator UID - removals_.insert(DicomTag(0x0008, 0x0201)); // Timezone Offset From UTC - removals_.insert(DicomTag(0x0008, 0x0300)); // Current Patient Location - removals_.insert(DicomTag(0x0008, 0x1010)); // Station Name - removals_.insert(DicomTag(0x0008, 0x1030)); // Study Description - removals_.insert(DicomTag(0x0008, 0x103e)); // Series Description - removals_.insert(DicomTag(0x0008, 0x1040)); // Institutional Department Name - removals_.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of Record - removals_.insert(DicomTag(0x0008, 0x1049)); // Physician(s) of Record Identification Sequence - removals_.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians' Name - removals_.insert(DicomTag(0x0008, 0x1052)); // Performing Physicians Identification Sequence - removals_.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study - removals_.insert(DicomTag(0x0008, 0x1062)); // Physician Reading Study Identification Sequence - removals_.insert(DicomTag(0x0008, 0x1070)); // Operators' Name - removals_.insert(DicomTag(0x0008, 0x1072)); // Operators' Identification Sequence - removals_.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description - removals_.insert(DicomTag(0x0008, 0x1084)); // Admitting Diagnoses Code Sequence - removals_.insert(DicomTag(0x0008, 0x1110)); // Referenced Study Sequence - removals_.insert(DicomTag(0x0008, 0x1111)); // Referenced Performed Procedure Step Sequence - removals_.insert(DicomTag(0x0008, 0x1120)); // Referenced Patient Sequence - removals_.insert(DicomTag(0x0008, 0x1140)); // Referenced Image Sequence - removals_.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID - removals_.insert(DicomTag(0x0008, 0x1195)); // Transaction UID - removals_.insert(DicomTag(0x0008, 0x2111)); // Derivation Description - removals_.insert(DicomTag(0x0008, 0x2112)); // Source Image Sequence - removals_.insert(DicomTag(0x0008, 0x4000)); // Identifying Comments - removals_.insert(DicomTag(0x0008, 0x9123)); // Creator Version UID - //removals_.insert(DicomTag(0x0010, 0x0010)); // Patient's Name => cf. below (*) - //removals_.insert(DicomTag(0x0010, 0x0020)); // Patient ID => cf. below (*) - removals_.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date - removals_.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time - clearings_.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex - removals_.insert(DicomTag(0x0010, 0x0050)); // Patient's Insurance Plan Code Sequence - removals_.insert(DicomTag(0x0010, 0x0101)); // Patient's Primary Language Code Sequence - removals_.insert(DicomTag(0x0010, 0x0102)); // Patient's Primary Language Modifier Code Sequence - removals_.insert(DicomTag(0x0010, 0x1000)); // Other Patient Ids - removals_.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names - removals_.insert(DicomTag(0x0010, 0x1002)); // Other Patient IDs Sequence - removals_.insert(DicomTag(0x0010, 0x1005)); // Patient's Birth Name - removals_.insert(DicomTag(0x0010, 0x1010)); // Patient's Age - removals_.insert(DicomTag(0x0010, 0x1020)); // Patient's Size - removals_.insert(DicomTag(0x0010, 0x1030)); // Patient's Weight - removals_.insert(DicomTag(0x0010, 0x1040)); // Patient's Address - removals_.insert(DicomTag(0x0010, 0x1050)); // Insurance Plan Identification - removals_.insert(DicomTag(0x0010, 0x1060)); // Patient's Mother's Birth Name - removals_.insert(DicomTag(0x0010, 0x1080)); // Military Rank - removals_.insert(DicomTag(0x0010, 0x1081)); // Branch of Service - removals_.insert(DicomTag(0x0010, 0x1090)); // Medical Record Locator - removals_.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts - removals_.insert(DicomTag(0x0010, 0x2110)); // Allergies - removals_.insert(DicomTag(0x0010, 0x2150)); // Country of Residence - removals_.insert(DicomTag(0x0010, 0x2152)); // Region of Residence - removals_.insert(DicomTag(0x0010, 0x2154)); // PatientTelephoneNumbers - removals_.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group - removals_.insert(DicomTag(0x0010, 0x2180)); // Occupation - removals_.insert(DicomTag(0x0010, 0x21a0)); // Smoking Status - removals_.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History - removals_.insert(DicomTag(0x0010, 0x21c0)); // Pregnancy Status - removals_.insert(DicomTag(0x0010, 0x21d0)); // Last Menstrual Date - removals_.insert(DicomTag(0x0010, 0x21f0)); // Patient's Religious Preference - removals_.insert(DicomTag(0x0010, 0x2203)); // Patient's Sex Neutered - removals_.insert(DicomTag(0x0010, 0x2297)); // Responsible Person - removals_.insert(DicomTag(0x0010, 0x2299)); // Responsible Organization - removals_.insert(DicomTag(0x0010, 0x4000)); // Patient Comments - removals_.insert(DicomTag(0x0018, 0x0010)); // Contrast Bolus Agent - removals_.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number - removals_.insert(DicomTag(0x0018, 0x1002)); // Device UID - removals_.insert(DicomTag(0x0018, 0x1004)); // Plate ID - removals_.insert(DicomTag(0x0018, 0x1005)); // Generator ID - removals_.insert(DicomTag(0x0018, 0x1007)); // Cassette ID - removals_.insert(DicomTag(0x0018, 0x1008)); // Gantry ID - removals_.insert(DicomTag(0x0018, 0x1030)); // Protocol Name - removals_.insert(DicomTag(0x0018, 0x1400)); // Acquisition Device Processing Description - removals_.insert(DicomTag(0x0018, 0x4000)); // Acquisition Comments - removals_.insert(DicomTag(0x0018, 0x700a)); // Detector ID - removals_.insert(DicomTag(0x0018, 0xa003)); // Contribution Description - removals_.insert(DicomTag(0x0018, 0x9424)); // Acquisition Protocol Description - //removals_.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => set in Apply() - //removals_.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => set in Apply() - removals_.insert(DicomTag(0x0020, 0x0010)); // Study ID - removals_.insert(DicomTag(0x0020, 0x0052)); // Frame of Reference UID - removals_.insert(DicomTag(0x0020, 0x0200)); // Synchronization Frame of Reference UID - removals_.insert(DicomTag(0x0020, 0x3401)); // Modifying Device ID - removals_.insert(DicomTag(0x0020, 0x3404)); // Modifying Device Manufacturer - removals_.insert(DicomTag(0x0020, 0x3406)); // Modified Image Description - removals_.insert(DicomTag(0x0020, 0x4000)); // Image Comments - removals_.insert(DicomTag(0x0020, 0x9158)); // Frame Comments - removals_.insert(DicomTag(0x0020, 0x9161)); // Concatenation UID - removals_.insert(DicomTag(0x0020, 0x9164)); // Dimension Organization UID - //removals_.insert(DicomTag(0x0028, 0x1199)); // Palette Color Lookup Table UID => TODO: replace with a non-zero length UID that is internally consistent within a set of Instances - //removals_.insert(DicomTag(0x0028, 0x1214)); // Large Palette Color Lookup Table UID => TODO: replace with a non-zero length UID that is internally consistent within a set of Instances - removals_.insert(DicomTag(0x0028, 0x4000)); // Image Presentation Comments - removals_.insert(DicomTag(0x0032, 0x0012)); // Study ID Issuer - removals_.insert(DicomTag(0x0032, 0x1020)); // Scheduled Study Location - removals_.insert(DicomTag(0x0032, 0x1021)); // Scheduled Study Location AE Title - removals_.insert(DicomTag(0x0032, 0x1030)); // Reason for Study - removals_.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician - removals_.insert(DicomTag(0x0032, 0x1033)); // Requesting Service - removals_.insert(DicomTag(0x0032, 0x1060)); // Requesting Procedure Description - removals_.insert(DicomTag(0x0032, 0x1070)); // Requested Contrast Agent - removals_.insert(DicomTag(0x0032, 0x4000)); // Study Comments - removals_.insert(DicomTag(0x0038, 0x0010)); // Admission ID - removals_.insert(DicomTag(0x0038, 0x0011)); // Issuer of Admission ID - removals_.insert(DicomTag(0x0038, 0x001e)); // Scheduled Patient Institution Residence - removals_.insert(DicomTag(0x0038, 0x0020)); // Admitting Date - removals_.insert(DicomTag(0x0038, 0x0021)); // Admitting Time - removals_.insert(DicomTag(0x0038, 0x0040)); // Discharge Diagnosis Description - removals_.insert(DicomTag(0x0038, 0x0050)); // Special Needs - removals_.insert(DicomTag(0x0038, 0x0060)); // Service Episode ID - removals_.insert(DicomTag(0x0038, 0x0061)); // Issuer of Service Episode ID - removals_.insert(DicomTag(0x0038, 0x0062)); // Service Episode Description - removals_.insert(DicomTag(0x0038, 0x0400)); // Patient's Institution Residence - removals_.insert(DicomTag(0x0038, 0x0500)); // Patient State - removals_.insert(DicomTag(0x0038, 0x4000)); // Visit Comments - removals_.insert(DicomTag(0x0038, 0x1234)); // Referenced Patient Alias Sequence - removals_.insert(DicomTag(0x0040, 0x0001)); // Scheduled Station AE Title - removals_.insert(DicomTag(0x0040, 0x0002)); // Scheduled Procedure Step Start Date - removals_.insert(DicomTag(0x0040, 0x0003)); // Scheduled Procedure Step Start Time - removals_.insert(DicomTag(0x0040, 0x0004)); // Scheduled Procedure Step End Date - removals_.insert(DicomTag(0x0040, 0x0005)); // Scheduled Procedure Step End Time - removals_.insert(DicomTag(0x0040, 0x0006)); // Scheduled Performing Physician Name - removals_.insert(DicomTag(0x0040, 0x0007)); // Scheduled Procedure Step Description - removals_.insert(DicomTag(0x0040, 0x000b)); // Scheduled Performing Physician Identification Sequence - removals_.insert(DicomTag(0x0040, 0x0010)); // Scheduled Station Name - removals_.insert(DicomTag(0x0040, 0x0011)); // Scheduled Procedure Step Location - removals_.insert(DicomTag(0x0040, 0x0012)); // Pre-Medication - removals_.insert(DicomTag(0x0040, 0x0241)); // Performed Station AE Title - removals_.insert(DicomTag(0x0040, 0x0242)); // Performed Station Name - removals_.insert(DicomTag(0x0040, 0x0243)); // Performed Location - removals_.insert(DicomTag(0x0040, 0x0244)); // Performed Procedure Step Start Date - removals_.insert(DicomTag(0x0040, 0x0245)); // Performed Procedure Step Start Time - removals_.insert(DicomTag(0x0040, 0x0248)); // Performed Station Name Code Sequence - removals_.insert(DicomTag(0x0040, 0x0253)); // Performed Procedure Step ID - removals_.insert(DicomTag(0x0040, 0x0254)); // Performed Procedure Step Description - removals_.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence - removals_.insert(DicomTag(0x0040, 0x0280)); // Comments on Performed Procedure Step - removals_.insert(DicomTag(0x0040, 0x0555)); // Acquisition Context Sequence - removals_.insert(DicomTag(0x0040, 0x1001)); // Requested Procedure ID - removals_.insert(DicomTag(0x0040, 0x1010)); // Names of Intended Recipient of Results - removals_.insert(DicomTag(0x0040, 0x1011)); // Intended Recipient of Results Identification Sequence - removals_.insert(DicomTag(0x0040, 0x1004)); // Patient Transport Arrangements - removals_.insert(DicomTag(0x0040, 0x1005)); // Requested Procedure Location - removals_.insert(DicomTag(0x0040, 0x1101)); // Person Identification Code Sequence - removals_.insert(DicomTag(0x0040, 0x1102)); // Person Address - removals_.insert(DicomTag(0x0040, 0x1103)); // Person Telephone Numbers - removals_.insert(DicomTag(0x0040, 0x1400)); // Requested Procedure Comments - removals_.insert(DicomTag(0x0040, 0x2001)); // Reason for Imaging Service Request - removals_.insert(DicomTag(0x0040, 0x2008)); // Order Entered By - removals_.insert(DicomTag(0x0040, 0x2009)); // Order Enterer Location - removals_.insert(DicomTag(0x0040, 0x2010)); // Order Callback Phone Number - removals_.insert(DicomTag(0x0040, 0x2016)); // Placer Order Number of Imaging Service Request - removals_.insert(DicomTag(0x0040, 0x2017)); // Filler Order Number of Imaging Service Request - removals_.insert(DicomTag(0x0040, 0x2400)); // Imaging Service Request Comments - removals_.insert(DicomTag(0x0040, 0x4023)); // Referenced General Purpose Scheduled Procedure Step Transaction UID - removals_.insert(DicomTag(0x0040, 0x4025)); // Scheduled Station Name Code Sequence - removals_.insert(DicomTag(0x0040, 0x4027)); // Scheduled Station Geographic Location Code Sequence - removals_.insert(DicomTag(0x0040, 0x4030)); // Performed Station Geographic Location Code Sequence - removals_.insert(DicomTag(0x0040, 0x4034)); // Scheduled Human Performers Sequence - removals_.insert(DicomTag(0x0040, 0x4035)); // Actual Human Performers Sequence - removals_.insert(DicomTag(0x0040, 0x4036)); // Human Performers Organization - removals_.insert(DicomTag(0x0040, 0x4037)); // Human Performers Name - removals_.insert(DicomTag(0x0040, 0xa027)); // Verifying Organization - removals_.insert(DicomTag(0x0040, 0xa073)); // Verifying Observer Sequence - removals_.insert(DicomTag(0x0040, 0xa075)); // Verifying Observer Name - removals_.insert(DicomTag(0x0040, 0xa078)); // Author Observer Sequence - removals_.insert(DicomTag(0x0040, 0xa07a)); // Participant Sequence - removals_.insert(DicomTag(0x0040, 0xa07c)); // Custodial Organization Sequence - removals_.insert(DicomTag(0x0040, 0xa088)); // Verifying Observer Identification Code Sequence - removals_.insert(DicomTag(0x0040, 0xa123)); // Person Name - removals_.insert(DicomTag(0x0040, 0xa124)); // UID - removals_.insert(DicomTag(0x0040, 0xa730)); // Content Sequence - removals_.insert(DicomTag(0x0040, 0x3001)); // Confidentiality Constraint on Patient Data Description - removals_.insert(DicomTag(0x0040, 0xdb0c)); // Template Extension Organization UID - removals_.insert(DicomTag(0x0040, 0xdb0d)); // Template Extension Creator UID - removals_.insert(DicomTag(0x0070, 0x0001)); // Graphic Annotation Sequence - removals_.insert(DicomTag(0x0070, 0x0084)); // Content Creator's Name - removals_.insert(DicomTag(0x0070, 0x0086)); // Content Creator's Identification Code Sequence - removals_.insert(DicomTag(0x0070, 0x031a)); // Fiducial UID - removals_.insert(DicomTag(0x0088, 0x0140)); // Storage Media File-set UID - removals_.insert(DicomTag(0x0088, 0x0200)); // Icon Image Sequence - removals_.insert(DicomTag(0x0088, 0x0904)); // Topic Title - removals_.insert(DicomTag(0x0088, 0x0906)); // Topic Subject - removals_.insert(DicomTag(0x0088, 0x0910)); // Topic Author - removals_.insert(DicomTag(0x0088, 0x0912)); // Topic Key Words - removals_.insert(DicomTag(0x0400, 0x0100)); // Digital Signature UID - removals_.insert(DicomTag(0x0400, 0x0402)); // Referenced Digital Signature Sequence - removals_.insert(DicomTag(0x0400, 0x0403)); // Referenced SOP Instance MAC Sequence - removals_.insert(DicomTag(0x0400, 0x0404)); // MAC - removals_.insert(DicomTag(0x0400, 0x0550)); // Modified Attributes Sequence - removals_.insert(DicomTag(0x0400, 0x0561)); // Original Attributes Sequence - removals_.insert(DicomTag(0x2030, 0x0020)); // Text String - removals_.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID - removals_.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID - removals_.insert(DicomTag(0x300a, 0x0013)); // Dose Reference UID - removals_.insert(DicomTag(0x300e, 0x0008)); // Reviewer Name - removals_.insert(DicomTag(0x4000, 0x0010)); // Arbitrary - removals_.insert(DicomTag(0x4000, 0x4000)); // Text Comments - removals_.insert(DicomTag(0x4008, 0x0042)); // Results ID Issuer - removals_.insert(DicomTag(0x4008, 0x0102)); // Interpretation Recorder - removals_.insert(DicomTag(0x4008, 0x010a)); // Interpretation Transcriber - removals_.insert(DicomTag(0x4008, 0x010b)); // Interpretation Text - removals_.insert(DicomTag(0x4008, 0x010c)); // Interpretation Author - removals_.insert(DicomTag(0x4008, 0x0111)); // Interpretation Approver Sequence - removals_.insert(DicomTag(0x4008, 0x0114)); // Physician Approving Interpretation - removals_.insert(DicomTag(0x4008, 0x0115)); // Interpretation Diagnosis Description - removals_.insert(DicomTag(0x4008, 0x0118)); // Results Distribution List Sequence - removals_.insert(DicomTag(0x4008, 0x0119)); // Distribution Name - removals_.insert(DicomTag(0x4008, 0x011a)); // Distribution Address - removals_.insert(DicomTag(0x4008, 0x0202)); // Interpretation ID Issuer - removals_.insert(DicomTag(0x4008, 0x0300)); // Impressions - removals_.insert(DicomTag(0x4008, 0x4000)); // Results Comments - removals_.insert(DicomTag(0xfffa, 0xfffa)); // Digital Signature Sequence - removals_.insert(DicomTag(0xfffc, 0xfffc)); // Data Set Trailing Padding - //removals_.insert(DicomTag(0x60xx, 0x4000)); // Overlay Comments => TODO - //removals_.insert(DicomTag(0x60xx, 0x3000)); // Overlay Data => TODO - - // Set the DeidentificationMethod tag - ReplaceInternal(DICOM_TAG_DEIDENTIFICATION_METHOD, ORTHANC_DEIDENTIFICATION_METHOD_2011); - } diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/Database/FindResponse.cpp diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed Oct 09 10:23:04 2024 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed Oct 09 11:01:11 2024 +0200 @@ -582,285 +582,6 @@ } - bool StatelessDatabaseOperations::ExpandResource(ExpandedResource& target, - const std::string& publicId, - ResourceType level, - const std::set& requestedTags, - ExpandResourceFlags expandFlags) - { - class Operations : public ReadOnlyOperationsT6< - bool&, ExpandedResource&, const std::string&, ResourceType, const std::set&, ExpandResourceFlags> - { - private: - bool hasLabelsSupport_; - - static bool LookupStringMetadata(std::string& result, - const std::map& metadata, - MetadataType type) - { - std::map::const_iterator found = metadata.find(type); - - if (found == metadata.end()) - { - return false; - } - else - { - result = found->second; - return true; - } - } - - - static bool LookupIntegerMetadata(int64_t& result, - const std::map& metadata, - MetadataType type) - { - std::string s; - if (!LookupStringMetadata(s, metadata, type)) - { - return false; - } - - try - { - result = boost::lexical_cast(s); - return true; - } - catch (boost::bad_lexical_cast&) - { - return false; - } - } - - - public: - explicit Operations(bool hasLabelsSupport) : - hasLabelsSupport_(hasLabelsSupport) - { - } - - virtual void ApplyTuple(ReadOnlyTransaction& transaction, - const Tuple& tuple) ORTHANC_OVERRIDE - { - // Lookup for the requested resource - int64_t internalId; - ResourceType type; - std::string parent; - if (!transaction.LookupResourceAndParent(internalId, type, parent, tuple.get<2>()) || - type != tuple.get<3>()) - { - tuple.get<0>() = false; - } - else - { - ExpandedResource& target = tuple.get<1>(); - ExpandResourceFlags expandFlags = tuple.get<5>(); - - // Set information about the parent resource (if it exists) - if (type == ResourceType_Patient) - { - if (!parent.empty()) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - } - else - { - if (parent.empty()) - { - throw OrthancException(ErrorCode_DatabasePlugin); - } - - target.parentId_ = parent; - } - - target.SetResource(type, tuple.get<2>()); - - if (expandFlags & ExpandResourceFlags_IncludeChildren) - { - // List the children resources - transaction.GetChildrenPublicId(target.childrenIds_, internalId); - } - - if (expandFlags & ExpandResourceFlags_IncludeMetadata) - { - // Extract the metadata - transaction.GetAllMetadata(target.metadata_, internalId); - - switch (type) - { - case ResourceType_Patient: - case ResourceType_Study: - break; - - case ResourceType_Series: - { - int64_t i; - if (LookupIntegerMetadata(i, target.metadata_, MetadataType_Series_ExpectedNumberOfInstances)) - { - target.expectedNumberOfInstances_ = static_cast(i); - target.status_ = EnumerationToString(transaction.GetSeriesStatus(internalId, i)); - } - else - { - target.expectedNumberOfInstances_ = -1; - target.status_ = EnumerationToString(SeriesStatus_Unknown); - } - - break; - } - - case ResourceType_Instance: - { - FileInfo attachment; - int64_t revision; // ignored - if (!transaction.LookupAttachment(attachment, revision, internalId, FileContentType_Dicom)) - { - throw OrthancException(ErrorCode_InternalError); - } - - target.fileSize_ = static_cast(attachment.GetUncompressedSize()); - target.fileUuid_ = attachment.GetUuid(); - - int64_t i; - if (LookupIntegerMetadata(i, target.metadata_, MetadataType_Instance_IndexInSeries)) - { - target.indexInSeries_ = static_cast(i); - } - else - { - target.indexInSeries_ = -1; - } - - break; - } - - default: - throw OrthancException(ErrorCode_InternalError); - } - - // check the main dicom tags list has not changed since the resource was stored - target.mainDicomTagsSignature_ = DicomMap::GetDefaultMainDicomTagsSignatureFrom1_11(type); - LookupStringMetadata(target.mainDicomTagsSignature_, target.metadata_, MetadataType_MainDicomTagsSignature); - } - - if (expandFlags & ExpandResourceFlags_IncludeMainDicomTags) - { - // read all tags from DB - transaction.GetMainDicomTags(target.GetMainDicomTags(), internalId); - - // read all main sequences from DB - std::string serializedSequences; - if (LookupStringMetadata(serializedSequences, target.metadata_, MetadataType_MainDicomSequences)) - { - Json::Value jsonMetadata; - Toolbox::ReadJson(jsonMetadata, serializedSequences); - - assert(jsonMetadata["Version"].asInt() == 1); - target.GetMainDicomTags().FromDicomAsJson(jsonMetadata["Sequences"], true /* append */, true /* parseSequences */); - } - - // check if we have access to all requestedTags or if we must get tags from parents - const std::set& requestedTags = tuple.get<4>(); - - if (requestedTags.size() > 0) - { - std::set savedMainDicomTags; - - FromDcmtkBridge::ParseListOfTags(savedMainDicomTags, target.mainDicomTagsSignature_); - - // read parent main dicom tags as long as we have not gathered all requested tags - ResourceType currentLevel = target.GetLevel(); - int64_t currentInternalId = internalId; - Toolbox::GetMissingsFromSet(target.missingRequestedTags_, requestedTags, savedMainDicomTags); - - while ((target.missingRequestedTags_.size() > 0) - && currentLevel != ResourceType_Patient) - { - currentLevel = GetParentResourceType(currentLevel); - - int64_t currentParentId; - if (!transaction.LookupParent(currentParentId, currentInternalId)) - { - break; - } - - std::map parentMetadata; - transaction.GetAllMetadata(parentMetadata, currentParentId); - - std::string parentMainDicomTagsSignature = DicomMap::GetDefaultMainDicomTagsSignatureFrom1_11(currentLevel); - LookupStringMetadata(parentMainDicomTagsSignature, parentMetadata, MetadataType_MainDicomTagsSignature); - - std::set parentSavedMainDicomTags; - FromDcmtkBridge::ParseListOfTags(parentSavedMainDicomTags, parentMainDicomTagsSignature); - - size_t previousMissingCount = target.missingRequestedTags_.size(); - Toolbox::AppendSets(savedMainDicomTags, parentSavedMainDicomTags); - Toolbox::GetMissingsFromSet(target.missingRequestedTags_, requestedTags, savedMainDicomTags); - - // read the parent tags from DB only if it reduces the number of missing tags - if (target.missingRequestedTags_.size() < previousMissingCount) - { - Toolbox::AppendSets(savedMainDicomTags, parentSavedMainDicomTags); - - DicomMap parentTags; - transaction.GetMainDicomTags(parentTags, currentParentId); - - target.GetMainDicomTags().Merge(parentTags); - } - - currentInternalId = currentParentId; - } - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeLabels) && - hasLabelsSupport_) - { - transaction.ListLabels(target.labels_, internalId); - } - - std::string tmp; - - if (LookupStringMetadata(tmp, target.metadata_, MetadataType_AnonymizedFrom)) - { - target.anonymizedFrom_ = tmp; - } - - if (LookupStringMetadata(tmp, target.metadata_, MetadataType_ModifiedFrom)) - { - target.modifiedFrom_ = tmp; - } - - if (type == ResourceType_Patient || - type == ResourceType_Study || - type == ResourceType_Series) - { - target.isStable_ = !transaction.GetTransactionContext().IsUnstableResource(type, internalId); - - if (LookupStringMetadata(tmp, target.metadata_, MetadataType_LastUpdate)) - { - target.lastUpdate_ = tmp; - } - } - else - { - target.isStable_ = false; - } - - tuple.get<0>() = true; - } - } - }; - - bool found; - Operations operations(db_.GetDatabaseCapabilities().HasLabelsSupport()); - operations.Apply(*this, found, target, publicId, level, requestedTags, expandFlags); - return found; - } - - void StatelessDatabaseOperations::GetAllMetadata(std::map& target, const std::string& publicId, ResourceType level) @@ -1885,77 +1606,6 @@ } - void StatelessDatabaseOperations::ApplyLookupResources(std::vector& resourcesId, - std::vector* instancesId, - const DatabaseLookup& lookup, - ResourceType queryLevel, - const std::set& labels, - LabelsConstraint labelsConstraint, - uint32_t limit) - { - class Operations : public ReadOnlyOperationsT6&, LabelsConstraint, size_t> - { - private: - std::list resourcesList_; - std::list instancesList_; - - public: - const std::list& GetResourcesList() const - { - return resourcesList_; - } - - const std::list& GetInstancesList() const - { - return instancesList_; - } - - virtual void ApplyTuple(ReadOnlyTransaction& transaction, - const Tuple& tuple) ORTHANC_OVERRIDE - { - // TODO - CANDIDATE FOR "TransactionType_Implicit" - if (tuple.get<0>()) - { - transaction.ApplyLookupResources( - resourcesList_, &instancesList_, tuple.get<1>(), tuple.get<2>(), tuple.get<3>(), tuple.get<4>(), tuple.get<5>()); - } - else - { - transaction.ApplyLookupResources( - resourcesList_, NULL, tuple.get<1>(), tuple.get<2>(), tuple.get<3>(), tuple.get<4>(), tuple.get<5>()); - } - } - }; - - if (!labels.empty() && - !db_.GetDatabaseCapabilities().HasLabelsSupport()) - { - throw OrthancException(ErrorCode_NotImplemented, "The database backend doesn't support labels"); - } - - for (std::set::const_iterator it = labels.begin(); it != labels.end(); ++it) - { - ServerToolbox::CheckValidLabel(*it); - } - - DatabaseDicomTagConstraints normalized; - - assert(mainDicomTagsRegistry_.get() != NULL); - mainDicomTagsRegistry_->NormalizeLookup(normalized, lookup, queryLevel); - - Operations operations; - operations.Apply(*this, (instancesId != NULL), normalized, queryLevel, labels, labelsConstraint, limit); - - CopyListToVector(resourcesId, operations.GetResourcesList()); - - if (instancesId != NULL) - { - CopyListToVector(*instancesId, operations.GetInstancesList()); - } - } - - bool StatelessDatabaseOperations::DeleteResource(Json::Value& remainingAncestor, const std::string& uuid, ResourceType expectedType) diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Wed Oct 09 10:23:04 2024 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Wed Oct 09 11:01:11 2024 +0200 @@ -39,102 +39,6 @@ class ParsedDicomFile; struct ServerIndexChange; - class ExpandedResource : public boost::noncopyable - { - private: - std::string id_; - ResourceType level_; - DicomMap tags_; // all main tags and main sequences from DB - - public: - std::string mainDicomTagsSignature_; - std::string parentId_; - std::list childrenIds_; - std::map metadata_; - std::string anonymizedFrom_; - std::string modifiedFrom_; - std::string lastUpdate_; - std::set missingRequestedTags_; - - // for patients/studies/series - bool isStable_; - - // for series only - int expectedNumberOfInstances_; - std::string status_; - - // for instances only - size_t fileSize_; - std::string fileUuid_; - int indexInSeries_; - - // New in Orthanc 1.12.0 - std::set labels_; - - public: - // TODO - Cleanup - ExpandedResource() : - level_(ResourceType_Instance), - isStable_(false), - expectedNumberOfInstances_(0), - fileSize_(0), - indexInSeries_(0) - { - } - - void SetResource(ResourceType level, - const std::string& id) - { - level_ = level; - id_ = id; - } - - const std::string& GetPublicId() const - { - return id_; - } - - ResourceType GetLevel() const - { - return level_; - } - - DicomMap& GetMainDicomTags() - { - return tags_; - } - - const DicomMap& GetMainDicomTags() const - { - return tags_; - } - }; - - enum ExpandResourceFlags - { - ExpandResourceFlags_None = 0, - // used to fetch from DB and for output - ExpandResourceFlags_IncludeMetadata = (1 << 0), - ExpandResourceFlags_IncludeChildren = (1 << 1), - ExpandResourceFlags_IncludeMainDicomTags = (1 << 2), - ExpandResourceFlags_IncludeLabels = (1 << 3), - - // only used for output - ExpandResourceFlags_IncludeAllMetadata = (1 << 4), // new in Orthanc 1.12.4 - ExpandResourceFlags_IncludeIsStable = (1 << 5), // new in Orthanc 1.12.4 - - ExpandResourceFlags_DefaultExtract = (ExpandResourceFlags_IncludeMetadata | - ExpandResourceFlags_IncludeChildren | - ExpandResourceFlags_IncludeMainDicomTags | - ExpandResourceFlags_IncludeLabels), - - ExpandResourceFlags_DefaultOutput = (ExpandResourceFlags_IncludeMetadata | - ExpandResourceFlags_IncludeChildren | - ExpandResourceFlags_IncludeMainDicomTags | - ExpandResourceFlags_IncludeLabels | - ExpandResourceFlags_IncludeIsStable) - }; - class StatelessDatabaseOperations : public boost::noncopyable { public: @@ -641,12 +545,6 @@ void Apply(IReadWriteOperations& operations); - bool ExpandResource(ExpandedResource& target, - const std::string& publicId, - ResourceType level, - const std::set& requestedTags, - ExpandResourceFlags expandFlags); - void GetAllMetadata(std::map& target, const std::string& publicId, ResourceType level); @@ -753,14 +651,6 @@ const std::string& publicId, ResourceType parentType); - void ApplyLookupResources(std::vector& resourcesId, - std::vector* instancesId, // Can be NULL if not needed - const DatabaseLookup& lookup, - ResourceType queryLevel, - const std::set& labels, - LabelsConstraint labelsConstraint, - uint32_t limit); - bool DeleteResource(Json::Value& remainingAncestor /* out */, const std::string& uuid, ResourceType expectedType); diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/OrthancFindRequestHandler.cpp --- a/OrthancServer/Sources/OrthancFindRequestHandler.cpp Wed Oct 09 10:23:04 2024 +0200 +++ b/OrthancServer/Sources/OrthancFindRequestHandler.cpp Wed Oct 09 11:01:11 2024 +0200 @@ -82,102 +82,6 @@ } - static void AddAnswer(DicomFindAnswers& answers, - ServerContext& context, - const std::string& publicId, - const std::string& instanceId, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson, - ResourceType level, - const DicomArray& query, - const std::list& sequencesToReturn, - const std::string& defaultPrivateCreator, - const std::map& privateCreators, - const std::string& retrieveAet, - bool allowStorageAccess) - { - ExpandedResource resource; - std::set requestedTags; - - query.GetTags(requestedTags); - requestedTags.erase(DICOM_TAG_QUERY_RETRIEVE_LEVEL); // this is not part of the answer - - // reuse ExpandResource to get missing tags and computed tags (ModalitiesInStudy ...). This code is therefore shared between C-Find, tools/find, list-resources and QIDO-RS - context.ExpandResource(resource, publicId, mainDicomTags, instanceId, dicomAsJson, - level, requestedTags, ExpandResourceFlags_IncludeMainDicomTags, allowStorageAccess); - - DicomMap result; - - /** - * Add the mandatory "Retrieve AE Title (0008,0054)" tag, which was missing in Orthanc <= 1.7.2. - * http://dicom.nema.org/medical/dicom/current/output/html/part04.html#sect_C.4.1.1.3.2 - * https://groups.google.com/g/orthanc-users/c/-7zNTKR_PMU/m/kfjwzEVNAgAJ - **/ - result.SetValue(DICOM_TAG_RETRIEVE_AE_TITLE, retrieveAet, false /* not binary */); - - for (size_t i = 0; i < query.GetSize(); i++) - { - if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL) - { - // Fix issue 30 on Google Code (QR response missing "Query/Retrieve Level" (008,0052)) - result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue()); - } - else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET) - { - // Do not include the encoding, this is handled by class ParsedDicomFile - } - else - { - const DicomTag& tag = query.GetElement(i).GetTag(); - const DicomValue* value = resource.GetMainDicomTags().TestAndGetValue(tag); - - if (value != NULL && - !value->IsNull() && - !value->IsBinary()) - { - result.SetValue(tag, value->GetContent(), false); - } - else - { - result.SetValue(tag, "", false); - } - } - } - - if (result.GetSize() == 0 && - sequencesToReturn.empty()) - { - CLOG(WARNING, DICOM) << "The C-FIND request does not return any DICOM tag"; - } - else if (sequencesToReturn.empty()) - { - answers.Add(result); - } - else if (dicomAsJson == NULL) - { - CLOG(WARNING, DICOM) << "C-FIND query requesting a sequence, but reading JSON from disk is disabled"; - answers.Add(result); - } - else - { - ParsedDicomFile dicom(result, GetDefaultDicomEncoding(), - true /* be permissive, cf. issue #136 */, defaultPrivateCreator, privateCreators); - - for (std::list::const_iterator tag = sequencesToReturn.begin(); - tag != sequencesToReturn.end(); ++tag) - { - assert(dicomAsJson != NULL); - const Json::Value& source = (*dicomAsJson) [tag->Format()]; - - CopySequence(dicom, *tag, source, defaultPrivateCreator, privateCreators); - } - - answers.Add(dicom); - } - } - - - bool OrthancFindRequestHandler::FilterQueryTag(std::string& value /* can be modified */, ResourceType level, const DicomTag& tag, @@ -248,88 +152,6 @@ } - class OrthancFindRequestHandler::LookupVisitor : public ServerContext::ILookupVisitor - { - private: - DicomFindAnswers& answers_; - ServerContext& context_; - ResourceType level_; - const DicomMap& query_; - DicomArray queryAsArray_; - const std::list& sequencesToReturn_; - std::string defaultPrivateCreator_; // the private creator to use if the group is not defined in the query itself - const std::map& privateCreators_; // the private creators defined in the query itself - std::string retrieveAet_; - FindStorageAccessMode findStorageAccessMode_; - - public: - LookupVisitor(DicomFindAnswers& answers, - ServerContext& context, - ResourceType level, - const DicomMap& query, - const std::list& sequencesToReturn, - const std::map& privateCreators, - FindStorageAccessMode findStorageAccessMode) : - answers_(answers), - context_(context), - level_(level), - query_(query), - queryAsArray_(query), - sequencesToReturn_(sequencesToReturn), - privateCreators_(privateCreators), - findStorageAccessMode_(findStorageAccessMode) - { - answers_.SetComplete(false); - - { - OrthancConfiguration::ReaderLock lock; - defaultPrivateCreator_ = lock.GetConfiguration().GetDefaultPrivateCreator(); - retrieveAet_ = lock.GetConfiguration().GetOrthancAET(); - } - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - // Ask the "DICOM-as-JSON" attachment only if sequences are to - // be returned OR if "query_" contains non-main DICOM tags! - - DicomMap withoutSpecialTags; - withoutSpecialTags.Assign(query_); - - // Check out "ComputeCounters()" - withoutSpecialTags.Remove(DICOM_TAG_MODALITIES_IN_STUDY); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES); - withoutSpecialTags.Remove(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES); - withoutSpecialTags.Remove(DICOM_TAG_SOP_CLASSES_IN_STUDY); - - // Check out "AddAnswer()" - withoutSpecialTags.Remove(DICOM_TAG_SPECIFIC_CHARACTER_SET); - withoutSpecialTags.Remove(DICOM_TAG_QUERY_RETRIEVE_LEVEL); - - return (!sequencesToReturn_.empty() || - !withoutSpecialTags.HasOnlyMainDicomTags()); - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - answers_.SetComplete(true); - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson) ORTHANC_OVERRIDE - { - AddAnswer(answers_, context_, publicId, instanceId, mainDicomTags, dicomAsJson, level_, queryAsArray_, sequencesToReturn_, - defaultPrivateCreator_, privateCreators_, retrieveAet_, IsStorageAccessAllowedForAnswers(findStorageAccessMode_)); - } - }; - - namespace { class LookupVisitorV2 : public ResourceFinder::IVisitor @@ -622,31 +444,12 @@ * Run the query. **/ - size_t limit = (level == ResourceType_Instance) ? maxInstances_ : maxResults_; - - - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - - ResourceFinder finder(level, ResponseContentFlags_ID); - finder.SetDatabaseLookup(lookup); - finder.AddRequestedTags(requestedTags); + ResourceFinder finder(level, ResponseContentFlags_ID); + finder.SetDatabaseLookup(lookup); + finder.AddRequestedTags(requestedTags); - LookupVisitorV2 visitor(answers, *filteredInput, sequencesToReturn, privateCreators); - finder.Execute(visitor, context_); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - - LookupVisitor visitor(answers, context_, level, *filteredInput, sequencesToReturn, privateCreators, context_.GetFindStorageAccessMode()); - context_.Apply(visitor, lookup, level, 0 /* "since" is not relevant to C-FIND */, limit); - } + LookupVisitorV2 visitor(answers, *filteredInput, sequencesToReturn, privateCreators); + finder.Execute(visitor, context_); } diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Wed Oct 09 10:23:04 2024 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Wed Oct 09 11:01:11 2024 +0200 @@ -154,77 +154,6 @@ // List all the patients, studies, series or instances ---------------------- - static void AnswerListOfResources1(RestApiOutput& output, - ServerContext& context, - const std::list& resources, - const std::map& instancesIds, // optional: the id of an instance for each found resource. - const std::map >& resourcesMainDicomTags, // optional: all tags read from DB for a resource (current level and upper levels) - const std::map >& resourcesDicomAsJson, // optional: the dicom-as-json for each resource - ResourceType level, - bool expand, - DicomToJsonFormat format, - const std::set& requestedTags, - bool allowStorageAccess) - { - Json::Value answer = Json::arrayValue; - - for (std::list::const_iterator - resource = resources.begin(); resource != resources.end(); ++resource) - { - if (expand) - { - Json::Value expanded; - - std::map::const_iterator instanceId = instancesIds.find(*resource); - if (instanceId != instancesIds.end()) // if it is found in instancesIds, it is also in resourcesDicomAsJson and mainDicomTags - { - // reuse data already collected before (e.g during lookup) - std::map >::const_iterator mainDicomTags = resourcesMainDicomTags.find(*resource); - std::map >::const_iterator dicomAsJson = resourcesDicomAsJson.find(*resource); - - context.ExpandResource(expanded, *resource, - *(mainDicomTags->second.get()), - instanceId->second, - dicomAsJson->second.get(), - level, format, requestedTags, allowStorageAccess); - } - else - { - context.ExpandResource(expanded, *resource, level, format, requestedTags, allowStorageAccess); - } - - if (expanded.type() == Json::objectValue) - { - answer.append(expanded); - } - } - else - { - answer.append(*resource); - } - } - - output.AnswerJson(answer); - } - - - static void AnswerListOfResources2(RestApiOutput& output, - ServerContext& context, - const std::list& resources, - ResourceType level, - bool expand, - DicomToJsonFormat format, - const std::set& requestedTags, - bool allowStorageAccess) - { - std::map unusedInstancesIds; - std::map > unusedResourcesMainDicomTags; - std::map > unusedResourcesDicomAsJson; - - AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess); - } - - template static void ListResources(RestApiGetCall& call) { @@ -247,96 +176,45 @@ .SetHttpGetSample("https://orthanc.uclouvain.be/demo/" + resources + "?since=0&limit=2", true); return; } - - ServerIndex& index = OrthancRestApi::GetIndex(call); - ServerContext& context = OrthancRestApi::GetContext(call); - - if (true) + + // TODO-FIND: include the FindRequest options parsing in a method (parse from get-arguments and from post payload) + // TODO-FIND: support other values for expand like expand=MainDicomTags,Labels,Parent,SeriesStatus + const bool expand = (call.HasArgument("expand") && + call.GetBooleanArgument("expand", true)); + + std::set requestedTags; + OrthancRestApi::GetRequestedTags(requestedTags, call); + + ResourceFinder finder(resourceType, (expand ? ResponseContentFlags_ExpandTrue : ResponseContentFlags_ID)); + finder.AddRequestedTags(requestedTags); + + if (call.HasArgument("limit") || + call.HasArgument("since")) { - /** - * EXPERIMENTAL VERSION - **/ - - // TODO-FIND: include the FindRequest options parsing in a method (parse from get-arguments and from post payload) - // TODO-FIND: support other values for expand like expand=MainDicomTags,Labels,Parent,SeriesStatus - const bool expand = (call.HasArgument("expand") && - call.GetBooleanArgument("expand", true)); - - std::set requestedTags; - OrthancRestApi::GetRequestedTags(requestedTags, call); - - ResourceFinder finder(resourceType, (expand ? ResponseContentFlags_ExpandTrue : ResponseContentFlags_ID)); - finder.AddRequestedTags(requestedTags); - - if (call.HasArgument("limit") || - call.HasArgument("since")) + if (!call.HasArgument("limit")) { - if (!call.HasArgument("limit")) - { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"limit\" argument for GET request against: " + - call.FlattenUri()); - } - - if (!call.HasArgument("since")) - { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"since\" argument for GET request against: " + - call.FlattenUri()); - } - - uint64_t since = boost::lexical_cast(call.GetArgument("since", "")); - uint64_t limit = boost::lexical_cast(call.GetArgument("limit", "")); - finder.SetLimitsSince(since); - finder.SetLimitsCount(limit); + throw OrthancException(ErrorCode_BadRequest, + "Missing \"limit\" argument for GET request against: " + + call.FlattenUri()); } - Json::Value answer; - finder.Execute(answer, context, OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), false /* no "Metadata" field */); - call.GetOutput().AnswerJson(answer); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - - std::list result; - - std::set requestedTags; - OrthancRestApi::GetRequestedTags(requestedTags, call); - - if (call.HasArgument("limit") || - call.HasArgument("since")) + if (!call.HasArgument("since")) { - if (!call.HasArgument("limit")) - { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"limit\" argument for GET request against: " + - call.FlattenUri()); - } - - if (!call.HasArgument("since")) - { - throw OrthancException(ErrorCode_BadRequest, - "Missing \"since\" argument for GET request against: " + - call.FlattenUri()); - } - - size_t since = boost::lexical_cast(call.GetArgument("since", "")); - size_t limit = boost::lexical_cast(call.GetArgument("limit", "")); - index.GetAllUuids(result, resourceType, since, limit); + throw OrthancException(ErrorCode_BadRequest, + "Missing \"since\" argument for GET request against: " + + call.FlattenUri()); } - else - { - index.GetAllUuids(result, resourceType); - } - - AnswerListOfResources2(call.GetOutput(), context, result, resourceType, call.HasArgument("expand") && call.GetBooleanArgument("expand", true), - OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), - requestedTags, - true /* allowStorageAccess */); + + uint64_t since = boost::lexical_cast(call.GetArgument("since", "")); + uint64_t limit = boost::lexical_cast(call.GetArgument("limit", "")); + finder.SetLimitsSince(since); + finder.SetLimitsCount(limit); } + + Json::Value answer; + finder.Execute(answer, OrthancRestApi::GetContext(call), + OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), false /* no "Metadata" field */); + call.GetOutput().AnswerJson(answer); } @@ -360,39 +238,19 @@ return; } - const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); - std::set requestedTags; OrthancRestApi::GetRequestedTags(requestedTags, call); - if (true) + const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); + + ResourceFinder finder(resourceType, ResponseContentFlags_ExpandTrue); + finder.AddRequestedTags(requestedTags); + finder.SetOrthancId(resourceType, call.GetUriComponent("id", "")); + + Json::Value json; + if (finder.ExecuteOneResource(json, OrthancRestApi::GetContext(call), format, false /* no "Metadata" field */)) { - /** - * EXPERIMENTAL VERSION - **/ - - ResourceFinder finder(resourceType, ResponseContentFlags_ExpandTrue); - finder.AddRequestedTags(requestedTags); - finder.SetOrthancId(resourceType, call.GetUriComponent("id", "")); - - Json::Value json; - if (finder.ExecuteOneResource(json, OrthancRestApi::GetContext(call), format, false /* no "Metadata" field */)) - { - call.GetOutput().AnswerJson(json); - } - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - - Json::Value json; - if (OrthancRestApi::GetContext(call).ExpandResource( - json, call.GetUriComponent("id", ""), resourceType, format, requestedTags, true /* allowStorageAccess */)) - { - call.GetOutput().AnswerJson(json); - } + call.GetOutput().AnswerJson(json); } } @@ -3181,70 +3039,6 @@ } - namespace - { - class FindVisitor : public ServerContext::ILookupVisitor - { - private: - bool isComplete_; - std::list resources_; - FindStorageAccessMode findStorageAccessMode_; - - // cache the data we used during lookup and that we could reuse when building the answers - std::map instancesIds_; // the id of an instance for each found resource. - std::map > resourcesMainDicomTags_; // all tags read from DB for a resource (current level and upper levels) - std::map > resourcesDicomAsJson_; // the dicom-as-json for a resource - - DicomToJsonFormat format_; - - public: - explicit FindVisitor(DicomToJsonFormat format, FindStorageAccessMode findStorageAccessMode) : - isComplete_(false), - findStorageAccessMode_(findStorageAccessMode), - format_(format) - { - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - return false; // (*) - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - isComplete_ = true; // Unused information as of Orthanc 1.5.0 - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson) ORTHANC_OVERRIDE - { - resources_.push_back(publicId); - instancesIds_[publicId] = instanceId; - resourcesMainDicomTags_[publicId].reset(mainDicomTags.Clone()); - if (dicomAsJson != NULL) - { - resourcesDicomAsJson_[publicId].reset(new Json::Value(*dicomAsJson)); // keep our own copy because we might reuse it between lookup and answers - } - else - { - resourcesDicomAsJson_[publicId] = boost::shared_ptr(); - } - } - - void Answer(RestApiOutput& output, - ServerContext& context, - ResourceType level, - bool expand, - const std::set& requestedTags) const - { - AnswerListOfResources1(output, context, resources_, instancesIds_, resourcesMainDicomTags_, resourcesDicomAsJson_, level, expand, format_, requestedTags, IsStorageAccessAllowedForAnswers(findStorageAccessMode_)); - } - }; - } - - static void Find(RestApiPostCall& call) { static const char* const KEY_CASE_SENSITIVE = "CaseSensitive"; @@ -3412,10 +3206,6 @@ } else if (true) { - /** - * EXPERIMENTAL VERSION - **/ - ResponseContentFlags responseContent = ResponseContentFlags_ID; if (request.isMember(KEY_RESPONSE_CONTENT)) @@ -3661,125 +3451,6 @@ finder.Execute(answer, context, format, false /* no "Metadata" field */); call.GetOutput().AnswerJson(answer); } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - bool expand = false; - if (request.isMember(KEY_EXPAND)) - { - expand = request[KEY_EXPAND].asBool(); - } - - bool caseSensitive = false; - if (request.isMember(KEY_CASE_SENSITIVE)) - { - caseSensitive = request[KEY_CASE_SENSITIVE].asBool(); - } - - size_t limit = 0; - if (request.isMember(KEY_LIMIT)) - { - int tmp = request[KEY_LIMIT].asInt(); - if (tmp < 0) - { - throw OrthancException(ErrorCode_ParameterOutOfRange, - "Field \"" + std::string(KEY_LIMIT) + "\" must be a positive integer"); - } - - limit = static_cast(tmp); - } - - size_t since = 0; - if (request.isMember(KEY_SINCE)) - { - int tmp = request[KEY_SINCE].asInt(); - if (tmp < 0) - { - throw OrthancException(ErrorCode_ParameterOutOfRange, - "Field \"" + std::string(KEY_SINCE) + "\" must be a positive integer"); - } - - since = static_cast(tmp); - } - - std::set requestedTags; - - if (request.isMember(KEY_REQUESTED_TAGS)) - { - FromDcmtkBridge::ParseListOfTags(requestedTags, request[KEY_REQUESTED_TAGS]); - } - - ResourceType level = StringToResourceType(request[KEY_LEVEL].asCString()); - - DatabaseLookup query; - - Json::Value::Members members = request[KEY_QUERY].getMemberNames(); - for (size_t i = 0; i < members.size(); i++) - { - if (request[KEY_QUERY][members[i]].type() != Json::stringValue) - { - throw OrthancException(ErrorCode_BadRequest, - "Tag \"" + members[i] + "\" must be associated with a string"); - } - - const std::string value = request[KEY_QUERY][members[i]].asString(); - - if (!value.empty()) - { - // An empty string corresponds to an universal constraint, - // so we ignore it. This mimics the behavior of class - // "OrthancFindRequestHandler" - query.AddRestConstraint(FromDcmtkBridge::ParseTag(members[i]), - value, caseSensitive, true); - } - } - - std::set labels; - - if (request.isMember(KEY_LABELS)) // New in Orthanc 1.12.0 - { - for (Json::Value::ArrayIndex i = 0; i < request[KEY_LABELS].size(); i++) - { - if (request[KEY_LABELS][i].type() != Json::stringValue) - { - throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS) + "\" must contain strings"); - } - else - { - labels.insert(request[KEY_LABELS][i].asString()); - } - } - } - - LabelsConstraint labelsConstraint = LabelsConstraint_All; - - if (request.isMember(KEY_LABELS_CONSTRAINT)) - { - const std::string& s = request[KEY_LABELS_CONSTRAINT].asString(); - if (s == "All") - { - labelsConstraint = LabelsConstraint_All; - } - else if (s == "Any") - { - labelsConstraint = LabelsConstraint_Any; - } - else if (s == "None") - { - labelsConstraint = LabelsConstraint_None; - } - else - { - throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be \"All\", \"Any\", or \"None\""); - } - } - - FindVisitor visitor(OrthancRestApi::GetDicomFormat(request, DicomToJsonFormat_Human), context.GetFindStorageAccessMode()); - context.Apply(visitor, query, level, labels, labelsConstraint, since, limit); - visitor.Answer(call.GetOutput(), context, level, expand, requestedTags); - } } @@ -3807,9 +3478,6 @@ return; } - ServerIndex& index = OrthancRestApi::GetIndex(call); - ServerContext& context = OrthancRestApi::GetContext(call); - const bool expand = (!call.HasArgument("expand") || // this "expand" is the only one to have a false default value to keep backward compatibility call.GetBooleanArgument("expand", false)); @@ -3818,48 +3486,13 @@ std::set requestedTags; OrthancRestApi::GetRequestedTags(requestedTags, call); - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - - ResourceFinder finder(end, (expand ? ResponseContentFlags_ExpandTrue : ResponseContentFlags_ID)); - finder.SetOrthancId(start, call.GetUriComponent("id", "")); - finder.AddRequestedTags(requestedTags); - - Json::Value answer; - finder.Execute(answer, context, format, false /* no "Metadata" field */); - call.GetOutput().AnswerJson(answer); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - std::list a, b, c; - a.push_back(call.GetUriComponent("id", "")); - - ResourceType type = start; - while (type != end) - { - b.clear(); - - for (std::list::const_iterator - it = a.begin(); it != a.end(); ++it) - { - index.GetChildren(c, *it); - b.splice(b.begin(), c); - } - - type = GetChildResourceType(type); - - a.clear(); - a.splice(a.begin(), b); - } - - AnswerListOfResources2(call.GetOutput(), context, a, type, expand, format, requestedTags, true /* allowStorageAccess */); - } + ResourceFinder finder(end, (expand ? ResponseContentFlags_ExpandTrue : ResponseContentFlags_ID)); + finder.SetOrthancId(start, call.GetUriComponent("id", "")); + finder.AddRequestedTags(requestedTags); + + Json::Value answer; + finder.Execute(answer, OrthancRestApi::GetContext(call), format, false /* no "Metadata" field */); + call.GetOutput().AnswerJson(answer); } @@ -3972,26 +3605,9 @@ const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); Json::Value resource; - - if (true) + if (ExpandResource(resource, OrthancRestApi::GetContext(call), currentType, current, format, false)) { - /** - * EXPERIMENTAL VERSION - **/ - if (ExpandResource(resource, OrthancRestApi::GetContext(call), currentType, current, format, false)) - { - call.GetOutput().AnswerJson(resource); - } - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - if (OrthancRestApi::GetContext(call).ExpandResource(resource, current, end, format, requestedTags, true /* allowStorageAccess */)) - { - call.GetOutput().AnswerJson(resource); - } + call.GetOutput().AnswerJson(resource); } } @@ -4268,24 +3884,6 @@ } - static void AddMetadata(Json::Value& target, - ServerIndex& index, - const std::string& resource, - ResourceType level) - { - target = Json::objectValue; - - std::map content; - index.GetAllMetadata(content, resource, level); - - for (std::map::const_iterator - it = content.begin(); it != content.end(); ++it) - { - target[EnumerationToString(it->first)] = it->second; - } - } - - static void BulkContent(RestApiPostCall& call) { static const char* const LEVEL = "Level"; @@ -4422,34 +4020,10 @@ for (std::set::const_iterator it = interest.begin(); it != interest.end(); ++it) { - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - Json::Value item; - if (ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata)) - { - answer.append(item); - } - } - else + Json::Value item; + if (ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata)) { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - Json::Value item; - std::set emptyRequestedTags; // not supported for bulk content - - if (OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */)) - { - if (metadata) - { - AddMetadata(item[METADATA], index, *it, level); - } - - answer.append(item); - } + answer.append(item); } } } @@ -4464,38 +4038,15 @@ { ResourceType level; Json::Value item; - std::set emptyRequestedTags; // not supported for bulk content - - if (true) + + if (index.LookupResourceType(level, *it) && + ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata)) { - /** - * EXPERIMENTAL VERSION - **/ - if (index.LookupResourceType(level, *it) && - ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata)) - { - answer.append(item); - } + answer.append(item); } else { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - if (index.LookupResourceType(level, *it) && - OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */)) - { - if (metadata) - { - AddMetadata(item[METADATA], index, *it, level); - } - - answer.append(item); - } - else - { - CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it; - } + CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it; } } } diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/OrthancWebDav.cpp --- a/OrthancServer/Sources/OrthancWebDav.cpp Wed Oct 09 10:23:04 2024 +0200 +++ b/OrthancServer/Sources/OrthancWebDav.cpp Wed Oct 09 11:01:11 2024 +0200 @@ -86,99 +86,6 @@ } - class OrthancWebDav::DicomIdentifiersVisitor : public ServerContext::ILookupVisitor - { - private: - ServerContext& context_; - bool isComplete_; - Collection& target_; - ResourceType level_; - - public: - DicomIdentifiersVisitor(ServerContext& context, - Collection& target, - ResourceType level) : - context_(context), - isComplete_(false), - target_(target), - level_(level) - { - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - return false; // (*) - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - isComplete_ = true; // TODO - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId /* unused */, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson /* unused (*) */) ORTHANC_OVERRIDE - { - DicomTag tag(0, 0); - MetadataType timeMetadata; - - switch (level_) - { - case ResourceType_Study: - tag = DICOM_TAG_STUDY_INSTANCE_UID; - timeMetadata = MetadataType_LastUpdate; - break; - - case ResourceType_Series: - tag = DICOM_TAG_SERIES_INSTANCE_UID; - timeMetadata = MetadataType_LastUpdate; - break; - - case ResourceType_Instance: - tag = DICOM_TAG_SOP_INSTANCE_UID; - timeMetadata = MetadataType_Instance_ReceptionDate; - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - std::string s; - if (mainDicomTags.LookupStringValue(s, tag, false) && - !s.empty()) - { - std::unique_ptr resource; - - if (level_ == ResourceType_Instance) - { - FileInfo info; - int64_t revision; // Ignored - if (context_.GetIndex().LookupAttachment(info, revision, publicId, FileContentType_Dicom)) - { - std::unique_ptr f(new File(s + ".dcm")); - f->SetMimeType(MimeType_Dicom); - f->SetContentLength(info.GetUncompressedSize()); - resource.reset(f.release()); - } - } - else - { - resource.reset(new Folder(s)); - } - - if (resource.get() != NULL) - { - boost::posix_time::ptime t; - LookupTime(t, context_, publicId, level_, timeMetadata); - resource->SetCreationTime(t); - target_.AddResource(resource.release()); - } - } - } - }; - - class OrthancWebDav::DicomIdentifiersVisitorV2 : public ResourceFinder::IVisitor { private: @@ -271,58 +178,6 @@ }; - class OrthancWebDav::DicomFileVisitor : public ServerContext::ILookupVisitor - { - private: - ServerContext& context_; - bool success_; - std::string& target_; - boost::posix_time::ptime& time_; - - public: - DicomFileVisitor(ServerContext& context, - std::string& target, - boost::posix_time::ptime& time) : - context_(context), - success_(false), - target_(target), - time_(time) - { - } - - bool IsSuccess() const - { - return success_; - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - return false; // (*) - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId /* unused */, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson /* unused (*) */) ORTHANC_OVERRIDE - { - if (success_) - { - success_ = false; // Two matches => Error - } - else - { - LookupTime(time_, context_, publicId, ResourceType_Instance, MetadataType_Instance_ReceptionDate); - context_.ReadDicom(target_, publicId); - success_ = true; - } - } - }; - - class OrthancWebDav::DicomFileVisitorV2 : public ResourceFinder::IVisitor { private: @@ -377,67 +232,6 @@ }; - class OrthancWebDav::OrthancJsonVisitor : public ServerContext::ILookupVisitor - { - private: - ServerContext& context_; - bool success_; - std::string& target_; - ResourceType level_; - - public: - OrthancJsonVisitor(ServerContext& context, - std::string& target, - ResourceType level) : - context_(context), - success_(false), - target_(target), - level_(level) - { - } - - bool IsSuccess() const - { - return success_; - } - - virtual bool IsDicomAsJsonNeeded() const ORTHANC_OVERRIDE - { - return false; // (*) - } - - virtual void MarkAsComplete() ORTHANC_OVERRIDE - { - } - - virtual void Visit(const std::string& publicId, - const std::string& instanceId /* unused */, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson /* unused (*) */) ORTHANC_OVERRIDE - { - Json::Value resource; - std::set emptyRequestedTags; // not supported for webdav - - if (context_.ExpandResource(resource, publicId, level_, DicomToJsonFormat_Human, emptyRequestedTags, true /* allowStorageAccess */)) - { - if (success_) - { - success_ = false; // Two matches => Error - } - else - { - target_ = resource.toStyledString(); - - // Replace UNIX newlines with DOS newlines - boost::replace_all(target_, "\n", "\r\n"); - - success_ = true; - } - } - } - }; - - class OrthancWebDav::ResourcesIndex : public boost::noncopyable { public: @@ -1569,12 +1363,11 @@ { DatabaseLookup query; ResourceType level; - size_t limit = 0; // By default, no limits if (path.size() == 1) { level = ResourceType_Study; - limit = 0; // TODO - Should we limit here? + // TODO - Should we limit here? } else if (path.size() == 2) { @@ -1599,47 +1392,31 @@ return false; } - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - - ResourceFinder finder(level, ResponseContentFlags_ID); - finder.SetDatabaseLookup(query); - finder.SetRetrieveMetadata(true); + ResourceFinder finder(level, ResponseContentFlags_ID); + finder.SetDatabaseLookup(query); + finder.SetRetrieveMetadata(true); - switch (level) - { - case ResourceType_Study: - finder.AddRequestedTag(DICOM_TAG_STUDY_INSTANCE_UID); - break; - - case ResourceType_Series: - finder.AddRequestedTag(DICOM_TAG_SERIES_INSTANCE_UID); - break; + switch (level) + { + case ResourceType_Study: + finder.AddRequestedTag(DICOM_TAG_STUDY_INSTANCE_UID); + break; - case ResourceType_Instance: - finder.AddRequestedTag(DICOM_TAG_SOP_INSTANCE_UID); - finder.SetRetrieveAttachments(true); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } + case ResourceType_Series: + finder.AddRequestedTag(DICOM_TAG_SERIES_INSTANCE_UID); + break; - DicomIdentifiersVisitorV2 visitor(collection); - finder.Execute(visitor, context_); + case ResourceType_Instance: + finder.AddRequestedTag(DICOM_TAG_SOP_INSTANCE_UID); + finder.SetRetrieveAttachments(true); + break; + + default: + throw OrthancException(ErrorCode_InternalError); } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - DicomIdentifiersVisitor visitor(context_, collection, level); - context_.Apply(visitor, query, level, 0 /* since */, limit); - } + DicomIdentifiersVisitorV2 visitor(collection); + finder.Execute(visitor, context_); return true; } @@ -1707,23 +1484,7 @@ true /* case sensitive */, true /* mandatory tag */); mime = MimeType_Json; - - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - return GetOrthancJson(content, context_, ResourceType_Study, query); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - OrthancJsonVisitor visitor(context_, content, ResourceType_Study); - context_.Apply(visitor, query, ResourceType_Study, 0 /* since */, 0 /* no limit */); - return visitor.IsSuccess(); - } + return GetOrthancJson(content, context_, ResourceType_Study, query); } else if (path.size() == 4 && path[3] == SERIES_INFO) @@ -1735,23 +1496,7 @@ true /* case sensitive */, true /* mandatory tag */); mime = MimeType_Json; - - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - return GetOrthancJson(content, context_, ResourceType_Series, query); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - OrthancJsonVisitor visitor(context_, content, ResourceType_Series); - context_.Apply(visitor, query, ResourceType_Series, 0 /* since */, 0 /* no limit */); - return visitor.IsSuccess(); - } + return GetOrthancJson(content, context_, ResourceType_Series, query); } else if (path.size() == 4 && boost::ends_with(path[3], ".dcm")) @@ -1768,30 +1513,15 @@ mime = MimeType_Dicom; - if (true) - { - /** - * EXPERIMENTAL VERSION - **/ - ResourceFinder finder(ResourceType_Instance, ResponseContentFlags_ID); - finder.SetDatabaseLookup(query); - finder.SetRetrieveMetadata(true); - finder.SetRetrieveAttachments(true); + ResourceFinder finder(ResourceType_Instance, ResponseContentFlags_ID); + finder.SetDatabaseLookup(query); + finder.SetRetrieveMetadata(true); + finder.SetRetrieveAttachments(true); - DicomFileVisitorV2 visitor(context_, content, modificationTime); - finder.Execute(visitor, context_); + DicomFileVisitorV2 visitor(context_, content, modificationTime); + finder.Execute(visitor, context_); - return visitor.IsSuccess(); - } - else - { - /** - * VERSION IN ORTHANC <= 1.12.4 - **/ - DicomFileVisitor visitor(context_, content, modificationTime); - context_.Apply(visitor, query, ResourceType_Instance, 0 /* since */, 0 /* no limit */); - return visitor.IsSuccess(); - } + return visitor.IsSuccess(); } else { diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/OrthancWebDav.h --- a/OrthancServer/Sources/OrthancWebDav.h Wed Oct 09 10:23:04 2024 +0200 +++ b/OrthancServer/Sources/OrthancWebDav.h Wed Oct 09 11:01:11 2024 +0200 @@ -38,9 +38,7 @@ typedef std::map Templates; class DicomDeleteVisitor; - class DicomFileVisitor; class DicomFileVisitorV2; - class DicomIdentifiersVisitor; class DicomIdentifiersVisitorV2; class InstancesOfSeries; class InternalNode; diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Wed Oct 09 10:23:04 2024 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Wed Oct 09 11:01:11 2024 +0200 @@ -69,12 +69,6 @@ namespace Orthanc { - static void ComputeStudyTags(ExpandedResource& resource, - ServerContext& context, - const std::string& studyPublicId, - const std::set& requestedTags); - - static bool IsUncompressedTransferSyntax(DicomTransferSyntax transferSyntax) { return (transferSyntax == DicomTransferSyntax_LittleEndianImplicit || @@ -1629,188 +1623,6 @@ } - void ServerContext::Apply(ILookupVisitor& visitor, - const DatabaseLookup& lookup, - ResourceType queryLevel, - const std::set& labels, - LabelsConstraint labelsConstraint, - size_t since, - size_t limit) - { - const uint64_t databaseLimit = GetDatabaseLimits(queryLevel); - - std::vector resources, instances; - const DicomTagConstraint* dicomModalitiesConstraint = NULL; - - bool hasModalitiesInStudyLookup = (queryLevel == ResourceType_Study && - lookup.GetConstraint(dicomModalitiesConstraint, DICOM_TAG_MODALITIES_IN_STUDY) && - ((dicomModalitiesConstraint->GetConstraintType() == ConstraintType_Equal && !dicomModalitiesConstraint->GetValue().empty()) || - (dicomModalitiesConstraint->GetConstraintType() == ConstraintType_List && !dicomModalitiesConstraint->GetValues().empty()))); - - std::unique_ptr fastLookup(lookup.Clone()); - - if (hasModalitiesInStudyLookup) - { - fastLookup->RemoveConstraint(DICOM_TAG_MODALITIES_IN_STUDY); - } - - const size_t lookupLimit = (databaseLimit == 0 ? 0 : databaseLimit + 1); - GetIndex().ApplyLookupResources(resources, &instances, *fastLookup, queryLevel, labels, labelsConstraint, lookupLimit); - - bool complete = (databaseLimit == 0 || - resources.size() <= databaseLimit); - - LOG(INFO) << "Number of candidate resources after fast DB filtering on main DICOM tags: " << resources.size(); - - /** - * "resources" contains the Orthanc ID of the resource at level - * "queryLevel", "instances" contains one the Orthanc ID of one - * sample instance from this resource. - **/ - assert(resources.size() == instances.size()); - - size_t countResults = 0; - size_t skipped = 0; - - const bool isDicomAsJsonNeeded = visitor.IsDicomAsJsonNeeded(); - - for (size_t i = 0; i < instances.size(); i++) - { - // Optimization in Orthanc 1.5.1 - Don't read the full JSON from - // the disk if only "main DICOM tags" are to be returned - - boost::shared_ptr dicomAsJson; - - bool hasOnlyMainDicomTags; - DicomMap dicom; - DicomMap allMainDicomTagsFromDB; - - if (!IsStorageAccessAllowedForAnswers(findStorageAccessMode_) - || fastLookup->HasOnlyMainDicomTags()) - { - // Case (1): The main DICOM tags, as stored in the database, - // are sufficient to look for match - - if (!GetIndex().GetAllMainDicomTags(allMainDicomTagsFromDB, instances[i])) - { - // The instance has been removed during the execution of the - // lookup, ignore it - continue; - } - - // New in Orthanc 1.6.0: Only keep the main DICOM tags at the - // level of interest for the query - switch (queryLevel) - { - // WARNING: Don't reorder cases below, and don't add "break" - case ResourceType_Instance: - dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Instance); - - case ResourceType_Series: - dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Series); - - case ResourceType_Study: - dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Study); - - case ResourceType_Patient: - dicom.MergeMainDicomTags(allMainDicomTagsFromDB, ResourceType_Patient); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - hasOnlyMainDicomTags = true; - } - else - { - // Case (2): Need to read the "DICOM-as-JSON" attachment from - // the storage area - dicomAsJson.reset(new Json::Value); - ReadDicomAsJson(*dicomAsJson, instances[i]); - - dicom.FromDicomAsJson(*dicomAsJson); - - // This map contains the entire JSON, i.e. more than the main DICOM tags - hasOnlyMainDicomTags = false; - } - - if (fastLookup->IsMatch(dicom)) - { - bool isMatch = true; - - if (hasModalitiesInStudyLookup) - { - std::set requestedTags; - requestedTags.insert(DICOM_TAG_MODALITIES_IN_STUDY); - ExpandedResource resource; - ComputeStudyTags(resource, *this, resources[i], requestedTags); - - std::vector modalities; - Toolbox::TokenizeString(modalities, resource.GetMainDicomTags().GetValue(DICOM_TAG_MODALITIES_IN_STUDY).GetContent(), '\\'); - bool hasAtLeastOneModalityMatching = false; - for (size_t m = 0; m < modalities.size(); m++) - { - hasAtLeastOneModalityMatching |= dicomModalitiesConstraint->IsMatch(modalities[m]); - } - - isMatch = isMatch && hasAtLeastOneModalityMatching; - // copy the value of ModalitiesInStudy such that it can be reused to build the answer - allMainDicomTagsFromDB.SetValue(DICOM_TAG_MODALITIES_IN_STUDY, resource.GetMainDicomTags().GetValue(DICOM_TAG_MODALITIES_IN_STUDY)); - } - - if (isMatch) - { - if (skipped < since) - { - skipped++; - } - else if (limit != 0 && - countResults >= limit) - { - // Too many results, don't mark as complete - complete = false; - break; - } - else - { - if (IsStorageAccessAllowedForAnswers(findStorageAccessMode_) && - dicomAsJson.get() == NULL && - isDicomAsJsonNeeded) - { - dicomAsJson.reset(new Json::Value); - ReadDicomAsJson(*dicomAsJson, instances[i]); - } - - if (hasOnlyMainDicomTags) - { - // This is Case (1): The variable "dicom" only contains the main DICOM tags - visitor.Visit(resources[i], instances[i], allMainDicomTagsFromDB, dicomAsJson.get()); - } - else - { - // Remove the non-main DICOM tags from "dicom" if Case (2) - // was used, for consistency with Case (1) - - DicomMap mainDicomTags; - mainDicomTags.ExtractMainDicomTags(dicom); - visitor.Visit(resources[i], instances[i], mainDicomTags, dicomAsJson.get()); - } - - countResults ++; - } - } - } - } - - if (complete) - { - visitor.MarkAsComplete(); - } - - LOG(INFO) << "Number of matching resources: " << countResults; - } - bool ServerContext::LookupOrReconstructMetadata(std::string& target, const std::string& publicId, ResourceType level, @@ -2220,600 +2032,6 @@ isUnknownSopClassAccepted_ = accepted; } - - static void SerializeExpandedResource(Json::Value& target, - const ExpandedResource& resource, - DicomToJsonFormat format, - const std::set& requestedTags, - ExpandResourceFlags expandFlags) - { - target = Json::objectValue; - - target["Type"] = GetResourceTypeText(resource.GetLevel(), false, true); - target["ID"] = resource.GetPublicId(); - - if (!resource.parentId_.empty()) - { - switch (resource.GetLevel()) - { - case ResourceType_Patient: - break; - - case ResourceType_Study: - target["ParentPatient"] = resource.parentId_; - break; - - case ResourceType_Series: - target["ParentStudy"] = resource.parentId_; - break; - - case ResourceType_Instance: - target["ParentSeries"] = resource.parentId_; - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeChildren) != 0) - { - switch (resource.GetLevel()) - { - case ResourceType_Patient: - case ResourceType_Study: - case ResourceType_Series: - { - Json::Value c = Json::arrayValue; - - for (std::list::const_iterator - it = resource.childrenIds_.begin(); it != resource.childrenIds_.end(); ++it) - { - c.append(*it); - } - - if (resource.GetLevel() == ResourceType_Patient) - { - target["Studies"] = c; - } - else if (resource.GetLevel() == ResourceType_Study) - { - target["Series"] = c; - } - else - { - target["Instances"] = c; - } - break; - } - - case ResourceType_Instance: - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeMetadata) != 0) - { - switch (resource.GetLevel()) - { - case ResourceType_Patient: - case ResourceType_Study: - break; - - case ResourceType_Series: - if (resource.expectedNumberOfInstances_ < 0) - { - target["ExpectedNumberOfInstances"] = Json::nullValue; - } - else - { - target["ExpectedNumberOfInstances"] = resource.expectedNumberOfInstances_; - } - target["Status"] = resource.status_; - break; - - case ResourceType_Instance: - { - target["FileSize"] = static_cast(resource.fileSize_); - target["FileUuid"] = resource.fileUuid_; - - if (resource.indexInSeries_ < 0) - { - target["IndexInSeries"] = Json::nullValue; - } - else - { - target["IndexInSeries"] = resource.indexInSeries_; - } - - break; - } - - default: - throw OrthancException(ErrorCode_InternalError); - } - - if (!resource.anonymizedFrom_.empty()) - { - target["AnonymizedFrom"] = resource.anonymizedFrom_; - } - - if (!resource.modifiedFrom_.empty()) - { - target["ModifiedFrom"] = resource.modifiedFrom_; - } - } - - if (resource.GetLevel() == ResourceType_Patient || - resource.GetLevel() == ResourceType_Study || - resource.GetLevel() == ResourceType_Series) - { - if ((expandFlags & ExpandResourceFlags_IncludeIsStable) != 0) - { - target["IsStable"] = resource.isStable_; - } - - if (!resource.lastUpdate_.empty()) - { - target["LastUpdate"] = resource.lastUpdate_; - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeMainDicomTags) != 0) - { - // serialize tags - - static const char* const MAIN_DICOM_TAGS = "MainDicomTags"; - static const char* const PATIENT_MAIN_DICOM_TAGS = "PatientMainDicomTags"; - - DicomMap mainDicomTags; - resource.GetMainDicomTags().ExtractResourceInformation(mainDicomTags, resource.GetLevel()); - - target[MAIN_DICOM_TAGS] = Json::objectValue; - FromDcmtkBridge::ToJson(target[MAIN_DICOM_TAGS], mainDicomTags, format); - - if (resource.GetLevel() == ResourceType_Study) - { - DicomMap patientMainDicomTags; - resource.GetMainDicomTags().ExtractPatientInformation(patientMainDicomTags); - - target[PATIENT_MAIN_DICOM_TAGS] = Json::objectValue; - FromDcmtkBridge::ToJson(target[PATIENT_MAIN_DICOM_TAGS], patientMainDicomTags, format); - } - - if (requestedTags.size() > 0) - { - static const char* const REQUESTED_TAGS = "RequestedTags"; - - DicomMap tags; - resource.GetMainDicomTags().ExtractTags(tags, requestedTags); - - target[REQUESTED_TAGS] = Json::objectValue; - FromDcmtkBridge::ToJson(target[REQUESTED_TAGS], tags, format); - - } - } - - if ((expandFlags & ExpandResourceFlags_IncludeLabels) != 0) - { - Json::Value labels = Json::arrayValue; - - for (std::set::const_iterator it = resource.labels_.begin(); it != resource.labels_.end(); ++it) - { - labels.append(*it); - } - - target["Labels"] = labels; - } - - // new in Orthanc 1.12.4 - if ((expandFlags & ExpandResourceFlags_IncludeAllMetadata) != 0) - { - Json::Value metadata = Json::objectValue; - - for (std::map::const_iterator it = resource.metadata_.begin(); it != resource.metadata_.end(); ++it) - { - metadata[EnumerationToString(it->first)] = it->second; - } - - target["Metadata"] = metadata; - } - } - - - static void ComputeInstanceTags(ExpandedResource& resource, - ServerContext& context, - const std::string& instancePublicId, - const std::set& requestedTags) - { - if (requestedTags.count(DICOM_TAG_INSTANCE_AVAILABILITY) > 0) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_INSTANCE_AVAILABILITY, "ONLINE", false); - resource.missingRequestedTags_.erase(DICOM_TAG_INSTANCE_AVAILABILITY); - } - } - - - static void ComputeSeriesTags(ExpandedResource& resource, - ServerContext& context, - const std::string& seriesPublicId, - const std::set& requestedTags) - { - if (requestedTags.count(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES) > 0) - { - ServerIndex& index = context.GetIndex(); - std::list instances; - - index.GetChildren(instances, seriesPublicId); - - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES, - boost::lexical_cast(instances.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES); - } - } - - static void ComputeStudyTags(ExpandedResource& resource, - ServerContext& context, - const std::string& studyPublicId, - const std::set& requestedTags) - { - ServerIndex& index = context.GetIndex(); - std::list series; - std::list instances; - - bool hasNbRelatedSeries = requestedTags.count(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES) > 0; - bool hasNbRelatedInstances = requestedTags.count(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES) > 0; - bool hasModalitiesInStudy = requestedTags.count(DICOM_TAG_MODALITIES_IN_STUDY) > 0; - bool hasSopClassesInStudy = requestedTags.count(DICOM_TAG_SOP_CLASSES_IN_STUDY) > 0; - - index.GetChildren(series, studyPublicId); - - if (hasModalitiesInStudy) - { - std::set values; - - for (std::list::const_iterator - it = series.begin(); it != series.end(); ++it) - { - DicomMap tags; - index.GetMainDicomTags(tags, *it, ResourceType_Series, ResourceType_Series); - - const DicomValue* value = tags.TestAndGetValue(DICOM_TAG_MODALITY); - - if (value != NULL && - !value->IsNull() && - !value->IsBinary()) - { - values.insert(value->GetContent()); - } - } - - std::string modalities; - Toolbox::JoinStrings(modalities, values, "\\"); - - resource.GetMainDicomTags().SetValue(DICOM_TAG_MODALITIES_IN_STUDY, modalities, false); - resource.missingRequestedTags_.erase(DICOM_TAG_MODALITIES_IN_STUDY); - } - - if (hasNbRelatedSeries) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES, - boost::lexical_cast(series.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_STUDY_RELATED_SERIES); - } - - if (hasNbRelatedInstances || hasSopClassesInStudy) - { - for (std::list::const_iterator - it = series.begin(); it != series.end(); ++it) - { - std::list seriesInstancesIds; - index.GetChildren(seriesInstancesIds, *it); - - instances.splice(instances.end(), seriesInstancesIds); - } - - if (hasNbRelatedInstances) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES, - boost::lexical_cast(instances.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_STUDY_RELATED_INSTANCES); - } - - if (hasSopClassesInStudy) - { - std::set values; - - for (std::list::const_iterator - it = instances.begin(); it != instances.end(); ++it) - { - std::string value; - - if (context.LookupOrReconstructMetadata(value, *it, ResourceType_Instance, MetadataType_Instance_SopClassUid)) - { - values.insert(value); - } - } - - if (values.size() > 0) - { - std::string sopClassUids; - Toolbox::JoinStrings(sopClassUids, values, "\\"); - resource.GetMainDicomTags().SetValue(DICOM_TAG_SOP_CLASSES_IN_STUDY, sopClassUids, false); - } - - resource.missingRequestedTags_.erase(DICOM_TAG_SOP_CLASSES_IN_STUDY); - } - } - } - - static void ComputePatientTags(ExpandedResource& resource, - ServerContext& context, - const std::string& patientPublicId, - const std::set& requestedTags) - { - ServerIndex& index = context.GetIndex(); - - std::list studies; - std::list series; - std::list instances; - - bool hasNbRelatedStudies = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES) > 0; - bool hasNbRelatedSeries = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES) > 0; - bool hasNbRelatedInstances = requestedTags.count(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES) > 0; - - index.GetChildren(studies, patientPublicId); - - if (hasNbRelatedStudies) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES, - boost::lexical_cast(studies.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES); - } - - if (hasNbRelatedSeries || hasNbRelatedInstances) - { - for (std::list::const_iterator - it = studies.begin(); it != studies.end(); ++it) - { - std::list thisSeriesIds; - index.GetChildren(thisSeriesIds, *it); - series.splice(series.end(), thisSeriesIds); - } - - if (hasNbRelatedSeries) - { - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES, - boost::lexical_cast(series.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_SERIES); - } - } - - if (hasNbRelatedInstances) - { - for (std::list::const_iterator - it = series.begin(); it != series.end(); ++it) - { - std::list thisInstancesIds; - index.GetChildren(thisInstancesIds, *it); - instances.splice(instances.end(), thisInstancesIds); - } - - resource.GetMainDicomTags().SetValue(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES, - boost::lexical_cast(instances.size()), false); - resource.missingRequestedTags_.erase(DICOM_TAG_NUMBER_OF_PATIENT_RELATED_INSTANCES); - } - } - - - static void ComputeTags(ExpandedResource& resource, - ServerContext& context, - const std::string& resourceId, - ResourceType level, - const std::set& requestedTags) - { - if (level == ResourceType_Patient - && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Patient)) - { - ComputePatientTags(resource, context, resourceId, requestedTags); - } - - if (level == ResourceType_Study - && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Study)) - { - ComputeStudyTags(resource, context, resourceId, requestedTags); - } - - if (level == ResourceType_Series - && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Series)) - { - ComputeSeriesTags(resource, context, resourceId, requestedTags); - } - - if (level == ResourceType_Instance - && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Instance)) - { - ComputeInstanceTags(resource, context, resourceId, requestedTags); - } - } - - bool ServerContext::ExpandResource(Json::Value& target, - const std::string& publicId, - ResourceType level, - DicomToJsonFormat format, - const std::set& requestedTags, - bool allowStorageAccess) - { - std::string unusedInstanceId; - Json::Value* unusedDicomAsJson = NULL; - DicomMap unusedMainDicomTags; - - return ExpandResource(target, publicId, unusedMainDicomTags, unusedInstanceId, unusedDicomAsJson, level, format, requestedTags, allowStorageAccess); - } - - bool ServerContext::ExpandResource(Json::Value& target, - const std::string& publicId, - const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) - const std::string& instanceId, // optional: the id of an instance for the resource (if already available) - const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource (if already available) - ResourceType level, - DicomToJsonFormat format, - const std::set& requestedTags, - bool allowStorageAccess) - { - ExpandedResource resource; - - if (ExpandResource(resource, publicId, mainDicomTags, instanceId, dicomAsJson, level, requestedTags, ExpandResourceFlags_DefaultExtract, allowStorageAccess)) - { - SerializeExpandedResource(target, resource, format, requestedTags, ExpandResourceFlags_DefaultOutput); - return true; - } - - return false; - } - - bool ServerContext::ExpandResource(ExpandedResource& resource, - const std::string& publicId, - const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) - const std::string& instanceId, // optional: the id of an instance for the resource (if already available) - const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource (if already available) - ResourceType level, - const std::set& requestedTags, - ExpandResourceFlags expandFlags, - bool allowStorageAccess) - { - // first try to get the tags from what is already available - - if ((expandFlags & ExpandResourceFlags_IncludeMainDicomTags) && - mainDicomTags.GetSize() > 0 && - dicomAsJson != NULL) - { - - resource.GetMainDicomTags().Merge(mainDicomTags); - - if (dicomAsJson->isObject()) - { - resource.GetMainDicomTags().FromDicomAsJson(*dicomAsJson); - } - - std::set retrievedTags; - std::set missingTags; - resource.GetMainDicomTags().GetTags(retrievedTags); - - Toolbox::GetMissingsFromSet(missingTags, requestedTags, retrievedTags); - - // if all possible tags have been read, no need to get them from DB anymore - if (missingTags.size() > 0 && DicomMap::HasOnlyComputedTags(missingTags)) - { - resource.missingRequestedTags_ = missingTags; - ComputeTags(resource, *this, publicId, level, requestedTags); - return true; - } - else if (missingTags.size() == 0) - { - expandFlags = static_cast(expandFlags & ~ExpandResourceFlags_IncludeMainDicomTags); - } - - if (missingTags.size() == 0 && expandFlags == ExpandResourceFlags_None) // we have already retrieved anything we need - { - return true; - } - } - - if (expandFlags != ExpandResourceFlags_None && - GetIndex().ExpandResource(resource, publicId, level, requestedTags, - static_cast(expandFlags | ExpandResourceFlags_IncludeMetadata))) // we always need the metadata to get the mainDicomTagsSignature - { - // check the main dicom tags list has not changed since the resource was stored - if (resource.mainDicomTagsSignature_ != DicomMap::GetMainDicomTagsSignature(resource.GetLevel())) - { - OrthancConfiguration::ReaderLock lock; - if (lock.GetConfiguration().IsWarningEnabled(Warnings_002_InconsistentDicomTagsInDb)) - { - LOG(WARNING) << "W002: " << Orthanc::GetResourceTypeText(resource.GetLevel(), false , false) - << " has been stored with another version of Main Dicom Tags list, you should POST to /" - << Orthanc::GetResourceTypeText(resource.GetLevel(), true, false) - << "/" << resource.GetPublicId() - << "/reconstruct to update the list of tags saved in DB. Some MainDicomTags might be missing from this answer."; - } - } - - // possibly merge missing requested tags from dicom-as-json - if (allowStorageAccess && - !resource.missingRequestedTags_.empty() && - !DicomMap::HasOnlyComputedTags(resource.missingRequestedTags_)) - { - OrthancConfiguration::ReaderLock lock; - if (lock.GetConfiguration().IsWarningEnabled(Warnings_001_TagsBeingReadFromStorage)) - { - std::set missingTags; - Toolbox::AppendSets(missingTags, resource.missingRequestedTags_); - for (std::set::const_iterator it = resource.missingRequestedTags_.begin(); it != resource.missingRequestedTags_.end(); ++it) - { - if (DicomMap::IsComputedTag(*it)) - { - missingTags.erase(*it); - } - } - - std::string missings; - FromDcmtkBridge::FormatListOfTags(missings, missingTags); - - LOG(WARNING) << "W001: Accessing Dicom tags from storage when accessing " - << Orthanc::GetResourceTypeText(resource.GetLevel(), false, false) - << " : " << missings; - } - - - std::string instanceId_ = instanceId; - DicomMap tagsFromJson; - - if (dicomAsJson == NULL) - { - if (instanceId_.empty()) - { - if (level == ResourceType_Instance) - { - instanceId_ = publicId; - } - else - { - std::list instancesIds; - GetIndex().GetChildInstances(instancesIds, publicId); - if (instancesIds.size() < 1) - { - throw OrthancException(ErrorCode_InternalError, "ExpandResource: no instances found"); - } - instanceId_ = instancesIds.front(); - } - } - - Json::Value tmpDicomAsJson; - ReadDicomAsJson(tmpDicomAsJson, instanceId_, resource.missingRequestedTags_ /* ignoreTagLength */); // read all tags from DICOM and avoid cropping requested tags - tagsFromJson.FromDicomAsJson(tmpDicomAsJson, false /* append */, true /* parseSequences*/); - } - else - { - tagsFromJson.FromDicomAsJson(*dicomAsJson, false /* append */, true /* parseSequences*/); - } - - resource.GetMainDicomTags().Merge(tagsFromJson); - } - - // compute the requested tags - ComputeTags(resource, *this, publicId, level, requestedTags); - } - else - { - return false; - } - - return true; - } - int64_t ServerContext::GetServerUpTime() const { boost::posix_time::ptime nowUtc = boost::posix_time::second_clock::universal_time(); diff -r dd2af8692cbc -r 58c549b881ae OrthancServer/Sources/ServerContext.h --- a/OrthancServer/Sources/ServerContext.h Wed Oct 09 10:23:04 2024 +0200 +++ b/OrthancServer/Sources/ServerContext.h Wed Oct 09 11:01:11 2024 +0200 @@ -66,25 +66,6 @@ friend class ServerIndex; // To access "RemoveFile()" public: - class ILookupVisitor : public boost::noncopyable - { - public: - virtual ~ILookupVisitor() - { - } - - virtual bool IsDicomAsJsonNeeded() const = 0; - - virtual void MarkAsComplete() = 0; - - // NB: "dicomAsJson" must *not* be deleted, and can be NULL if - // "!IsDicomAsJsonNeeded()" - virtual void Visit(const std::string& publicId, - const std::string& instanceId, - const DicomMap& mainDicomTags, - const Json::Value* dicomAsJson) = 0; - }; - struct StoreResult { private: @@ -467,23 +448,6 @@ return (level == ResourceType_Instance ? limitFindInstances_ : limitFindResults_); } - void Apply(ILookupVisitor& visitor, - const DatabaseLookup& lookup, - ResourceType queryLevel, - const std::set& labels, - LabelsConstraint labelsConstraint, - size_t since, - size_t limit); - - void Apply(ILookupVisitor& visitor, - const DatabaseLookup& lookup, - ResourceType queryLevel, - size_t since, - size_t limit) - { - Apply(visitor, lookup, queryLevel, std::set(), LabelsConstraint_All, since, limit); - } - bool LookupOrReconstructMetadata(std::string& target, const std::string& publicId, ResourceType level, @@ -622,33 +586,6 @@ void SetUnknownSopClassAccepted(bool accepted); - bool ExpandResource(Json::Value& target, - const std::string& publicId, - ResourceType level, - DicomToJsonFormat format, - const std::set& requestedTags, - bool allowStorageAccess); - - bool ExpandResource(Json::Value& target, - const std::string& publicId, - const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) - const std::string& instanceId, // optional: the id of an instance for the resource - const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource - ResourceType level, - DicomToJsonFormat format, - const std::set& requestedTags, - bool allowStorageAccess); - - bool ExpandResource(ExpandedResource& target, - const std::string& publicId, - const DicomMap& mainDicomTags, // optional: the main dicom tags for the resource (if already available) - const std::string& instanceId, // optional: the id of an instance for the resource - const Json::Value* dicomAsJson, // optional: the dicom-as-json for the resource - ResourceType level, - const std::set& requestedTags, - ExpandResourceFlags expandFlags, - bool allowStorageAccess); - FindStorageAccessMode GetFindStorageAccessMode() const { return findStorageAccessMode_;