Mercurial > hg > orthanc
changeset 4961:1b76853e1797 more-tags
DbOptimizer plugin
author | Alain Mazy <am@osimis.io> |
---|---|
date | Wed, 23 Mar 2022 11:56:28 +0100 |
parents | c68265bf1f94 |
children | 501411a67f10 |
files | NEWS OrthancServer/CMakeLists.txt OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h OrthancServer/Plugins/Samples/DbOptimizer/Plugin.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp |
diffstat | 6 files changed, 516 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Wed Mar 23 11:52:19 2022 +0100 +++ b/NEWS Wed Mar 23 11:56:28 2022 +0100 @@ -7,6 +7,12 @@ * New configuration "ExtraMainDicomTags" to store more tags in the Index DB to speed up, e.g, building C-Find, dicom-web or tools/find answers +* New sample plugin: "DbOptimizer" that will re-construct the DB/Storage + when it detects there is room for improvements, e.g: + - if files were stored with a version of Orthanc prior to 1.9.1, + the storage might still contain dicom-as-json files that are not needed + anymore + - if "ExtraMainDicomTags" has been changed. * New configuration "Warnings" to enable/disable individual warnings that can be identified by a W0XX prefix in the logs. These warnings have been added: @@ -25,14 +31,13 @@ may be tags from the MainDicomTags in DB, from the DICOM file or 'computed' like ModalitiesInStudy. Check the new configuration "ExtraMainDicomTags" and "Warnings" to optimize your queries. - - "EnableStorageAccessOnSearch": TODO: not implemented yet - - "EnableStorageAccessOnAnwers": TODO: not implemented yet * new query argument "requestedTags" in all API routes listing resources: - /patients, /patients/../studies, /patients/../series, /patients/../instances - /studies, /studies/../series, /studies/../instances - /series, /series/../instances - /instances -* new field "MainDicomTags" in the /system route response +* new field "MainDicomTags" in the /system route response to list the tags that + are saved in DB Documentation
--- a/OrthancServer/CMakeLists.txt Wed Mar 23 11:52:19 2022 +0100 +++ b/OrthancServer/CMakeLists.txt Wed Mar 23 11:56:28 2022 +0100 @@ -59,6 +59,7 @@ SET(BUILD_RECOVER_COMPRESSED_FILE ON CACHE BOOL "Whether to build the companion tool to recover files compressed using Orthanc") SET(BUILD_SERVE_FOLDERS ON CACHE BOOL "Whether to build the ServeFolders plugin") SET(BUILD_CONNECTIVITY_CHECKS ON CACHE BOOL "Whether to build the ConnectivityChecks plugin") +SET(BUILD_DB_OPTIMIZER ON CACHE BOOL "Whether to build the DbOptimizer plugin") SET(ENABLE_PLUGINS ON CACHE BOOL "Enable plugins") SET(UNIT_TESTS_WITH_HTTP_CONNEXIONS ON CACHE BOOL "Allow unit tests to make HTTP requests") @@ -315,6 +316,7 @@ -DHAS_ORTHANC_EXCEPTION=0 -DMODALITY_WORKLISTS_VERSION="${ORTHANC_VERSION}" -DSERVE_FOLDERS_VERSION="${ORTHANC_VERSION}" + -DDB_OPTIMIZER_VERSION="${ORTHANC_VERSION}" ) @@ -427,7 +429,7 @@ ##################################################################### if (ENABLE_PLUGINS AND - (BUILD_SERVE_FOLDERS OR BUILD_MODALITY_WORKLISTS)) + (BUILD_SERVE_FOLDERS OR BUILD_MODALITY_WORKLISTS OR BUILD_DB_OPTIMIZER)) add_library(ThirdPartyPlugins STATIC ${BOOST_SOURCES} ${JSONCPP_SOURCES} @@ -598,6 +600,50 @@ ##################################################################### +## Build the "DbOptimizer" plugin +##################################################################### + +if (ENABLE_PLUGINS AND BUILD_DB_OPTIMIZER) +if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") +execute_process( + COMMAND + ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/../OrthancFramework/Resources/WindowsResources.py + ${ORTHANC_VERSION} DbOptimizer DbOptimizer.dll "Sample Orthanc plugin to optimizer/clean the DB/Storage" + ERROR_VARIABLE Failure + OUTPUT_FILE ${AUTOGENERATED_DIR}/DbOptimizer.rc + ) + +if (Failure) + message(FATAL_ERROR "Error while computing the version information: ${Failure}") +endif() + +list(APPEND DB_OPTIMIZER_RESOURCES ${AUTOGENERATED_DIR}/DbOptimizer.rc) +endif() + +add_library(DbOptimizer SHARED +${CMAKE_SOURCE_DIR}/Plugins/Samples/DbOptimizer/Plugin.cpp +${DB_OPTIMIZER_RESOURCES} +) + +target_link_libraries(DbOptimizer + ThirdPartyPlugins + ) + +set_target_properties( + DbOptimizer PROPERTIES +VERSION ${ORTHANC_VERSION} +SOVERSION ${ORTHANC_VERSION} +) + +install( +TARGETS DbOptimizer +RUNTIME DESTINATION lib # Destination for Windows +LIBRARY DESTINATION share/orthanc/plugins # Destination for Linux +) +endif() + + +##################################################################### ## Build the companion tool to recover files compressed using Orthanc #####################################################################
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Wed Mar 23 11:52:19 2022 +0100 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Wed Mar 23 11:56:28 2022 +0100 @@ -1546,24 +1546,18 @@ " is required)"); } - - bool CheckMinimalOrthancVersion(unsigned int major, - unsigned int minor, - unsigned int revision) + bool CheckMinimalVersion(const char* version, + unsigned int major, + unsigned int minor, + unsigned int revision) { - if (!HasGlobalContext()) - { - LogError("Bad Orthanc context in the plugin"); - return false; - } - - if (!strcmp(GetGlobalContext()->orthancVersion, "mainline")) + if (!strcmp(version, "mainline")) { // Assume compatibility with the mainline return true; } - // Parse the version of the Orthanc core + // Parse the version int aa, bb, cc; if ( #ifdef _MSC_VER @@ -1571,7 +1565,7 @@ #else sscanf #endif - (GetGlobalContext()->orthancVersion, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 || + (version, "%4d.%4d.%4d", &aa, &bb, &cc) != 3 || aa < 0 || bb < 0 || cc < 0) @@ -1595,7 +1589,6 @@ return false; } - // Check the minor version number assert(a == major); @@ -1623,6 +1616,21 @@ } + bool CheckMinimalOrthancVersion(unsigned int major, + unsigned int minor, + unsigned int revision) + { + if (!HasGlobalContext()) + { + LogError("Bad Orthanc context in the plugin"); + return false; + } + + return CheckMinimalVersion(GetGlobalContext()->orthancVersion, + major, minor, revision); + } + + #if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 5, 0) const char* AutodetectMimeType(const std::string& path) {
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Wed Mar 23 11:52:19 2022 +0100 +++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h Wed Mar 23 11:56:28 2022 +0100 @@ -297,6 +297,11 @@ return str_; } + bool IsNullOrEmpty() const + { + return str_ == NULL || str_[0] == 0; + } + void ToString(std::string& target) const; void ToJson(Json::Value& target) const; @@ -604,6 +609,10 @@ unsigned int minor, unsigned int revision); + bool CheckMinimalVersion(const char* version, + unsigned int major, + unsigned int minor, + unsigned int revision); namespace Internals {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Samples/DbOptimizer/Plugin.cpp Wed Mar 23 11:56:28 2022 +0100 @@ -0,0 +1,426 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2022 Osimis S.A., Belgium + * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "../../../../OrthancFramework/Sources/Compatibility.h" +#include "../Common/OrthancPluginCppWrapper.h" + +#include <boost/thread.hpp> +#include <json/value.h> +#include <json/writer.h> +#include <string.h> +#include <iostream> +#include <algorithm> +#include <map> + +static int globalPropertyId_ = 0; +static bool force_ = false; +static uint throttleDelay_ = 0; +static std::unique_ptr<boost::thread> workerThread_; +static bool workerThreadShouldStop = false; + +struct DbConfiguration +{ + std::string orthancVersion; + std::map<OrthancPluginResourceType, std::string> mainDicomTagsSignature; + + DbConfiguration() + { + } + + bool IsDefined() const + { + return !orthancVersion.empty() && mainDicomTagsSignature.size() == 4; + } + + void Clear() + { + orthancVersion.clear(); + mainDicomTagsSignature.clear(); + } + + void ToJson(Json::Value& target) + { + if (!IsDefined()) + { + target = Json::nullValue; + } + else + { + Json::Value signatures; + + target = Json::objectValue; + + // default main dicom tags signature are the one from Orthanc 1.4.2 (last time the list was changed): + signatures["Patient"] = mainDicomTagsSignature[OrthancPluginResourceType_Patient]; + signatures["Study"] = mainDicomTagsSignature[OrthancPluginResourceType_Study]; + signatures["Series"] = mainDicomTagsSignature[OrthancPluginResourceType_Series]; + signatures["Instance"] = mainDicomTagsSignature[OrthancPluginResourceType_Instance]; + + target["MainDicomTagsSignature"] = signatures; + target["OrthancVersion"] = orthancVersion; + } + } + + void FromJson(Json::Value& source) + { + if (!source.isNull()) + { + orthancVersion = source["OrthancVersion"].asString(); + + const Json::Value& signatures = source["MainDicomTagsSignature"]; + mainDicomTagsSignature[OrthancPluginResourceType_Patient] = signatures["Patient"].asString(); + mainDicomTagsSignature[OrthancPluginResourceType_Study] = signatures["Study"].asString(); + mainDicomTagsSignature[OrthancPluginResourceType_Series] = signatures["Series"].asString(); + mainDicomTagsSignature[OrthancPluginResourceType_Instance] = signatures["Instance"].asString(); + } + } +}; + +struct PluginStatus +{ + int statusVersion; + int64_t lastProcessedChange; + int64_t lastChangeToProcess; + + DbConfiguration currentlyProcessingConfiguration; // last configuration being processed (has not reached last change yet) + DbConfiguration lastProcessedConfiguration; // last configuration that has been fully processed (till last change) + + PluginStatus() + : statusVersion(1), + lastProcessedChange(-1), + lastChangeToProcess(-1) + { + } + + void ToJson(Json::Value& target) + { + target = Json::objectValue; + + target["Version"] = statusVersion; + target["LastProcessedChange"] = Json::Value::Int64(lastProcessedChange); + target["LastChangeToProcess"] = Json::Value::Int64(lastChangeToProcess); + + currentlyProcessingConfiguration.ToJson(target["CurrentlyProcessingConfiguration"]); + lastProcessedConfiguration.ToJson(target["LastProcessedConfiguration"]); + } + + void FromJson(Json::Value& source) + { + statusVersion = source["Version"].asInt(); + lastProcessedChange = source["LastProcessedChange"].asInt64(); + lastChangeToProcess = source["LastChangeToProcess"].asInt64(); + + Json::Value& current = source["CurrentlyProcessingConfiguration"]; + Json::Value& last = source["LastProcessedConfiguration"]; + + currentlyProcessingConfiguration.FromJson(current); + lastProcessedConfiguration.FromJson(last); + } +}; + + +static void ReadStatusFromDb(PluginStatus& pluginStatus) +{ + OrthancPlugins::OrthancString globalPropertyContent; + + globalPropertyContent.Assign(OrthancPluginGetGlobalProperty(OrthancPlugins::GetGlobalContext(), + globalPropertyId_, + "")); + + if (!globalPropertyContent.IsNullOrEmpty()) + { + Json::Value jsonStatus; + globalPropertyContent.ToJson(jsonStatus); + pluginStatus.FromJson(jsonStatus); + } + else + { + // default config + pluginStatus.statusVersion = 1; + pluginStatus.lastProcessedChange = -1; + pluginStatus.lastChangeToProcess = -1; + + pluginStatus.currentlyProcessingConfiguration.orthancVersion = "1.9.0"; // when we don't know, we assume some files were stored with Orthanc 1.9.0 (last version saving the dicom-as-json files) + + // default main dicom tags signature are the one from Orthanc 1.4.2 (last time the list was changed): + pluginStatus.currentlyProcessingConfiguration.mainDicomTagsSignature[OrthancPluginResourceType_Patient] = "0010,0010;0010,0020;0010,0030;0010,0040;0010,1000"; + pluginStatus.currentlyProcessingConfiguration.mainDicomTagsSignature[OrthancPluginResourceType_Study] = "0008,0020;0008,0030;0008,0050;0008,0080;0008,0090;0008,1030;0020,000d;0020,0010;0032,1032;0032,1060"; + pluginStatus.currentlyProcessingConfiguration.mainDicomTagsSignature[OrthancPluginResourceType_Series] = "0008,0021;0008,0031;0008,0060;0008,0070;0008,1010;0008,103e;0008,1070;0018,0010;0018,0015;0018,0024;0018,1030;0018,1090;0018,1400;0020,000e;0020,0011;0020,0037;0020,0105;0020,1002;0040,0254;0054,0081;0054,0101;0054,1000"; + pluginStatus.currentlyProcessingConfiguration.mainDicomTagsSignature[OrthancPluginResourceType_Instance] = "0008,0012;0008,0013;0008,0018;0020,0012;0020,0013;0020,0032;0020,0037;0020,0100;0020,4000;0028,0008;0054,1330"; + } +} + +static void SaveStatusInDb(PluginStatus& pluginStatus) +{ + Json::Value jsonStatus; + pluginStatus.ToJson(jsonStatus); + + Json::StreamWriterBuilder builder; + builder.settings_["indentation"] = " "; + std::string serializedStatus = Json::writeString(builder, jsonStatus); + + OrthancPluginSetGlobalProperty(OrthancPlugins::GetGlobalContext(), + globalPropertyId_, + serializedStatus.c_str()); +} + +static void GetCurrentDbConfiguration(DbConfiguration& configuration) +{ + Json::Value signatures; + Json::Value systemInfo; + + OrthancPlugins::RestApiGet(systemInfo, "/system", false); + configuration.mainDicomTagsSignature[OrthancPluginResourceType_Patient] = systemInfo["MainDicomTags"]["Patient"].asString(); + configuration.mainDicomTagsSignature[OrthancPluginResourceType_Study] = systemInfo["MainDicomTags"]["Study"].asString(); + configuration.mainDicomTagsSignature[OrthancPluginResourceType_Series] = systemInfo["MainDicomTags"]["Series"].asString(); + configuration.mainDicomTagsSignature[OrthancPluginResourceType_Instance] = systemInfo["MainDicomTags"]["Instance"].asString(); + + configuration.orthancVersion = OrthancPlugins::GetGlobalContext()->orthancVersion; +} + +static bool NeedsProcessing(const DbConfiguration& current, const DbConfiguration& last) +{ + if (!last.IsDefined()) + { + return true; + } + + const char* lastVersion = last.orthancVersion.c_str(); + const std::map<OrthancPluginResourceType, std::string>& lastTags = last.mainDicomTagsSignature; + const std::map<OrthancPluginResourceType, std::string>& currentTags = current.mainDicomTagsSignature; + bool needsProcessing = false; + + if (!OrthancPlugins::CheckMinimalVersion(lastVersion, 1, 9, 1)) + { + OrthancPlugins::LogWarning("DbOptimizer: your storage might still contain some dicom-as-json files -> will reconstruct DB"); + needsProcessing = true; + } + + if (lastTags.at(OrthancPluginResourceType_Patient) != currentTags.at(OrthancPluginResourceType_Patient)) + { + OrthancPlugins::LogWarning("DbOptimizer: Patient main dicom tags have changed, -> will reconstruct DB"); + needsProcessing = true; + } + + if (lastTags.at(OrthancPluginResourceType_Study) != currentTags.at(OrthancPluginResourceType_Study)) + { + OrthancPlugins::LogWarning("DbOptimizer: Study main dicom tags have changed, -> will reconstruct DB"); + needsProcessing = true; + } + + if (lastTags.at(OrthancPluginResourceType_Series) != currentTags.at(OrthancPluginResourceType_Series)) + { + OrthancPlugins::LogWarning("DbOptimizer: Series main dicom tags have changed, -> will reconstruct DB"); + needsProcessing = true; + } + + if (lastTags.at(OrthancPluginResourceType_Instance) != currentTags.at(OrthancPluginResourceType_Instance)) + { + OrthancPlugins::LogWarning("DbOptimizer: Instance main dicom tags have changed, -> will reconstruct DB"); + needsProcessing = true; + } + + return needsProcessing; +} + +static bool ProcessChanges(PluginStatus& pluginStatus, const DbConfiguration& currentDbConfiguration) +{ + Json::Value changes; + + pluginStatus.currentlyProcessingConfiguration = currentDbConfiguration; + + OrthancPlugins::RestApiGet(changes, "/changes?since=" + boost::lexical_cast<std::string>(pluginStatus.lastProcessedChange) + "&limit=100", false); + + for (Json::ArrayIndex i = 0; i < changes["Changes"].size(); i++) + { + const Json::Value& change = changes["Changes"][i]; + int64_t seq = change["Seq"].asInt64(); + + if (change["ChangeType"] == "NewStudy") // some StableStudy might be missing if orthanc was shutdown during a StableAge -> consider only the NewStudy events that can not be missed + { + Json::Value result; + OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/reconstruct", std::string(""), false); + boost::this_thread::sleep(boost::posix_time::milliseconds(throttleDelay_*1000)); + } + + if (seq >= pluginStatus.lastChangeToProcess) // we are done ! + { + return true; + } + + pluginStatus.lastProcessedChange = seq; + } + + return false; +} + + +static void WorkerThread() +{ + PluginStatus pluginStatus; + DbConfiguration currentDbConfiguration; + + OrthancPluginLogWarning(OrthancPlugins::GetGlobalContext(), "Starting DB optimizer worker thread"); + + ReadStatusFromDb(pluginStatus); + GetCurrentDbConfiguration(currentDbConfiguration); + + if (!NeedsProcessing(currentDbConfiguration, pluginStatus.lastProcessedConfiguration)) + { + OrthancPlugins::LogWarning("DbOptimizer: everything has been processed already !"); + return; + } + + if (force_ || NeedsProcessing(currentDbConfiguration, pluginStatus.currentlyProcessingConfiguration)) + { + if (force_) + { + OrthancPlugins::LogWarning("DbOptimizer: forcing execution -> will reconstruct DB"); + } + else + { + OrthancPlugins::LogWarning("DbOptimizer: the DB configuration has changed since last run, will reprocess the whole DB !"); + } + + Json::Value changes; + OrthancPlugins::RestApiGet(changes, "/changes?last", false); + + pluginStatus.lastProcessedChange = 0; + pluginStatus.lastChangeToProcess = changes["Last"].asInt64(); // the last change is the last change at the time we start. We assume that every new ingested file will be constructed correctly + } + else + { + OrthancPlugins::LogWarning("DbOptimizer: the DB configuration has not changed since last run, will continue processing changes"); + } + + bool completed = pluginStatus.lastChangeToProcess == 0; // if the DB is empty at start, no need to process anyting + while (!workerThreadShouldStop && !completed) + { + completed = ProcessChanges(pluginStatus, currentDbConfiguration); + SaveStatusInDb(pluginStatus); + + if (!completed) + { + OrthancPlugins::LogInfo("DbOptimizer: processed changes " + + boost::lexical_cast<std::string>(pluginStatus.lastProcessedChange) + + " / " + boost::lexical_cast<std::string>(pluginStatus.lastChangeToProcess)); + + boost::this_thread::sleep(boost::posix_time::milliseconds(throttleDelay_*100)); // wait 1/10 of the delay between changes + } + } + + if (completed) + { + pluginStatus.lastProcessedConfiguration = currentDbConfiguration; + pluginStatus.currentlyProcessingConfiguration.Clear(); + + pluginStatus.lastProcessedChange = -1; + pluginStatus.lastChangeToProcess = -1; + + SaveStatusInDb(pluginStatus); + + OrthancPluginLogWarning(OrthancPlugins::GetGlobalContext(), "DbOptimizer: finished processing all changes"); + } +} + +extern "C" +{ + OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeType changeType, + OrthancPluginResourceType resourceType, + const char* resourceId) + { + switch (changeType) + { + case OrthancPluginChangeType_OrthancStarted: + { + OrthancPluginLogWarning(OrthancPlugins::GetGlobalContext(), "Starting DB Optmizer worker thread"); + workerThread_.reset(new boost::thread(WorkerThread)); + return OrthancPluginErrorCode_Success; + } + case OrthancPluginChangeType_OrthancStopped: + { + if (workerThread_ && workerThread_->joinable()) + { + workerThreadShouldStop = true; + workerThread_->join(); + } + } + default: + return OrthancPluginErrorCode_Success; + } + } + + ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c) + { + OrthancPlugins::SetGlobalContext(c); + + /* Check the version of the Orthanc core */ + if (OrthancPluginCheckVersion(c) == 0) + { + OrthancPlugins::ReportMinimalOrthancVersion(ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, + ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, + ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); + return -1; + } + + OrthancPlugins::LogWarning("DB Optimizer plugin is initializing"); + OrthancPluginSetDescription(c, "Optimizes your DB and storage."); + + OrthancPlugins::OrthancConfiguration configuration; + + OrthancPlugins::OrthancConfiguration dbOptimizer; + configuration.GetSection(dbOptimizer, "DbOptimizer"); + + bool enabled = dbOptimizer.GetBooleanValue("Enable", false); + if (enabled) + { + globalPropertyId_ = dbOptimizer.GetIntegerValue("GlobalPropertyId", 1025); + force_ = dbOptimizer.GetBooleanValue("Force", false); + throttleDelay_ = dbOptimizer.GetUnsignedIntegerValue("ThrottleDelay", 0); + OrthancPluginRegisterOnChangeCallback(c, OnChangeCallback); + } + else + { + OrthancPlugins::LogWarning("DB Optimizer plugin is disabled by the configuration file"); + } + + return 0; + } + + + ORTHANC_PLUGINS_API void OrthancPluginFinalize() + { + OrthancPlugins::LogWarning("DB Optimizer plugin is finalizing"); + } + + + ORTHANC_PLUGINS_API const char* OrthancPluginGetName() + { + return "db-optimizer"; + } + + + ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() + { + return DB_OPTIMIZER_VERSION; + } +}
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp Wed Mar 23 11:52:19 2022 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp Wed Mar 23 11:56:28 2022 +0100 @@ -52,17 +52,10 @@ { Json::Value v; - FromDcmtkBridge::FormatListOfTags(v, DicomMap::GetMainDicomTags(ResourceType_Patient)); - result["Patient"] = v; - - FromDcmtkBridge::FormatListOfTags(v, DicomMap::GetMainDicomTags(ResourceType_Study)); - result["Study"] = v; - - FromDcmtkBridge::FormatListOfTags(v, DicomMap::GetMainDicomTags(ResourceType_Series)); - result["Series"] = v; - - FromDcmtkBridge::FormatListOfTags(v, DicomMap::GetMainDicomTags(ResourceType_Instance)); - result["Instance"] = v; + result["Patient"] = DicomMap::GetMainDicomTagsSignature(ResourceType_Patient); + result["Study"] = DicomMap::GetMainDicomTagsSignature(ResourceType_Study); + result["Series"] = DicomMap::GetMainDicomTagsSignature(ResourceType_Series); + result["Instance"] = DicomMap::GetMainDicomTagsSignature(ResourceType_Instance); } static void GetSystemInformation(RestApiGetCall& call)