├── .gitignore ├── Dockerfile ├── Framework ├── Common │ ├── DatabaseManager.cpp │ ├── DatabaseManager.h │ ├── DatabasesEnumerations.cpp │ ├── DatabasesEnumerations.h │ ├── IDatabase.h │ ├── IDatabaseFactory.h │ └── ITransaction.h ├── MongoDB │ ├── MongoDatabase.cpp │ └── MongoDatabase.h └── Plugins │ ├── DatabaseBackendAdapterV2.cpp │ ├── DatabaseBackendAdapterV2.h │ ├── DatabaseBackendAdapterV3.cpp │ ├── DatabaseBackendAdapterV3.h │ ├── GlobalProperties.h │ ├── IDatabaseBackend.h │ ├── IDatabaseBackendOutput.h │ ├── IndexBackend.cpp │ ├── IndexBackend.h │ ├── IndexUnitTests.h │ ├── PluginInitialization.cpp │ └── PluginInitialization.h ├── LICENSE.md ├── MongoDB ├── CMakeLists.txt ├── Plugins │ ├── IndexPlugin.cpp │ ├── MongoDBIndex.cpp │ ├── MongoDBIndex.h │ ├── MongoDBStorageArea.cpp │ ├── MongoDBStorageArea.h │ └── StoragePlugin.cpp ├── Tests │ └── StorageTest.cpp └── UnitTests │ └── UnitTestsMain.cpp ├── README.md ├── Resources ├── CMake │ ├── DatabasesFrameworkConfiguration.cmake │ ├── DatabasesFrameworkParameters.cmake │ ├── DatabasesPluginConfiguration.cmake │ ├── DatabasesPluginParameters.cmake │ └── MongoDBConfiguration.cmake ├── Config │ └── configuration.json ├── MongoDB │ ├── AutoConfig.cmake │ ├── mongo-c-driver.txt.in │ └── mongo-cxx-driver.txt.in ├── Orthanc │ ├── CMake │ │ ├── AutoGeneratedCode.cmake │ │ ├── Compiler.cmake │ │ ├── DownloadOrthancFramework.cmake │ │ ├── DownloadPackage.cmake │ │ └── GoogleTestConfiguration.cmake │ ├── Databases │ │ ├── DatabaseConstraint.cpp │ │ └── DatabaseConstraint.h │ ├── EmbedResources.py │ ├── LinuxStandardBaseToolchain.cmake │ ├── MinGW-W64-Toolchain32.cmake │ ├── MinGW-W64-Toolchain64.cmake │ ├── MinGWToolchain.cmake │ ├── Plugins │ │ ├── ExportedSymbolsPlugins.list │ │ ├── OrthancPluginCppWrapper.cpp │ │ ├── OrthancPluginCppWrapper.h │ │ ├── OrthancPluginException.h │ │ ├── OrthancPluginsExports.cmake │ │ └── VersionScriptPlugins.map │ ├── Sdk-0.9.5 │ │ └── orthanc │ │ │ ├── OrthancCDatabasePlugin.h │ │ │ └── OrthancCPlugin.h │ ├── Sdk-1.4.0 │ │ └── orthanc │ │ │ ├── OrthancCDatabasePlugin.h │ │ │ └── OrthancCPlugin.h │ ├── Sdk-1.5.2 │ │ └── orthanc │ │ │ ├── OrthancCDatabasePlugin.h │ │ │ └── OrthancCPlugin.h │ ├── Sdk-1.5.4 │ │ └── orthanc │ │ │ ├── OrthancCDatabasePlugin.h │ │ │ └── OrthancCPlugin.h │ └── Sdk-1.9.2 │ │ └── orthanc │ │ ├── OrthancCDatabasePlugin.h │ │ └── OrthancCPlugin.h └── SyncOrthancFolder.py ├── compose.yaml └── docs ├── DEPENDENCIES_AUTO_CONFIG.md ├── PLUGIN_COMPILATION.md ├── PLUGIN_CONFIGURATION.md ├── PREREQUISITES.md ├── README.md ├── TESTING.md └── img └── green_tick.png /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | .DS_Store 4 | ._.DS_Store 5 | .idea 6 | *.iml 7 | *ThirdPartyDownloads* 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oraclelinux:9 AS base 2 | 3 | RUN dnf config-manager --enable ol9_addons 4 | RUN yum -y install patch \ 5 | git \ 6 | curl \ 7 | libuuid-devel \ 8 | openssl-devel \ 9 | cyrus-sasl-devel \ 10 | zlib-devel \ 11 | unzip \ 12 | cmake \ 13 | make \ 14 | gcc \ 15 | gdb \ 16 | gcc-c++ 17 | 18 | FROM base AS dev 19 | 20 | WORKDIR /usr/share/src 21 | ADD . /usr/share/src 22 | 23 | RUN mkdir -p build 24 | RUN cd build 25 | # RUN cmake ../MongoDB -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=/usr/local -DSTATIC_BUILD=ON -DAUTO_INSTALL_DEPENDENCIES=ON -DBUILD_TESTS=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/usr/local/orthanc/OrthancFramwork/Sources 26 | 27 | # RUN make 28 | 29 | FROM dev AS runtime 30 | 31 | ENV MONGO_URL=mongodb://database:27017/inpacs?retryWrites=false 32 | 33 | WORKDIR /usr/local/runtime 34 | 35 | RUN curl https://orthanc.uclouvain.be/downloads/linux-standard-base/orthanc/1.11.3/Orthanc -o Orthanc 36 | RUN curl https://orthanc.uclouvain.be/downloads/linux-standard-base/orthanc/1.11.3/libServeFolders.so -o libServeFolders.so 37 | RUN curl https://orthanc.uclouvain.be/downloads/linux-standard-base/orthanc/1.11.3/libModalityWorklists.so -o libModalityWorklists.so 38 | RUN curl https://orthanc.uclouvain.be/downloads/linux-standard-base/orthanc-explorer-2/1.2.1/libOrthancExplorer2.so -o libOrthancExplorer2.so 39 | RUN curl https://orthanc.uclouvain.be/downloads/linux-standard-base/orthanc-explorer-2/1.2.1/dist.zip -o dist.zip 40 | RUN curl https://orthanc.uclouvain.be/downloads/linux-standard-base/stone-web-viewer/2.5/libStoneWebViewer.so -o libStoneWebViewer.so 41 | RUN curl https://orthanc.uclouvain.be/downloads/linux-standard-base/stone-web-viewer/2.5/wasm-binaries.zip -o wasm-binaries.zip 42 | RUN curl https://orthanc.uclouvain.be/downloads/linux-standard-base/orthanc-dicomweb/1.10/libOrthancDicomWeb.so -o libOrthancDicomWeb.so 43 | 44 | 45 | RUN chmod +x ./Orthanc 46 | RUN unzip dist.zip 47 | RUN unzip wasm-binaries.zip 48 | RUN cp /usr/local/src/Resources/Config/configuration.json . 49 | 50 | EXPOSE 4242 51 | EXPOSE 8042 52 | 53 | CMD ./Orthanc ./configuration.json -------------------------------------------------------------------------------- /Framework/Common/DatabaseManager.cpp: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #include "DatabaseManager.h" 4 | #include "ITransaction.h" 5 | 6 | #include // For std::unique_ptr<> 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace OrthancDatabases 13 | { 14 | void DatabaseManager::Close() 15 | { 16 | LOG(TRACE) << "Closing the connection to the database"; 17 | 18 | // Rollback active transaction, if any 19 | transaction_.reset(NULL); 20 | 21 | // Close the database 22 | database_.reset(NULL); 23 | 24 | LOG(TRACE) << "Connection to the database is closed"; 25 | } 26 | 27 | void DatabaseManager::CloseIfUnavailable(Orthanc::ErrorCode e) 28 | { 29 | if (e != Orthanc::ErrorCode_Success 30 | #if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 9, 2) 31 | && e != Orthanc::ErrorCode_DatabaseCannotSerialize 32 | #endif 33 | ) 34 | { 35 | transaction_.reset(NULL); 36 | } 37 | 38 | if (e == Orthanc::ErrorCode_DatabaseUnavailable) 39 | { 40 | LOG(ERROR) << "The database is not available, closing the connection"; 41 | Close(); 42 | } 43 | } 44 | 45 | ITransaction &DatabaseManager::GetTransaction() 46 | { 47 | if (transaction_.get() == NULL) 48 | { 49 | LOG(TRACE) << "Automatically creating an implicit database transaction"; 50 | 51 | try 52 | { 53 | transaction_.reset(GetDatabase().CreateTransaction(TransactionType_Implicit)); 54 | } 55 | catch (Orthanc::OrthancException &e) 56 | { 57 | CloseIfUnavailable(e.GetErrorCode()); 58 | throw; 59 | } 60 | } 61 | 62 | assert(transaction_.get() != NULL); 63 | return *transaction_; 64 | } 65 | 66 | DatabaseManager::DatabaseManager(IDatabaseFactory *factory) : factory_(factory) 67 | { 68 | if (factory == NULL) 69 | { 70 | throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 71 | } 72 | } 73 | 74 | IDatabase &DatabaseManager::GetDatabase() 75 | { 76 | assert(factory_.get() != NULL); 77 | 78 | if (database_.get() == NULL) 79 | { 80 | database_.reset(factory_->Open()); 81 | 82 | if (database_.get() == NULL) 83 | { 84 | throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 85 | } 86 | } 87 | 88 | return *database_; 89 | } 90 | 91 | void DatabaseManager::StartTransaction(TransactionType type) 92 | { 93 | try 94 | { 95 | if (transaction_.get() != NULL) 96 | { 97 | LOG(ERROR) << "Cannot start another transaction while there is an uncommitted transaction"; 98 | throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 99 | } 100 | 101 | transaction_.reset(GetDatabase().CreateTransaction(type)); 102 | } 103 | catch (Orthanc::OrthancException &e) 104 | { 105 | CloseIfUnavailable(e.GetErrorCode()); 106 | throw; 107 | } 108 | } 109 | 110 | void DatabaseManager::CommitTransaction() 111 | { 112 | if (transaction_.get() == NULL) 113 | { 114 | LOG(ERROR) << "Cannot commit a non-existing transaction"; 115 | throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 116 | } 117 | else 118 | { 119 | try 120 | { 121 | transaction_->Commit(); 122 | transaction_.reset(NULL); 123 | } 124 | catch (Orthanc::OrthancException &e) 125 | { 126 | CloseIfUnavailable(e.GetErrorCode()); 127 | throw; 128 | } 129 | } 130 | } 131 | 132 | void DatabaseManager::RollbackTransaction() 133 | { 134 | if (transaction_.get() == NULL) 135 | { 136 | LOG(INFO) << "Cannot rollback a non-existing transaction"; 137 | throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 138 | } 139 | else 140 | { 141 | try 142 | { 143 | transaction_->Rollback(); 144 | transaction_.reset(NULL); 145 | } 146 | catch (Orthanc::OrthancException &e) 147 | { 148 | CloseIfUnavailable(e.GetErrorCode()); 149 | throw; 150 | } 151 | } 152 | } 153 | 154 | DatabaseManager::Transaction::Transaction(DatabaseManager &manager, 155 | TransactionType type) : manager_(manager), 156 | database_(manager.GetDatabase()), 157 | active_(true) 158 | { 159 | manager_.StartTransaction(type); 160 | } 161 | 162 | DatabaseManager::Transaction::~Transaction() 163 | { 164 | if (active_) 165 | { 166 | try 167 | { 168 | manager_.RollbackTransaction(); 169 | } 170 | catch (Orthanc::OrthancException &e) 171 | { 172 | // Don't rethrow the exception as we are in a destructor 173 | LOG(ERROR) << "Uncatched error during some transaction rollback: " << e.What(); 174 | } 175 | } 176 | } 177 | 178 | void DatabaseManager::Transaction::Commit() 179 | { 180 | if (active_) 181 | { 182 | manager_.CommitTransaction(); 183 | active_ = false; 184 | } 185 | else 186 | { 187 | throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 188 | } 189 | } 190 | 191 | void DatabaseManager::Transaction::Rollback() 192 | { 193 | if (active_) 194 | { 195 | manager_.RollbackTransaction(); 196 | active_ = false; 197 | } 198 | else 199 | { 200 | throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 201 | } 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /Framework/Common/DatabaseManager.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | #include "IDatabaseFactory.h" 6 | #include "ITransaction.h" 7 | 8 | #include // For std::unique_ptr<> 9 | #include 10 | 11 | #include 12 | 13 | 14 | namespace OrthancDatabases 15 | { 16 | /** 17 | * WARNING: In PostgreSQL releases <= 3.3 and in MySQL releases <= 18 | * 3.0, this class was protected by a mutex. It is now assumed that 19 | * locking must be implemented at a higher level. 20 | * 21 | * This class maintains a list of precompiled statements. At any 22 | * time, this class handles 0 or 1 active transaction. 23 | * 24 | * "DatabaseManager" takes a "IDatabaseFactory" as input, in order 25 | * to be able to automatically re-open the database connection if 26 | * the latter gets lost. 27 | **/ 28 | class DatabaseManager : public boost::noncopyable 29 | { 30 | private: 31 | std::unique_ptr factory_; 32 | std::unique_ptr database_; 33 | std::unique_ptr transaction_; 34 | 35 | void CloseIfUnavailable(Orthanc::ErrorCode e); 36 | 37 | ITransaction& GetTransaction(); 38 | 39 | public: 40 | explicit DatabaseManager(IDatabaseFactory* factory); // Takes ownership 41 | 42 | ~DatabaseManager() 43 | { 44 | Close(); 45 | } 46 | 47 | IDatabase& GetDatabase(); 48 | 49 | void Close(); 50 | 51 | void StartTransaction(TransactionType type); 52 | 53 | void CommitTransaction(); 54 | 55 | void RollbackTransaction(); 56 | 57 | 58 | // This class is only used in the "StorageBackend" and in 59 | // "IDatabaseBackend::ConfigureDatabase()" 60 | class Transaction : public boost::noncopyable 61 | { 62 | private: 63 | DatabaseManager& manager_; 64 | IDatabase& database_; 65 | bool active_; 66 | 67 | public: 68 | explicit Transaction(DatabaseManager& manager, 69 | TransactionType type); 70 | 71 | ~Transaction(); 72 | 73 | void Commit(); 74 | 75 | void Rollback(); 76 | 77 | /** 78 | * WARNING: Don't call "GetDatabaseTransaction().Commit()" and 79 | * "GetDatabaseTransaction().Rollback()", but use the "Commit()" 80 | * and "Rollback()" methods above. 81 | **/ 82 | ITransaction& GetDatabaseTransaction() 83 | { 84 | return manager_.GetTransaction(); 85 | } 86 | }; 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /Framework/Common/DatabasesEnumerations.cpp: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #include "DatabasesEnumerations.h" 4 | 5 | #include 6 | 7 | namespace OrthancDatabases 8 | { 9 | const char* EnumerationToString(ValueType type) 10 | { 11 | switch (type) 12 | { 13 | case ValueType_BinaryString: 14 | return "BinaryString"; 15 | 16 | case ValueType_InputFile: 17 | return "InputFile"; 18 | 19 | case ValueType_Integer64: 20 | return "Integer64"; 21 | 22 | case ValueType_Null: 23 | return "Null"; 24 | 25 | case ValueType_ResultFile: 26 | return "ResultFile"; 27 | 28 | case ValueType_Utf8String: 29 | return "Utf8String"; 30 | 31 | default: 32 | throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Framework/Common/DatabasesEnumerations.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | 6 | namespace OrthancDatabases 7 | { 8 | enum ValueType 9 | { 10 | ValueType_BinaryString, 11 | ValueType_InputFile, 12 | ValueType_Integer64, 13 | ValueType_Null, 14 | ValueType_ResultFile, 15 | ValueType_Utf8String 16 | }; 17 | 18 | enum Dialect 19 | { 20 | Dialect_MySQL, 21 | Dialect_PostgreSQL, 22 | Dialect_SQLite, 23 | Dialect_MSSQL, 24 | Dialect_Unknown 25 | }; 26 | 27 | enum TransactionType 28 | { 29 | TransactionType_ReadWrite, 30 | TransactionType_ReadOnly, // Should only arise with Orthanc SDK >= 1.9.2 in the index plugin 31 | TransactionType_Implicit // Should only arise with Orthanc SDK <= 1.9.1 32 | }; 33 | 34 | const char* EnumerationToString(ValueType type); 35 | } 36 | -------------------------------------------------------------------------------- /Framework/Common/IDatabase.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | #include "DatabasesEnumerations.h" 8 | #include "ITransaction.h" 9 | 10 | namespace OrthancDatabases 11 | { 12 | class IDatabase : public boost::noncopyable 13 | { 14 | public: 15 | virtual ~IDatabase() 16 | { 17 | } 18 | 19 | virtual ITransaction* CreateTransaction(TransactionType type) = 0; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /Framework/Common/IDatabaseFactory.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | #include "IDatabase.h" 6 | 7 | namespace OrthancDatabases 8 | { 9 | class IDatabaseFactory : public boost::noncopyable 10 | { 11 | public: 12 | virtual ~IDatabaseFactory() 13 | { 14 | } 15 | 16 | virtual IDatabase* Open() = 0; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /Framework/Common/ITransaction.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace OrthancDatabases 9 | { 10 | class ITransaction : public boost::noncopyable 11 | { 12 | public: 13 | virtual ~ITransaction() 14 | { 15 | } 16 | 17 | virtual bool IsImplicit() const = 0; 18 | 19 | virtual void Rollback() = 0; 20 | 21 | virtual void Commit() = 0; 22 | 23 | virtual bool DoesTableExist(const std::string& name) = 0; 24 | 25 | virtual bool DoesTriggerExist(const std::string& name) = 0; // Only for MySQL 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /Framework/MongoDB/MongoDatabase.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | 20 | #include "MongoDatabase.h" 21 | #include "../Common/ITransaction.h" 22 | 23 | #include 24 | 25 | namespace OrthancDatabases { 26 | namespace { 27 | class DummyTransaction : public ITransaction { 28 | 29 | public: 30 | explicit DummyTransaction() {} 31 | 32 | virtual bool IsImplicit() const ORTHANC_OVERRIDE { 33 | return false; 34 | } 35 | 36 | virtual void Rollback() override { 37 | } 38 | 39 | virtual void Commit() override { 40 | } 41 | 42 | virtual bool DoesTableExist(const std::string &name) override { 43 | return true; 44 | } 45 | 46 | virtual bool DoesTriggerExist(const std::string &name) override { 47 | return false; 48 | } 49 | }; 50 | } 51 | 52 | ITransaction *MongoDatabase::CreateTransaction(TransactionType type) { 53 | return new DummyTransaction(); 54 | } 55 | 56 | // factory related 57 | class MongoDatabase::Factory : public IDatabaseFactory { 58 | private: 59 | std::string url_; 60 | int chunkSize_; 61 | 62 | public: 63 | Factory(const std::string &url, const int &chunkSize) : url_(url), chunkSize_(chunkSize) {} 64 | 65 | virtual IDatabase *Open() override { 66 | std::unique_ptr db(new MongoDatabase); 67 | db->SetChunkSize(chunkSize_); 68 | db->Open(url_); 69 | 70 | return db.release(); 71 | } 72 | }; 73 | 74 | IDatabaseFactory *MongoDatabase::CreateDatabaseFactory(const std::string &url, const int &chunkSize) { 75 | return new Factory(url, chunkSize); 76 | } 77 | 78 | MongoDatabase *MongoDatabase::CreateDatabaseConnection(const std::string &url, const int &chunkSize) { 79 | Factory factory(url, chunkSize); 80 | return dynamic_cast(factory.Open()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Framework/MongoDB/MongoDatabase.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | 20 | #pragma once 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "../Common/IDatabase.h" 28 | #include "../Common/IDatabaseFactory.h" 29 | #include 30 | 31 | // mongocxx related 32 | using bsoncxx::builder::basic::kvp; 33 | using bsoncxx::builder::basic::make_document; 34 | 35 | namespace OrthancDatabases { 36 | static mongocxx::instance &inst = mongocxx::instance::current(); 37 | 38 | class MongoDatabase : public IDatabase { 39 | private: 40 | class Factory; 41 | 42 | int chunkSize_; 43 | std::string dbname_; 44 | mongocxx::pool* pool_; 45 | 46 | public: 47 | ~MongoDatabase() { 48 | 49 | } 50 | 51 | void Open(const std::string &url) { 52 | auto const uri = mongocxx::uri{url}; 53 | 54 | dbname_ = uri.database(); 55 | mongocxx::pool *p = new mongocxx::pool(uri); 56 | 57 | SetPool(p); 58 | } 59 | 60 | void SetChunkSize(const int &chunkSize) { 61 | chunkSize_ = chunkSize; 62 | } 63 | 64 | void SetPool(mongocxx::pool *pool) { 65 | pool_ = pool; 66 | } 67 | 68 | mongocxx::pool &GetPool() const { 69 | return *pool_; 70 | } 71 | 72 | mongocxx::pool::entry GetPoolEntry() const { 73 | return GetPool().acquire(); 74 | } 75 | 76 | mongocxx::database GetObject() const { 77 | auto entry = GetPoolEntry(); 78 | return (*entry)[dbname_]; 79 | } 80 | 81 | mongocxx::collection GetCollection(const std::string &name) const { 82 | auto database = GetObject(); 83 | return database[name]; 84 | } 85 | 86 | mongocxx::collection GetCollection(const mongocxx::database &database, const std::string &name) const { 87 | return database[name]; 88 | } 89 | 90 | // database related tasks 91 | bool IsMaster() const { 92 | auto database = GetObject(); 93 | auto isMasterDocument = database.run_command(make_document(kvp("isMaster", 1))); 94 | 95 | return isMasterDocument.view()["ismaster"].get_bool().value; 96 | } 97 | 98 | void CreateIndices() { 99 | auto database = GetObject(); 100 | 101 | GetCollection(database, "fs.files").create_index(make_document(kvp("filename", 1))); 102 | 103 | GetCollection(database, "Resources").create_index(make_document(kvp("parentId", 1))); 104 | GetCollection(database, "Resources").create_index(make_document(kvp("publicId", 1))); 105 | GetCollection(database, "Resources").create_index(make_document(kvp("resourceType", 1))); 106 | GetCollection(database, "Resources").create_index(make_document(kvp("internalId", 1))); 107 | GetCollection(database, "PatientRecyclingOrder").create_index(make_document(kvp("patientId", 1))); 108 | GetCollection(database, "MainDicomTags").create_index(make_document(kvp("id", 1))); 109 | GetCollection(database, "MainDicomTags").create_index( 110 | make_document(kvp("tagGroup", 1), kvp("tagElement", 1), kvp("value", 1)) 111 | ); 112 | GetCollection(database, "DicomIdentifiers").create_index(make_document(kvp("id", 1))); 113 | GetCollection(database, "DicomIdentifiers").create_index( 114 | make_document(kvp("tagGroup", 1), kvp("tagElement", 1), kvp("value", 1)) 115 | ); 116 | GetCollection(database, "Changes").create_index(make_document(kvp("internalId", 1))); 117 | GetCollection(database, "AttachedFiles").create_index(make_document(kvp("id", 1))); 118 | GetCollection(database, "Metadata").create_index(make_document(kvp("id", 1))); 119 | GetCollection(database, "GlobalProperties").create_index(make_document(kvp("property", 1))); 120 | GetCollection(database, "ServerProperties").create_index( 121 | make_document(kvp("server", 1), kvp("property", 1)) 122 | ); 123 | } 124 | 125 | int64_t GetNextSequence(const std::string &sequence) const { 126 | int64_t num = 1; 127 | auto collection = GetCollection("Sequences"); 128 | 129 | mongocxx::options::find_one_and_update options; 130 | options.return_document(mongocxx::options::return_document::k_after); 131 | auto seqDocument = collection.find_one_and_update( 132 | make_document(kvp("name", sequence)), 133 | make_document(kvp("$inc", make_document(kvp("i", int64_t(1))))), 134 | options 135 | ); 136 | 137 | if (seqDocument) { 138 | bsoncxx::document::element element = seqDocument->view()["i"]; 139 | num = element.get_int64().value; 140 | } else { 141 | collection.insert_one(make_document( 142 | kvp("name", sequence), 143 | kvp("i", int64_t(1)) 144 | )); 145 | } 146 | 147 | return num; 148 | } 149 | 150 | virtual ITransaction *CreateTransaction(TransactionType type) override; 151 | 152 | static IDatabaseFactory* CreateDatabaseFactory(const std::string &url, const int &chunkSize); 153 | static MongoDatabase* CreateDatabaseConnection(const std::string &url, const int &chunkSize); 154 | }; 155 | } 156 | -------------------------------------------------------------------------------- /Framework/Plugins/DatabaseBackendAdapterV2.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | /** 4 | * NOTE: Until Orthanc 1.4.0, this file was part of the Orthanc source 5 | * distribution. This file is now part of "orthanc-databases", in 6 | * order to uncouple its evolution from the Orthanc core. 7 | **/ 8 | 9 | #pragma once 10 | 11 | #include "IDatabaseBackend.h" 12 | 13 | 14 | namespace OrthancDatabases 15 | { 16 | /** 17 | * @brief Bridge between C and C++ database engines. 18 | * 19 | * Class creating the bridge between the C low-level primitives for 20 | * custom database engines, and the high-level IDatabaseBackend C++ 21 | * interface, for Orthanc <= 1.9.1. 22 | * 23 | * @ingroup Callbacks 24 | **/ 25 | class DatabaseBackendAdapterV2 26 | { 27 | private: 28 | // This class cannot be instantiated 29 | DatabaseBackendAdapterV2() 30 | { 31 | } 32 | 33 | public: 34 | class Adapter; 35 | class Output; 36 | 37 | class Factory : public IDatabaseBackendOutput::IFactory 38 | { 39 | private: 40 | OrthancPluginContext* context_; 41 | OrthancPluginDatabaseContext* database_; 42 | 43 | public: 44 | Factory(OrthancPluginContext* context, 45 | OrthancPluginDatabaseContext* database) : 46 | context_(context), 47 | database_(database) 48 | { 49 | } 50 | 51 | virtual IDatabaseBackendOutput* CreateOutput() ORTHANC_OVERRIDE; 52 | }; 53 | 54 | static void Register(IDatabaseBackend* backend); 55 | 56 | static void Finalize(); 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /Framework/Plugins/DatabaseBackendAdapterV3.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | 4 | #pragma once 5 | 6 | #include "IndexBackend.h" 7 | 8 | 9 | #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 10 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 2) 11 | 12 | namespace OrthancDatabases 13 | { 14 | /** 15 | * @brief Bridge between C and C++ database engines. 16 | * 17 | * Class creating the bridge between the C low-level primitives for 18 | * custom database engines, and the high-level IDatabaseBackend C++ 19 | * interface, for Orthanc >= 1.9.2. 20 | **/ 21 | class DatabaseBackendAdapterV3 22 | { 23 | private: 24 | class Output; 25 | 26 | // This class cannot be instantiated 27 | DatabaseBackendAdapterV3() 28 | { 29 | } 30 | 31 | public: 32 | class Adapter; 33 | class Transaction; 34 | 35 | class Factory : public IDatabaseBackendOutput::IFactory 36 | { 37 | public: 38 | virtual IDatabaseBackendOutput* CreateOutput() ORTHANC_OVERRIDE; 39 | }; 40 | 41 | static void Register(IndexBackend* backend, 42 | size_t countConnections, 43 | unsigned int maxDatabaseRetries); 44 | 45 | static void Finalize(); 46 | }; 47 | } 48 | 49 | # endif 50 | #endif 51 | -------------------------------------------------------------------------------- /Framework/Plugins/GlobalProperties.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | #define MISSING_SERVER_IDENTIFIER "" 6 | 7 | 8 | namespace Orthanc 9 | { 10 | /** 11 | * The enum "GlobalProperty" is a subset of the "GlobalProperty_XXX" 12 | * values from the Orthanc server that have a special meaning to the 13 | * database plugins: 14 | * https://hg.orthanc-server.com/orthanc/file/default/OrthancServer/Sources/ServerEnumerations.h 15 | * 16 | * WARNING: The values must be the same between the Orthanc core and 17 | * this enum! 18 | **/ 19 | 20 | enum GlobalProperty 21 | { 22 | GlobalProperty_DatabaseSchemaVersion = 1, // Unused in the Orthanc core as of Orthanc 0.9.5 23 | GlobalProperty_GetTotalSizeIsFast = 6, // New in Orthanc 1.5.2 24 | 25 | // Reserved values for internal use by the database plugins 26 | GlobalProperty_DatabasePatchLevel = 4, 27 | GlobalProperty_DatabaseInternal0 = 10, 28 | GlobalProperty_DatabaseInternal1 = 11, 29 | GlobalProperty_DatabaseInternal2 = 12, 30 | GlobalProperty_DatabaseInternal3 = 13, 31 | GlobalProperty_DatabaseInternal4 = 14, 32 | GlobalProperty_DatabaseInternal5 = 15, 33 | GlobalProperty_DatabaseInternal6 = 16, 34 | GlobalProperty_DatabaseInternal7 = 17, 35 | GlobalProperty_DatabaseInternal8 = 18, 36 | GlobalProperty_DatabaseInternal9 = 19 // Only used in unit tests 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /Framework/Plugins/IDatabaseBackend.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | #include "IDatabaseBackendOutput.h" 6 | #include "../Common/DatabasesEnumerations.h" 7 | #include "../Common/DatabaseManager.h" 8 | 9 | #include 10 | 11 | namespace OrthancDatabases 12 | { 13 | class IDatabaseBackend : public boost::noncopyable 14 | { 15 | public: 16 | virtual ~IDatabaseBackend() 17 | { 18 | } 19 | 20 | virtual OrthancPluginContext* GetContext() = 0; 21 | 22 | virtual IDatabaseFactory* CreateDatabaseFactory() = 0; 23 | 24 | // This function is invoked once, even if multiple connections are open 25 | virtual void ConfigureDatabase(DatabaseManager& database) = 0; 26 | 27 | virtual void SetOutputFactory(IDatabaseBackendOutput::IFactory* factory) = 0; 28 | 29 | virtual IDatabaseBackendOutput* CreateOutput() = 0; 30 | 31 | virtual bool HasRevisionsSupport() const = 0; 32 | 33 | virtual void AddAttachment(DatabaseManager& manager, 34 | int64_t id, 35 | const OrthancPluginAttachment& attachment, 36 | int64_t revision) = 0; 37 | 38 | virtual void AttachChild(DatabaseManager& manager, 39 | int64_t parent, 40 | int64_t child) = 0; 41 | 42 | virtual void ClearChanges(DatabaseManager& manager) = 0; 43 | 44 | virtual void ClearExportedResources(DatabaseManager& manager) = 0; 45 | 46 | virtual int64_t CreateResource(DatabaseManager& manager, 47 | const char* publicId, 48 | OrthancPluginResourceType type) = 0; 49 | 50 | virtual void DeleteAttachment(IDatabaseBackendOutput& output, 51 | DatabaseManager& manager, 52 | int64_t id, 53 | int32_t attachment) = 0; 54 | 55 | virtual void DeleteMetadata(DatabaseManager& manager, 56 | int64_t id, 57 | int32_t metadataType) = 0; 58 | 59 | virtual void DeleteResource(IDatabaseBackendOutput& output, 60 | DatabaseManager& manager, 61 | int64_t id) = 0; 62 | 63 | virtual void GetAllInternalIds(std::list& target, 64 | DatabaseManager& manager, 65 | OrthancPluginResourceType resourceType) = 0; 66 | 67 | virtual void GetAllPublicIds(std::list& target, 68 | DatabaseManager& manager, 69 | OrthancPluginResourceType resourceType) = 0; 70 | 71 | virtual void GetAllPublicIds(std::list& target, 72 | DatabaseManager& manager, 73 | OrthancPluginResourceType resourceType, 74 | uint64_t since, 75 | uint64_t limit) = 0; 76 | 77 | /* Use GetOutput().AnswerChange() */ 78 | virtual void GetChanges(IDatabaseBackendOutput& output, 79 | bool& done /*out*/, 80 | DatabaseManager& manager, 81 | int64_t since, 82 | uint32_t maxResults) = 0; 83 | 84 | virtual void GetChildrenInternalId(std::list& target /*out*/, 85 | DatabaseManager& manager, 86 | int64_t id) = 0; 87 | 88 | virtual void GetChildrenPublicId(std::list& target /*out*/, 89 | DatabaseManager& manager, 90 | int64_t id) = 0; 91 | 92 | /* Use GetOutput().AnswerExportedResource() */ 93 | virtual void GetExportedResources(IDatabaseBackendOutput& output, 94 | bool& done /*out*/, 95 | DatabaseManager& manager, 96 | int64_t since, 97 | uint32_t maxResults) = 0; 98 | 99 | /* Use GetOutput().AnswerChange() */ 100 | virtual void GetLastChange(IDatabaseBackendOutput& output, 101 | DatabaseManager& manager) = 0; 102 | 103 | /* Use GetOutput().AnswerExportedResource() */ 104 | virtual void GetLastExportedResource(IDatabaseBackendOutput& output, 105 | DatabaseManager& manager) = 0; 106 | 107 | /* Use GetOutput().AnswerDicomTag() */ 108 | virtual void GetMainDicomTags(IDatabaseBackendOutput& output, 109 | DatabaseManager& manager, 110 | int64_t id) = 0; 111 | 112 | virtual std::string GetPublicId(DatabaseManager& manager, 113 | int64_t resourceId) = 0; 114 | 115 | virtual uint64_t GetResourcesCount(DatabaseManager& manager, 116 | OrthancPluginResourceType resourceType) = 0; 117 | 118 | virtual OrthancPluginResourceType GetResourceType(DatabaseManager& manager, 119 | int64_t resourceId) = 0; 120 | 121 | virtual uint64_t GetTotalCompressedSize(DatabaseManager& manager) = 0; 122 | 123 | virtual uint64_t GetTotalUncompressedSize(DatabaseManager& manager) = 0; 124 | 125 | virtual bool IsExistingResource(DatabaseManager& manager, 126 | int64_t internalId) = 0; 127 | 128 | virtual bool IsProtectedPatient(DatabaseManager& manager, 129 | int64_t internalId) = 0; 130 | 131 | virtual void ListAvailableMetadata(std::list& target /*out*/, 132 | DatabaseManager& manager, 133 | int64_t id) = 0; 134 | 135 | virtual void ListAvailableAttachments(std::list& target /*out*/, 136 | DatabaseManager& manager, 137 | int64_t id) = 0; 138 | 139 | virtual void LogChange(DatabaseManager& manager, 140 | int32_t changeType, 141 | int64_t resourceId, 142 | OrthancPluginResourceType resourceType, 143 | const char* date) = 0; 144 | 145 | virtual void LogExportedResource(DatabaseManager& manager, 146 | const OrthancPluginExportedResource& resource) = 0; 147 | 148 | /* Use GetOutput().AnswerAttachment() */ 149 | virtual bool LookupAttachment(IDatabaseBackendOutput& output, 150 | int64_t& revision /*out*/, 151 | DatabaseManager& manager, 152 | int64_t id, 153 | int32_t contentType) = 0; 154 | 155 | virtual bool LookupGlobalProperty(std::string& target /*out*/, 156 | DatabaseManager& manager, 157 | const char* serverIdentifier, 158 | int32_t property) = 0; 159 | 160 | virtual void LookupIdentifier(std::list& target /*out*/, 161 | DatabaseManager& manager, 162 | OrthancPluginResourceType resourceType, 163 | uint16_t group, 164 | uint16_t element, 165 | OrthancPluginIdentifierConstraint constraint, 166 | const char* value) = 0; 167 | 168 | virtual void LookupIdentifierRange(std::list& target /*out*/, 169 | DatabaseManager& manager, 170 | OrthancPluginResourceType resourceType, 171 | uint16_t group, 172 | uint16_t element, 173 | const char* start, 174 | const char* end) = 0; 175 | 176 | virtual bool LookupMetadata(std::string& target /*out*/, 177 | int64_t& revision /*out*/, 178 | DatabaseManager& manager, 179 | int64_t id, 180 | int32_t metadataType) = 0; 181 | 182 | virtual bool LookupParent(int64_t& parentId /*out*/, 183 | DatabaseManager& manager, 184 | int64_t resourceId) = 0; 185 | 186 | virtual bool LookupResource(int64_t& id /*out*/, 187 | OrthancPluginResourceType& type /*out*/, 188 | DatabaseManager& manager, 189 | const char* publicId) = 0; 190 | 191 | virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/, 192 | DatabaseManager& manager) = 0; 193 | 194 | virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/, 195 | DatabaseManager& manager, 196 | int64_t patientIdToAvoid) = 0; 197 | 198 | virtual void SetGlobalProperty(DatabaseManager& manager, 199 | const char* serverIdentifier, 200 | int32_t property, 201 | const char* utf8) = 0; 202 | 203 | virtual void SetMainDicomTag(DatabaseManager& manager, 204 | int64_t id, 205 | uint16_t group, 206 | uint16_t element, 207 | const char* value) = 0; 208 | 209 | virtual void SetIdentifierTag(DatabaseManager& manager, 210 | int64_t id, 211 | uint16_t group, 212 | uint16_t element, 213 | const char* value) = 0; 214 | 215 | virtual void SetMetadata(DatabaseManager& manager, 216 | int64_t id, 217 | int32_t metadataType, 218 | const char* value, 219 | int64_t revision) = 0; 220 | 221 | virtual void SetProtectedPatient(DatabaseManager& manager, 222 | int64_t internalId, 223 | bool isProtected) = 0; 224 | 225 | virtual uint32_t GetDatabaseVersion(DatabaseManager& manager) = 0; 226 | 227 | /** 228 | * Upgrade the database to the specified version of the database 229 | * schema. The upgrade script is allowed to make calls to 230 | * OrthancPluginReconstructMainDicomTags(). 231 | **/ 232 | virtual void UpgradeDatabase(DatabaseManager& manager, 233 | uint32_t targetVersion, 234 | OrthancPluginStorageArea* storageArea) = 0; 235 | 236 | virtual void ClearMainDicomTags(DatabaseManager& manager, 237 | int64_t internalId) = 0; 238 | 239 | virtual bool HasCreateInstance() const = 0; 240 | 241 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 242 | virtual void LookupResources(IDatabaseBackendOutput& output, 243 | DatabaseManager& manager, 244 | const std::vector& lookup, 245 | OrthancPluginResourceType queryLevel, 246 | uint32_t limit, 247 | bool requestSomeInstance) = 0; 248 | #endif 249 | 250 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 251 | virtual void CreateInstance(OrthancPluginCreateInstanceResult& result, 252 | DatabaseManager& manager, 253 | const char* hashPatient, 254 | const char* hashStudy, 255 | const char* hashSeries, 256 | const char* hashInstance) = 0; 257 | #endif 258 | 259 | 260 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 261 | virtual void SetResourcesContent( 262 | DatabaseManager& manager, 263 | uint32_t countIdentifierTags, 264 | const OrthancPluginResourcesContentTags* identifierTags, 265 | uint32_t countMainDicomTags, 266 | const OrthancPluginResourcesContentTags* mainDicomTags, 267 | uint32_t countMetadata, 268 | const OrthancPluginResourcesContentMetadata* metadata) = 0; 269 | #endif 270 | 271 | 272 | virtual void GetChildrenMetadata(std::list& target, 273 | DatabaseManager& manager, 274 | int64_t resourceId, 275 | int32_t metadata) = 0; 276 | 277 | virtual int64_t GetLastChangeIndex(DatabaseManager& manager) = 0; 278 | 279 | virtual void TagMostRecentPatient(DatabaseManager& manager, 280 | int64_t patientId) = 0; 281 | 282 | #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 283 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) 284 | // NB: "parentPublicId" must be cleared if the resource has no parent 285 | virtual bool LookupResourceAndParent(int64_t& id, 286 | OrthancPluginResourceType& type, 287 | std::string& parentPublicId, 288 | DatabaseManager& manager, 289 | const char* publicId) = 0; 290 | # endif 291 | #endif 292 | 293 | #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 294 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) 295 | virtual void GetAllMetadata(std::map& result, 296 | DatabaseManager& manager, 297 | int64_t id) = 0; 298 | # endif 299 | #endif 300 | }; 301 | } 302 | -------------------------------------------------------------------------------- /Framework/Plugins/IDatabaseBackendOutput.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | #include "../../Resources/Orthanc/Databases/DatabaseConstraint.h" 6 | 7 | namespace OrthancDatabases 8 | { 9 | class IDatabaseBackendOutput : public boost::noncopyable 10 | { 11 | public: 12 | /** 13 | * Contrarily to its parent "IDatabaseBackendOutput" class, the 14 | * "IFactory" subclass *can* be invoked from multiple threads if 15 | * used through "DatabaseBackendAdapterV3". Make sure to implement 16 | * proper locking if need be. 17 | **/ 18 | class IFactory : public boost::noncopyable 19 | { 20 | public: 21 | virtual ~IFactory() 22 | { 23 | } 24 | 25 | virtual IDatabaseBackendOutput* CreateOutput() = 0; 26 | }; 27 | 28 | virtual ~IDatabaseBackendOutput() 29 | { 30 | } 31 | 32 | virtual void SignalDeletedAttachment(const std::string& uuid, 33 | int32_t contentType, 34 | uint64_t uncompressedSize, 35 | const std::string& uncompressedHash, 36 | int32_t compressionType, 37 | uint64_t compressedSize, 38 | const std::string& compressedHash) = 0; 39 | 40 | virtual void SignalDeletedResource(const std::string& publicId, 41 | OrthancPluginResourceType resourceType) = 0; 42 | 43 | virtual void SignalRemainingAncestor(const std::string& ancestorId, 44 | OrthancPluginResourceType ancestorType) = 0; 45 | 46 | virtual void AnswerAttachment(const std::string& uuid, 47 | int32_t contentType, 48 | uint64_t uncompressedSize, 49 | const std::string& uncompressedHash, 50 | int32_t compressionType, 51 | uint64_t compressedSize, 52 | const std::string& compressedHash) = 0; 53 | 54 | virtual void AnswerChange(int64_t seq, 55 | int32_t changeType, 56 | OrthancPluginResourceType resourceType, 57 | const std::string& publicId, 58 | const std::string& date) = 0; 59 | 60 | virtual void AnswerDicomTag(uint16_t group, 61 | uint16_t element, 62 | const std::string& value) = 0; 63 | 64 | virtual void AnswerExportedResource(int64_t seq, 65 | OrthancPluginResourceType resourceType, 66 | const std::string& publicId, 67 | const std::string& modality, 68 | const std::string& date, 69 | const std::string& patientId, 70 | const std::string& studyInstanceUid, 71 | const std::string& seriesInstanceUid, 72 | const std::string& sopInstanceUid) = 0; 73 | 74 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 75 | virtual void AnswerMatchingResource(const std::string& resourceId) = 0; 76 | #endif 77 | 78 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 79 | virtual void AnswerMatchingResource(const std::string& resourceId, 80 | const std::string& someInstanceId) = 0; 81 | #endif 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /Framework/Plugins/PluginInitialization.cpp: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #include "PluginInitialization.h" 4 | 5 | #include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" 6 | 7 | #include 8 | #include 9 | 10 | 11 | namespace OrthancDatabases 12 | { 13 | static bool DisplayPerformanceWarning(const std::string& dbms, 14 | bool isIndex) 15 | { 16 | (void) DisplayPerformanceWarning; // Disable warning about unused function 17 | LOG(WARNING) << "Performance warning in " << dbms 18 | << (isIndex ? " index" : " storage area") 19 | << ": Non-release build, runtime debug assertions are turned on"; 20 | return true; 21 | } 22 | 23 | 24 | bool InitializePlugin(OrthancPluginContext* context, 25 | const std::string& dbms, 26 | bool isIndex) 27 | { 28 | #if ORTHANC_FRAMEWORK_VERSION_IS_ABOVE(1, 7, 2) 29 | Orthanc::Logging::InitializePluginContext(context); 30 | #else 31 | Orthanc::Logging::Initialize(context); 32 | #endif 33 | 34 | Orthanc::Logging::EnableInfoLevel(true); 35 | OrthancPlugins::SetGlobalContext(context); 36 | 37 | assert(DisplayPerformanceWarning(dbms, isIndex)); 38 | 39 | /* Check the version of the Orthanc core */ 40 | 41 | bool useFallback = true; 42 | bool isOptimal = false; 43 | 44 | #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 45 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 4, 0) 46 | if (OrthancPluginCheckVersionAdvanced(context, 0, 9, 5) == 0) 47 | { 48 | LOG(ERROR) << "Your version of Orthanc (" << context->orthancVersion 49 | << ") must be above 0.9.5 to run this plugin"; 50 | return false; 51 | } 52 | 53 | if (OrthancPluginCheckVersionAdvanced(context, 54 | ORTHANC_OPTIMAL_VERSION_MAJOR, 55 | ORTHANC_OPTIMAL_VERSION_MINOR, 56 | ORTHANC_OPTIMAL_VERSION_REVISION) == 1) 57 | { 58 | isOptimal = true; 59 | } 60 | 61 | useFallback = false; 62 | # endif 63 | #endif 64 | 65 | if (useFallback && 66 | OrthancPluginCheckVersion(context) == 0) 67 | { 68 | LOG(ERROR) << "Your version of Orthanc (" 69 | << context->orthancVersion << ") must be above " 70 | << ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER << "." 71 | << ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER << "." 72 | << ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 73 | << " to run this plugin"; 74 | return false; 75 | } 76 | 77 | if (useFallback) 78 | { 79 | std::string v(context->orthancVersion); 80 | 81 | if (v == "mainline") 82 | { 83 | isOptimal = true; 84 | } 85 | else 86 | { 87 | std::vector tokens; 88 | Orthanc::Toolbox::TokenizeString(tokens, v, '.'); 89 | 90 | if (tokens.size() != 3) 91 | { 92 | LOG(ERROR) << "Bad version of Orthanc: " << v; 93 | return false; 94 | } 95 | 96 | int major = boost::lexical_cast(tokens[0]); 97 | int minor = boost::lexical_cast(tokens[1]); 98 | int revision = boost::lexical_cast(tokens[2]); 99 | 100 | isOptimal = (major > ORTHANC_OPTIMAL_VERSION_MAJOR || 101 | (major == ORTHANC_OPTIMAL_VERSION_MAJOR && 102 | minor > ORTHANC_OPTIMAL_VERSION_MINOR) || 103 | (major == ORTHANC_OPTIMAL_VERSION_MAJOR && 104 | minor == ORTHANC_OPTIMAL_VERSION_MINOR && 105 | revision >= ORTHANC_OPTIMAL_VERSION_REVISION)); 106 | } 107 | } 108 | 109 | if (!isOptimal && 110 | isIndex) 111 | { 112 | LOG(WARNING) << "Performance warning in " << dbms 113 | << " index: Your version of Orthanc (" 114 | << context->orthancVersion << ") should be upgraded to " 115 | << ORTHANC_OPTIMAL_VERSION_MAJOR << "." 116 | << ORTHANC_OPTIMAL_VERSION_MINOR << "." 117 | << ORTHANC_OPTIMAL_VERSION_REVISION 118 | << " to benefit from best performance"; 119 | } 120 | 121 | 122 | std::string description = ("Stores the Orthanc " + 123 | std::string(isIndex ? "index" : "storage area") + 124 | " into a " + dbms + " database"); 125 | 126 | OrthancPluginSetDescription(context, description.c_str()); 127 | 128 | return true; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Framework/Plugins/PluginInitialization.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace OrthancDatabases 10 | { 11 | bool InitializePlugin(OrthancPluginContext* context, 12 | const std::string& dbms, 13 | bool isIndex); 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 2 | Copyright (C) 2017 (Doc Cirrus GmbH) 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU Affero General Public License as 6 | published by the Free Software Foundation, either version 3 of the 7 | License, or (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | 14 | You should have received a copy of the GNU Affero General Public License 15 | along with this program. If not, see . 16 | -------------------------------------------------------------------------------- /MongoDB/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | # Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | cmake_minimum_required(VERSION 3.2) 20 | project(OrthancMongoDB) 21 | 22 | set(ORTHANC_OPTIMAL_VERSION_MAJOR 1) 23 | set(ORTHANC_OPTIMAL_VERSION_MINOR 11) 24 | set(ORTHANC_OPTIMAL_VERSION_REVISION 3) 25 | 26 | if (ORTHANC_PLUGIN_VERSION STREQUAL "mainline") 27 | set(ORTHANC_FRAMEWORK_VERSION "mainline") 28 | set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") 29 | else () 30 | set(ORTHANC_FRAMEWORK_VERSION "1.11.3") 31 | set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") 32 | endif () 33 | 34 | include(${CMAKE_SOURCE_DIR}/../Resources/CMake/DatabasesPluginParameters.cmake) 35 | 36 | set(CMAKE_CXX_FLAGS '-fPIC') 37 | set(CMAKE_CXX_STANDARD 17) 38 | set(USE_SYSTEM_OPENSSL ON) 39 | set(ORTHANC_PLUGIN_VERSION ${ORTHANC_FRAMEWORK_VERSION}) 40 | 41 | include(${CMAKE_SOURCE_DIR}/../Resources/CMake/DatabasesPluginConfiguration.cmake) 42 | 43 | add_library(OrthancMongoFramework STATIC 44 | ${DATABASES_SOURCES} 45 | ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/PluginInitialization.cpp 46 | Plugins/MongoDBIndex.cpp 47 | Plugins/MongoDBStorageArea.cpp 48 | ) 49 | 50 | set_target_properties(OrthancMongoFramework PROPERTIES 51 | POSITION_INDEPENDENT_CODE ON 52 | COMPILE_FLAGS -DORTHANC_ENABLE_LOGGING_PLUGIN=1 53 | ) 54 | 55 | target_link_libraries(OrthancMongoFramework ${MONGODB_LIBS}) 56 | 57 | add_library(OrthancMongoDBIndex SHARED 58 | ${INDEX_RESOURCES} 59 | Plugins/IndexPlugin.cpp 60 | ) 61 | 62 | add_library(OrthancMongoDBStorage SHARED 63 | ${STORAGE_RESOURCES} 64 | Plugins/StoragePlugin.cpp 65 | ) 66 | 67 | 68 | target_link_libraries(OrthancMongoDBIndex OrthancMongoFramework) 69 | target_link_libraries(OrthancMongoDBStorage OrthancMongoFramework) 70 | message("Setting the version of the libraries to ${ORTHANC_PLUGIN_VERSION}") 71 | 72 | add_definitions( 73 | -DORTHANC_PLUGIN_VERSION="${ORTHANC_PLUGIN_VERSION}" 74 | ) 75 | 76 | set_target_properties(OrthancMongoDBStorage PROPERTIES 77 | VERSION ${ORTHANC_PLUGIN_VERSION} 78 | SOVERSION ${ORTHANC_PLUGIN_VERSION} 79 | COMPILE_FLAGS -DORTHANC_ENABLE_LOGGING_PLUGIN=1 80 | ) 81 | 82 | set_target_properties(OrthancMongoDBIndex PROPERTIES 83 | VERSION ${ORTHANC_PLUGIN_VERSION} 84 | SOVERSION ${ORTHANC_PLUGIN_VERSION} 85 | COMPILE_FLAGS -DORTHANC_ENABLE_LOGGING_PLUGIN=1 86 | ) 87 | 88 | install( 89 | TARGETS OrthancMongoDBIndex OrthancMongoDBStorage 90 | RUNTIME DESTINATION lib # Destination for Windows 91 | LIBRARY DESTINATION share/orthanc/plugins # Destination for Linux 92 | ) 93 | 94 | # investigate unit tests 95 | IF (BUILD_TESTS) 96 | add_executable(StorageTest 97 | Tests/StorageTest.cpp 98 | Plugins/MongoDBStorageArea.cpp 99 | ${DATABASES_SOURCES} 100 | ${GOOGLE_TEST_SOURCES} 101 | ) 102 | 103 | target_link_libraries(StorageTest ${GOOGLE_TEST_LIBRARIES}) 104 | set_target_properties(StorageTest PROPERTIES 105 | COMPILE_FLAGS -DORTHANC_ENABLE_LOGGING_PLUGIN=0 106 | ) 107 | ENDIF() 108 | -------------------------------------------------------------------------------- /MongoDB/Plugins/IndexPlugin.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | #include 20 | #include "MongoDBIndex.h" 21 | 22 | #include "../../Framework/Plugins/PluginInitialization.h" 23 | #include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" 24 | 25 | #include 26 | 27 | 28 | extern "C" 29 | { 30 | ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext *context) { 31 | if (!OrthancDatabases::InitializePlugin(context, "MongoDB", true)) { 32 | return -1; 33 | } 34 | 35 | OrthancPlugins::OrthancConfiguration configuration; 36 | 37 | if (!configuration.IsSection("MongoDB")) { 38 | LOG(WARNING) << "No available configuration for the MongoDB index plugin"; 39 | return 0; 40 | } 41 | 42 | OrthancPlugins::OrthancConfiguration mongodb; 43 | configuration.GetSection(mongodb, "MongoDB"); 44 | 45 | bool enable; 46 | if (!mongodb.LookupBooleanValue(enable, "EnableIndex") || !enable) { 47 | LOG(WARNING) << "The MongoDB index is currently disabled, set \"EnableIndex\" " 48 | << "to \"true\" in the \"MongoDB\" section of the configuration file of Orthanc"; 49 | return 0; 50 | } 51 | 52 | try { 53 | /* Register the MongoDB index into Orthanc */ 54 | mongoc_init(); 55 | 56 | const std::string connectionUri = mongodb.GetStringValue("ConnectionUri", ""); 57 | const unsigned int chunkSize = mongodb.GetUnsignedIntegerValue("ChunkSize", 261120); 58 | 59 | const unsigned int countConnections = mongodb.GetUnsignedIntegerValue("IndexConnectionsCount", 5); 60 | const unsigned int maxConnectionRetries = mongodb.GetUnsignedIntegerValue("MaxConnectionRetries", 10); 61 | 62 | if (connectionUri.empty()) { 63 | throw Orthanc::OrthancException( 64 | Orthanc::ErrorCode_ParameterOutOfRange, 65 | "No connection string provided for the MongoDB index" 66 | ); 67 | } 68 | 69 | OrthancDatabases::IndexBackend::Register( 70 | new OrthancDatabases::MongoDBIndex(context, connectionUri, chunkSize), 71 | countConnections, maxConnectionRetries 72 | ); 73 | } 74 | catch (Orthanc::OrthancException &e) { 75 | LOG(ERROR) << e.What(); 76 | return -1; 77 | } 78 | catch (...) { 79 | LOG(ERROR) << "Native exception while initializing the plugin"; 80 | return -1; 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | 87 | ORTHANC_PLUGINS_API void OrthancPluginFinalize() { 88 | LOG(WARNING) << "MongoDB index is finalizing"; 89 | OrthancDatabases::IndexBackend::Finalize(); 90 | 91 | mongoc_cleanup(); 92 | } 93 | 94 | 95 | ORTHANC_PLUGINS_API const char *OrthancPluginGetName() { 96 | return "mongodb-index"; 97 | } 98 | 99 | 100 | ORTHANC_PLUGINS_API const char *OrthancPluginGetVersion() { 101 | return ORTHANC_PLUGIN_VERSION; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /MongoDB/Plugins/MongoDBIndex.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | 24 | #include "../../Framework/Plugins/IndexBackend.h" 25 | 26 | namespace OrthancDatabases { 27 | class MongoDBIndex : public IndexBackend { 28 | private: 29 | std::string url_; 30 | int chunkSize_; 31 | 32 | protected: 33 | // methods overriden for mongodb 34 | void SignalDeletedFiles( 35 | IDatabaseBackendOutput &output, 36 | mongocxx::cursor& cursor 37 | ); 38 | 39 | void SignalDeletedResources( 40 | IDatabaseBackendOutput &output, 41 | const std::vector deleted_resources_vec 42 | ); 43 | 44 | public: 45 | explicit MongoDBIndex(OrthancPluginContext *context); // Opens in memory 46 | 47 | MongoDBIndex(OrthancPluginContext *context, const std::string &url_, const int &chunkSize_); 48 | 49 | IDatabaseFactory *CreateDatabaseFactory() override; 50 | 51 | void ConfigureDatabase(DatabaseManager &manager) override; 52 | 53 | bool HasRevisionsSupport() const override { 54 | return true; 55 | } 56 | 57 | // 58 | virtual void AddAttachment(DatabaseManager &manager, int64_t id, const OrthancPluginAttachment &attachment, 59 | int64_t revision) override; 60 | 61 | virtual void AttachChild(DatabaseManager &manager, int64_t parent, int64_t child) override; 62 | 63 | virtual void ClearChanges(DatabaseManager &manager) override; 64 | 65 | virtual void ClearExportedResources(DatabaseManager &manager) override; 66 | 67 | virtual void DeleteAttachment(IDatabaseBackendOutput &output, 68 | DatabaseManager &manager, 69 | int64_t id, 70 | int32_t attachment) override; 71 | 72 | virtual void DeleteMetadata(DatabaseManager &manager, 73 | int64_t id, 74 | int32_t metadataType) override; 75 | 76 | virtual void DeleteResource(IDatabaseBackendOutput &output, 77 | DatabaseManager &manager, 78 | int64_t id) override; 79 | 80 | virtual void GetAllInternalIds(std::list &target, 81 | DatabaseManager &manager, 82 | OrthancPluginResourceType resourceType) override; 83 | 84 | virtual void GetAllPublicIds(std::list &target, 85 | DatabaseManager &manager, 86 | OrthancPluginResourceType resourceType) override; 87 | 88 | virtual void GetAllPublicIds(std::list &target, 89 | DatabaseManager &manager, 90 | OrthancPluginResourceType resourceType, 91 | uint64_t since, 92 | uint64_t limit) override; 93 | 94 | virtual void GetChanges(IDatabaseBackendOutput &output, 95 | bool &done /*out*/, 96 | DatabaseManager &manager, 97 | int64_t since, 98 | uint32_t maxResults) override; 99 | 100 | virtual void GetChildrenInternalId(std::list &target /*out*/, 101 | DatabaseManager &manager, 102 | int64_t id) override; 103 | 104 | virtual void GetChildrenPublicId(std::list &target /*out*/, 105 | DatabaseManager &manager, 106 | int64_t id) override; 107 | 108 | virtual void GetExportedResources(IDatabaseBackendOutput &output, 109 | bool &done /*out*/, 110 | DatabaseManager &manager, 111 | int64_t since, 112 | uint32_t maxResults) override; 113 | 114 | virtual void GetLastChange(IDatabaseBackendOutput &output, 115 | DatabaseManager &manager) override; 116 | 117 | virtual void GetLastExportedResource(IDatabaseBackendOutput &output, 118 | DatabaseManager &manager) override; 119 | 120 | virtual void GetMainDicomTags(IDatabaseBackendOutput &output, 121 | DatabaseManager &manager, 122 | int64_t id) override; 123 | 124 | virtual std::string GetPublicId(DatabaseManager &manager, 125 | int64_t resourceId) override; 126 | 127 | virtual uint64_t GetResourcesCount(DatabaseManager &manager, 128 | OrthancPluginResourceType resourceType) override; 129 | 130 | virtual OrthancPluginResourceType GetResourceType(DatabaseManager &manager, 131 | int64_t resourceId) override; 132 | 133 | virtual uint64_t GetTotalCompressedSize(DatabaseManager &manager) override; 134 | 135 | virtual uint64_t GetTotalUncompressedSize(DatabaseManager &manager) override; 136 | 137 | virtual bool IsExistingResource(DatabaseManager &manager, 138 | int64_t internalId) override; 139 | 140 | virtual bool IsProtectedPatient(DatabaseManager &manager, 141 | int64_t internalId) override; 142 | 143 | virtual void ListAvailableMetadata(std::list &target /*out*/, 144 | DatabaseManager &manager, 145 | int64_t id) override; 146 | 147 | virtual void ListAvailableAttachments(std::list &target /*out*/, 148 | DatabaseManager &manager, 149 | int64_t id) override; 150 | 151 | virtual void LogChange(DatabaseManager &manager, 152 | int32_t changeType, 153 | int64_t resourceId, 154 | OrthancPluginResourceType resourceType, 155 | const char *date) override; 156 | 157 | virtual void LogExportedResource(DatabaseManager &manager, 158 | const OrthancPluginExportedResource &resource) override; 159 | 160 | virtual bool LookupAttachment(IDatabaseBackendOutput &output, 161 | int64_t &revision /*out*/, 162 | DatabaseManager &manager, 163 | int64_t id, 164 | int32_t contentType) override; 165 | 166 | bool LookupGlobalProperty(std::string &target /*out*/, DatabaseManager &manager, const char *serverIdentifier, 167 | int32_t property) override; 168 | 169 | virtual void LookupIdentifier(std::list &target /*out*/, 170 | DatabaseManager &manager, 171 | OrthancPluginResourceType resourceType, 172 | uint16_t group, 173 | uint16_t element, 174 | OrthancPluginIdentifierConstraint constraint, 175 | const char *value) override; 176 | 177 | virtual void LookupIdentifierRange(std::list &target /*out*/, 178 | DatabaseManager &manager, 179 | OrthancPluginResourceType resourceType, 180 | uint16_t group, 181 | uint16_t element, 182 | const char *start, 183 | const char *end) override; 184 | 185 | virtual bool LookupMetadata(std::string &target /*out*/, 186 | int64_t &revision /*out*/, 187 | DatabaseManager &manager, 188 | int64_t id, 189 | int32_t metadataType) override; 190 | 191 | virtual bool LookupParent(int64_t &parentId /*out*/, 192 | DatabaseManager &manager, 193 | int64_t resourceId) override; 194 | 195 | virtual bool LookupResource(int64_t &id /*out*/, 196 | OrthancPluginResourceType &type /*out*/, 197 | DatabaseManager &manager, 198 | const char *publicId) override; 199 | 200 | virtual bool SelectPatientToRecycle(int64_t &internalId /*out*/, 201 | DatabaseManager &manager) override; 202 | 203 | virtual bool SelectPatientToRecycle(int64_t &internalId /*out*/, 204 | DatabaseManager &manager, 205 | int64_t patientIdToAvoid) override; 206 | 207 | void SetGlobalProperty(DatabaseManager &manager, const char *serverIdentifier, int32_t property, 208 | const char *utf8) override; 209 | 210 | virtual void SetMainDicomTag(DatabaseManager &manager, 211 | int64_t id, 212 | uint16_t group, 213 | uint16_t element, 214 | const char *value) override; 215 | 216 | virtual void SetIdentifierTag(DatabaseManager &manager, 217 | int64_t id, 218 | uint16_t group, 219 | uint16_t element, 220 | const char *value) override; 221 | 222 | virtual void SetMetadata(DatabaseManager &manager, 223 | int64_t id, 224 | int32_t metadataType, 225 | const char *value, 226 | int64_t revision) override; 227 | 228 | virtual void SetProtectedPatient(DatabaseManager &manager, 229 | int64_t internalId, 230 | bool isProtected) override; 231 | 232 | virtual void ClearMainDicomTags(DatabaseManager &manager, 233 | int64_t internalId) override; 234 | 235 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 236 | 237 | // New primitive since Orthanc 1.5.2 238 | virtual void LookupResources(IDatabaseBackendOutput &output, 239 | DatabaseManager &manager, 240 | const std::vector &lookup, 241 | OrthancPluginResourceType queryLevel, 242 | uint32_t limit, 243 | bool requestSomeInstance) override; 244 | 245 | #endif 246 | 247 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 248 | 249 | // New primitive since Orthanc 1.5.2 250 | virtual void SetResourcesContent( 251 | DatabaseManager &manager, 252 | uint32_t countIdentifierTags, 253 | const OrthancPluginResourcesContentTags *identifierTags, 254 | uint32_t countMainDicomTags, 255 | const OrthancPluginResourcesContentTags *mainDicomTags, 256 | uint32_t countMetadata, 257 | const OrthancPluginResourcesContentMetadata *metadata) override; 258 | 259 | #endif 260 | 261 | // New primitive since Orthanc 1.5.2 262 | virtual void GetChildrenMetadata(std::list &target, 263 | DatabaseManager &manager, 264 | int64_t resourceId, 265 | int32_t metadata) override; 266 | 267 | virtual void TagMostRecentPatient(DatabaseManager &manager, 268 | int64_t patient) override; 269 | 270 | #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 271 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) 272 | 273 | // New primitive since Orthanc 1.5.4 274 | virtual bool LookupResourceAndParent(int64_t &id, 275 | OrthancPluginResourceType &type, 276 | std::string &parentPublicId, 277 | DatabaseManager &manager, 278 | const char *publicId) override; 279 | 280 | # endif 281 | #endif 282 | 283 | #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 284 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 4) 285 | 286 | // New primitive since Orthanc 1.5.4 287 | virtual void GetAllMetadata(std::map &result, 288 | DatabaseManager &manager, 289 | int64_t id) override; 290 | 291 | # endif 292 | #endif 293 | 294 | virtual bool HasCreateInstance() const override { 295 | // This extension is available in PostgreSQL and MySQL, but is 296 | // emulated by "CreateInstanceGeneric()" in SQLite 297 | return true; 298 | } 299 | 300 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 301 | virtual void CreateInstance(OrthancPluginCreateInstanceResult &result, 302 | DatabaseManager &manager, 303 | const char *hashPatient, 304 | const char *hashStudy, 305 | const char *hashSeries, 306 | const char *hashInstance) override; 307 | 308 | #endif 309 | 310 | int64_t CreateResource(DatabaseManager &manager, 311 | const char *publicId, 312 | OrthancPluginResourceType type) override { 313 | return -1; 314 | } 315 | 316 | // New primitive since Orthanc 1.5.2 317 | int64_t GetLastChangeIndex(DatabaseManager &manager) override { 318 | return -1; 319 | } 320 | 321 | // methods overriden for mongodb 322 | }; 323 | } 324 | -------------------------------------------------------------------------------- /MongoDB/Plugins/MongoDBStorageArea.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | #if HAS_ORTHANC_EXCEPTION != 1 20 | # error HAS_ORTHANC_EXCEPTION must be set to 1 21 | #endif 22 | 23 | #include "MongoDBStorageArea.h" 24 | 25 | #include 26 | 27 | #include // For std::unique_ptr<> 28 | #include 29 | 30 | 31 | #define ORTHANC_PLUGINS_DATABASE_CATCH \ 32 | catch (::Orthanc::OrthancException& e) \ 33 | { \ 34 | return static_cast(e.GetErrorCode()); \ 35 | } \ 36 | catch (::std::runtime_error& e) \ 37 | { \ 38 | std::string s = "Exception in storage area back-end: " + std::string(e.what()); \ 39 | OrthancPluginLogError(context_, s.c_str()); \ 40 | return OrthancPluginErrorCode_DatabasePlugin; \ 41 | } \ 42 | catch (...) \ 43 | { \ 44 | OrthancPluginLogError(context_, "Native exception"); \ 45 | return OrthancPluginErrorCode_DatabasePlugin; \ 46 | } 47 | 48 | namespace OrthancDatabases { 49 | static OrthancPluginContext *context_ = nullptr; 50 | static std::unique_ptr backend_; 51 | 52 | // overrides 53 | mongoc_gridfs_file_t *MongoDBStorageArea::Accessor::CreateMongoDBFile( 54 | mongoc_gridfs_t *gridfs, 55 | const std::string &uuid, 56 | OrthancPluginContentType type, 57 | bool createFile = true 58 | ) { 59 | mongoc_gridfs_file_t *file; 60 | 61 | if (createFile) { 62 | auto filename = uuid + " - " + std::to_string(type); 63 | mongoc_gridfs_file_opt_t options = {nullptr}; 64 | options.chunk_size = chunk_size_; 65 | options.filename = filename.c_str(); 66 | 67 | file = mongoc_gridfs_create_file(gridfs, &options); 68 | } else { 69 | bson_t *filter; 70 | auto regex = "^" + uuid; 71 | 72 | filter = BCON_NEW ("filename", "{", "$regex", BCON_REGEX(BCON_UTF8(regex.c_str()), "x"), "}"); 73 | file = mongoc_gridfs_find_one_with_opts(gridfs, filter, nullptr, nullptr); 74 | 75 | bson_destroy(filter); 76 | } 77 | 78 | if (!file) { 79 | LOG(ERROR) << "MongoDBGridFS::CreateMongoDBFile - Could not create file."; 80 | throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); 81 | } 82 | return file; 83 | } 84 | 85 | mongoc_stream_t *MongoDBStorageArea::Accessor::CreateMongoDBStream(mongoc_gridfs_file_t *file) { 86 | mongoc_stream_t *stream = mongoc_stream_gridfs_new(file); 87 | 88 | if (!stream) { 89 | LOG(ERROR) << "MongoDBGridFS::CreateMongoDBStream - Could not create stream."; 90 | throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 91 | } 92 | 93 | return stream; 94 | } 95 | 96 | void MongoDBStorageArea::Accessor::Create(const std::string &uuid, 97 | const void *content, 98 | size_t size, 99 | OrthancPluginContentType type) { 100 | mongoc_client_t *client = PopClient(); 101 | mongoc_gridfs_t *gridfs = mongoc_client_get_gridfs(client, database_name_, nullptr, nullptr); 102 | 103 | mongoc_gridfs_file_t *file = CreateMongoDBFile(gridfs, uuid, type, true); 104 | mongoc_stream_t *stream = CreateMongoDBStream(file); 105 | 106 | mongoc_iovec_t iov; 107 | iov.iov_len = size; 108 | iov.iov_base = const_cast(content); 109 | 110 | mongoc_stream_writev(stream, &iov, 1, 0); 111 | if (!mongoc_gridfs_file_save(file)) { 112 | LOG(ERROR) << "MongoDBStorageArea::Accessor::Create - Could write file."; 113 | throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 114 | } 115 | 116 | mongoc_stream_destroy(stream); 117 | mongoc_gridfs_file_destroy(file); 118 | mongoc_gridfs_destroy(gridfs); 119 | 120 | PushClient(client); 121 | } 122 | 123 | void MongoDBStorageArea::Accessor::ReadWhole(OrthancPluginMemoryBuffer64 *target, 124 | const std::string &uuid, 125 | OrthancPluginContentType type) { 126 | mongoc_client_t *client = PopClient(); 127 | mongoc_gridfs_t *gridfs = mongoc_client_get_gridfs(client, database_name_, nullptr, nullptr); 128 | 129 | mongoc_gridfs_file_t *file = CreateMongoDBFile(gridfs, uuid, type, false); 130 | mongoc_stream_t *stream = CreateMongoDBStream(file); 131 | 132 | if (OrthancPluginCreateMemoryBuffer64(context_, target, static_cast(mongoc_gridfs_file_get_length(file))) != OrthancPluginErrorCode_Success){ 133 | throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory); 134 | } 135 | 136 | mongoc_iovec_t iov; 137 | iov.iov_len = target->size; 138 | iov.iov_base = target->data; 139 | 140 | mongoc_stream_readv(stream, &iov, 1, -1, 0); 141 | 142 | mongoc_stream_destroy(stream); 143 | mongoc_gridfs_file_destroy(file); 144 | mongoc_gridfs_destroy(gridfs); 145 | 146 | PushClient(client); 147 | }; 148 | 149 | void MongoDBStorageArea::Accessor::ReadRange(OrthancPluginMemoryBuffer64 *target, 150 | const std::string &uuid, 151 | OrthancPluginContentType type, 152 | uint64_t rangeStart) { 153 | mongoc_client_t *client = PopClient(); 154 | mongoc_gridfs_t *gridfs = mongoc_client_get_gridfs(client, database_name_, nullptr, nullptr); 155 | 156 | mongoc_gridfs_file_t *file = CreateMongoDBFile(gridfs, uuid, type, false); 157 | mongoc_gridfs_file_seek(file, static_cast(rangeStart), SEEK_SET); 158 | 159 | mongoc_stream_t *stream = CreateMongoDBStream(file); 160 | 161 | mongoc_iovec_t iov; 162 | iov.iov_len = target->size; 163 | iov.iov_base = malloc(target->size); 164 | 165 | mongoc_stream_read(stream, iov.iov_base, target->size, -1, 0); 166 | memcpy(target->data, iov.iov_base, target->size); 167 | 168 | mongoc_stream_destroy(stream); 169 | mongoc_gridfs_file_destroy(file); 170 | mongoc_gridfs_destroy(gridfs); 171 | 172 | PushClient(client); 173 | }; 174 | 175 | void MongoDBStorageArea::Accessor::Remove(const std::string &uuid, OrthancPluginContentType type) { 176 | bson_error_t error; 177 | mongoc_client_t *client = PopClient(); 178 | mongoc_gridfs_t *gridfs = mongoc_client_get_gridfs(client, database_name_, nullptr, nullptr); 179 | 180 | mongoc_gridfs_file_t *file = CreateMongoDBFile(gridfs, uuid, type, false); 181 | 182 | bool r = mongoc_gridfs_file_remove(file, nullptr); 183 | if (r) mongoc_gridfs_file_destroy(file); 184 | else { 185 | LOG(ERROR) << "MongoDBStorageArea::Accessor::Remove - Could not remove file: " << std::string(error.message); 186 | throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 187 | } 188 | 189 | mongoc_gridfs_destroy(gridfs); 190 | PushClient(client); 191 | }; 192 | 193 | MongoDBStorageArea::MongoDBStorageArea(const std::string &url, const int &chunkSize, 194 | const int &maxConnectionRetries) : 195 | chunkSize_(chunkSize) { 196 | uri_ = mongoc_uri_new(url.c_str()); 197 | pool_ = mongoc_client_pool_new(uri_); 198 | 199 | mongoc_client_pool_set_error_api(pool_, MONGOC_ERROR_API_VERSION_2); 200 | } 201 | 202 | MongoDBStorageArea::~MongoDBStorageArea() { 203 | mongoc_client_pool_destroy(pool_); 204 | mongoc_uri_destroy(uri_); 205 | } 206 | 207 | static OrthancPluginErrorCode StorageCreate(const char *uuid, 208 | const void *content, 209 | int64_t size, 210 | OrthancPluginContentType type) { 211 | try { 212 | MongoDBStorageArea::Accessor *accessor = backend_->CreateAccessor(); 213 | accessor->Create(uuid, content, size, type); 214 | 215 | delete accessor; 216 | accessor = nullptr; 217 | 218 | return OrthancPluginErrorCode_Success; 219 | } 220 | ORTHANC_PLUGINS_DATABASE_CATCH; 221 | } 222 | 223 | 224 | #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) 225 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 0) 226 | 227 | static OrthancPluginErrorCode StorageReadWhole(OrthancPluginMemoryBuffer64 *target, 228 | const char *uuid, 229 | OrthancPluginContentType type) { 230 | try { 231 | MongoDBStorageArea::Accessor *accessor = backend_->CreateAccessor(); 232 | accessor->ReadWhole(target, uuid, type); 233 | 234 | delete accessor; 235 | accessor = nullptr; 236 | 237 | return OrthancPluginErrorCode_Success; 238 | } 239 | ORTHANC_PLUGINS_DATABASE_CATCH; 240 | } 241 | 242 | 243 | static OrthancPluginErrorCode StorageReadRange(OrthancPluginMemoryBuffer64 *target, 244 | const char *uuid, 245 | OrthancPluginContentType type, 246 | uint64_t start) { 247 | try { 248 | MongoDBStorageArea::Accessor *accessor = backend_->CreateAccessor(); 249 | accessor->ReadRange(target, uuid, type, start); 250 | 251 | delete accessor; 252 | accessor = nullptr; 253 | 254 | return OrthancPluginErrorCode_Success; 255 | } 256 | ORTHANC_PLUGINS_DATABASE_CATCH; 257 | } 258 | 259 | # endif 260 | #endif 261 | 262 | 263 | static OrthancPluginErrorCode StorageRead(void **data, 264 | int64_t *size, 265 | const char *uuid, 266 | OrthancPluginContentType type) { 267 | try { 268 | // not implemented now 269 | return OrthancPluginErrorCode_Success; 270 | } 271 | ORTHANC_PLUGINS_DATABASE_CATCH; 272 | } 273 | 274 | 275 | static OrthancPluginErrorCode StorageRemove(const char *uuid, 276 | OrthancPluginContentType type) { 277 | try { 278 | MongoDBStorageArea::Accessor *accessor = backend_->CreateAccessor(); 279 | accessor->Remove(uuid, type); 280 | 281 | delete accessor; 282 | accessor = nullptr; 283 | 284 | return OrthancPluginErrorCode_Success; 285 | } 286 | ORTHANC_PLUGINS_DATABASE_CATCH; 287 | } 288 | 289 | void MongoDBStorageArea::Register(OrthancPluginContext *context, MongoDBStorageArea *backend) { 290 | if (context == nullptr || backend == nullptr) { 291 | throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 292 | } else if (context_ != nullptr || backend_.get() != nullptr) { 293 | // This function can only be invoked once in the plugin 294 | throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 295 | } else { 296 | context_ = context; 297 | backend_.reset(backend); 298 | 299 | bool hasLoadedV2 = false; 300 | 301 | #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 302 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 9, 0) 303 | if (OrthancPluginCheckVersionAdvanced(context, 1, 9, 0) == 1) { 304 | OrthancPluginRegisterStorageArea2(context_, StorageCreate, StorageReadWhole, StorageReadRange, 305 | StorageRemove); 306 | hasLoadedV2 = true; 307 | } 308 | # endif 309 | #endif 310 | 311 | if (!hasLoadedV2) { 312 | LOG(WARNING) 313 | << "Performance warning: Your version of the Orthanc core or SDK doesn't support reading of file ranges"; 314 | OrthancPluginRegisterStorageArea(context_, StorageCreate, StorageRead, StorageRemove); 315 | } 316 | } 317 | } 318 | 319 | void MongoDBStorageArea::Finalize() { 320 | backend_.reset(nullptr); 321 | context_ = nullptr; 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /MongoDB/Plugins/MongoDBStorageArea.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" 25 | 26 | #include // For std::unique_ptr<> 27 | #include 28 | 29 | namespace OrthancDatabases { 30 | class MongoDBStorageArea : public boost::noncopyable { 31 | private: 32 | int chunkSize_; 33 | mongoc_uri_t *uri_; 34 | mongoc_client_pool_t *pool_; 35 | 36 | public: 37 | class Accessor : public boost::noncopyable { 38 | 39 | private: 40 | // does not own that 41 | mongoc_client_pool_t *pool_; 42 | mongoc_uri_t *uri_; 43 | const char *database_name_; 44 | 45 | int chunk_size_; 46 | 47 | mongoc_gridfs_file_t *CreateMongoDBFile(mongoc_gridfs_t *gridfs, const std::string &uuid, 48 | OrthancPluginContentType type, bool createFile); 49 | 50 | static mongoc_stream_t *CreateMongoDBStream(mongoc_gridfs_file_t *file); 51 | 52 | public: 53 | explicit Accessor(mongoc_client_pool_t *pool, mongoc_uri_t *uri, int chunk_size) : pool_(pool), uri_(uri), 54 | chunk_size_(chunk_size) { 55 | database_name_ = mongoc_uri_get_database(uri_); 56 | if (!database_name_) { 57 | LOG(ERROR) << "MongoDBGridFS::MongoDBGridFS - Cannot not parse mongodb URI."; 58 | throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 59 | } 60 | } 61 | 62 | virtual ~Accessor() {}; 63 | 64 | mongoc_client_t* PopClient() { 65 | mongoc_client_t *client = mongoc_client_pool_pop (pool_); 66 | 67 | if (!client) { 68 | LOG(ERROR) << "MongoDBGridFS::MongoDBGridFS - Cannot initialize mongodb client."; 69 | throw Orthanc::OrthancException(Orthanc::ErrorCode_Database); 70 | } 71 | 72 | return client; 73 | } 74 | 75 | void PushClient(mongoc_client_t * client) { 76 | mongoc_client_pool_push (pool_, client); 77 | } 78 | 79 | virtual void Create(const std::string &uuid, 80 | const void *content, 81 | size_t size, 82 | OrthancPluginContentType type); 83 | 84 | virtual void ReadWhole(OrthancPluginMemoryBuffer64 *target, 85 | const std::string &uuid, 86 | OrthancPluginContentType type); 87 | 88 | virtual void ReadRange(OrthancPluginMemoryBuffer64 *target, 89 | const std::string &uuid, 90 | OrthancPluginContentType type, 91 | uint64_t rangeStart); 92 | 93 | virtual void Remove(const std::string &uuid, OrthancPluginContentType type); 94 | }; 95 | 96 | explicit MongoDBStorageArea(const std::string &url, const int &chunkSize, const int &maxConnectionRetries); 97 | 98 | ~MongoDBStorageArea(); 99 | 100 | static void Register(OrthancPluginContext *context, MongoDBStorageArea *backend); // Takes ownership 101 | 102 | static void Finalize(); 103 | 104 | virtual Accessor* CreateAccessor() { 105 | return new Accessor(pool_, uri_, chunkSize_); 106 | } 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /MongoDB/Plugins/StoragePlugin.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | #include 20 | #include "MongoDBStorageArea.h" 21 | 22 | #include "../../Framework/Plugins/PluginInitialization.h" 23 | #include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" 24 | 25 | #include 26 | 27 | 28 | extern "C" 29 | { 30 | ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext *context) { 31 | if (!OrthancDatabases::InitializePlugin(context, "MongoDB", false)) { 32 | return -1; 33 | } 34 | 35 | OrthancPlugins::OrthancConfiguration configuration; 36 | 37 | if (!configuration.IsSection("MongoDB")) { 38 | LOG(WARNING) << "No available configuration for the MongoDB storage area plugin"; 39 | return 0; 40 | } 41 | 42 | OrthancPlugins::OrthancConfiguration mongodb; 43 | configuration.GetSection(mongodb, "MongoDB"); 44 | 45 | bool enable; 46 | if (!mongodb.LookupBooleanValue(enable, "EnableStorage") || 47 | !enable) { 48 | LOG(WARNING) << "The MongoDB storage area is currently disabled, set \"EnableStorage\" " 49 | << "to \"true\" in the \"MongoDB\" section of the configuration file of Orthanc"; 50 | return 0; 51 | } 52 | 53 | try { 54 | /* Register the MongoDB storage into Orthanc */ 55 | mongoc_init(); 56 | 57 | const std::string connectionUri = mongodb.GetStringValue("ConnectionUri", ""); 58 | const unsigned int chunkSize = mongodb.GetUnsignedIntegerValue("ChunkSize", 261120); 59 | 60 | // const unsigned int countConnections = mongodb.GetUnsignedIntegerValue("IndexConnectionsCount", 5); 61 | const unsigned int maxConnectionRetries = mongodb.GetUnsignedIntegerValue("MaxConnectionRetries", 10); 62 | 63 | if (connectionUri.empty()) { 64 | throw Orthanc::OrthancException( 65 | Orthanc::ErrorCode_ParameterOutOfRange, 66 | "No connection string provided for the MongoDB index" 67 | ); 68 | } 69 | 70 | OrthancDatabases::MongoDBStorageArea::Register( 71 | context, new OrthancDatabases::MongoDBStorageArea( 72 | connectionUri, static_cast(chunkSize), static_cast(maxConnectionRetries) 73 | ) 74 | ); 75 | } 76 | catch (Orthanc::OrthancException &e) { 77 | LOG(ERROR) << e.What(); 78 | return -1; 79 | } 80 | catch (...) { 81 | LOG(ERROR) << "Native exception while initializing the plugin"; 82 | return -1; 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | 89 | ORTHANC_PLUGINS_API void OrthancPluginFinalize() { 90 | LOG(WARNING) << "MongoDB storage area is finalizing"; 91 | OrthancDatabases::MongoDBStorageArea::Finalize(); 92 | 93 | mongoc_cleanup(); 94 | } 95 | 96 | 97 | ORTHANC_PLUGINS_API const char *OrthancPluginGetName() { 98 | return "mongodb-storage"; 99 | } 100 | 101 | 102 | ORTHANC_PLUGINS_API const char *OrthancPluginGetVersion() { 103 | return ORTHANC_PLUGIN_VERSION; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /MongoDB/Tests/StorageTest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Otrhanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 (Doc Cirrus GmbH) Ronald Wertlen, Ihor Mozil 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | #include "gtest/gtest.h" 20 | #include "../Plugins/MongoDBStorageArea.h" 21 | 22 | #include "../../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" 23 | 24 | #include // For std::unique_ptr<> 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | std::string connection_str = "mongodb://host.docker.internal:27017/"; 38 | std::string test_database = "test_db_" + Orthanc::Toolbox::GenerateUuid(); 39 | 40 | class MongoDBStorageTest : public ::testing::Test { 41 | protected: 42 | std::unique_ptr storage_; 43 | 44 | virtual void SetUp() 45 | { 46 | storage_ = std::make_unique(std::string(connection_str) + test_database, 261120, 10); 47 | DropDB(); 48 | } 49 | 50 | void DropDB() 51 | { 52 | // drop test DB 53 | mongocxx::client client{mongocxx::uri{std::string(connection_str) + test_database}}; 54 | client[test_database].drop(); 55 | } 56 | 57 | virtual void TearDown() 58 | { 59 | DropDB(); 60 | } 61 | }; 62 | 63 | const static std::string input_data(1024*1024, 'A'); 64 | const static std::string filename = Orthanc::Toolbox::GenerateUuid(); 65 | const static OrthancPluginContentType type = OrthancPluginContentType_Unknown; 66 | 67 | TEST_F(MongoDBStorageTest, StoreFiles) 68 | { 69 | 70 | auto *accessor = storage_->CreateAccessor(); 71 | accessor->Create(filename, input_data.c_str(), input_data.length(), type); 72 | 73 | OrthancPluginMemoryBuffer64 *target; 74 | accessor->ReadWhole(target, filename, type); 75 | 76 | // convert dtaa to string of the specified size 77 | char* d = static_cast(target->data); 78 | std::string res(d, d + target->size); 79 | 80 | ASSERT_EQ(input_data.length(), target->size); 81 | ASSERT_EQ(input_data, res); 82 | 83 | // free allocated by the Read method memory 84 | free(target); 85 | 86 | accessor->Remove(filename, type); 87 | 88 | ASSERT_THROW(accessor->ReadWhole(target, filename, type), Orthanc::OrthancException); 89 | 90 | delete accessor; 91 | accessor = nullptr; 92 | } 93 | 94 | 95 | int main(int argc, char **argv) 96 | { 97 | ::testing::InitGoogleTest(&argc, argv); 98 | Orthanc::Logging::Initialize(); 99 | Orthanc::Logging::EnableInfoLevel(true); 100 | // Orthanc::Logging::EnableTraceLevel(true); 101 | 102 | int result = RUN_ALL_TESTS(); 103 | 104 | Orthanc::Logging::Finalize(); 105 | 106 | return result; 107 | } -------------------------------------------------------------------------------- /MongoDB/UnitTests/UnitTestsMain.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | * Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Affero General Public License as 7 | * published by the Free Software Foundation, either version 3 of the 8 | * License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Affero General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Affero General Public License 16 | * along with this program. If not, see . 17 | **/ 18 | 19 | #include "../../Framework/MongoDB/MongoDatabase.h" 20 | #include "../Plugins/MongoDBIndex.h" 21 | 22 | #include // For std::unique_ptr<> 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | 29 | // #include "../../Framework/Plugins/IndexUnitTests.h" 30 | 31 | 32 | TEST(MongoDBIndex, Lock) 33 | { 34 | 35 | } 36 | 37 | 38 | 39 | int main(int argc, char **argv) 40 | { 41 | ::testing::InitGoogleTest(&argc, argv); 42 | Orthanc::Logging::Initialize(); 43 | Orthanc::Logging::EnableInfoLevel(true); 44 | // Orthanc::Logging::EnableTraceLevel(true); 45 | 46 | int result = RUN_ALL_TESTS(); 47 | 48 | Orthanc::Logging::Finalize(); 49 | 50 | return result; 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # MongoDB database plugins for Orthanc DICOM Server 4 | 5 | ## Overview 6 | 7 | The repository contains two plugins to store the data in MongoDB database. 8 | This was based on [orthanc-database](https://hg.orthanc-server.com/orthanc-databases/) and supporting the latest database API (orthanc@1.9.2+). 9 | We've comprehensively refactored the codebase, eliminating all SQL-dependent components and replacing them with their MongoDB counterparts. 10 | Additionally, we've extended the existing build toolchain to seamlessly integrate with MongoDB, ensuring a smooth transition to this new data storage solution. 11 | 12 | ## Notes 13 | 14 | - **This is still a work in progress (UnitTests, etc...)** check todos 15 | - If you skipped the [1.9.1](https://github.com/Doc-Cirrus/orthanc-mongodb/tree/1.9.1) tag, the migration section there 16 | will still need to executed, unless this is a fresh installation. 17 | 18 | ## Documentation 19 | 20 | For complete documentation check [Docs](./docs/README.md). 21 | 22 | ## Todos 23 | 24 | - Add unit tests and extend them. 25 | - Add coverage. 26 | -------------------------------------------------------------------------------- /Resources/CMake/DatabasesFrameworkConfiguration.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | 4 | ##################################################################### 5 | ## Configure the Orthanc Framework 6 | ##################################################################### 7 | if (ORTHANC_FRAMEWORK_SOURCE STREQUAL "system") 8 | if (ORTHANC_FRAMEWORK_USE_SHARED) 9 | include(FindBoost) 10 | find_package(Boost COMPONENTS regex thread) 11 | 12 | if (NOT Boost_FOUND) 13 | message(FATAL_ERROR "Unable to locate Boost on this system") 14 | endif() 15 | 16 | link_libraries(${Boost_LIBRARIES} jsoncpp) 17 | endif() 18 | 19 | link_libraries(${ORTHANC_FRAMEWORK_LIBRARIES}) 20 | 21 | if (ENABLE_SQLITE_BACKEND) 22 | add_definitions(-DORTHANC_ENABLE_SQLITE=1) 23 | endif() 24 | 25 | set(USE_SYSTEM_GOOGLE_TEST ON CACHE BOOL "Use the system version of Google Test") 26 | set(USE_GOOGLE_TEST_DEBIAN_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)") 27 | mark_as_advanced(USE_GOOGLE_TEST_DEBIAN_PACKAGE) 28 | include(${CMAKE_CURRENT_LIST_DIR}/../Orthanc/CMake/GoogleTestConfiguration.cmake) 29 | 30 | else() 31 | # Those modules of the Orthanc framework are not needed when dealing 32 | # with databases 33 | set(ENABLE_MODULE_IMAGES OFF) 34 | set(ENABLE_MODULE_JOBS OFF) 35 | set(ENABLE_MODULE_DICOM OFF) 36 | 37 | include(${ORTHANC_FRAMEWORK_ROOT}/../Resources/CMake/OrthancFrameworkConfiguration.cmake) 38 | include_directories(${ORTHANC_FRAMEWORK_ROOT}) 39 | endif() 40 | 41 | 42 | 43 | ##################################################################### 44 | ## Common source files for the databases 45 | ##################################################################### 46 | 47 | set(ORTHANC_DATABASES_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..) 48 | 49 | set(DATABASES_SOURCES 50 | ${ORTHANC_DATABASES_ROOT}/Framework/Common/DatabaseManager.cpp 51 | ${ORTHANC_DATABASES_ROOT}/Framework/Common/DatabasesEnumerations.cpp 52 | ${ORTHANC_DATABASES_ROOT}/Framework/MongoDB/MongoDatabase.cpp 53 | ) 54 | 55 | ##################################################################### 56 | ## Configure MongoDB 57 | ##################################################################### 58 | add_definitions(-DORTHANC_ENABLE_MONGODB=1) 59 | include(${CMAKE_CURRENT_LIST_DIR}/MongoDBConfiguration.cmake) 60 | -------------------------------------------------------------------------------- /Resources/CMake/DatabasesFrameworkParameters.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | ##################################################################### 4 | ## Import the parameters of the Orthanc Framework 5 | ##################################################################### 6 | 7 | include(${CMAKE_CURRENT_LIST_DIR}/../Orthanc/CMake/DownloadOrthancFramework.cmake) 8 | 9 | if (NOT ORTHANC_FRAMEWORK_SOURCE STREQUAL "system") 10 | include(${ORTHANC_FRAMEWORK_ROOT}/../Resources/CMake/OrthancFrameworkParameters.cmake) 11 | endif() 12 | -------------------------------------------------------------------------------- /Resources/CMake/DatabasesPluginConfiguration.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | include(${CMAKE_CURRENT_LIST_DIR}/DatabasesFrameworkConfiguration.cmake) 4 | include(${CMAKE_CURRENT_LIST_DIR}/../Orthanc/CMake/AutoGeneratedCode.cmake) 5 | include(${CMAKE_CURRENT_LIST_DIR}/../Orthanc/Plugins/OrthancPluginsExports.cmake) 6 | 7 | 8 | if (STATIC_BUILD OR NOT USE_SYSTEM_ORTHANC_SDK) 9 | if (ORTHANC_SDK_VERSION STREQUAL "0.9.5") 10 | include_directories(${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Sdk-0.9.5) 11 | elseif (ORTHANC_SDK_VERSION STREQUAL "1.4.0") 12 | include_directories(${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Sdk-1.4.0) 13 | elseif (ORTHANC_SDK_VERSION STREQUAL "1.5.2") 14 | include_directories(${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Sdk-1.5.2) 15 | elseif (ORTHANC_SDK_VERSION STREQUAL "1.5.4") 16 | include_directories(${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Sdk-1.5.4) 17 | elseif (ORTHANC_SDK_VERSION STREQUAL "1.9.2") 18 | include_directories(${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Sdk-1.9.2) 19 | elseif (ORTHANC_SDK_VERSION STREQUAL "framework") 20 | set(tmp ${ORTHANC_FRAMEWORK_ROOT}/../../OrthancServer/Plugins/Include/) 21 | message(${tmp}) 22 | if (NOT EXISTS ${tmp}/orthanc/OrthancCDatabasePlugin.h) 23 | message(FATAL_ERROR "Your copy of the Orthanc framework doesn't contain the Orthanc plugin SDK") 24 | endif() 25 | include_directories(${tmp}) 26 | else() 27 | message(FATAL_ERROR "Unsupported version of the Orthanc plugin SDK: ${ORTHANC_SDK_VERSION}") 28 | endif() 29 | else () 30 | CHECK_INCLUDE_FILE_CXX(orthanc/OrthancCDatabasePlugin.h HAVE_ORTHANC_H) 31 | if (NOT HAVE_ORTHANC_H) 32 | message(FATAL_ERROR "Please install the headers of the Orthanc plugins SDK") 33 | endif() 34 | endif() 35 | 36 | 37 | if (NOT DEFINED ORTHANC_OPTIMAL_VERSION_MAJOR) 38 | message(FATAL_ERROR "ORTHANC_OPTIMAL_VERSION_MAJOR is not defined") 39 | endif() 40 | 41 | if (NOT DEFINED ORTHANC_OPTIMAL_VERSION_MINOR) 42 | message(FATAL_ERROR "ORTHANC_OPTIMAL_VERSION_MINOR is not defined") 43 | endif() 44 | 45 | if (NOT DEFINED ORTHANC_OPTIMAL_VERSION_REVISION) 46 | message(FATAL_ERROR "ORTHANC_OPTIMAL_VERSION_REVISION is not defined") 47 | endif() 48 | 49 | 50 | add_definitions( 51 | -DHAS_ORTHANC_EXCEPTION=1 52 | -DORTHANC_BUILDING_SERVER_LIBRARY=0 53 | -DORTHANC_ENABLE_PLUGINS=1 54 | -DORTHANC_OPTIMAL_VERSION_MAJOR=${ORTHANC_OPTIMAL_VERSION_MAJOR} 55 | -DORTHANC_OPTIMAL_VERSION_MINOR=${ORTHANC_OPTIMAL_VERSION_MINOR} 56 | -DORTHANC_OPTIMAL_VERSION_REVISION=${ORTHANC_OPTIMAL_VERSION_REVISION} 57 | ) 58 | 59 | 60 | list(APPEND DATABASES_SOURCES 61 | ${ORTHANC_CORE_SOURCES} 62 | ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/DatabaseBackendAdapterV2.cpp 63 | ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/DatabaseBackendAdapterV3.cpp 64 | ${ORTHANC_DATABASES_ROOT}/Framework/Plugins/IndexBackend.cpp 65 | ${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Databases/DatabaseConstraint.cpp 66 | ${ORTHANC_DATABASES_ROOT}/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp 67 | ) 68 | -------------------------------------------------------------------------------- /Resources/CMake/DatabasesPluginParameters.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | set(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages") 4 | set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") 5 | set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") 6 | set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") 7 | 8 | # Advanced parameters to fine-tune linking against system libraries 9 | set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK") 10 | set(ORTHANC_SDK_VERSION "1.9.2" CACHE STRING "Version of the Orthanc plugin SDK to use, if not using the system version (can be \"0.9.5\", \"1.4.0\", \"1.5.2\", \"1.5.4\", \"1.9.2\" or \"framework\")") 11 | 12 | include(${CMAKE_CURRENT_LIST_DIR}/DatabasesFrameworkParameters.cmake) 13 | 14 | set(ENABLE_GOOGLE_TEST ON) 15 | -------------------------------------------------------------------------------- /Resources/CMake/MongoDBConfiguration.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | # Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | # MongoDB configuration cmake deps 20 | 21 | IF ( MSVC ) 22 | IF (NOT AUTO_INSTALL_DEPENDENCIES) 23 | set(MONGOC_ROOT "/mongo-c-driver" CACHE STRING "Mongo C driver root.") 24 | set(MONGOCXX_ROOT "/mongo-cxx-driver" CACHE STRING "Mongo CXX driver root.") 25 | # Set reqired include pathes for MSVC 26 | set(BSON_INCLUDE_DIRS "${MONGOC_ROOT}/include/libbson-1.0") 27 | set(MONGOCLIB_INCLUDE_DIRS "${MONGOC_ROOT}/include/libmongoc-1.0") 28 | set(BSONCXX_INCLUDE_DIRS "${MONGOCXX_ROOT}/include/bsoncxx/v_noabi") 29 | set(MONGOCXX_INCLUDE_DIRS "${MONGOCXX_ROOT}/include/mongocxx/v_noabi") 30 | set(JSONCPP_INCLUDE_DIRS "${LIBJSON_ROOT}/include") 31 | ELSE () 32 | # Install, build and configure next components: jsoncpp, libbson, libmongoc, libbsoncxx, libmongocxx 33 | include(${CMAKE_CURRENT_LIST_DIR}/../MongoDB/AutoConfig.cmake) 34 | ENDIF () 35 | set(MONGODB_LIBS ${MONGODB_LIBS} RpcRT4.Lib) 36 | ELSE () 37 | #rely on pkg-config 38 | include(FindPkgConfig) 39 | IF (NOT AUTO_INSTALL_DEPENDENCIES) 40 | find_package(libmongoc-1.0 REQUIRED) 41 | pkg_search_module(JSONCPP REQUIRED jsoncpp) 42 | IF (LINK_STATIC_LIBS) 43 | find_package(libbsoncxx-static REQUIRED) 44 | find_package(libmongocxx-static REQUIRED) 45 | ELSE() 46 | pkg_search_module(BSONCXX REQUIRED libbsoncxx) 47 | pkg_search_module(MONGOCXX REQUIRED libmongocxx) 48 | ENDIF() 49 | ELSE () 50 | # Install, build and configure next components: jsoncpp, libbson, libmongoc, libbsoncxx, libmongocxx 51 | include(${CMAKE_CURRENT_LIST_DIR}/../MongoDB/AutoConfig.cmake) 52 | ENDIF () 53 | pkg_search_module(OPENSSL openssl) 54 | IF (OPENSSL_FOUND) 55 | find_library(CYRUS sasl2) 56 | set(MONGODB_LIBS ${MONGODB_LIBS} ${OPENSSL_LIBRARIES} "sasl2") 57 | ENDIF() 58 | IF (CYRUS_FOUND) 59 | set(MONGODB_LIBS ${MONGODB_LIBS} ${CYRUS_LIBRARIES}) 60 | ENDIF() 61 | set(CMAKE_CXX_FLAGS "-std=c++14") 62 | #Linux specific switches 63 | IF (NOT APPLE) 64 | set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-z,defs") 65 | set(MONGODB_LIBS ${MONGODB_LIBS} "uuid" "pthread" "rt") 66 | ENDIF() 67 | set(MONGODB_LIBS ${MONGODB_LIBS} "z" "resolv") 68 | ENDIF () 69 | 70 | # Include directories 71 | include_directories(${BSON_INCLUDE_DIRS}) 72 | include_directories(${MONGOC_INCLUDE_DIRS}) 73 | include_directories(${LIBMONGOCXX_STATIC_INCLUDE_DIRS}) 74 | include_directories(${LIBBSONCXX_STATIC_INCLUDE_DIRS}) 75 | include_directories(${JSONCPP_INCLUDE_DIRS}) 76 | 77 | IF (NOT AUTO_INSTALL_DEPENDENCIES) 78 | set(LIBJSON_LIB_NAMES jsoncpp ) 79 | set(BSON_LIB_NAMES bson-1.0 ) 80 | set(MONGOC_LIB_NAMES mongoc-1.0) 81 | set(BSONCXX_LIB_NAMES bsoncxx ) 82 | set(MONGOCXX_LIB_NAMES mongocxx ) 83 | 84 | IF (LINK_STATIC_LIBS) 85 | ################################################################## 86 | #link against static libraries 87 | set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib ${CMAKE_FIND_LIBRARY_SUFFIXES}) 88 | set(LIBJSON_LIB_NAMES jsoncpp_static ${LIBJSON_LIB_NAMES} ) 89 | set(BSON_LIB_NAMES bson-static-1.0 bson-1.0 ${BSON_LIB_NAMES} ) 90 | set(MONGOC_LIB_NAMES mongoc-static-1.0 mongoc-1.0 ${MONGOC_LIB_NAMES} ) 91 | set(BSONCXX_LIB_NAMES bsoncxx-static ${LIBBSONCXX_STATIC_LIBRARIES} ) 92 | set(MONGOCXX_LIB_NAMES mongocxx-static ${LIBMONGOCXX_STATIC_LIBRARIES}) 93 | ENDIF() 94 | 95 | find_library(LIBJSON_LIBS 96 | NAMES ${LIBJSON_LIB_NAMES} 97 | PATHS "${LIBJSON_ROOT}/lib" 98 | ) 99 | find_library(BSON_LIBS 100 | NAMES ${BSON_LIB_NAMES} 101 | PATHS "${MONGOC_ROOT}/lib" 102 | ) 103 | find_library(MONGOC_LIBS 104 | NAMES ${MONGOC_LIB_NAMES} 105 | PATHS "${MONGOC_ROOT}/lib" 106 | ) 107 | find_library(BSONXX_LIBS 108 | NAMES ${BSONCXX_LIB_NAMES} 109 | PATHS "${MONGOCXX_ROOT}/lib" 110 | ) 111 | find_library(AMONGOCXX_LIBS 112 | NAMES ${MONGOCXX_LIB_NAMES} 113 | PATHS "${MONGOCXX_ROOT}/lib" 114 | ) 115 | ENDIF () 116 | 117 | message("Found libraries:") 118 | message(" LIBJSON_LIBS " ${LIBJSON_LIBS} ) 119 | message(" MONGOCXX_LIBS " ${AMONGOCXX_LIBS} ) 120 | message(" BSONCXX_LIBS " ${BSONXX_LIBS} ) 121 | message(" MONGOC_LIBS " ${MONGOC_LIBS} ) 122 | message(" BSON_LIBS " ${BSON_LIBS}) 123 | 124 | set(MONGODB_LIBS ${MONGODB_LIBS} ${LIBJSON_LIBS} ${AMONGOCXX_LIBS} ${BSONXX_LIBS} ${MONGOC_LIBS} ${BSON_LIBS}) 125 | link_libraries(${MONGODB_LIBS}) 126 | -------------------------------------------------------------------------------- /Resources/Config/configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "RemoteAccessAllowed": true, 3 | "AuthenticationEnabled": false, 4 | "DicomAlwaysAllowFind": true, 5 | "DicomAlwaysAllowFindWorklist": true, 6 | "DicomAlwaysAllowGet": true, 7 | "DicomAlwaysAllowMove": true, 8 | "DicomCheckModalityHost": true, 9 | "Plugins": ["/usr/local/runtime"], 10 | "ExtraMainDicomTags": { 11 | "Instance": [ 12 | "Rows", 13 | "Columns", 14 | "ImageType", 15 | "SOPClassUID", 16 | "ContentDate", 17 | "ContentTime", 18 | "FrameOfReferenceUID", 19 | "PixelSpacing", 20 | "SpecificCharacterSet", 21 | "BitsAllocated", 22 | "BitsStored", 23 | "RescaleSlope", 24 | "RescaleIntercept", 25 | "SliceThickness", 26 | "WindowCenter", 27 | "WindowWidth", 28 | "PhotometricInterpretation", 29 | "PixelRepresentation" 30 | ], 31 | "Series": [ 32 | "TimezoneOffsetFromUTC", 33 | "PerformedProcedureStepStartDate", 34 | "PerformedProcedureStepStartTime", 35 | "RequestAttributesSequence" 36 | ], 37 | "Study": ["TimezoneOffsetFromUTC"], 38 | "Patient": [] 39 | }, 40 | "StoneWebViewer": { 41 | "DicomCacheSize": 2048 42 | }, 43 | "DicomWeb": { 44 | "Enable": true, 45 | "MetadataWorkerThreadsCount": 10, 46 | "StudiesMetadata": "MainDicomTags", 47 | "SeriesMetadata": "MainDicomTags" 48 | }, 49 | "OrthancExplorer2": { 50 | "Enable": true, 51 | "IsDefaultOrthancUI": true 52 | }, 53 | "MongoDB": { 54 | "ConnectionUri": "mongodb://database:27017/inpacs?retryWrites=false", 55 | "EnableIndex": true, 56 | "EnableStorage": true, 57 | "ChunkSize": 261120 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Resources/MongoDB/AutoConfig.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # MongoDB Plugin - A plugin for Orthanc DICOM Server for storing DICOM data in MongoDB Database 3 | # Copyright (C) 2017 - 2023 (Doc Cirrus GmbH) 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | # 18 | 19 | # Common variables 20 | set(BUILD_DIR_POSTFIX "-build") 21 | set(INSTALL_DIR_POSTFIX "-install") 22 | set(CONFIGURATION_DIR_POSTFIX "-config") 23 | 24 | 25 | # Mongo C Driver variables 26 | set(MONGO_C_PROJECT "mongo-c-driver") 27 | set(MONGO_C_VERSION "1.28.0") 28 | set(MONGO_C_SOURCE_DIR "${CMAKE_BINARY_DIR}/${MONGO_C_PROJECT}") 29 | set(MONGO_C_BINARY_DIR "${CMAKE_BINARY_DIR}/${MONGO_C_PROJECT}${BUILD_DIR_POSTFIX}") 30 | set(MONGO_C_INSTALL_DIR "${CMAKE_BINARY_DIR}/${MONGO_C_PROJECT}${INSTALL_DIR_POSTFIX}") 31 | set(MONGO_C_CONFIG_DIR "${CMAKE_BINARY_DIR}/${MONGO_C_PROJECT}${CONFIGURATION_DIR_POSTFIX}") 32 | 33 | # Mongo CXX Driver variables 34 | set(MONGO_CXX_PROJECT "mongo-cxx-driver") 35 | set(MONGO_CXX_VERSION "3.10.2") 36 | set(MONGO_CXX_SOURCE_DIR "${CMAKE_BINARY_DIR}/${MONGO_CXX_PROJECT}") 37 | set(MONGO_CXX_BINARY_DIR "${CMAKE_BINARY_DIR}/${MONGO_CXX_PROJECT}${BUILD_DIR_POSTFIX}") 38 | set(MONGO_CXX_INSTALL_DIR "${CMAKE_BINARY_DIR}/${MONGO_CXX_PROJECT}${INSTALL_DIR_POSTFIX}") 39 | set(MONGO_CXX_CONFIG_DIR "${CMAKE_BINARY_DIR}/${MONGO_CXX_PROJECT}${CONFIGURATION_DIR_POSTFIX}") 40 | 41 | string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPERCASE) 42 | 43 | # Macroses 44 | macro(CheckError RESULT) 45 | if(${RESULT}) 46 | message(FATAL_ERROR "Failed to build project: ${RESULT}") 47 | endif() 48 | endmacro() 49 | 50 | macro(InstallPackage PROJECT_WORKING_DIR) 51 | execute_process( 52 | COMMAND ${CMAKE_COMMAND} . 53 | RESULT_VARIABLE RESULT 54 | WORKING_DIRECTORY ${PROJECT_WORKING_DIR} 55 | ) 56 | 57 | CheckError(RESULT) 58 | 59 | execute_process( 60 | COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE} 61 | RESULT_VARIABLE RESULT 62 | WORKING_DIRECTORY ${PROJECT_WORKING_DIR} 63 | ) 64 | 65 | CheckError(RESULT) 66 | endmacro() 67 | 68 | IF (MSVC AND STATIC_BUILD) 69 | # Link statically against MSVC's runtime libraries 70 | set(CompilerFlags 71 | CMAKE_CXX_FLAGS 72 | CMAKE_CXX_FLAGS_DEBUG 73 | CMAKE_CXX_FLAGS_RELEASE 74 | CMAKE_C_FLAGS 75 | CMAKE_C_FLAGS_DEBUG 76 | CMAKE_C_FLAGS_RELEASE 77 | ) 78 | foreach(CompilerFlag ${CompilerFlags}) 79 | string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}") 80 | string(REPLACE "/MDd" "/MTd" ${CompilerFlag} "${${CompilerFlag}}") 81 | endforeach() 82 | ENDIF () 83 | 84 | 85 | # Install mongo-c-driver 86 | 87 | STRING(REGEX MATCH "-fPIC" FPIC ${CMAKE_CXX_FLAGS}) 88 | IF(${FPIC} MATCHES "-fPIC") 89 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") 90 | set(CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE} "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}} -fPIC") 91 | ENDIF() 92 | 93 | configure_file( 94 | ${CMAKE_CURRENT_LIST_DIR}/${MONGO_C_PROJECT}.txt.in 95 | ${MONGO_C_CONFIG_DIR}/CMakeLists.txt) 96 | 97 | InstallPackage(${MONGO_C_CONFIG_DIR}) 98 | 99 | set(bson-1.0_DIR "${MONGO_C_INSTALL_DIR}/lib/cmake/bson-1.0/") 100 | set(mongoc-1.0_DIR "${MONGO_C_INSTALL_DIR}/lib/cmake/mongoc-1.0/") 101 | 102 | find_package(bson-1.0 103 | PATHS 104 | # Alternatives 105 | "${MONGO_C_INSTALL_DIR}/lib/cmake/bson-1.0/" 106 | "${MONGO_C_INSTALL_DIR}/lib32/cmake/bson-1.0/" 107 | "${MONGO_C_INSTALL_DIR}/lib64/cmake/bson-1.0/" 108 | NO_DEFAULT_PATH 109 | REQUIRED) 110 | 111 | include_directories("${MONGO_C_INSTALL_DIR}/include/libbson-1.0") 112 | include_directories("${MONGO_C_INSTALL_DIR}/include/libbson-1.0/bson") 113 | find_package(mongoc-1.0 114 | PATHS 115 | # Alternatives 116 | "${MONGO_C_INSTALL_DIR}/lib/cmake/mongoc-1.0/" 117 | "${MONGO_C_INSTALL_DIR}/lib32/cmake/mongoc-1.0/" 118 | "${MONGO_C_INSTALL_DIR}/lib64/cmake/mongoc-1.0/" 119 | NO_DEFAULT_PATH 120 | REQUIRED) 121 | 122 | include_directories("${MONGO_C_INSTALL_DIR}/include/libmongoc-1.0") 123 | include_directories("${MONGO_C_INSTALL_DIR}/include/libmongoc-1.0/mongoc") 124 | 125 | IF (STATIC_BUILD) 126 | get_target_property(BSON_LIBS mongo::bson_static LOCATION) 127 | get_target_property(BSON_INCLUDE_DIRS mongo::bson_static INTERFACE_INCLUDE_DIRECTORIES) 128 | get_target_property(MONGOC_LIBS mongo::mongoc_static LOCATION) 129 | get_target_property(MONGOCLIB_INCLUDE_DIRS mongo::mongoc_static INTERFACE_INCLUDE_DIRECTORIES) 130 | ELSE () 131 | IF (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 132 | get_target_property(BSON_LIBS mongo::bson_shared IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE_UPPERCASE}) 133 | ELSE () 134 | get_target_property(BSON_LIBS mongo::bson_shared LOCATION) 135 | ENDIF () 136 | get_target_property(BSON_INCLUDE_DIRS mongo::bson_shared INTERFACE_INCLUDE_DIRECTORIES) 137 | 138 | IF (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 139 | get_target_property(MONGOC_LIBS mongo::mongoc_shared IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE_UPPERCASE}) 140 | ELSE () 141 | get_target_property(MONGOC_LIBS mongo::mongoc_shared LOCATION) 142 | ENDIF () 143 | get_target_property(MONGOCLIB_INCLUDE_DIRS mongo::mongoc_shared INTERFACE_INCLUDE_DIRECTORIES) 144 | ENDIF () 145 | 146 | #Install mongo-cxx-driver 147 | 148 | IF (STATIC_BUILD) 149 | set(MONGO_CXX_BUILD_SHARED_LIBS OFF) 150 | ELSE () 151 | set(MONGO_CXX_BUILD_SHARED_LIBS ON) 152 | ENDIF () 153 | 154 | configure_file( 155 | ${CMAKE_CURRENT_LIST_DIR}/${MONGO_CXX_PROJECT}.txt.in 156 | ${MONGO_CXX_CONFIG_DIR}/CMakeLists.txt) 157 | 158 | unset(MONGO_CXX_BUILD_SHARED_LIBS) 159 | 160 | InstallPackage(${MONGO_CXX_CONFIG_DIR}) 161 | 162 | set(bsoncxx_DIR "${MONGO_CXX_INSTALL_DIR}/lib/cmake/bsoncxx-${MONGO_CXX_VERSION}/") 163 | set(mongocxx_DIR "${MONGO_CXX_INSTALL_DIR}/lib/cmake/mongocxx-${MONGO_CXX_VERSION}/") 164 | 165 | find_package(bsoncxx 166 | PATHS 167 | # Alternatives 168 | "${MONGO_CXX_INSTALL_DIR}/lib/cmake/bsoncxx-${MONGO_CXX_VERSION}/" 169 | "${MONGO_CXX_INSTALL_DIR}/lib32/cmake/bsoncxx-${MONGO_CXX_VERSION}/" 170 | "${MONGO_CXX_INSTALL_DIR}/lib64/cmake/bsoncxx-${MONGO_CXX_VERSION}/" 171 | NO_DEFAULT_PATH 172 | REQUIRED) 173 | 174 | find_package(mongocxx 175 | PATHS 176 | # Alternatives 177 | "${MONGO_CXX_INSTALL_DIR}/lib/cmake/mongocxx-${MONGO_CXX_VERSION}/" 178 | "${MONGO_CXX_INSTALL_DIR}/lib32/cmake/mongocxx-${MONGO_CXX_VERSION}/" 179 | "${MONGO_CXX_INSTALL_DIR}/lib64/cmake/mongocxx-${MONGO_CXX_VERSION}/" 180 | NO_DEFAULT_PATH 181 | REQUIRED) 182 | include_directories("${MONGO_CXX_INSTALL_DIR}/include/bsoncxx/v_noabi") 183 | include_directories("${MONGO_CXX_INSTALL_DIR}/include/mongocxx/v_noabi") 184 | IF (STATIC_BUILD) 185 | get_target_property(BSONXX_LIBS mongo::bsoncxx_static LOCATION) 186 | get_target_property(BSONCXX_INCLUDE_DIRS mongo::bsoncxx_static INTERFACE_INCLUDE_DIRECTORIES) 187 | get_target_property(AMONGOCXX_LIBS mongo::mongocxx_static LOCATION) 188 | get_target_property(MONGOCXX_INCLUDE_DIRS mongo::mongocxx_static INTERFACE_INCLUDE_DIRECTORIES) 189 | ELSE () 190 | IF (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 191 | get_target_property(BSONXX_LIBS mongo::bsoncxx_shared IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE_UPPERCASE}) 192 | ELSE () 193 | get_target_property(BSONXX_LIBS mongo::bsoncxx_shared LOCATION) 194 | ENDIF () 195 | get_target_property(BSONCXX_INCLUDE_DIRS mongo::bsoncxx_shared INTERFACE_INCLUDE_DIRECTORIES) 196 | 197 | IF (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 198 | get_target_property(AMONGOCXX_LIBS mongo::mongocxx_shared IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE_UPPERCASE}) 199 | ELSE () 200 | get_target_property(AMONGOCXX_LIBS mongo::mongocxx_shared LOCATION) 201 | ENDIF () 202 | get_target_property(MONGOCXX_INCLUDE_DIRS mongo::mongocxx_shared INTERFACE_INCLUDE_DIRECTORIES) 203 | ENDIF () 204 | 205 | # Set runtime path for libraries 206 | get_filename_component(MONGO_C_RPATH "${MONGOC_LIBS}" PATH) 207 | get_filename_component(MONGO_CXX_RPATH "${AMONGOCXX_LIBS}" PATH) 208 | 209 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 210 | set(CMAKE_INSTALL_RPATH "${MONGO_C_RPATH};${MONGO_CXX_RPATH}") 211 | 212 | # Inlude boost headers in case if boost used 213 | IF (BOOST_ROOT) 214 | include_directories(${BOOST_ROOT}) 215 | ENDIF () 216 | 217 | IF (MSVC AND STATIC_BUILD) 218 | # Link with some system libraries 219 | set(LIBS ${LIBS} ws2_32.lib Secur32.lib Crypt32.lib BCrypt.lib Dnsapi.lib) 220 | # Add preprocessor definitions which are required for correct linking 221 | add_definitions( 222 | -DBSON_STATIC 223 | -DMONGOC_STATIC 224 | -DBSONCXX_STATIC 225 | -DMONGOCXX_STATIC 226 | ) 227 | ENDIF () 228 | -------------------------------------------------------------------------------- /Resources/MongoDB/mongo-c-driver.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | project(${MONGO_C_PROJECT}) 4 | 5 | include(ExternalProject) 6 | 7 | ExternalProject_Add(${MONGO_C_PROJECT} 8 | URL "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/${MONGO_C_VERSION}.tar.gz" 9 | SOURCE_DIR "${MONGO_C_SOURCE_DIR}" 10 | BINARY_DIR "${MONGO_C_BINARY_DIR}" 11 | INSTALL_DIR "${MONGO_C_INSTALL_DIR}" 12 | CMAKE_CACHE_ARGS 13 | "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}" 14 | "-DCMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}:STRING=${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}}" 15 | "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}" 16 | "-DCMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}:STRING=${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}}" 17 | -DCMAKE_INSTALL_PREFIX:PATH=${MONGO_C_INSTALL_DIR} 18 | -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} 19 | -DBUILD_TESTING:BOOL=OFF 20 | -DENABLE_AUTOMATIC_INIT_AND_CLEANUP:BOOL=OFF 21 | ) 22 | -------------------------------------------------------------------------------- /Resources/MongoDB/mongo-cxx-driver.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | project(${MONGO_CXX_PROJECT}) 4 | 5 | include(ExternalProject) 6 | 7 | ExternalProject_Add(${MONGO_CXX_PROJECT} 8 | URL "https://github.com/mongodb/mongo-cxx-driver/releases/download/r${MONGO_CXX_VERSION}/mongo-cxx-driver-r${MONGO_CXX_VERSION}.tar.gz" 9 | SOURCE_DIR "${MONGO_CXX_SOURCE_DIR}" 10 | BINARY_DIR "${MONGO_CXX_BINARY_DIR}" 11 | INSTALL_DIR "${MONGO_CXX_INSTALL_DIR}" 12 | CMAKE_CACHE_ARGS 13 | "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}" 14 | "-DCMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}:STRING=${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPERCASE}}" 15 | -DCMAKE_INSTALL_PREFIX:PATH=${MONGO_CXX_INSTALL_DIR} 16 | -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} 17 | -DCMAKE_PREFIX_PATH:PATH=${MONGO_C_INSTALL_DIR} 18 | -DBUILD_SHARED_LIBS:BOOL=${MONGO_CXX_BUILD_SHARED_LIBS} 19 | -DBUILD_SHARED_AND_STATIC_LIBS:BOOL=OFF 20 | -DCMAKE_CXX_STANDARD:STRING=17 21 | # Required for windows build with msvc. Also could be used on other platforms 22 | -DBOOST_ROOT:PATH="${BOOST_ROOT}" 23 | ) 24 | -------------------------------------------------------------------------------- /Resources/Orthanc/CMake/AutoGeneratedCode.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | set(EMBED_RESOURCES_PYTHON "${CMAKE_CURRENT_LIST_DIR}/../EmbedResources.py" 4 | CACHE INTERNAL "Path to the EmbedResources.py script from Orthanc") 5 | set(AUTOGENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/AUTOGENERATED") 6 | set(AUTOGENERATED_SOURCES) 7 | 8 | file(MAKE_DIRECTORY ${AUTOGENERATED_DIR}) 9 | include_directories(${AUTOGENERATED_DIR}) 10 | 11 | macro(EmbedResources) 12 | # Convert a semicolon separated list to a whitespace separated string 13 | set(SCRIPT_OPTIONS) 14 | set(SCRIPT_ARGUMENTS) 15 | set(DEPENDENCIES) 16 | set(IS_PATH_NAME false) 17 | 18 | set(TARGET_BASE "${AUTOGENERATED_DIR}/EmbeddedResources") 19 | 20 | # Loop over the arguments of the function 21 | foreach(arg ${ARGN}) 22 | # Extract the first character of the argument 23 | string(SUBSTRING "${arg}" 0 1 FIRST_CHAR) 24 | if (${FIRST_CHAR} STREQUAL "-") 25 | # If the argument starts with a dash "-", this is an option to 26 | # EmbedResources.py 27 | if (${arg} MATCHES "--target=.*") 28 | # Does the argument starts with "--target="? 29 | string(SUBSTRING "${arg}" 9 -1 TARGET) # 9 is the length of "--target=" 30 | set(TARGET_BASE "${AUTOGENERATED_DIR}/${TARGET}") 31 | else() 32 | list(APPEND SCRIPT_OPTIONS ${arg}) 33 | endif() 34 | else() 35 | if (${IS_PATH_NAME}) 36 | list(APPEND SCRIPT_ARGUMENTS "${arg}") 37 | list(APPEND DEPENDENCIES "${arg}") 38 | set(IS_PATH_NAME false) 39 | else() 40 | list(APPEND SCRIPT_ARGUMENTS "${arg}") 41 | set(IS_PATH_NAME true) 42 | endif() 43 | endif() 44 | endforeach() 45 | 46 | add_custom_command( 47 | OUTPUT 48 | "${TARGET_BASE}.h" 49 | "${TARGET_BASE}.cpp" 50 | COMMAND ${PYTHON_EXECUTABLE} ${EMBED_RESOURCES_PYTHON} 51 | ${SCRIPT_OPTIONS} "${TARGET_BASE}" ${SCRIPT_ARGUMENTS} 52 | DEPENDS 53 | ${EMBED_RESOURCES_PYTHON} 54 | ${DEPENDENCIES} 55 | ) 56 | 57 | list(APPEND AUTOGENERATED_SOURCES 58 | "${TARGET_BASE}.cpp" 59 | ) 60 | endmacro() 61 | -------------------------------------------------------------------------------- /Resources/Orthanc/CMake/Compiler.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | # This file sets all the compiler-related flags 4 | 5 | 6 | # Save the current compiler flags to the cache every time cmake configures the project 7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "compiler flags" FORCE) 8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE STRING "compiler flags" FORCE) 9 | 10 | 11 | include(CheckLibraryExists) 12 | 13 | if ((CMAKE_CROSSCOMPILING AND NOT 14 | "${CMAKE_SYSTEM_VERSION}" STREQUAL "CrossToolNg") OR 15 | "${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase") 16 | # Cross-compilation necessarily implies standalone and static build 17 | SET(STATIC_BUILD ON) 18 | SET(STANDALONE_BUILD ON) 19 | endif() 20 | 21 | 22 | if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase") 23 | # Cache the environment variables "LSB_CC" and "LSB_CXX" for further 24 | # use by "ExternalProject" in CMake 25 | SET(CMAKE_LSB_CC $ENV{LSB_CC} CACHE STRING "") 26 | SET(CMAKE_LSB_CXX $ENV{LSB_CXX} CACHE STRING "") 27 | endif() 28 | 29 | 30 | if (CMAKE_COMPILER_IS_GNUCXX) 31 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-long-long") 32 | 33 | # --std=c99 makes libcurl not to compile 34 | # -pedantic gives a lot of warnings on OpenSSL 35 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -Wno-variadic-macros") 36 | 37 | if (CMAKE_CROSSCOMPILING) 38 | # http://stackoverflow.com/a/3543845/881731 39 | set(CMAKE_RC_COMPILE_OBJECT " -O coff -I ") 40 | endif() 41 | 42 | elseif (MSVC) 43 | # Use static runtime under Visual Studio 44 | # http://www.cmake.org/Wiki/CMake_FAQ#Dynamic_Replace 45 | # http://stackoverflow.com/a/6510446 46 | foreach(flag_var 47 | CMAKE_C_FLAGS_DEBUG 48 | CMAKE_CXX_FLAGS_DEBUG 49 | CMAKE_C_FLAGS_RELEASE 50 | CMAKE_CXX_FLAGS_RELEASE 51 | CMAKE_C_FLAGS_MINSIZEREL 52 | CMAKE_CXX_FLAGS_MINSIZEREL 53 | CMAKE_C_FLAGS_RELWITHDEBINFO 54 | CMAKE_CXX_FLAGS_RELWITHDEBINFO) 55 | string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") 56 | string(REGEX REPLACE "/MDd" "/MTd" ${flag_var} "${${flag_var}}") 57 | endforeach(flag_var) 58 | 59 | # Add /Zm256 compiler option to Visual Studio to fix PCH errors 60 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zm256") 61 | 62 | # New in Orthanc 1.5.5 63 | if (MSVC_MULTIPLE_PROCESSES) 64 | # "If you omit the processMax argument in the /MP option, the 65 | # compiler obtains the number of effective processors from the 66 | # operating system, and then creates one process per effective 67 | # processor" 68 | # https://blog.kitware.com/cmake-building-with-all-your-cores/ 69 | # https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes 70 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") 71 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") 72 | endif() 73 | 74 | add_definitions( 75 | -D_CRT_SECURE_NO_WARNINGS=1 76 | -D_CRT_SECURE_NO_DEPRECATE=1 77 | ) 78 | 79 | if (MSVC_VERSION LESS 1600) 80 | # Starting with Visual Studio >= 2010 (i.e. macro _MSC_VER >= 81 | # 1600), Microsoft ships a standard-compliant 82 | # header. For earlier versions of Visual Studio, give access to a 83 | # compatibility header. 84 | # http://stackoverflow.com/a/70630/881731 85 | # https://en.wikibooks.org/wiki/C_Programming/C_Reference/stdint.h#External_links 86 | include_directories(${CMAKE_CURRENT_LIST_DIR}/../../Resources/ThirdParty/VisualStudio) 87 | endif() 88 | 89 | link_libraries(netapi32) 90 | endif() 91 | 92 | 93 | if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR 94 | ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 95 | # In FreeBSD/OpenBSD, the "/usr/local/" folder contains the ports and need to be imported 96 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/local/include") 97 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/local/include") 98 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/local/lib") 99 | SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -L/usr/local/lib") 100 | endif() 101 | 102 | 103 | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR 104 | ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR 105 | ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR 106 | ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 107 | 108 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND 109 | NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") 110 | # The "--no-undefined" linker flag makes the shared libraries 111 | # (plugins ModalityWorklists and ServeFolders) fail to compile on 112 | # OpenBSD, and make the PostgreSQL plugin complain about missing 113 | # "environ" global variable in FreeBSD 114 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-undefined") 115 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") 116 | endif() 117 | 118 | # Remove the "-rdynamic" option 119 | # http://www.mail-archive.com/cmake@cmake.org/msg08837.html 120 | set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") 121 | link_libraries(pthread) 122 | 123 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 124 | link_libraries(rt) 125 | endif() 126 | 127 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND 128 | NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 129 | link_libraries(dl) 130 | endif() 131 | 132 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND 133 | NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 134 | # The "--as-needed" linker flag is not available on FreeBSD and OpenBSD 135 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") 136 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed") 137 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed") 138 | endif() 139 | 140 | if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND 141 | NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 142 | # FreeBSD/OpenBSD have just one single interface for file 143 | # handling, which is 64bit clean, so there is no need to define macro 144 | # for LFS (Large File Support). 145 | # https://ohse.de/uwe/articles/lfs.html 146 | add_definitions( 147 | -D_LARGEFILE64_SOURCE=1 148 | -D_FILE_OFFSET_BITS=64 149 | ) 150 | endif() 151 | 152 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") 153 | if (MSVC) 154 | message("MSVC compiler version = " ${MSVC_VERSION} "\n") 155 | # Starting Visual Studio 2013 (version 1800), it is not possible 156 | # to target Windows XP anymore 157 | if (MSVC_VERSION LESS 1800) 158 | add_definitions( 159 | -DWINVER=0x0501 160 | -D_WIN32_WINNT=0x0501 161 | ) 162 | endif() 163 | else() 164 | add_definitions( 165 | -DWINVER=0x0501 166 | -D_WIN32_WINNT=0x0501 167 | ) 168 | endif() 169 | 170 | add_definitions( 171 | -D_CRT_SECURE_NO_WARNINGS=1 172 | ) 173 | link_libraries(rpcrt4 ws2_32 iphlpapi) # "iphlpapi" is for "SystemToolbox::GetMacAddresses()" 174 | 175 | if (CMAKE_COMPILER_IS_GNUCXX) 176 | # Some additional C/C++ compiler flags for MinGW 177 | SET(MINGW_NO_WARNINGS "-Wno-unused-function -Wno-unused-variable") 178 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MINGW_NO_WARNINGS} -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") 179 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MINGW_NO_WARNINGS}") 180 | 181 | if (DYNAMIC_MINGW_STDLIB) 182 | else() 183 | # This is a patch for MinGW64 184 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++") 185 | SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++") 186 | endif() 187 | 188 | CHECK_LIBRARY_EXISTS(winpthread pthread_create "" HAVE_WIN_PTHREAD) 189 | if (HAVE_WIN_PTHREAD) 190 | if (DYNAMIC_MINGW_STDLIB) 191 | else() 192 | # This line is necessary to compile with recent versions of MinGW, 193 | # otherwise "libwinpthread-1.dll" is not statically linked. 194 | SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic") 195 | endif() 196 | add_definitions(-DHAVE_WIN_PTHREAD=1) 197 | else() 198 | add_definitions(-DHAVE_WIN_PTHREAD=0) 199 | endif() 200 | endif() 201 | 202 | elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") 203 | add_definitions( 204 | -D_XOPEN_SOURCE=1 205 | ) 206 | link_libraries(iconv) 207 | 208 | elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten") 209 | message("Building using Emscripten (for WebAssembly or asm.js targets)") 210 | include(${CMAKE_CURRENT_LIST_DIR}/EmscriptenParameters.cmake) 211 | 212 | elseif (CMAKE_SYSTEM_NAME STREQUAL "Android") 213 | 214 | else() 215 | message("Unknown target platform: ${CMAKE_SYSTEM_NAME}") 216 | message(FATAL_ERROR "Support your platform here") 217 | endif() 218 | 219 | 220 | if (DEFINED ENABLE_PROFILING AND ENABLE_PROFILING) 221 | if (CMAKE_COMPILER_IS_GNUCXX) 222 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") 223 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg") 224 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg") 225 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pg") 226 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg") 227 | else() 228 | message(FATAL_ERROR "Don't know how to enable profiling on your configuration") 229 | endif() 230 | endif() 231 | 232 | 233 | if (CMAKE_COMPILER_IS_GNUCXX) 234 | # "When creating a static library using binutils (ar) and there 235 | # exist a duplicate object name (e.g. a/Foo.cpp.o, b/Foo.cpp.o), the 236 | # resulting static library can end up having only one of the 237 | # duplicate objects. [...] This bug only happens if there are many 238 | # objects." The trick consists in replacing the "r" argument 239 | # ("replace") provided to "ar" (as used in CMake < 3.1) by the "q" 240 | # argument ("quick append"). This is because of the fact that CMake 241 | # will invoke "ar" several times with several batches of ".o" 242 | # objects, and using "r" would overwrite symbols defined in 243 | # preceding batches. https://cmake.org/Bug/view.php?id=14874 244 | set(CMAKE_CXX_ARCHIVE_APPEND " q ") 245 | endif() 246 | -------------------------------------------------------------------------------- /Resources/Orthanc/CMake/DownloadPackage.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | macro(GetUrlFilename TargetVariable Url) 4 | string(REGEX REPLACE "^.*/" "" ${TargetVariable} "${Url}") 5 | endmacro() 6 | 7 | 8 | macro(GetUrlExtension TargetVariable Url) 9 | #string(REGEX REPLACE "^.*/[^.]*\\." "" TMP "${Url}") 10 | string(REGEX REPLACE "^.*\\." "" TMP "${Url}") 11 | string(TOLOWER "${TMP}" "${TargetVariable}") 12 | endmacro() 13 | 14 | 15 | 16 | ## 17 | ## Setup the patch command-line tool 18 | ## 19 | 20 | if (NOT ORTHANC_DISABLE_PATCH) 21 | if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") 22 | set(PATCH_EXECUTABLE ${CMAKE_CURRENT_LIST_DIR}/../ThirdParty/patch/patch.exe) 23 | if (NOT EXISTS ${PATCH_EXECUTABLE}) 24 | message(FATAL_ERROR "Unable to find the patch.exe tool that is shipped with Orthanc") 25 | endif() 26 | 27 | else () 28 | find_program(PATCH_EXECUTABLE patch) 29 | if (${PATCH_EXECUTABLE} MATCHES "PATCH_EXECUTABLE-NOTFOUND") 30 | message(FATAL_ERROR "Please install the 'patch' standard command-line tool") 31 | endif() 32 | endif() 33 | endif() 34 | 35 | 36 | 37 | ## 38 | ## Check the existence of the required decompression tools 39 | ## 40 | 41 | if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") 42 | find_program(ZIP_EXECUTABLE 7z 43 | PATHS 44 | "$ENV{ProgramFiles}/7-Zip" 45 | "$ENV{ProgramW6432}/7-Zip" 46 | ) 47 | 48 | if (${ZIP_EXECUTABLE} MATCHES "ZIP_EXECUTABLE-NOTFOUND") 49 | message(FATAL_ERROR "Please install the '7-zip' software (http://www.7-zip.org/)") 50 | endif() 51 | 52 | else() 53 | find_program(UNZIP_EXECUTABLE unzip) 54 | if (${UNZIP_EXECUTABLE} MATCHES "UNZIP_EXECUTABLE-NOTFOUND") 55 | message(FATAL_ERROR "Please install the 'unzip' package") 56 | endif() 57 | 58 | find_program(TAR_EXECUTABLE tar) 59 | if (${TAR_EXECUTABLE} MATCHES "TAR_EXECUTABLE-NOTFOUND") 60 | message(FATAL_ERROR "Please install the 'tar' package") 61 | endif() 62 | 63 | find_program(GUNZIP_EXECUTABLE gunzip) 64 | if (${GUNZIP_EXECUTABLE} MATCHES "GUNZIP_EXECUTABLE-NOTFOUND") 65 | message(FATAL_ERROR "Please install the 'gzip' package") 66 | endif() 67 | endif() 68 | 69 | 70 | macro(DownloadFile MD5 Url) 71 | GetUrlFilename(TMP_FILENAME "${Url}") 72 | 73 | set(TMP_PATH "${CMAKE_SOURCE_DIR}/ThirdPartyDownloads/${TMP_FILENAME}") 74 | if (NOT EXISTS "${TMP_PATH}") 75 | message("Downloading ${Url}") 76 | 77 | # This fixes issue 6: "I think cmake shouldn't download the 78 | # packages which are not in the system, it should stop and let 79 | # user know." 80 | # https://code.google.com/p/orthanc/issues/detail?id=6 81 | if (NOT STATIC_BUILD AND NOT ALLOW_DOWNLOADS) 82 | message(FATAL_ERROR "CMake is not allowed to download from Internet. Please set the ALLOW_DOWNLOADS option to ON") 83 | endif() 84 | 85 | if ("${MD5}" STREQUAL "no-check") 86 | message(WARNING "Not checking the MD5 of: ${Url}") 87 | file(DOWNLOAD "${Url}" "${TMP_PATH}" 88 | SHOW_PROGRESS TIMEOUT 300 INACTIVITY_TIMEOUT 60 89 | STATUS Failure) 90 | else() 91 | file(DOWNLOAD "${Url}" "${TMP_PATH}" 92 | SHOW_PROGRESS TIMEOUT 300 INACTIVITY_TIMEOUT 60 93 | EXPECTED_MD5 "${MD5}" STATUS Failure) 94 | endif() 95 | 96 | list(GET Failure 0 Status) 97 | if (NOT Status EQUAL 0) 98 | message(FATAL_ERROR "Cannot download file: ${Url}") 99 | endif() 100 | 101 | else() 102 | message("Using local copy of ${Url}") 103 | 104 | if ("${MD5}" STREQUAL "no-check") 105 | message(WARNING "Not checking the MD5 of: ${Url}") 106 | else() 107 | file(MD5 ${TMP_PATH} ActualMD5) 108 | if (NOT "${ActualMD5}" STREQUAL "${MD5}") 109 | message(FATAL_ERROR "The MD5 hash of a previously download file is invalid: ${TMP_PATH}") 110 | endif() 111 | endif() 112 | endif() 113 | endmacro() 114 | 115 | 116 | macro(DownloadPackage MD5 Url TargetDirectory) 117 | if (NOT IS_DIRECTORY "${TargetDirectory}") 118 | DownloadFile("${MD5}" "${Url}") 119 | 120 | GetUrlExtension(TMP_EXTENSION "${Url}") 121 | #message(${TMP_EXTENSION}) 122 | message("Uncompressing ${TMP_FILENAME}") 123 | 124 | if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") 125 | # How to silently extract files using 7-zip 126 | # http://superuser.com/questions/331148/7zip-command-line-extract-silently-quietly 127 | 128 | if (("${TMP_EXTENSION}" STREQUAL "gz") OR 129 | ("${TMP_EXTENSION}" STREQUAL "tgz") OR 130 | ("${TMP_EXTENSION}" STREQUAL "xz")) 131 | execute_process( 132 | COMMAND ${ZIP_EXECUTABLE} e -y ${TMP_PATH} 133 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 134 | RESULT_VARIABLE Failure 135 | OUTPUT_QUIET 136 | ) 137 | 138 | if (Failure) 139 | message(FATAL_ERROR "Error while running the uncompression tool") 140 | endif() 141 | 142 | if ("${TMP_EXTENSION}" STREQUAL "tgz") 143 | string(REGEX REPLACE ".tgz$" ".tar" TMP_FILENAME2 "${TMP_FILENAME}") 144 | elseif ("${TMP_EXTENSION}" STREQUAL "gz") 145 | string(REGEX REPLACE ".gz$" "" TMP_FILENAME2 "${TMP_FILENAME}") 146 | elseif ("${TMP_EXTENSION}" STREQUAL "xz") 147 | string(REGEX REPLACE ".xz" "" TMP_FILENAME2 "${TMP_FILENAME}") 148 | endif() 149 | 150 | execute_process( 151 | COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_FILENAME2} 152 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 153 | RESULT_VARIABLE Failure 154 | OUTPUT_QUIET 155 | ) 156 | elseif ("${TMP_EXTENSION}" STREQUAL "zip") 157 | execute_process( 158 | COMMAND ${ZIP_EXECUTABLE} x -y ${TMP_PATH} 159 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 160 | RESULT_VARIABLE Failure 161 | OUTPUT_QUIET 162 | ) 163 | else() 164 | message(FATAL_ERROR "Unsupported package extension: ${TMP_EXTENSION}") 165 | endif() 166 | 167 | else() 168 | if ("${TMP_EXTENSION}" STREQUAL "zip") 169 | execute_process( 170 | COMMAND sh -c "${UNZIP_EXECUTABLE} -q ${TMP_PATH}" 171 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 172 | RESULT_VARIABLE Failure 173 | ) 174 | elseif (("${TMP_EXTENSION}" STREQUAL "gz") OR ("${TMP_EXTENSION}" STREQUAL "tgz")) 175 | #message("tar xvfz ${TMP_PATH}") 176 | execute_process( 177 | COMMAND sh -c "${TAR_EXECUTABLE} xfz ${TMP_PATH}" 178 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 179 | RESULT_VARIABLE Failure 180 | ) 181 | elseif ("${TMP_EXTENSION}" STREQUAL "bz2") 182 | execute_process( 183 | COMMAND sh -c "${TAR_EXECUTABLE} xfj ${TMP_PATH}" 184 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 185 | RESULT_VARIABLE Failure 186 | ) 187 | elseif ("${TMP_EXTENSION}" STREQUAL "xz") 188 | execute_process( 189 | COMMAND sh -c "${TAR_EXECUTABLE} xf ${TMP_PATH}" 190 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 191 | RESULT_VARIABLE Failure 192 | ) 193 | else() 194 | message(FATAL_ERROR "Unsupported package extension: ${TMP_EXTENSION}") 195 | endif() 196 | endif() 197 | 198 | if (Failure) 199 | message(FATAL_ERROR "Error while running the uncompression tool") 200 | endif() 201 | 202 | if (NOT IS_DIRECTORY "${TargetDirectory}") 203 | message(FATAL_ERROR "The package was not uncompressed at the proper location. Check the CMake instructions.") 204 | endif() 205 | endif() 206 | endmacro() 207 | 208 | 209 | 210 | macro(DownloadCompressedFile MD5 Url TargetFile) 211 | if (NOT EXISTS "${TargetFile}") 212 | DownloadFile("${MD5}" "${Url}") 213 | 214 | GetUrlExtension(TMP_EXTENSION "${Url}") 215 | #message(${TMP_EXTENSION}) 216 | message("Uncompressing ${TMP_FILENAME}") 217 | 218 | if ("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") 219 | # How to silently extract files using 7-zip 220 | # http://superuser.com/questions/331148/7zip-command-line-extract-silently-quietly 221 | 222 | if ("${TMP_EXTENSION}" STREQUAL "gz") 223 | execute_process( 224 | # "-so" writes uncompressed file to stdout 225 | COMMAND ${ZIP_EXECUTABLE} e -so -y ${TMP_PATH} 226 | OUTPUT_FILE "${TargetFile}" 227 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 228 | RESULT_VARIABLE Failure 229 | OUTPUT_QUIET 230 | ) 231 | 232 | if (Failure) 233 | message(FATAL_ERROR "Error while running the uncompression tool") 234 | endif() 235 | 236 | else() 237 | message(FATAL_ERROR "Unsupported file extension: ${TMP_EXTENSION}") 238 | endif() 239 | 240 | else() 241 | if ("${TMP_EXTENSION}" STREQUAL "gz") 242 | execute_process( 243 | COMMAND sh -c "${GUNZIP_EXECUTABLE} -c ${TMP_PATH}" 244 | OUTPUT_FILE "${TargetFile}" 245 | RESULT_VARIABLE Failure 246 | ) 247 | else() 248 | message(FATAL_ERROR "Unsupported file extension: ${TMP_EXTENSION}") 249 | endif() 250 | endif() 251 | 252 | if (Failure) 253 | message(FATAL_ERROR "Error while running the uncompression tool") 254 | endif() 255 | 256 | if (NOT EXISTS "${TargetFile}") 257 | message(FATAL_ERROR "The file was not uncompressed at the proper location. Check the CMake instructions.") 258 | endif() 259 | endif() 260 | endmacro() 261 | -------------------------------------------------------------------------------- /Resources/Orthanc/CMake/GoogleTestConfiguration.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | if (USE_GOOGLE_TEST_DEBIAN_PACKAGE) 4 | find_path(GOOGLE_TEST_DEBIAN_SOURCES_DIR 5 | NAMES src/gtest-all.cc 6 | PATHS 7 | ${CROSSTOOL_NG_IMAGE}/usr/src/gtest 8 | ${CROSSTOOL_NG_IMAGE}/usr/src/googletest/googletest 9 | PATH_SUFFIXES src 10 | ) 11 | 12 | find_path(GOOGLE_TEST_DEBIAN_INCLUDE_DIR 13 | NAMES gtest.h 14 | PATHS 15 | ${CROSSTOOL_NG_IMAGE}/usr/include/gtest 16 | ) 17 | 18 | message("Path to the Debian Google Test sources: ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}") 19 | message("Path to the Debian Google Test includes: ${GOOGLE_TEST_DEBIAN_INCLUDE_DIR}") 20 | 21 | set(GOOGLE_TEST_SOURCES 22 | ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}/src/gtest-all.cc 23 | ) 24 | 25 | include_directories(${GOOGLE_TEST_DEBIAN_SOURCES_DIR}) 26 | 27 | if (NOT EXISTS ${GOOGLE_TEST_SOURCES} OR 28 | NOT EXISTS ${GOOGLE_TEST_DEBIAN_INCLUDE_DIR}/gtest.h) 29 | message(FATAL_ERROR "Please install the libgtest-dev package") 30 | endif() 31 | 32 | elseif (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_TEST) 33 | set(GOOGLE_TEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/googletest-release-1.8.1) 34 | set(GOOGLE_TEST_URL "https://orthanc.uclouvain.be/downloads/third-party-downloads/gtest-1.8.1.tar.gz") 35 | set(GOOGLE_TEST_MD5 "2e6fbeb6a91310a16efe181886c59596") 36 | 37 | DownloadPackage(${GOOGLE_TEST_MD5} ${GOOGLE_TEST_URL} "${GOOGLE_TEST_SOURCES_DIR}") 38 | 39 | include_directories( 40 | ${GOOGLE_TEST_SOURCES_DIR}/googletest 41 | ${GOOGLE_TEST_SOURCES_DIR}/googletest/include 42 | ${GOOGLE_TEST_SOURCES_DIR} 43 | ) 44 | 45 | set(GOOGLE_TEST_SOURCES 46 | ${GOOGLE_TEST_SOURCES_DIR}/googletest/src/gtest-all.cc 47 | ) 48 | 49 | # https://code.google.com/p/googletest/issues/detail?id=412 50 | if (MSVC) # VS2012 does not support tuples correctly yet 51 | add_definitions(/D _VARIADIC_MAX=10) 52 | endif() 53 | 54 | if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase") 55 | add_definitions(-DGTEST_HAS_CLONE=0) 56 | endif() 57 | 58 | source_group(ThirdParty\\GoogleTest REGULAR_EXPRESSION ${GOOGLE_TEST_SOURCES_DIR}/.*) 59 | 60 | else() 61 | include(FindGTest) 62 | if (NOT GTEST_FOUND) 63 | message(FATAL_ERROR "Unable to find GoogleTest") 64 | endif() 65 | 66 | include_directories(${GTEST_INCLUDE_DIRS}) 67 | 68 | # The variable GTEST_LIBRARIES contains the shared library of 69 | # Google Test, create an alias for more uniformity 70 | set(GOOGLE_TEST_LIBRARIES ${GTEST_LIBRARIES}) 71 | endif() 72 | -------------------------------------------------------------------------------- /Resources/Orthanc/Databases/DatabaseConstraint.cpp: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | 4 | #if !defined(ORTHANC_BUILDING_SERVER_LIBRARY) 5 | # error Macro ORTHANC_BUILDING_SERVER_LIBRARY must be defined 6 | #endif 7 | 8 | #if ORTHANC_BUILDING_SERVER_LIBRARY == 1 9 | # include "../PrecompiledHeadersServer.h" 10 | #endif 11 | 12 | #include "DatabaseConstraint.h" 13 | 14 | #if ORTHANC_BUILDING_SERVER_LIBRARY == 1 15 | # include "../../../OrthancFramework/Sources/OrthancException.h" 16 | #else 17 | # include 18 | #endif 19 | 20 | #include 21 | 22 | 23 | namespace Orthanc 24 | { 25 | namespace Plugins 26 | { 27 | #if ORTHANC_ENABLE_PLUGINS == 1 28 | OrthancPluginResourceType Convert(ResourceType type) 29 | { 30 | switch (type) 31 | { 32 | case ResourceType_Patient: 33 | return OrthancPluginResourceType_Patient; 34 | 35 | case ResourceType_Study: 36 | return OrthancPluginResourceType_Study; 37 | 38 | case ResourceType_Series: 39 | return OrthancPluginResourceType_Series; 40 | 41 | case ResourceType_Instance: 42 | return OrthancPluginResourceType_Instance; 43 | 44 | default: 45 | throw OrthancException(ErrorCode_ParameterOutOfRange); 46 | } 47 | } 48 | #endif 49 | 50 | 51 | #if ORTHANC_ENABLE_PLUGINS == 1 52 | ResourceType Convert(OrthancPluginResourceType type) 53 | { 54 | switch (type) 55 | { 56 | case OrthancPluginResourceType_Patient: 57 | return ResourceType_Patient; 58 | 59 | case OrthancPluginResourceType_Study: 60 | return ResourceType_Study; 61 | 62 | case OrthancPluginResourceType_Series: 63 | return ResourceType_Series; 64 | 65 | case OrthancPluginResourceType_Instance: 66 | return ResourceType_Instance; 67 | 68 | default: 69 | throw OrthancException(ErrorCode_ParameterOutOfRange); 70 | } 71 | } 72 | #endif 73 | 74 | 75 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 76 | OrthancPluginConstraintType Convert(ConstraintType constraint) 77 | { 78 | switch (constraint) 79 | { 80 | case ConstraintType_Equal: 81 | return OrthancPluginConstraintType_Equal; 82 | 83 | case ConstraintType_GreaterOrEqual: 84 | return OrthancPluginConstraintType_GreaterOrEqual; 85 | 86 | case ConstraintType_SmallerOrEqual: 87 | return OrthancPluginConstraintType_SmallerOrEqual; 88 | 89 | case ConstraintType_Wildcard: 90 | return OrthancPluginConstraintType_Wildcard; 91 | 92 | case ConstraintType_List: 93 | return OrthancPluginConstraintType_List; 94 | 95 | default: 96 | throw OrthancException(ErrorCode_ParameterOutOfRange); 97 | } 98 | } 99 | #endif 100 | 101 | 102 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 103 | ConstraintType Convert(OrthancPluginConstraintType constraint) 104 | { 105 | switch (constraint) 106 | { 107 | case OrthancPluginConstraintType_Equal: 108 | return ConstraintType_Equal; 109 | 110 | case OrthancPluginConstraintType_GreaterOrEqual: 111 | return ConstraintType_GreaterOrEqual; 112 | 113 | case OrthancPluginConstraintType_SmallerOrEqual: 114 | return ConstraintType_SmallerOrEqual; 115 | 116 | case OrthancPluginConstraintType_Wildcard: 117 | return ConstraintType_Wildcard; 118 | 119 | case OrthancPluginConstraintType_List: 120 | return ConstraintType_List; 121 | 122 | default: 123 | throw OrthancException(ErrorCode_ParameterOutOfRange); 124 | } 125 | } 126 | #endif 127 | } 128 | 129 | DatabaseConstraint::DatabaseConstraint(ResourceType level, 130 | const DicomTag& tag, 131 | bool isIdentifier, 132 | ConstraintType type, 133 | const std::vector& values, 134 | bool caseSensitive, 135 | bool mandatory) : 136 | level_(level), 137 | tag_(tag), 138 | isIdentifier_(isIdentifier), 139 | constraintType_(type), 140 | values_(values), 141 | caseSensitive_(caseSensitive), 142 | mandatory_(mandatory) 143 | { 144 | if (type != ConstraintType_List && 145 | values_.size() != 1) 146 | { 147 | throw OrthancException(ErrorCode_ParameterOutOfRange); 148 | } 149 | } 150 | 151 | 152 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 153 | DatabaseConstraint::DatabaseConstraint(const OrthancPluginDatabaseConstraint& constraint) : 154 | level_(Plugins::Convert(constraint.level)), 155 | tag_(constraint.tagGroup, constraint.tagElement), 156 | isIdentifier_(constraint.isIdentifierTag), 157 | constraintType_(Plugins::Convert(constraint.type)), 158 | caseSensitive_(constraint.isCaseSensitive), 159 | mandatory_(constraint.isMandatory) 160 | { 161 | if (constraintType_ != ConstraintType_List && 162 | constraint.valuesCount != 1) 163 | { 164 | throw OrthancException(ErrorCode_ParameterOutOfRange); 165 | } 166 | 167 | values_.resize(constraint.valuesCount); 168 | 169 | for (uint32_t i = 0; i < constraint.valuesCount; i++) 170 | { 171 | assert(constraint.values[i] != NULL); 172 | values_[i].assign(constraint.values[i]); 173 | } 174 | } 175 | #endif 176 | 177 | 178 | const std::string& DatabaseConstraint::GetValue(size_t index) const 179 | { 180 | if (index >= values_.size()) 181 | { 182 | throw OrthancException(ErrorCode_ParameterOutOfRange); 183 | } 184 | else 185 | { 186 | return values_[index]; 187 | } 188 | } 189 | 190 | 191 | const std::string& DatabaseConstraint::GetSingleValue() const 192 | { 193 | if (values_.size() != 1) 194 | { 195 | throw OrthancException(ErrorCode_BadSequenceOfCalls); 196 | } 197 | else 198 | { 199 | return values_[0]; 200 | } 201 | } 202 | 203 | 204 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 205 | void DatabaseConstraint::EncodeForPlugins(OrthancPluginDatabaseConstraint& constraint, 206 | std::vector& tmpValues) const 207 | { 208 | memset(&constraint, 0, sizeof(constraint)); 209 | 210 | tmpValues.resize(values_.size()); 211 | 212 | for (size_t i = 0; i < values_.size(); i++) 213 | { 214 | tmpValues[i] = values_[i].c_str(); 215 | } 216 | 217 | constraint.level = Plugins::Convert(level_); 218 | constraint.tagGroup = tag_.GetGroup(); 219 | constraint.tagElement = tag_.GetElement(); 220 | constraint.isIdentifierTag = isIdentifier_; 221 | constraint.isCaseSensitive = caseSensitive_; 222 | constraint.isMandatory = mandatory_; 223 | constraint.type = Plugins::Convert(constraintType_); 224 | constraint.valuesCount = values_.size(); 225 | constraint.values = (tmpValues.empty() ? NULL : &tmpValues[0]); 226 | } 227 | #endif 228 | } 229 | -------------------------------------------------------------------------------- /Resources/Orthanc/Databases/DatabaseConstraint.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | 4 | #pragma once 5 | 6 | #if !defined(ORTHANC_BUILDING_SERVER_LIBRARY) 7 | # error Macro ORTHANC_BUILDING_SERVER_LIBRARY must be defined 8 | #endif 9 | 10 | #if ORTHANC_BUILDING_SERVER_LIBRARY == 1 11 | # include "../../../OrthancFramework/Sources/DicomFormat/DicomMap.h" 12 | #else 13 | // This is for the "orthanc-databases" project to reuse this file 14 | # include 15 | #endif 16 | 17 | #define ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT 0 18 | 19 | #if ORTHANC_ENABLE_PLUGINS == 1 20 | # include 21 | # if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in 1.3.1 22 | # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 2) 23 | # undef ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT 24 | # define ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT 1 25 | # endif 26 | # endif 27 | #endif 28 | 29 | namespace Orthanc 30 | { 31 | enum ConstraintType 32 | { 33 | ConstraintType_Equal, 34 | ConstraintType_SmallerOrEqual, 35 | ConstraintType_GreaterOrEqual, 36 | ConstraintType_Wildcard, 37 | ConstraintType_List 38 | }; 39 | 40 | namespace Plugins 41 | { 42 | #if ORTHANC_ENABLE_PLUGINS == 1 43 | OrthancPluginResourceType Convert(ResourceType type); 44 | #endif 45 | 46 | #if ORTHANC_ENABLE_PLUGINS == 1 47 | ResourceType Convert(OrthancPluginResourceType type); 48 | #endif 49 | 50 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 51 | OrthancPluginConstraintType Convert(ConstraintType constraint); 52 | #endif 53 | 54 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 55 | ConstraintType Convert(OrthancPluginConstraintType constraint); 56 | #endif 57 | } 58 | 59 | 60 | // This class is also used by the "orthanc-databases" project 61 | class DatabaseConstraint 62 | { 63 | private: 64 | ResourceType level_; 65 | DicomTag tag_; 66 | bool isIdentifier_; 67 | ConstraintType constraintType_; 68 | std::vector values_; 69 | bool caseSensitive_; 70 | bool mandatory_; 71 | 72 | public: 73 | DatabaseConstraint(ResourceType level, 74 | const DicomTag& tag, 75 | bool isIdentifier, 76 | ConstraintType type, 77 | const std::vector& values, 78 | bool caseSensitive, 79 | bool mandatory); 80 | 81 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 82 | explicit DatabaseConstraint(const OrthancPluginDatabaseConstraint& constraint); 83 | #endif 84 | 85 | ResourceType GetLevel() const 86 | { 87 | return level_; 88 | } 89 | 90 | const DicomTag& GetTag() const 91 | { 92 | return tag_; 93 | } 94 | 95 | bool IsIdentifier() const 96 | { 97 | return isIdentifier_; 98 | } 99 | 100 | ConstraintType GetConstraintType() const 101 | { 102 | return constraintType_; 103 | } 104 | 105 | size_t GetValuesCount() const 106 | { 107 | return values_.size(); 108 | } 109 | 110 | const std::string& GetValue(size_t index) const; 111 | 112 | const std::string& GetSingleValue() const; 113 | 114 | bool IsCaseSensitive() const 115 | { 116 | return caseSensitive_; 117 | } 118 | 119 | bool IsMandatory() const 120 | { 121 | return mandatory_; 122 | } 123 | 124 | bool IsMatch(const DicomMap& dicom) const; 125 | 126 | #if ORTHANC_PLUGINS_HAS_DATABASE_CONSTRAINT == 1 127 | void EncodeForPlugins(OrthancPluginDatabaseConstraint& constraint, 128 | std::vector& tmpValues) const; 129 | #endif 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /Resources/Orthanc/EmbedResources.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 4 | 5 | import sys 6 | import os 7 | import os.path 8 | import pprint 9 | import re 10 | 11 | UPCASE_CHECK = True 12 | USE_SYSTEM_EXCEPTION = False 13 | EXCEPTION_CLASS = 'OrthancException' 14 | OUT_OF_RANGE_EXCEPTION = '::Orthanc::OrthancException(::Orthanc::ErrorCode_ParameterOutOfRange)' 15 | INEXISTENT_PATH_EXCEPTION = '::Orthanc::OrthancException(::Orthanc::ErrorCode_InexistentItem)' 16 | NAMESPACE = 'Orthanc.EmbeddedResources' 17 | FRAMEWORK_PATH = None 18 | 19 | ARGS = [] 20 | for i in range(len(sys.argv)): 21 | if not sys.argv[i].startswith('--'): 22 | ARGS.append(sys.argv[i]) 23 | elif sys.argv[i].lower() == '--no-upcase-check': 24 | UPCASE_CHECK = False 25 | elif sys.argv[i].lower() == '--system-exception': 26 | USE_SYSTEM_EXCEPTION = True 27 | EXCEPTION_CLASS = '::std::runtime_error' 28 | OUT_OF_RANGE_EXCEPTION = '%s("Parameter out of range")' % EXCEPTION_CLASS 29 | INEXISTENT_PATH_EXCEPTION = '%s("Unknown path in a directory resource")' % EXCEPTION_CLASS 30 | elif sys.argv[i].startswith('--namespace='): 31 | NAMESPACE = sys.argv[i][sys.argv[i].find('=') + 1 : ] 32 | elif sys.argv[i].startswith('--framework-path='): 33 | FRAMEWORK_PATH = sys.argv[i][sys.argv[i].find('=') + 1 : ] 34 | 35 | if len(ARGS) < 2 or len(ARGS) % 2 != 0: 36 | print ('Usage:') 37 | print ('python %s [--no-upcase-check] [--system-exception] [--namespace=] [ ]*' % sys.argv[0]) 38 | exit(-1) 39 | 40 | TARGET_BASE_FILENAME = ARGS[1] 41 | SOURCES = ARGS[2:] 42 | 43 | try: 44 | # Make sure the destination directory exists 45 | os.makedirs(os.path.normpath(os.path.join(TARGET_BASE_FILENAME, '..'))) 46 | except: 47 | pass 48 | 49 | 50 | ##################################################################### 51 | ## Read each resource file 52 | ##################################################################### 53 | 54 | def CheckNoUpcase(s): 55 | global UPCASE_CHECK 56 | if (UPCASE_CHECK and 57 | re.search('[A-Z]', s) != None): 58 | raise Exception("Path in a directory with an upcase letter: %s" % s) 59 | 60 | resources = {} 61 | 62 | counter = 0 63 | i = 0 64 | while i < len(SOURCES): 65 | resourceName = SOURCES[i].upper() 66 | pathName = SOURCES[i + 1] 67 | 68 | if not os.path.exists(pathName): 69 | raise Exception("Non existing path: %s" % pathName) 70 | 71 | if resourceName in resources: 72 | raise Exception("Twice the same resource: " + resourceName) 73 | 74 | if os.path.isdir(pathName): 75 | # The resource is a directory: Recursively explore its files 76 | content = {} 77 | for root, dirs, files in os.walk(pathName): 78 | dirs.sort() 79 | files.sort() 80 | base = os.path.relpath(root, pathName) 81 | 82 | # Fix issue #24 (Build fails on OSX when directory has .DS_Store files): 83 | # Ignore folders whose name starts with a dot (".") 84 | if base.find('/.') != -1: 85 | print('Ignoring folder: %s' % root) 86 | continue 87 | 88 | for f in files: 89 | if f.find('~') == -1: # Ignore Emacs backup files 90 | if base == '.': 91 | r = f 92 | else: 93 | r = os.path.join(base, f) 94 | 95 | CheckNoUpcase(r) 96 | r = '/' + r.replace('\\', '/') 97 | if r in content: 98 | raise Exception("Twice the same filename (check case): " + r) 99 | 100 | content[r] = { 101 | 'Filename' : os.path.join(root, f), 102 | 'Index' : counter 103 | } 104 | counter += 1 105 | 106 | resources[resourceName] = { 107 | 'Type' : 'Directory', 108 | 'Files' : content 109 | } 110 | 111 | elif os.path.isfile(pathName): 112 | resources[resourceName] = { 113 | 'Type' : 'File', 114 | 'Index' : counter, 115 | 'Filename' : pathName 116 | } 117 | counter += 1 118 | 119 | else: 120 | raise Exception("Not a regular file, nor a directory: " + pathName) 121 | 122 | i += 2 123 | 124 | #pprint.pprint(resources) 125 | 126 | 127 | ##################################################################### 128 | ## Write .h header 129 | ##################################################################### 130 | 131 | header = open(TARGET_BASE_FILENAME + '.h', 'w') 132 | 133 | header.write(""" 134 | #pragma once 135 | 136 | #include 137 | #include 138 | 139 | #if defined(_MSC_VER) 140 | # pragma warning(disable: 4065) // "Switch statement contains 'default' but no 'case' labels" 141 | #endif 142 | 143 | """) 144 | 145 | 146 | for ns in NAMESPACE.split('.'): 147 | header.write('namespace %s {\n' % ns) 148 | 149 | 150 | header.write(""" 151 | enum FileResourceId 152 | { 153 | """) 154 | 155 | isFirst = True 156 | for name in resources: 157 | if resources[name]['Type'] == 'File': 158 | if isFirst: 159 | isFirst = False 160 | else: 161 | header.write(',\n') 162 | header.write(' %s' % name) 163 | 164 | header.write(""" 165 | }; 166 | 167 | enum DirectoryResourceId 168 | { 169 | """) 170 | 171 | isFirst = True 172 | for name in resources: 173 | if resources[name]['Type'] == 'Directory': 174 | if isFirst: 175 | isFirst = False 176 | else: 177 | header.write(',\n') 178 | header.write(' %s' % name) 179 | 180 | header.write(""" 181 | }; 182 | 183 | const void* GetFileResourceBuffer(FileResourceId id); 184 | size_t GetFileResourceSize(FileResourceId id); 185 | void GetFileResource(std::string& result, FileResourceId id); 186 | 187 | const void* GetDirectoryResourceBuffer(DirectoryResourceId id, const char* path); 188 | size_t GetDirectoryResourceSize(DirectoryResourceId id, const char* path); 189 | void GetDirectoryResource(std::string& result, DirectoryResourceId id, const char* path); 190 | 191 | void ListResources(std::list& result, DirectoryResourceId id); 192 | 193 | """) 194 | 195 | 196 | for ns in NAMESPACE.split('.'): 197 | header.write('}\n') 198 | 199 | header.close() 200 | 201 | 202 | 203 | ##################################################################### 204 | ## Write the resource content in the .cpp source 205 | ##################################################################### 206 | 207 | PYTHON_MAJOR_VERSION = sys.version_info[0] 208 | 209 | def WriteResource(cpp, item): 210 | cpp.write(' static const uint8_t resource%dBuffer[] = {' % item['Index']) 211 | 212 | f = open(item['Filename'], "rb") 213 | content = f.read() 214 | f.close() 215 | 216 | # http://stackoverflow.com/a/1035360 217 | pos = 0 218 | buffer = [] # instead of appending a few bytes at a time to the cpp file, 219 | # we first append each chunk to a list, join it and write it 220 | # to the file. We've measured that it was 2-3 times faster in python3. 221 | # Note that speed is important since if generation is too slow, 222 | # cmake might try to compile the EmbeddedResources.cpp file while it is 223 | # still being generated ! 224 | for b in content: 225 | if PYTHON_MAJOR_VERSION == 2: 226 | c = ord(b[0]) 227 | else: 228 | c = b 229 | 230 | if pos > 0: 231 | buffer.append(",") 232 | 233 | if (pos % 16) == 0: 234 | buffer.append("\n") 235 | 236 | if c < 0: 237 | raise Exception("Internal error") 238 | 239 | buffer.append("0x%02x" % c) 240 | pos += 1 241 | 242 | cpp.write("".join(buffer)) 243 | # Zero-size array are disallowed, so we put one single void character in it. 244 | if pos == 0: 245 | cpp.write(' 0') 246 | 247 | cpp.write(' };\n') 248 | cpp.write(' static const size_t resource%dSize = %d;\n' % (item['Index'], pos)) 249 | 250 | 251 | cpp = open(TARGET_BASE_FILENAME + '.cpp', 'w') 252 | 253 | cpp.write('#include "%s.h"\n' % os.path.basename(TARGET_BASE_FILENAME)) 254 | 255 | if USE_SYSTEM_EXCEPTION: 256 | cpp.write('#include ') 257 | elif FRAMEWORK_PATH != None: 258 | cpp.write('#include "%s/OrthancException.h"' % FRAMEWORK_PATH) 259 | else: 260 | cpp.write('#include ') 261 | 262 | cpp.write(""" 263 | #include 264 | #include 265 | 266 | """) 267 | 268 | for ns in NAMESPACE.split('.'): 269 | cpp.write('namespace %s {\n' % ns) 270 | 271 | 272 | for name in resources: 273 | if resources[name]['Type'] == 'File': 274 | WriteResource(cpp, resources[name]) 275 | else: 276 | for f in resources[name]['Files']: 277 | WriteResource(cpp, resources[name]['Files'][f]) 278 | 279 | 280 | 281 | ##################################################################### 282 | ## Write the accessors to the file resources in .cpp 283 | ##################################################################### 284 | 285 | cpp.write(""" 286 | const void* GetFileResourceBuffer(FileResourceId id) 287 | { 288 | switch (id) 289 | { 290 | """) 291 | for name in resources: 292 | if resources[name]['Type'] == 'File': 293 | cpp.write(' case %s:\n' % name) 294 | cpp.write(' return resource%dBuffer;\n' % resources[name]['Index']) 295 | 296 | cpp.write(""" 297 | default: 298 | throw %s; 299 | } 300 | } 301 | 302 | size_t GetFileResourceSize(FileResourceId id) 303 | { 304 | switch (id) 305 | { 306 | """ % OUT_OF_RANGE_EXCEPTION) 307 | 308 | for name in resources: 309 | if resources[name]['Type'] == 'File': 310 | cpp.write(' case %s:\n' % name) 311 | cpp.write(' return resource%dSize;\n' % resources[name]['Index']) 312 | 313 | cpp.write(""" 314 | default: 315 | throw %s; 316 | } 317 | } 318 | """ % OUT_OF_RANGE_EXCEPTION) 319 | 320 | 321 | 322 | ##################################################################### 323 | ## Write the accessors to the directory resources in .cpp 324 | ##################################################################### 325 | 326 | cpp.write(""" 327 | const void* GetDirectoryResourceBuffer(DirectoryResourceId id, const char* path) 328 | { 329 | switch (id) 330 | { 331 | """) 332 | 333 | for name in resources: 334 | if resources[name]['Type'] == 'Directory': 335 | cpp.write(' case %s:\n' % name) 336 | isFirst = True 337 | for path in resources[name]['Files']: 338 | cpp.write(' if (!strcmp(path, "%s"))\n' % path) 339 | cpp.write(' return resource%dBuffer;\n' % resources[name]['Files'][path]['Index']) 340 | cpp.write(' throw %s;\n\n' % INEXISTENT_PATH_EXCEPTION) 341 | 342 | cpp.write(""" default: 343 | throw %s; 344 | } 345 | } 346 | 347 | size_t GetDirectoryResourceSize(DirectoryResourceId id, const char* path) 348 | { 349 | switch (id) 350 | { 351 | """ % OUT_OF_RANGE_EXCEPTION) 352 | 353 | for name in resources: 354 | if resources[name]['Type'] == 'Directory': 355 | cpp.write(' case %s:\n' % name) 356 | isFirst = True 357 | for path in resources[name]['Files']: 358 | cpp.write(' if (!strcmp(path, "%s"))\n' % path) 359 | cpp.write(' return resource%dSize;\n' % resources[name]['Files'][path]['Index']) 360 | cpp.write(' throw %s;\n\n' % INEXISTENT_PATH_EXCEPTION) 361 | 362 | cpp.write(""" default: 363 | throw %s; 364 | } 365 | } 366 | """ % OUT_OF_RANGE_EXCEPTION) 367 | 368 | 369 | 370 | 371 | ##################################################################### 372 | ## List the resources in a directory 373 | ##################################################################### 374 | 375 | cpp.write(""" 376 | void ListResources(std::list& result, DirectoryResourceId id) 377 | { 378 | result.clear(); 379 | 380 | switch (id) 381 | { 382 | """) 383 | 384 | for name in resources: 385 | if resources[name]['Type'] == 'Directory': 386 | cpp.write(' case %s:\n' % name) 387 | for path in sorted(resources[name]['Files']): 388 | cpp.write(' result.push_back("%s");\n' % path) 389 | cpp.write(' break;\n\n') 390 | 391 | cpp.write(""" default: 392 | throw %s; 393 | } 394 | } 395 | """ % OUT_OF_RANGE_EXCEPTION) 396 | 397 | 398 | 399 | 400 | ##################################################################### 401 | ## Write the convenience wrappers in .cpp 402 | ##################################################################### 403 | 404 | cpp.write(""" 405 | void GetFileResource(std::string& result, FileResourceId id) 406 | { 407 | size_t size = GetFileResourceSize(id); 408 | result.resize(size); 409 | if (size > 0) 410 | memcpy(&result[0], GetFileResourceBuffer(id), size); 411 | } 412 | 413 | void GetDirectoryResource(std::string& result, DirectoryResourceId id, const char* path) 414 | { 415 | size_t size = GetDirectoryResourceSize(id, path); 416 | result.resize(size); 417 | if (size > 0) 418 | memcpy(&result[0], GetDirectoryResourceBuffer(id, path), size); 419 | } 420 | """) 421 | 422 | 423 | for ns in NAMESPACE.split('.'): 424 | cpp.write('}\n') 425 | 426 | cpp.close() 427 | -------------------------------------------------------------------------------- /Resources/Orthanc/LinuxStandardBaseToolchain.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | # 4 | # Full build, as used on the BuildBot CIS: 5 | # 6 | # $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake ../OrthancServer/ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON -DBOOST_LOCALE_BACKEND=icu -DENABLE_PKCS11=ON -G Ninja 7 | # 8 | # Or, more lightweight version (without libp11 and ICU): 9 | # 10 | # $ LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake ../OrthancServer/ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -G Ninja 11 | # 12 | 13 | INCLUDE(CMakeForceCompiler) 14 | 15 | SET(LSB_PATH $ENV{LSB_PATH} CACHE STRING "") 16 | SET(LSB_CC $ENV{LSB_CC} CACHE STRING "") 17 | SET(LSB_CXX $ENV{LSB_CXX} CACHE STRING "") 18 | SET(LSB_TARGET_VERSION "4.0" CACHE STRING "") 19 | 20 | IF ("${LSB_PATH}" STREQUAL "") 21 | SET(LSB_PATH "/opt/lsb") 22 | ENDIF() 23 | 24 | IF (EXISTS ${LSB_PATH}/lib64) 25 | SET(LSB_TARGET_PROCESSOR "x86_64") 26 | SET(LSB_LIBPATH ${LSB_PATH}/lib64-${LSB_TARGET_VERSION}) 27 | ELSEIF (EXISTS ${LSB_PATH}/lib) 28 | SET(LSB_TARGET_PROCESSOR "x86") 29 | SET(LSB_LIBPATH ${LSB_PATH}/lib-${LSB_TARGET_VERSION}) 30 | ELSE() 31 | MESSAGE(FATAL_ERROR "Unable to detect the target processor architecture. Check the LSB_PATH environment variable.") 32 | ENDIF() 33 | 34 | SET(LSB_CPPPATH ${LSB_PATH}/include) 35 | SET(PKG_CONFIG_PATH ${LSB_LIBPATH}/pkgconfig/) 36 | 37 | # the name of the target operating system 38 | SET(CMAKE_SYSTEM_NAME Linux) 39 | SET(CMAKE_SYSTEM_VERSION LinuxStandardBase) 40 | SET(CMAKE_SYSTEM_PROCESSOR ${LSB_TARGET_PROCESSOR}) 41 | 42 | # which compilers to use for C and C++ 43 | SET(CMAKE_C_COMPILER ${LSB_PATH}/bin/lsbcc) 44 | 45 | if (${CMAKE_VERSION} VERSION_LESS "3.6.0") 46 | CMAKE_FORCE_CXX_COMPILER(${LSB_PATH}/bin/lsbc++ GNU) 47 | else() 48 | SET(CMAKE_CXX_COMPILER ${LSB_PATH}/bin/lsbc++) 49 | endif() 50 | 51 | # here is the target environment located 52 | SET(CMAKE_FIND_ROOT_PATH ${LSB_PATH}) 53 | 54 | # adjust the default behaviour of the FIND_XXX() commands: 55 | # search headers and libraries in the target environment, search 56 | # programs in the host environment 57 | SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 58 | SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) 59 | SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) 60 | 61 | SET(CMAKE_CROSSCOMPILING OFF) 62 | 63 | 64 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -I${LSB_PATH}/include" CACHE INTERNAL "" FORCE) 65 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -nostdinc++ -I${LSB_PATH}/include -I${LSB_PATH}/include/c++ -I${LSB_PATH}/include/c++/backward" CACHE INTERNAL "" FORCE) 66 | SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH} --lsb-besteffort" CACHE INTERNAL "" FORCE) 67 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -L${LSB_LIBPATH} --lsb-besteffort" CACHE INTERNAL "" FORCE) 68 | 69 | if (NOT "${LSB_CXX}" STREQUAL "") 70 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-cxx=${LSB_CXX}") 71 | SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-cxx=${LSB_CXX}") 72 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-cxx=${LSB_CXX}") 73 | endif() 74 | 75 | if (NOT "${LSB_CC}" STREQUAL "") 76 | SET(CMAKE_C_FLAGS "${CMAKE_CC_FLAGS} --lsb-cc=${LSB_CC}") 77 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-cc=${LSB_CC}") 78 | SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --lsb-cc=${LSB_CC}") 79 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --lsb-cc=${LSB_CC}") 80 | endif() 81 | 82 | -------------------------------------------------------------------------------- /Resources/Orthanc/MinGW-W64-Toolchain32.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | # the name of the target operating system 4 | set(CMAKE_SYSTEM_NAME Windows) 5 | 6 | # which compilers to use for C and C++ 7 | set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) 8 | set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 9 | set(CMAKE_RC_COMPILER i686-w64-mingw32-windres) 10 | 11 | # here is the target environment located 12 | set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) 13 | 14 | # adjust the default behaviour of the FIND_XXX() commands: 15 | # search headers and libraries in the target environment, search 16 | # programs in the host environment 17 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 19 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 20 | -------------------------------------------------------------------------------- /Resources/Orthanc/MinGW-W64-Toolchain64.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | # the name of the target operating system 4 | set(CMAKE_SYSTEM_NAME Windows) 5 | 6 | # which compilers to use for C and C++ 7 | set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) 8 | set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 9 | set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) 10 | 11 | # here is the target environment located 12 | set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) 13 | 14 | # adjust the default behaviour of the FIND_XXX() commands: 15 | # search headers and libraries in the target environment, search 16 | # programs in the host environment 17 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 19 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 20 | -------------------------------------------------------------------------------- /Resources/Orthanc/MinGWToolchain.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | # the name of the target operating system 4 | set(CMAKE_SYSTEM_NAME Windows) 5 | 6 | # which compilers to use for C and C++ 7 | set(CMAKE_C_COMPILER i586-mingw32msvc-gcc) 8 | set(CMAKE_CXX_COMPILER i586-mingw32msvc-g++) 9 | set(CMAKE_RC_COMPILER i586-mingw32msvc-windres) 10 | 11 | # here is the target environment located 12 | set(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc) 13 | 14 | # adjust the default behaviour of the FIND_XXX() commands: 15 | # search headers and libraries in the target environment, search 16 | # programs in the host environment 17 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 19 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 20 | 21 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSTACK_SIZE_PARAM_IS_A_RESERVATION=0x10000" CACHE INTERNAL "" FORCE) 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTACK_SIZE_PARAM_IS_A_RESERVATION=0x10000" CACHE INTERNAL "" FORCE) 23 | -------------------------------------------------------------------------------- /Resources/Orthanc/Plugins/ExportedSymbolsPlugins.list: -------------------------------------------------------------------------------- 1 | # This is the list of the symbols that must be exported by Orthanc 2 | # plugins, if targeting OS X 3 | 4 | _OrthancPluginInitialize 5 | _OrthancPluginFinalize 6 | _OrthancPluginGetName 7 | _OrthancPluginGetVersion 8 | -------------------------------------------------------------------------------- /Resources/Orthanc/Plugins/OrthancPluginException.h: -------------------------------------------------------------------------------- 1 | // Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | 4 | #pragma once 5 | 6 | #if !defined(HAS_ORTHANC_EXCEPTION) 7 | # error The macro HAS_ORTHANC_EXCEPTION must be defined 8 | #endif 9 | 10 | 11 | #if HAS_ORTHANC_EXCEPTION == 1 12 | # include 13 | # define ORTHANC_PLUGINS_ERROR_ENUMERATION ::Orthanc::ErrorCode 14 | # define ORTHANC_PLUGINS_EXCEPTION_CLASS ::Orthanc::OrthancException 15 | # define ORTHANC_PLUGINS_GET_ERROR_CODE(code) ::Orthanc::ErrorCode_ ## code 16 | #else 17 | # include 18 | # define ORTHANC_PLUGINS_ERROR_ENUMERATION ::OrthancPluginErrorCode 19 | # define ORTHANC_PLUGINS_EXCEPTION_CLASS ::OrthancPlugins::PluginException 20 | # define ORTHANC_PLUGINS_GET_ERROR_CODE(code) ::OrthancPluginErrorCode_ ## code 21 | #endif 22 | 23 | 24 | #define ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code) \ 25 | throw ORTHANC_PLUGINS_EXCEPTION_CLASS(static_cast(code)); 26 | 27 | 28 | #define ORTHANC_PLUGINS_THROW_EXCEPTION(code) \ 29 | throw ORTHANC_PLUGINS_EXCEPTION_CLASS(ORTHANC_PLUGINS_GET_ERROR_CODE(code)); 30 | 31 | 32 | #define ORTHANC_PLUGINS_CHECK_ERROR(code) \ 33 | if (code != ORTHANC_PLUGINS_GET_ERROR_CODE(Success)) \ 34 | { \ 35 | ORTHANC_PLUGINS_THROW_EXCEPTION(code); \ 36 | } 37 | 38 | 39 | namespace OrthancPlugins 40 | { 41 | #if HAS_ORTHANC_EXCEPTION == 0 42 | class PluginException 43 | { 44 | private: 45 | OrthancPluginErrorCode code_; 46 | 47 | public: 48 | explicit PluginException(OrthancPluginErrorCode code) : code_(code) 49 | { 50 | } 51 | 52 | OrthancPluginErrorCode GetErrorCode() const 53 | { 54 | return code_; 55 | } 56 | 57 | const char* What(OrthancPluginContext* context) const 58 | { 59 | const char* description = OrthancPluginGetErrorDescription(context, code_); 60 | if (description) 61 | { 62 | return description; 63 | } 64 | else 65 | { 66 | return "No description available"; 67 | } 68 | } 69 | }; 70 | #endif 71 | } 72 | -------------------------------------------------------------------------------- /Resources/Orthanc/Plugins/OrthancPluginsExports.cmake: -------------------------------------------------------------------------------- 1 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 2 | 3 | # In Orthanc <= 1.7.1, the instructions below were part of 4 | # "Compiler.cmake", and were protected by the (now unused) option 5 | # "ENABLE_PLUGINS_VERSION_SCRIPT" in CMake 6 | 7 | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR 8 | ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD" OR 9 | ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR 10 | ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 11 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script=${CMAKE_CURRENT_LIST_DIR}/VersionScriptPlugins.map") 12 | elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") 13 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -exported_symbols_list ${CMAKE_CURRENT_LIST_DIR}/ExportedSymbolsPlugins.list") 14 | endif() 15 | -------------------------------------------------------------------------------- /Resources/Orthanc/Plugins/VersionScriptPlugins.map: -------------------------------------------------------------------------------- 1 | # This is a version-script for Orthanc plugins 2 | 3 | { 4 | global: 5 | OrthancPluginInitialize; 6 | OrthancPluginFinalize; 7 | OrthancPluginGetName; 8 | OrthancPluginGetVersion; 9 | 10 | local: 11 | *; 12 | }; 13 | -------------------------------------------------------------------------------- /Resources/SyncOrthancFolder.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Taken from https://hg.orthanc-server.com/orthanc-databases/ 4 | 5 | # 6 | # This maintenance script updates the content of the "Orthanc" folder 7 | # to match the latest version of the Orthanc source code. 8 | # 9 | 10 | import multiprocessing 11 | import os 12 | import stat 13 | import urllib2 14 | 15 | TARGET = os.path.join(os.path.dirname(__file__), 'Orthanc') 16 | PLUGIN_SDK_VERSION_OLD = [ '0.9.5', '1.4.0', '1.5.2', '1.5.4' ] 17 | PLUGIN_SDK_VERSION_NEW = [ '1.9.2' ] 18 | REPOSITORY = 'https://hg.orthanc-server.com/orthanc/raw-file' 19 | 20 | FILES = [ 21 | ('default', 'OrthancFramework/Resources/CMake/AutoGeneratedCode.cmake', 'CMake'), 22 | ('default', 'OrthancFramework/Resources/CMake/Compiler.cmake', 'CMake'), 23 | ('default', 'OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake', 'CMake'), 24 | ('default', 'OrthancFramework/Resources/CMake/DownloadPackage.cmake', 'CMake'), 25 | ('default', 'OrthancFramework/Resources/CMake/GoogleTestConfiguration.cmake', 'CMake'), 26 | ('default', 'OrthancFramework/Resources/EmbedResources.py', '.'), 27 | ('default', 'OrthancFramework/Resources/Toolchains/LinuxStandardBaseToolchain.cmake', '.'), 28 | ('default', 'OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain32.cmake', '.'), 29 | ('default', 'OrthancFramework/Resources/Toolchains/MinGW-W64-Toolchain64.cmake', '.'), 30 | ('default', 'OrthancFramework/Resources/Toolchains/MinGWToolchain.cmake', '.'), 31 | 32 | # TODO - Replace branch "openssl-3.x" by "default" once it is reintegrated into mainline 33 | ('openssl-3.x', 'OrthancServer/Plugins/Samples/Common/ExportedSymbolsPlugins.list', 'Plugins'), 34 | ('openssl-3.x', 'OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp', 'Plugins'), 35 | ('openssl-3.x', 'OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h', 'Plugins'), 36 | ('openssl-3.x', 'OrthancServer/Plugins/Samples/Common/OrthancPluginException.h', 'Plugins'), 37 | ('openssl-3.x', 'OrthancServer/Plugins/Samples/Common/OrthancPluginsExports.cmake', 'Plugins'), 38 | ('openssl-3.x', 'OrthancServer/Plugins/Samples/Common/VersionScriptPlugins.map', 'Plugins'), 39 | ('openssl-3.x', 'OrthancServer/Sources/Search/DatabaseConstraint.cpp', 'Databases'), 40 | ('openssl-3.x', 'OrthancServer/Sources/Search/DatabaseConstraint.h', 'Databases'), 41 | ] 42 | 43 | SDK = [ 44 | 'orthanc/OrthancCPlugin.h', 45 | 'orthanc/OrthancCDatabasePlugin.h', 46 | ] 47 | 48 | 49 | def Download(x): 50 | branch = x[0] 51 | source = x[1] 52 | target = os.path.join(TARGET, x[2]) 53 | print target 54 | 55 | try: 56 | os.makedirs(os.path.dirname(target)) 57 | except: 58 | pass 59 | 60 | url = '%s/%s/%s' % (REPOSITORY, branch, source) 61 | 62 | try: 63 | with open(target, 'w') as f: 64 | f.write(urllib2.urlopen(url).read()) 65 | except Exception as e: 66 | raise Exception('Cannot download: %s' % url) 67 | 68 | 69 | commands = [] 70 | 71 | for f in FILES: 72 | commands.append([ f[0], # Branch 73 | f[1], # Source file 74 | os.path.join(f[2], os.path.basename(f[1])) ]) 75 | 76 | for version in PLUGIN_SDK_VERSION_OLD: 77 | for f in SDK: 78 | commands.append([ 79 | 'Orthanc-%s' % version, 80 | 'Plugins/Include/%s' % f, 81 | 'Sdk-%s/%s' % (version, f) 82 | ]) 83 | 84 | for version in PLUGIN_SDK_VERSION_NEW: 85 | for f in SDK: 86 | commands.append([ 87 | 'Orthanc-%s' % version, 88 | 'OrthancServer/Plugins/Include/%s' % f, 89 | 'Sdk-%s/%s' % (version, f) 90 | ]) 91 | 92 | 93 | pool = multiprocessing.Pool(10) # simultaneous downloads 94 | pool.map(Download, commands) 95 | -------------------------------------------------------------------------------- /compose.yaml: -------------------------------------------------------------------------------- 1 | name: orthanc-mongodb 2 | 3 | networks: 4 | local: 5 | name: orthanc-mongodb 6 | 7 | volumes: 8 | build: 9 | name: orthanc-mongodb-build 10 | mongo: 11 | name: orthanc-mongodb-data 12 | 13 | 14 | services: 15 | orthanc: 16 | image: orthanc-mongodb-run 17 | stdin_open: true 18 | tty: true 19 | command: bash 20 | volumes: 21 | - ./:/usr/local/src/:rw,delegated 22 | - type: bind 23 | source: ../orthanc/Orthanc-1.11.3 24 | target: /usr/local/orthanc 25 | ports: 26 | - 8042 27 | - 4242 28 | networks: 29 | local: 30 | database: 31 | image: mongo:6.0 32 | volumes: 33 | - mongo:/var/lib/mongo 34 | ports: 35 | - 27017 36 | networks: 37 | local: -------------------------------------------------------------------------------- /docs/DEPENDENCIES_AUTO_CONFIG.md: -------------------------------------------------------------------------------- 1 | # Orthanc MongoDB autoconfig 2 | 3 | Install plugin dependencies automatically. 4 | 5 | ## Brief overview 6 | 7 | The Orthanc MongoDB plugin now supports new cmake option - ```AUTO_INSTALL_DEPENDENCIES``` 8 | 9 | If this option is enabled, cmake will take care about downloading, configuring and installing of next dependencies: 10 | 2. Mongoc ( + bson) - https://github.com/mongodb/mongo-c-driver 11 | 3. Mongocxx ( + bsoncxx) - https://github.com/mongodb/mongo-cxx-driver 12 | 13 | **Note**: You only need to take care about installing dependencies, which are required by above libraries. To find which dependencies are required, just look through the above links or links for instalation guides in main [Build Prerequisites](./PREREQUISITES.md) file. Such dependencies could be easily installed with a package manager, they do not require additional configuration steps. 14 | 15 | ## Current versions 16 | 17 | 2. ```MONGO_C_VERSION``` - 1.23.2 18 | 3. ```MONGO_CXX_VERSION``` - 3.7.0 19 | 20 | To use another versions of the above libraries - change version in respective cmake macros in [CMake/autoconfig.cmake](https://github.com/andrewDubyk/orthanc-mongodb/blob/Orthanc-1.5.7/CMake/autoconfig.cmake) file. 21 | 22 | ## Usage exmaple 23 | 24 | Dependencies will be downloded and installed when you execute cmake command. After that you can find all resulted files under build folder. 25 | 26 | Before building plugin itself - be sure that you have all needed libraries intsalled on your PC. 27 | 28 | ## UNIX 29 | 30 | ```bash 31 | mkdir orthanc-mongodb/build 32 | cd orthanc-mongodb/build 33 | cmake -DCMAKE_CXX_FLAGS='-fPIC' -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DAUTO_INSTALL_DEPENDENCIES=ON ../MongoDB/ 34 | make 35 | sudo make install 36 | ``` 37 | Use devtoolset-N commands (where N - toolset version) if it is need. 38 | ## Windows with MSVC 39 | 40 | ```bash 41 | cmake -DCMAKE_CXX_FLAGS="/EHsc" -DCMAKE_BUILD_TYPE=Release -DAUTO_INSTALL_DEPENDENCIES=ON -DBOOST_ROOT= ../MongoDB/ 42 | ``` 43 | 44 | * On Windows mongocxx require boost library, so ```BOOST_ROOT``` will be passed to mongocxx cmake during execution (tested with ```1_60_0```, but newer versions also could be used). 45 | * Windows Run-Time linkage: 46 | * If you want to link with MSVC static Run-Time Library - you need to build plugin with ```-DLINK_STATIC_LIBS=ON``` flag. In this case ```/MT(d)``` option will be added to build instructions. 47 | * In other case resulted dll files will be linked with dynamic Run-Time libraries and you must be sure that all of them are included in Windows system PATH varaibles or place them in the same folder as plugin dlls. Use [Dependency Walker](https://www.dependencywalker.com/) to figure out which dlls are missing. 48 | * Also you could experiment with other ```CMAKE_C_FLAGS``` and ```CMAKE_CXX_FLAGS``` flags. 49 | 50 | --- 51 | 52 | -------------------------------------------------------------------------------- /docs/PLUGIN_COMPILATION.md: -------------------------------------------------------------------------------- 1 | # Plugin Compilation 2 | 3 | ## Basic Build Example 4 | In order to compile the plugin, the prerequisites needs to be available (check [link](./PREREQUISITES.md) for mor details). 5 | You need either to download the master or the tagged release from GitHub and uncompressed it, here an example using curl: 6 | 7 | ```bash 8 | # Replace "LATEST_TAG" with last tag in github 9 | curl -L --output orthanc-mongodb.tar.gz https://github.com/Doc-Cirrus/orthanc-mongodb/archive/LATEST_TAG.tar.gz 10 | tar -xzf orthanc-mongodb.tar.gz 11 | ``` 12 | 13 | ### Centos like 14 | ```bash 15 | mkdir -p orthanc-mongodb/build 16 | cd orthanc-mongodb/build 17 | scl enable devtoolset-8 "cmake3 -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_PREFIX_PATH=/usr/local -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release -DAUTO_INSTALL_DEPENDENCIES=ON ../MongoDB/" 18 | scl enable devtoolset-8 "make" 19 | scl enable devtoolset-8 "sudo make install" 20 | ``` 21 | 22 | ### Debian like 23 | ```bash 24 | mkdir -p orthanc-mongodb/build 25 | cd orthanc-mongodb/build 26 | 27 | cmake -DCMAKE_INSTALL_PREFIX=/usr -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release -DAUTO_INSTALL_DEPENDENCIES=ON ../MongoDB/ 28 | make 29 | ``` 30 | 31 | ## Cmake Configuration Arguments 32 | * ```AUTO_INSTALL_DEPENDENCIES``` - Automatically build and compile dependencies (mongoc/mongocxx). 33 | * ```ORTHANC_FRAMEWORK_SOURCE``` - (not required) Orthanc server sources with theis values ("system", "hg", "web", "archive" or "path"), check [link](../Resources/Orthanc/CMake/DownloadOrthancFramework.cmake) for more info. 34 | * ```BUILD_TESTS``` - option to build tests, default off 35 | * ```BUILD_WITH_GCOV``` - option to include coverage default off 36 | 37 | ## Docker 38 | 39 | There is a docker image ready to build, with two targets 40 | * Base: base image with all the deps `--target base` 41 | * Build: compile the plugin `--target build` 42 | * Run: start orthanc with the plugin enabled (`--target runtime`), plus some other one (for testing purpuses). 43 | ``` 44 | $ docker build --network host --target runtime -t orthanc-mongodb-run . 45 | $ docker build --network host -p 127.0.0.1:8042:8042 -p 127.0.0.1:4242:4242 --target runtime -t orthanc-mongodb-run --name orthanc-mongodb . 46 | ``` 47 | 48 | After creating `orthanc-mongodb-run` image you can start compose, that has all the deps required. 49 | ``` 50 | $ docker compose up 51 | ``` 52 | 53 | Link to mongodb instance will need adjusting, for now it require a mongod docker container instance (see [more info](https://hub.docker.com/_/mongo)); -------------------------------------------------------------------------------- /docs/PLUGIN_CONFIGURATION.md: -------------------------------------------------------------------------------- 1 | # Plugin Configuration 2 | 3 | In order to use the plugin you need to include it in the `configuration.json` file of orthanc: 4 | 5 | ```json 6 | ... 7 | "Plugins": [ 8 | "path/to/plugin/libOrthancMongoDBIndex.so", 9 | "path/to/plugin/libOrthancMongoDBStorage.so" 10 | ] 11 | ... 12 | ``` 13 | 14 | ANd to configure the plugin itself 15 | 16 | ```json 17 | ... 18 | // MongoDB plugin confihuration section: 19 | "MongoDB" : { 20 | "EnableIndex" : true, // false to use default SQLite 21 | "EnableStorage" : true, // false to use default SQLite 22 | "ConnectionUri" : "mongodb://localhost:27017/orthanc_db", 23 | "ChunkSize" : 261120 24 | }, 25 | ... 26 | ``` 27 | 28 | Also it's possible to configure the plugin with separate config options: 29 | 30 | ```json 31 | ... 32 | "MongoDB" : { 33 | "host" : "customhost", 34 | "port" : 27001, 35 | "user" : "user", 36 | "database" : "database", 37 | "password" : "password", 38 | "authenticationDatabase" : "admin", 39 | "ChunkSize" : 261120 40 | } 41 | ... 42 | ``` 43 | 44 | **NOTE: Setting up the ConnectionUri overrides the host, port, database params. So if the ConnectionUri is set, the other parameters except the ChunkSize will be ignored.** 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs/PREREQUISITES.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | The host system will need som dependencies preinstall, which are needed by orthanc/mongoc/mongocxx 4 | 5 | ## General system dependencies 6 | ### Centos like 7 | 8 | ```bash 9 | yum -y install centos-release-scl centos-release-scl-rh epel-release 10 | yum -y install make devtoolset-8 libuuid-devel openssl-devel cyrus-sasl-devel cmake3 zlib-devel 11 | ``` 12 | 13 | ### Debian like 14 | ```bash 15 | apt -y install build-essential unzip cmake make libsasl2-dev uuid-dev libssl-dev zlib1g-dev git curl 16 | ``` 17 | 18 | ## Note 19 | It's highly recommended to Use the ```AUTO_INSTALL_DEPENDENCIES``` while building, 20 | since it will take care of all the dependencies but at a performance cost. 21 | Check [AUTO_CONFIG](./DEPENDENCIES_AUTO_CONFIG.md) for mo details. 22 | 23 | ## MongoC library 24 | The mongoc library needs to precompiled in order to be linked against the db plugin 25 | https://github.com/mongodb/mongo-c-driver/releases 26 | 27 | ### Centos like 28 | ```bash 29 | curl -L --output mongo-c-driver-1.23.2.tar.gz https://github.com/mongodb/mongo-c-driver/archive/1.23.2.tar.gz 30 | tar -xzf mongo-c-driver-1.23.2.tar.gz 31 | mkdir -p mongo-c-driver-1.23.2/build 32 | cd mongo-c-driver-1.23.2/build 33 | scl enable devtoolset-8 "cmake3 -DCMAKE_C_FLAGS='-fPIC' -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF .." 34 | scl enable devtoolset-8 "make" 35 | scl enable devtoolset-8 "sudo make install" 36 | ``` 37 | 38 | ### Debian like 39 | ```bash 40 | # Static build 41 | curl -L --output mongo-c-driver-1.23.2.tar.gz https://github.com/mongodb/mongo-c-driver/archive/1.23.2.tar.gz 42 | tar -xzf mongo-c-driver-1.23.2.tar.gz 43 | mkdir -p mongo-c-driver-1.23.2/build 44 | cd mongo-c-driver-1.23.2/build 45 | cmake -DCMAKE_C_FLAGS='-fPIC' -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DENABLE_STATIC=ON -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_ICU=OFF ../mongo-c-driver-1.23.2 46 | make 47 | sudo make install 48 | ``` 49 | 50 | ## MongoCXX library 51 | The mongocxx library needs to precompiled in order to be linked against the db plugin 52 | https://github.com/mongodb/mongo-cxx-driver/releases 53 | 54 | ### Centos like 55 | ```bash 56 | curl -L --output mongo-cxx-driver-3.7.0.tar.gz https://github.com/mongodb/mongo-cxx-driver/archive/3.7.0.tar.gz 57 | tar -xzf mongo-cxx-driver-3.7.0.tar.gz 58 | mkdir -p mongo-cxx-driver-3.7.0/build 59 | cd mongo-cxx-driver-r3.7.0/build 60 | scl enable devtoolset-8 "cmake3 -DCMAKE_CXX_FLAGS='-fPIC' -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DLIBBSON_DIR=/usr/local -DLIBMONGOC_DIR=/usr/local .." 61 | # for any reason it requires write permissions to /usr/local/include/bsoncxx/v_noabi/bsoncxx/third_party/mnmlstc/share/cmake/core 62 | # so use sudo for make too 63 | scl enable devtoolset-8 "sudo make" 64 | scl enable devtoolset-8 "sudo make install" 65 | ``` 66 | 67 | ### Debian like 68 | ```bash 69 | # static compilation 70 | curl -L --output mongo-cxx-driver-3.7.0.tar.gz https://github.com/mongodb/mongo-cxx-driver/archive/3.7.0.tar.gz 71 | tar -xzf mongo-cxx-driver-3.7.0.tar.gz 72 | mkdir -p mongo-cxx-driver-3.7.0/build 73 | cd mongo-cxx-driver-r3.7.0/build 74 | cmake -DCMAKE_CXX_FLAGS='-fPIC' -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DLIBBSON_DIR=/usr/local -DLIBMONGOC_DIR=/usr/local .. 75 | sudo make 76 | sudo make install 77 | ``` 78 | 79 | ## Useful resources 80 | - Mongoc library http://mongoc.org/libmongoc/current/installing.html 81 | - Mongo-cxx library https://mongodb.github.io/mongo-cxx-driver/mongocxx-v3/installation/ 82 | - Orthanc build https://hg.orthanc-server.com/orthanc/file/tip/INSTALL 83 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Documentation index 2 | 3 | - [Build Prerequisites](./PREREQUISITES.md) 4 | - [Plugin Compilation](./PLUGIN_COMPILATION.md) 5 | - [Plugin Configuration](./PLUGIN_CONFIGURATION.md) 6 | - [Testing](./TESTING.md) 7 | -------------------------------------------------------------------------------- /docs/TESTING.md: -------------------------------------------------------------------------------- 1 | # Orthanc MongoDB testing (*IN PROGRESS*) 2 | 3 | ## Brief test strategy 4 | 5 | The Orthanc MongoDB plugin allows storing DICOM data in MongoDB. 6 | It's only data storage/retrieval level and testing for the plugin is focused on this part. 7 | Whole DICOM Orthanc functionality is out of the scope of the MongoDB plugin testing. 8 | 9 | ### Test levels 10 | 11 | The plugin is tests on unit, integration and system level. 12 | 13 | * Unit tests are implemented for all methods of the IndexPlugin as well as for StoragePlugin 14 | * In the test strategy for the Orthanc MongoDB plugin the integration and system testing are combined together and are covered by: 15 | * Manual functional tests 16 | * Command line scripts for loading DCM files (load test) 17 | 18 | ### Environment requirements 19 | 20 | The plugin is being tested on Linux (primary), OSX and windows platforms. 21 | Configured Orthanc instance is required with the Index and Storage plugins activated. Please see the [Plugin configuration setup section in README.md](README.md) 22 | DICOM sample images are required for testing. 23 | 24 | ### Testing tools 25 | 26 | Manual tests are performed in web browser. 27 | Load tests are done with the [dcm4che tools 3](https://dcm4che.atlassian.net/wiki/display/lib/) 28 | 29 | ## Manual Test Scenarios 30 | 31 | 1. Upload test 32 | * Open Ortanc home -> got to upload and upload Test DCM file 33 | * Refresh page, ensure the Patient/Study/Series/Instance information is correct 34 | * Verify DICOM tags 35 | * Preview instance image 36 | * Expected results: DICOM tags are correct, image in preview appears correct. 37 | 2. Delete test 38 | * Upload patient 39 | * Refresh page 40 | * Delete patient 41 | * Refresh page 42 | * Expected results: patient is deleted with all studies/series/instances hierarchy. There are no records regarding patient in DB. 43 | 3. Protect-unprotect 44 | * Upload patient 45 | * Click on protect/un-protect option 46 | * Refresh page 47 | * Expected results: patient protected remains consistent. 48 | 4. Send to remote modality 49 | * Configure second Orthanc instance to accept DICOM images. ("DicomModalities" section of the config file.) 50 | * Upload patient 51 | * Send patient to remote modality 52 | * Verify the patient on remote Orthanc instance 53 | * Expected results: patient on remote Orthanc instance is the same. 54 | 5. Query/Retrieve from remote modality 55 | * Ensure there are patients in the remote instance 56 | * On the main instance go to the Query/Retrieve 57 | * Search patients in remote modality, retrieve to local 58 | * Expected results: patient search returns correct results, patients/studies/series correctly retrieved to local. 59 | 60 | ## Load test scripts 61 | 62 | Download sample test data from [here](https://wiki.cancerimagingarchive.net/display/Public/LIDC-IDRI#b261a131fc93463d83fd3dd09fd0edf6) 63 | or any other source. 64 | 65 | * Use storescu utility to upload samples: 66 | 67 | ``` 68 | storescu -c ORTHANC@orthank-host:4242 Folder_with_samples 69 | ``` 70 | * Generation 71 | 72 | It's possible to generate similar set from Folder_with_samples like that: 73 | 74 | ``` 75 | find Folder_with_samples -name '*.dcm' | xargs -L 1 -I {} dcmgen 1 {} generated_folder 76 | ``` 77 | 78 | * Generate bunch of patients from existing seed data. The script generate 50000 unique patients with 4 series 5 instances each. 79 | 80 | ``` 81 | #!/bin/bash 82 | GEN_FOLDER=generated 83 | 84 | for i in {1..50000} 85 | do 86 | echo "Generating: $i" 87 | dcmgen 20:4 seed_small.dcm $GEN_FOLDER --override PatientID=`uuidgen` --override PatientName=`uuidgen` --override StudyInstanceUID=`uuidgen` 88 | done 89 | ``` 90 | seed_small.dcm - is a single file to take sample data from and generate several DICOM tags. 91 | 92 | Then upload generated sources with storescu 93 | 94 | * Delete all patients 95 | ``` 96 | curl http://localhost:8042/patients | grep -o "[0-9a-z\-]*" | grep -v "^$" | xargs -I {} curl -X "DELETE" http://localhost:8042/patients/{} 97 | ``` 98 | 99 | Selectively verify uploaded data in UI. 100 | Check Orthanc server logs for errors. 101 | Check MongoDB server logs for errors. 102 | 103 | ## Test results 104 | 105 | ### Unit test results 106 | ``` 107 | [==========] Running 9 tests from 2 test cases. 108 | [----------] Global test environment set-up. 109 | [----------] 8 tests from MongoDBBackendTest 110 | [ RUN ] MongoDBBackendTest.Attachments 111 | [ OK ] MongoDBBackendTest.Attachments (700 ms) 112 | [ RUN ] MongoDBBackendTest.Resource 113 | [ OK ] MongoDBBackendTest.Resource (657 ms) 114 | [ RUN ] MongoDBBackendTest.Changes 115 | [ OK ] MongoDBBackendTest.Changes (774 ms) 116 | [ RUN ] MongoDBBackendTest.ExportedResources 117 | [ OK ] MongoDBBackendTest.ExportedResources (679 ms) 118 | [ RUN ] MongoDBBackendTest.Metadata 119 | [ OK ] MongoDBBackendTest.Metadata (669 ms) 120 | [ RUN ] MongoDBBackendTest.GlobalProperty 121 | [ OK ] MongoDBBackendTest.GlobalProperty (643 ms) 122 | [ RUN ] MongoDBBackendTest.ProtectedPatient 123 | [ OK ] MongoDBBackendTest.ProtectedPatient (681 ms) 124 | [ RUN ] MongoDBBackendTest.MainDicomTags 125 | [ OK ] MongoDBBackendTest.MainDicomTags (694 ms) 126 | [----------] 8 tests from MongoDBBackendTest (5497 ms total) 127 | 128 | [----------] 1 test from ConfigurationTest 129 | [ RUN ] ConfigurationTest.Configuration 130 | [ OK ] ConfigurationTest.Configuration (0 ms) 131 | [----------] 1 test from ConfigurationTest (0 ms total) 132 | 133 | [----------] Global test environment tear-down 134 | [==========] 9 tests from 2 test cases ran. (5497 ms total) 135 | [ PASSED ] 9 tests. 136 | ``` 137 | ``` 138 | [==========] Running 1 test from 1 test case. 139 | [----------] Global test environment set-up. 140 | [----------] 1 test from MongoDBStorageTest 141 | [ RUN ] MongoDBStorageTest.StoreFiles 142 | [ OK ] MongoDBStorageTest.StoreFiles (134 ms) 143 | [----------] 1 test from MongoDBStorageTest (134 ms total) 144 | 145 | [----------] Global test environment tear-down 146 | [==========] 1 test from 1 test case ran. (135 ms total) 147 | [ PASSED ] 1 test. 148 | ``` 149 | 150 | ### Manual test results 151 | 152 | Test | Linux | Windows | OSX 153 | --- | --- | --- | --- 154 | 1. Upload test | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) 155 | 2. Delete test | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) 156 | 3. Protect-unprotect | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) 157 | 4. Send to remote modality | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) 158 | 5. Query/Retrieve from remote modality | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) | ![OK](img/green_tick.png) 159 | 160 | 161 | ### Load test results: 162 | 163 | There were couple of load cycles, of course all depends on the network/hardware setup. 164 | Tests were made on i3 (2 physical/4 logical cores) and HDD (magnetic). 165 | Loading to localhost, samples were on a separate HDD, MongoDB database on own HDD. 166 | 167 | ``` 168 | Sent 244,255 objects (=127,645.219MB) in 10,871.803s (=11.741MB/s) - avg sample size: 0.5Mb 169 | Sent 732,765 objects (=382,892.125MB) in 35,922.105s (=10.659MB/s) - avg sample size: 0.5Mb 170 | Sent 240,000 objects (=342,417.25MB) in 18,605.527s (=18.404MB/s) - avg sample size: 1.43Mb 171 | ``` 172 | 173 | -------------------------------------------------------------------------------- /docs/img/green_tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Doc-Cirrus/orthanc-mongodb/a00920750d1fc481a9fc7e472866d57d0d7bdd2e/docs/img/green_tick.png --------------------------------------------------------------------------------