Mercurial > hg > orthanc
changeset 4944:f377d5643538 more-tags
new Warnings configuration + InstanceAvailability tag
author | Alain Mazy <am@osimis.io> |
---|---|
date | Thu, 17 Mar 2022 17:03:59 +0100 |
parents | 96a3e81eba90 |
children | 3778a0433dd3 |
files | NEWS OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake OrthancFramework/Sources/DicomFormat/DicomMap.cpp OrthancFramework/Sources/DicomFormat/DicomTag.h OrthancFramework/UnitTestsSources/DicomMapTests.cpp OrthancServer/Resources/Configuration.json OrthancServer/Sources/OrthancConfiguration.cpp OrthancServer/Sources/OrthancConfiguration.h OrthancServer/Sources/OrthancInitialization.cpp OrthancServer/Sources/ServerContext.cpp TODO |
diffstat | 11 files changed, 181 insertions(+), 28 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Wed Mar 16 09:50:33 2022 +0100 +++ b/NEWS Thu Mar 17 17:03:59 2022 +0100 @@ -2,6 +2,38 @@ =============================== +General +------- + +* 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 configuration "Warnings" to enable/disable individual warnings that can + be identified by a W0XX prefix in the logs. + These warnings have been added: + - W001_TagsBeingReadFromStorage + - W002_InconsistentDicomTagsInDb +* C-Find and QIDO-RS can now return the InstanceAvailability tag. Value is + always "ONLINE" + +REST API +-------- + +* API version upgraded to 17 +* new options in tools/find: + - "RequestedTags" (to use together with "Expand": true) contains a list of tags + that you'll receive in the "RequestedTags" field in the answers. These tags + 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 + + Documentation -------------
--- a/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkParameters.cmake Thu Mar 17 17:03:59 2022 +0100 @@ -38,7 +38,7 @@ # Version of the Orthanc API, can be retrieved from "/system" URI in # order to check whether new URI endpoints are available even if using # the mainline version of Orthanc -set(ORTHANC_API_VERSION "16") +set(ORTHANC_API_VERSION "17") #####################################################################
--- a/OrthancFramework/Sources/DicomFormat/DicomMap.cpp Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancFramework/Sources/DicomFormat/DicomMap.cpp Thu Mar 17 17:03:59 2022 +0100 @@ -634,16 +634,28 @@ IsMainDicomTag(tag, ResourceType_Instance)); } + static bool IsGenericComputedTag(const DicomTag& tag) + { + return tag == DICOM_TAG_RETRIEVE_URL || + tag == DICOM_TAG_RETRIEVE_AE_TITLE; + } + bool DicomMap::IsComputedTag(const DicomTag& tag) { return (IsComputedTag(tag, ResourceType_Patient) || IsComputedTag(tag, ResourceType_Study) || IsComputedTag(tag, ResourceType_Series) || - IsComputedTag(tag, ResourceType_Instance)); + IsComputedTag(tag, ResourceType_Instance) || + IsGenericComputedTag(tag)); } bool DicomMap::IsComputedTag(const DicomTag& tag, ResourceType level) { + if (IsGenericComputedTag(tag)) + { + return true; + } + switch (level) { case ResourceType_Patient: @@ -664,7 +676,9 @@ tag == DICOM_TAG_NUMBER_OF_SERIES_RELATED_INSTANCES ); case ResourceType_Instance: - return false; + return ( + tag == DICOM_TAG_INSTANCE_AVAILABILITY + ); default: throw OrthancException(ErrorCode_ParameterOutOfRange); }
--- a/OrthancFramework/Sources/DicomFormat/DicomTag.h Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancFramework/Sources/DicomFormat/DicomTag.h Thu Mar 17 17:03:59 2022 +0100 @@ -117,6 +117,7 @@ static const DicomTag DICOM_TAG_QUERY_RETRIEVE_LEVEL(0x0008, 0x0052); static const DicomTag DICOM_TAG_MODALITIES_IN_STUDY(0x0008, 0x0061); static const DicomTag DICOM_TAG_RETRIEVE_AE_TITLE(0x0008, 0x0054); + static const DicomTag DICOM_TAG_INSTANCE_AVAILABILITY(0x0008, 0x0056); // Tags for images static const DicomTag DICOM_TAG_COLUMNS(0x0028, 0x0011); @@ -229,4 +230,8 @@ static const DicomTag DICOM_TAG_OFFSET_OF_REFERENCED_LOWER_LEVEL_DIRECTORY_ENTITY(0x0004, 0x1420); static const DicomTag DICOM_TAG_REFERENCED_SOP_INSTANCE_UID_IN_FILE(0x0004, 0x1511); static const DicomTag DICOM_TAG_REFERENCED_FILE_ID(0x0004, 0x1500); + + // Tags for DicomWeb + static const Orthanc::DicomTag DICOM_TAG_RETRIEVE_URL(0x0008, 0x1190); + }
--- a/OrthancFramework/UnitTestsSources/DicomMapTests.cpp Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancFramework/UnitTestsSources/DicomMapTests.cpp Thu Mar 17 17:03:59 2022 +0100 @@ -582,6 +582,7 @@ { std::set<DicomTag> tags; tags.insert(DICOM_TAG_MODALITIES_IN_STUDY); + tags.insert(DICOM_TAG_RETRIEVE_URL); ASSERT_TRUE(DicomMap::HasOnlyComputedTags(tags)); ASSERT_TRUE(DicomMap::HasComputedTags(tags, ResourceType_Study));
--- a/OrthancServer/Resources/Configuration.json Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancServer/Resources/Configuration.json Thu Mar 17 17:03:59 2022 +0100 @@ -863,6 +863,7 @@ // Extra Main Dicom tags that are stored in DB together with all default // Main Dicom tags that are already stored (TODO: see book new page). // (new in Orthanc 1.11.0) + // Sequences tags are not supported. /** "ExtraMainDicomTags" : { "Instance" : [ @@ -883,11 +884,23 @@ }, */ - // Enables/disables a warning notifying you when you try to access a - // resource that has been saved with a different version of the - // ExtraMainDicomTags list + // Enables/disables warnings in the logs. + // "true" enables a warning. All warnings are enabled by default // TODO: see book new page // (new in Orthanc 1.11.0) - "EnableLogsForInconsistentMainDicomTags": true + "Warnings" : { + // A "RequestedTags" has been read from storage which is slower than + // reading it from DB. + // You might want to store this tag in ExtraMainDicomTags to build + // the response faster. + "W001_TagsBeingReadFromStorage": true, + + // Retrieving a list of Main dicom tags from a resource that has been + // saved with another "ExtraMainDicomTags" configuration which means that + // your response might be incomplete/inconsistent. + // You should call patients|studies|series|instances/../reconstruct to rebuild + // the DB. TODO: also check for "rebuild DB" plugin + "W002_InconsistentDicomTagsInDb": true + } }
--- a/OrthancServer/Sources/OrthancConfiguration.cpp Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancServer/Sources/OrthancConfiguration.cpp Thu Mar 17 17:03:59 2022 +0100 @@ -43,6 +43,7 @@ static const char* const ORTHANC_PEERS_IN_DB = "OrthancPeersInDatabase"; static const char* const TEMPORARY_DIRECTORY = "TemporaryDirectory"; static const char* const DATABASE_SERVER_IDENTIFIER = "DatabaseServerIdentifier"; +static const char* const WARNINGS = "Warnings"; namespace Orthanc { @@ -1055,14 +1056,48 @@ } } - bool OrthancConfiguration::IsInconsistentDicomTagsLogsEnabled() const + void OrthancConfiguration::LoadWarnings() { - return GetBooleanParameter("EnableLogsForInconsistentMainDicomTags", true); - } + if (json_.isMember(WARNINGS)) + { + const Json::Value& warnings = json_[WARNINGS]; + if (!warnings.isObject()) + { + throw OrthancException(ErrorCode_BadFileFormat, std::string(WARNINGS) + " configuration entry is not a Json object"); + } + + Json::Value::Members members = warnings.getMemberNames(); + + for (size_t i = 0; i < members.size(); i++) + { + const std::string& name = members[i]; + bool enabled = warnings[name].asBool(); - bool OrthancConfiguration::IsStorageAccessOnFindLogsEnabled() const - { - return GetBooleanParameter("EnableLogsForStorageAccessOnFind", true); + Warnings warning = Warnings_None; + if (name == "W001_TagsBeingReadFromStorage") + { + warning = Warnings_001_TagsBeingReadFromStorage; + } + else if (name == "W002_InconsistentDicomTagsInDb") + { + warning = Warnings_002_InconsistentDicomTagsInDb; + } + else + { + throw OrthancException(ErrorCode_BadFileFormat, name + " is not recognized as a valid warning name"); + } + + if (!enabled) + { + disabledWarnings_.insert(warning); + } + } + } + else + { + disabledWarnings_.clear(); + } + }
--- a/OrthancServer/Sources/OrthancConfiguration.h Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancServer/Sources/OrthancConfiguration.h Thu Mar 17 17:03:59 2022 +0100 @@ -31,6 +31,7 @@ #include <boost/filesystem.hpp> #include <boost/thread/shared_mutex.hpp> #include <boost/thread/lock_types.hpp> +#include <set> class DcmDataset; @@ -42,7 +43,15 @@ class ParsedDicomFile; class ServerIndex; class TemporaryFile; - + + enum Warnings + { + Warnings_None, + Warnings_001_TagsBeingReadFromStorage, + Warnings_002_InconsistentDicomTagsInDb, + }; + + class OrthancConfiguration : public boost::noncopyable { private: @@ -58,6 +67,7 @@ Modalities modalities_; Peers peers_; ServerIndex* serverIndex_; + std::set<Warnings> disabledWarnings_; OrthancConfiguration() : configurationFileArg_(NULL), @@ -153,7 +163,9 @@ // "SetServerIndex()" must have been called void LoadModalitiesAndPeers(); - + + void LoadWarnings(); + void RegisterFont(ServerResources::FileResourceId resource); bool LookupStringParameter(std::string& target, @@ -242,9 +254,10 @@ std::string GetDatabaseServerIdentifier() const; - bool IsInconsistentDicomTagsLogsEnabled() const; - - bool IsStorageAccessOnFindLogsEnabled() const; + bool IsWarningEnabled(Warnings warning) const + { + return disabledWarnings_.count(warning) == 0; + } static void DefaultExtractDicomSummary(DicomMap& target, const ParsedDicomFile& dicom);
--- a/OrthancServer/Sources/OrthancInitialization.cpp Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancServer/Sources/OrthancInitialization.cpp Thu Mar 17 17:03:59 2022 +0100 @@ -252,8 +252,24 @@ { const std::string& tagName = content[t].asString(); DicomTag tag(FromDcmtkBridge::ParseTag(tagName)); - DicomMap::AddMainDicomTag(tag, tagName, level); - LOG(INFO) << " - " << tagName; + + if (DicomMap::IsComputedTag(tag)) + { + LOG(WARNING) << " - " << tagName << " can not be added in the Extra Main Dicom Tags since the value of this tag is computed when requested"; + } + else + { + ValueRepresentation vr = FromDcmtkBridge::LookupValueRepresentation(tag); + if (vr == ValueRepresentation_Sequence) + { + LOG(WARNING) << " - " << tagName << " can not be added in the Extra Main Dicom Tags since it is a sequence"; + } + else + { + DicomMap::AddMainDicomTag(tag, tagName, level); + LOG(INFO) << " - " << tagName; + } + } } } } @@ -357,6 +373,8 @@ LoadExternalDictionaries(lock.GetJson()); // New in Orthanc 1.9.4 LoadCustomDictionary(lock.GetJson()); + lock.GetConfiguration().LoadWarnings(); + LoadMainDicomTags(lock.GetJson()); // New in Orthanc 1.11.0 lock.GetConfiguration().RegisterFont(ServerResources::FONT_UBUNTU_MONO_BOLD_16);
--- a/OrthancServer/Sources/ServerContext.cpp Wed Mar 16 09:50:33 2022 +0100 +++ b/OrthancServer/Sources/ServerContext.cpp Thu Mar 17 17:03:59 2022 +0100 @@ -1405,7 +1405,7 @@ // Case (1): The main DICOM tags, as stored in the database, // are sufficient to look for match - if (!GetIndex().GetAllMainDicomTags(allMainDicomTagsFromDB, instances[i])) // MORE_TAGS: TODO: we could read only the current and upper level to reduce the number of SQL queries + if (!GetIndex().GetAllMainDicomTags(allMainDicomTagsFromDB, instances[i])) { // The instance has been removed during the execution of the // lookup, ignore it @@ -2256,6 +2256,19 @@ } + static void ComputeInstanceTags(ExpandedResource& resource, + ServerContext& context, + const std::string& instancePublicId, + const std::set<DicomTag>& requestedTags) + { + if (requestedTags.count(DICOM_TAG_INSTANCE_AVAILABILITY) > 0) + { + resource.tags_.SetValue(DICOM_TAG_INSTANCE_AVAILABILITY, "ONLINE", false); + resource.missingRequestedTags_.erase(DICOM_TAG_INSTANCE_AVAILABILITY); + } + } + + static void ComputeSeriesTags(ExpandedResource& resource, ServerContext& context, const std::string& seriesPublicId, @@ -2451,6 +2464,12 @@ { ComputeSeriesTags(resource, context, resourceId, requestedTags); } + + if (level == ResourceType_Instance + && DicomMap::HasComputedTags(resource.missingRequestedTags_, ResourceType_Instance)) + { + ComputeInstanceTags(resource, context, resourceId, requestedTags); + } } bool ServerContext::ExpandResource(Json::Value& target, @@ -2537,9 +2556,9 @@ if (resource.mainDicomTagsSignature_ != DicomMap::GetMainDicomTagsSignature(resource.type_)) { OrthancConfiguration::ReaderLock lock; - if (lock.GetConfiguration().IsInconsistentDicomTagsLogsEnabled()) + if (lock.GetConfiguration().IsWarningEnabled(Warnings_002_InconsistentDicomTagsInDb)) { - LOG(WARNING) << Orthanc::GetResourceTypeText(resource.type_, false , false) << " has been stored with another version of Main Dicom Tags list, you should POST to /" << Orthanc::GetResourceTypeText(resource.type_, true, false) << "/" << resource.id_ << "/reconstruct to update the list of tags saved in DB. Some MainDicomTags might be missing from this answer."; + LOG(WARNING) << "W002: " << Orthanc::GetResourceTypeText(resource.type_, false , false) << " has been stored with another version of Main Dicom Tags list, you should POST to /" << Orthanc::GetResourceTypeText(resource.type_, true, false) << "/" << resource.id_ << "/reconstruct to update the list of tags saved in DB. Some MainDicomTags might be missing from this answer."; } } @@ -2547,7 +2566,7 @@ if (!resource.missingRequestedTags_.empty() && !DicomMap::HasOnlyComputedTags(resource.missingRequestedTags_)) { OrthancConfiguration::ReaderLock lock; - if (lock.GetConfiguration().IsStorageAccessOnFindLogsEnabled()) + if (lock.GetConfiguration().IsWarningEnabled(Warnings_001_TagsBeingReadFromStorage)) { std::set<DicomTag> missingTags; Toolbox::AppendSets(missingTags, resource.missingRequestedTags_); @@ -2562,7 +2581,7 @@ std::string missings; FromDcmtkBridge::FormatListOfTags(missings, missingTags); - LOG(WARNING) << "PERFORMANCE WARNING: Accessing Dicom tags from storage when accessing " << Orthanc::GetResourceTypeText(resource.type_, false , false) << " : " << missings; + LOG(WARNING) << "W001: Accessing Dicom tags from storage when accessing " << Orthanc::GetResourceTypeText(resource.type_, false , false) << " : " << missings; }
--- a/TODO Wed Mar 16 09:50:33 2022 +0100 +++ b/TODO Thu Mar 17 17:03:59 2022 +0100 @@ -120,9 +120,6 @@ - On SCP side: done by https://hg.orthanc-server.com/orthanc/rev/1ec3e1e18f50 - On SCU side: https://groups.google.com/d/msg/orthanc-users/wPl0g5mqZco/5X1Z8tEzBgAJ -* Support "Instance Availability" (0008,0056) in C-FIND: - http://dicom.nema.org/medical/DICOM/2019a/output/chtml/part04/sect_C.4.html#sect_C.4.1.1.3.2 - https://groups.google.com/d/msg/orthanc-users/hteDgE6igo8/j-ArqD7pBQAJ * Check Big Endian transfer syntax in ParsedDicomFile::EmbedImage and DicomImageDecoder * Strict hierarchical C-FIND: @@ -160,6 +157,12 @@ https://groups.google.com/g/orthanc-users/c/aN8nqcRd3jw/m/pmc9ylVeAwAJ. One solution could be: Filter first without ModalitiesInStudies and then cycle through the responses to filter out with ModalitiesInStudies + For C-Find results: we could store the computed tags + in metadata on some events like NewSeries + DeletedSeries (same for other computer tags). + OtherTags that could be saved in Metadata as well: + - ModalitiesInStudy + - all computed counters at series/study/patient level + - RequestAttributesSequence (sequence that must be included in all DicomWeb QIDO-RS for series) ======== Database