# HG changeset patch # User Sebastien Jodogne # Date 1382012413 -7200 # Node ID ce5d2040c47bba7a2f1828f7eae24df27f358f73 # Parent c2be0a0e049e92d01a6f8545c5770e0ffeea5d7d# Parent 49945044b06dc77c24edb0c63b2c064fa90aca17 integration mainline -> find-move-scp diff -r 49945044b06d -r ce5d2040c47b CMakeLists.txt --- a/CMakeLists.txt Tue Oct 15 16:23:42 2013 +0200 +++ b/CMakeLists.txt Thu Oct 17 14:20:13 2013 +0200 @@ -90,6 +90,7 @@ # Prepare the embedded files set(EMBEDDED_FILES PREPARE_DATABASE ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabase.sql + PREPARE_DATABASE_V4 ${CMAKE_CURRENT_SOURCE_DIR}/OrthancServer/PrepareDatabaseV4.sql CONFIGURATION_SAMPLE ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Configuration.json LUA_TOOLBOX ${CMAKE_CURRENT_SOURCE_DIR}/Resources/Toolbox.lua ) @@ -209,6 +210,7 @@ OrthancServer/ServerContext.cpp OrthancServer/ServerEnumerations.cpp OrthancServer/ServerToolbox.cpp + OrthancServer/OrthancFindRequestHandler.cpp ) # Ensure autogenerated code is built before building ServerLibrary diff -r 49945044b06d -r ce5d2040c47b Core/DicomFormat/DicomMap.cpp --- a/Core/DicomFormat/DicomMap.cpp Tue Oct 15 16:23:42 2013 +0200 +++ b/Core/DicomFormat/DicomMap.cpp Thu Oct 17 14:20:13 2013 +0200 @@ -280,4 +280,110 @@ SetValue(tag, source.GetValue(tag)); } } + + + bool DicomMap::IsMainDicomTag(const DicomTag& tag, ResourceType level) + { + DicomTag *tags = NULL; + size_t size; + + switch (level) + { + case ResourceType_Patient: + tags = patientTags; + size = sizeof(patientTags) / sizeof(DicomTag); + break; + + case ResourceType_Study: + tags = studyTags; + size = sizeof(studyTags) / sizeof(DicomTag); + break; + + case ResourceType_Series: + tags = seriesTags; + size = sizeof(seriesTags) / sizeof(DicomTag); + break; + + case ResourceType_Instance: + tags = instanceTags; + size = sizeof(instanceTags) / sizeof(DicomTag); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + for (size_t i = 0; i < size; i++) + { + if (tags[i] == tag) + { + return true; + } + } + + return false; + } + + bool DicomMap::IsMainDicomTag(const DicomTag& tag) + { + return (IsMainDicomTag(tag, ResourceType_Patient) || + IsMainDicomTag(tag, ResourceType_Study) || + IsMainDicomTag(tag, ResourceType_Series) || + IsMainDicomTag(tag, ResourceType_Instance)); + } + + + void DicomMap::GetMainDicomTagsInternal(std::set& result, ResourceType level) + { + DicomTag *tags = NULL; + size_t size; + + switch (level) + { + case ResourceType_Patient: + tags = patientTags; + size = sizeof(patientTags) / sizeof(DicomTag); + break; + + case ResourceType_Study: + tags = studyTags; + size = sizeof(studyTags) / sizeof(DicomTag); + break; + + case ResourceType_Series: + tags = seriesTags; + size = sizeof(seriesTags) / sizeof(DicomTag); + break; + + case ResourceType_Instance: + tags = instanceTags; + size = sizeof(instanceTags) / sizeof(DicomTag); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + for (size_t i = 0; i < size; i++) + { + result.insert(tags[i]); + } + } + + + void DicomMap::GetMainDicomTags(std::set& result, ResourceType level) + { + result.clear(); + GetMainDicomTagsInternal(result, level); + } + + + void DicomMap::GetMainDicomTags(std::set& result) + { + result.clear(); + GetMainDicomTagsInternal(result, ResourceType_Patient); + GetMainDicomTagsInternal(result, ResourceType_Study); + GetMainDicomTagsInternal(result, ResourceType_Series); + GetMainDicomTagsInternal(result, ResourceType_Instance); + } } diff -r 49945044b06d -r ce5d2040c47b Core/DicomFormat/DicomMap.h --- a/Core/DicomFormat/DicomMap.h Tue Oct 15 16:23:42 2013 +0200 +++ b/Core/DicomFormat/DicomMap.h Thu Oct 17 14:20:13 2013 +0200 @@ -35,7 +35,9 @@ #include "DicomTag.h" #include "DicomValue.h" #include "DicomString.h" +#include "../Enumerations.h" +#include #include #include @@ -63,6 +65,8 @@ void ExtractTags(DicomMap& source, const DicomTag* tags, size_t count) const; + + static void GetMainDicomTagsInternal(std::set& result, ResourceType level); public: DicomMap() @@ -148,5 +152,13 @@ void CopyTagIfExists(const DicomMap& source, const DicomTag& tag); + + static bool IsMainDicomTag(const DicomTag& tag, ResourceType level); + + static bool IsMainDicomTag(const DicomTag& tag); + + static void GetMainDicomTags(std::set& result, ResourceType level); + + static void GetMainDicomTags(std::set& result); }; } diff -r 49945044b06d -r ce5d2040c47b Core/DicomFormat/DicomTag.h --- a/Core/DicomFormat/DicomTag.h Tue Oct 15 16:23:42 2013 +0200 +++ b/Core/DicomFormat/DicomTag.h Thu Oct 17 14:20:13 2013 +0200 @@ -110,4 +110,8 @@ // DICOM tags used for fMRI (thanks to Will Ryder) static const DicomTag DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS(0x0020, 0x0105); static const DicomTag DICOM_TAG_TEMPORAL_POSITION_IDENTIFIER(0x0020, 0x0100); + + // Tags for C-FIND and C-MOVE + static const DicomTag DICOM_TAG_SPECIFIC_CHARACTER_SET(0x0008, 0x0005); + static const DicomTag DICOM_TAG_QUERY_RETRIEVE_LEVEL(0x0008, 0x0052); } diff -r 49945044b06d -r ce5d2040c47b Core/Enumerations.cpp --- a/Core/Enumerations.cpp Tue Oct 15 16:23:42 2013 +0200 +++ b/Core/Enumerations.cpp Thu Oct 17 14:20:13 2013 +0200 @@ -33,6 +33,7 @@ #include "Enumerations.h" #include "OrthancException.h" +#include "Toolbox.h" namespace Orthanc { @@ -222,4 +223,54 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); } } + + + const char* EnumerationToString(ResourceType type) + { + switch (type) + { + case ResourceType_Patient: + return "Patient"; + + case ResourceType_Study: + return "Study"; + + case ResourceType_Series: + return "Series"; + + case ResourceType_Instance: + return "Instance"; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + ResourceType StringToResourceType(const char* type) + { + std::string s(type); + Toolbox::ToUpperCase(s); + + if (s == "PATIENT") + { + return ResourceType_Patient; + } + else if (s == "STUDY") + { + return ResourceType_Study; + } + else if (s == "SERIES") + { + return ResourceType_Series; + } + else if (s == "INSTANCE" || s == "IMAGE") + { + return ResourceType_Instance; + } + else + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } } diff -r 49945044b06d -r ce5d2040c47b Core/Enumerations.h --- a/Core/Enumerations.h Tue Oct 15 16:23:42 2013 +0200 +++ b/Core/Enumerations.h Thu Oct 17 14:20:13 2013 +0200 @@ -228,9 +228,20 @@ FileContentType_Json = 2 }; + enum ResourceType + { + ResourceType_Patient = 1, + ResourceType_Study = 2, + ResourceType_Series = 3, + ResourceType_Instance = 4 + }; const char* EnumerationToString(HttpMethod method); const char* EnumerationToString(HttpStatus status); + + const char* EnumerationToString(ResourceType type); + + ResourceType StringToResourceType(const char* type); } diff -r 49945044b06d -r ce5d2040c47b OrthancServer/DatabaseWrapper.cpp --- a/OrthancServer/DatabaseWrapper.cpp Tue Oct 15 16:23:42 2013 +0200 +++ b/OrthancServer/DatabaseWrapper.cpp Thu Oct 17 14:20:13 2013 +0200 @@ -807,7 +807,7 @@ db_.Execute(query); } - // Sanity check of the version of the database + // Check the version of the database std::string version = GetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "Unknown"); bool ok = false; try @@ -815,9 +815,27 @@ LOG(INFO) << "Version of the Orthanc database: " << version; unsigned int v = boost::lexical_cast(version); - // This version of Orthanc is only compatible with version 3 of - // the DB schema (since Orthanc 0.3.2) - ok = (v == 3); + // This version of Orthanc is only compatible with versions 3 + // (Orthanc 0.3.2 to 0.6.1) and 4 (since Orthanc 0.6.2) of the + // DB schema + ok = (v == 3 || v == 4); + + if (v == 3) + { + LOG(WARNING) << "Upgrading the database from version 3 to version 4 (reconstructing the index)"; + + // Reconstruct the index for case insensitive queries in C-FIND + db_.Execute("DROP INDEX IF EXISTS MainDicomTagsIndexValues;"); + db_.Execute("DROP TABLE IF EXISTS AvailableTags;"); + + std::string query; + EmbeddedResources::GetFileResource(query, EmbeddedResources::PREPARE_DATABASE_V4); + db_.Execute(query); + + db_.Execute("INSERT INTO AvailableTags SELECT DISTINCT tagGroup, tagElement FROM MainDicomTags;"); + + //SetGlobalProperty(GlobalProperty_DatabaseSchemaVersion, "4"); + } } catch (boost::bad_lexical_cast&) { @@ -828,6 +846,8 @@ throw OrthancException(ErrorCode_IncompatibleDatabaseVersion); } + CompleteMainDicomTags(); + signalRemainingAncestor_ = new Internals::SignalRemainingAncestor; db_.Register(signalRemainingAncestor_); db_.Register(new Internals::SignalFileDeleted(listener_)); @@ -995,4 +1015,11 @@ result.push_back(s.ColumnInt64(0)); } } + + + void DatabaseWrapper::CompleteMainDicomTags() + { + std::set requiredTags; + + } } diff -r 49945044b06d -r ce5d2040c47b OrthancServer/DatabaseWrapper.h --- a/OrthancServer/DatabaseWrapper.h Tue Oct 15 16:23:42 2013 +0200 +++ b/OrthancServer/DatabaseWrapper.h Thu Oct 17 14:20:13 2013 +0200 @@ -72,6 +72,8 @@ int64_t since, unsigned int maxResults); + void CompleteMainDicomTags(); + public: void SetGlobalProperty(GlobalProperty property, const std::string& value); diff -r 49945044b06d -r ce5d2040c47b OrthancServer/OrthancFindRequestHandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Thu Oct 17 14:20:13 2013 +0200 @@ -0,0 +1,108 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * 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. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + +#include "OrthancFindRequestHandler.h" + +#include + +#include "../Core/DicomFormat/DicomArray.h" + +namespace Orthanc +{ + void OrthancFindRequestHandler::Handle(const DicomMap& input, + DicomFindAnswers& answers) + { + LOG(WARNING) << "Find-SCU request received"; + + /** + * Retrieve the query level. + **/ + + const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL); + if (levelTmp == NULL) + { + throw OrthancException(ErrorCode_BadRequest); + } + + ResourceType level = StringToResourceType(levelTmp->AsString().c_str()); + + if (level != ResourceType_Patient && + level != ResourceType_Study && + level != ResourceType_Series) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + + /** + * Retrieve the constraints of the query. + **/ + + DicomArray query(input); + + DicomMap constraintsTmp; + DicomMap wildcardConstraintsTmp; + + for (size_t i = 0; i < query.GetSize(); i++) + { + if (!query.GetElement(i).GetValue().IsNull() && + query.GetElement(i).GetTag() != DICOM_TAG_QUERY_RETRIEVE_LEVEL && + query.GetElement(i).GetTag() != DICOM_TAG_SPECIFIC_CHARACTER_SET) + { + DicomTag tag = query.GetElement(i).GetTag(); + std::string value = query.GetElement(i).GetValue().AsString(); + + if (value.find('*') != std::string::npos || + value.find('?') != std::string::npos || + value.find('\\') != std::string::npos || + value.find('-') != std::string::npos) + { + wildcardConstraintsTmp.SetValue(tag, value); + } + else + { + constraintsTmp.SetValue(tag, value); + } + } + } + + DicomArray constraints(constraintsTmp); + DicomArray wildcardConstraints(wildcardConstraintsTmp); + + // http://www.itk.org/Wiki/DICOM_QueryRetrieve_Explained + // http://dicomiseasy.blogspot.be/2012/01/dicom-queryretrieve-part-i.html + + constraints.Print(stdout); + printf("\n"); fflush(stdout); + wildcardConstraints.Print(stdout); + printf("\n"); fflush(stdout); + } +} diff -r 49945044b06d -r ce5d2040c47b OrthancServer/OrthancFindRequestHandler.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/OrthancFindRequestHandler.h Thu Oct 17 14:20:13 2013 +0200 @@ -0,0 +1,54 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, + * 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. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + +#pragma once + +#include "DicomProtocol/IFindRequestHandler.h" + +#include "ServerContext.h" + +namespace Orthanc +{ + class OrthancFindRequestHandler : public IFindRequestHandler + { + private: + ServerContext& context_; + + public: + OrthancFindRequestHandler(ServerContext& context) : + context_(context) + { + } + + virtual void Handle(const DicomMap& input, + DicomFindAnswers& answers); + }; +} diff -r 49945044b06d -r ce5d2040c47b OrthancServer/PrepareDatabase.sql --- a/OrthancServer/PrepareDatabase.sql Tue Oct 15 16:23:42 2013 +0200 +++ b/OrthancServer/PrepareDatabase.sql Thu Oct 17 14:20:13 2013 +0200 @@ -67,7 +67,6 @@ CREATE INDEX MainDicomTagsIndex1 ON MainDicomTags(id); CREATE INDEX MainDicomTagsIndex2 ON MainDicomTags(tagGroup, tagElement); -CREATE INDEX MainDicomTagsIndexValues ON MainDicomTags(value COLLATE BINARY); CREATE INDEX ChangesIndex ON Changes(internalId); diff -r 49945044b06d -r ce5d2040c47b OrthancServer/PrepareDatabaseV4.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/PrepareDatabaseV4.sql Thu Oct 17 14:20:13 2013 +0200 @@ -0,0 +1,12 @@ +-- New in database version 4 +CREATE TABLE AvailableTags( + tagGroup INTEGER, + tagElement INTEGER, + PRIMARY KEY(tagGroup, tagElement) + ); + +-- Until database version 4, the following index was set to "COLLATE +-- BINARY". This implies case-sensitive searches, but DICOM C-Find +-- requires case-insensitive searches. +-- http://www.sqlite.org/optoverview.html#like_opt +CREATE INDEX MainDicomTagsIndexValues ON MainDicomTags(value COLLATE NOCASE); diff -r 49945044b06d -r ce5d2040c47b OrthancServer/ServerEnumerations.cpp --- a/OrthancServer/ServerEnumerations.cpp Tue Oct 15 16:23:42 2013 +0200 +++ b/OrthancServer/ServerEnumerations.cpp Thu Oct 17 14:20:13 2013 +0200 @@ -33,6 +33,7 @@ #include "../Core/OrthancException.h" #include "../Core/EnumerationDictionary.h" +#include "../Core/Toolbox.h" #include @@ -82,27 +83,6 @@ return dictMetadataType_.Translate(str); } - const char* EnumerationToString(ResourceType type) - { - switch (type) - { - case ResourceType_Patient: - return "Patient"; - - case ResourceType_Study: - return "Study"; - - case ResourceType_Series: - return "Series"; - - case ResourceType_Instance: - return "Instance"; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } - } - std::string GetBasePath(ResourceType type, const std::string& publicId) { @@ -289,6 +269,4 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); } } - - } diff -r 49945044b06d -r ce5d2040c47b OrthancServer/ServerEnumerations.h --- a/OrthancServer/ServerEnumerations.h Tue Oct 15 16:23:42 2013 +0200 +++ b/OrthancServer/ServerEnumerations.h Thu Oct 17 14:20:13 2013 +0200 @@ -33,6 +33,8 @@ #include +#include "../Core/Enumerations.h" + namespace Orthanc { enum SeriesStatus @@ -71,14 +73,6 @@ GlobalProperty_AnonymizationSequence = 3 }; - enum ResourceType - { - ResourceType_Patient = 1, - ResourceType_Study = 2, - ResourceType_Series = 3, - ResourceType_Instance = 4 - }; - enum MetadataType { MetadataType_Instance_IndexInSeries = 1, @@ -122,8 +116,6 @@ MetadataType StringToMetadata(const std::string& str); - const char* EnumerationToString(ResourceType type); - std::string EnumerationToString(MetadataType type); const char* EnumerationToString(SeriesStatus status); diff -r 49945044b06d -r ce5d2040c47b OrthancServer/main.cpp --- a/OrthancServer/main.cpp Tue Oct 15 16:23:42 2013 +0200 +++ b/OrthancServer/main.cpp Thu Oct 17 14:20:13 2013 +0200 @@ -43,6 +43,7 @@ #include "DicomProtocol/DicomServer.h" #include "OrthancInitialization.h" #include "ServerContext.h" +#include "OrthancFindRequestHandler.h" using namespace Orthanc; @@ -72,27 +73,6 @@ }; -class MyFindRequestHandler : public IFindRequestHandler -{ -private: - ServerContext& context_; - -public: - MyFindRequestHandler(ServerContext& context) : - context_(context) - { - } - - virtual void Handle(const DicomMap& input, - DicomFindAnswers& answers) - { - LOG(WARNING) << "Find-SCU request received"; - DicomArray a(input); - a.Print(stdout); - } -}; - - class MyMoveRequestHandler : public IMoveRequestHandler { private: @@ -134,7 +114,7 @@ virtual IFindRequestHandler* ConstructFindRequestHandler() { - return new MyFindRequestHandler(context_); + return new OrthancFindRequestHandler(context_); } virtual IMoveRequestHandler* ConstructMoveRequestHandler() @@ -369,14 +349,13 @@ MyDicomServerFactory serverFactory(context); - { // DICOM server DicomServer dicomServer; dicomServer.SetCalledApplicationEntityTitleCheck(GetGlobalBoolParameter("DicomCheckCalledAet", false)); dicomServer.SetStoreRequestHandlerFactory(serverFactory); - //dicomServer.SetMoveRequestHandlerFactory(serverFactory); - //dicomServer.SetFindRequestHandlerFactory(serverFactory); + dicomServer.SetMoveRequestHandlerFactory(serverFactory); + dicomServer.SetFindRequestHandlerFactory(serverFactory); dicomServer.SetPortNumber(GetGlobalIntegerParameter("DicomPort", 4242)); dicomServer.SetApplicationEntityTitle(GetGlobalStringParameter("DicomAet", "ORTHANC")); diff -r 49945044b06d -r ce5d2040c47b UnitTests/ServerIndex.cpp --- a/UnitTests/ServerIndex.cpp Tue Oct 15 16:23:42 2013 +0200 +++ b/UnitTests/ServerIndex.cpp Thu Oct 17 14:20:13 2013 +0200 @@ -487,3 +487,11 @@ } + + +TEST(DicomMap, MainTags) +{ + ASSERT_TRUE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID)); + ASSERT_TRUE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID, ResourceType_Patient)); + ASSERT_FALSE(DicomMap::IsMainDicomTag(DICOM_TAG_PATIENT_ID, ResourceType_Study)); +} diff -r 49945044b06d -r ce5d2040c47b UnitTests/main.cpp --- a/UnitTests/main.cpp Tue Oct 15 16:23:42 2013 +0200 +++ b/UnitTests/main.cpp Thu Oct 17 14:20:13 2013 +0200 @@ -127,6 +127,10 @@ t = FromDcmtkBridge::ParseTag("0020-e040"); ASSERT_EQ(0x0020, t.GetGroup()); ASSERT_EQ(0xe040, t.GetElement()); + + // Test ==() and !=() operators + ASSERT_TRUE(DICOM_TAG_PATIENT_ID == DicomTag(0x0010, 0x0020)); + ASSERT_FALSE(DICOM_TAG_PATIENT_ID != DicomTag(0x0010, 0x0020)); } @@ -387,6 +391,13 @@ ASSERT_EQ("IndexInSeries", EnumerationToString(MetadataType_Instance_IndexInSeries)); ASSERT_EQ("LastUpdate", EnumerationToString(MetadataType_LastUpdate)); + ASSERT_EQ(ResourceType_Patient, StringToResourceType("PATienT")); + ASSERT_EQ(ResourceType_Study, StringToResourceType("STudy")); + ASSERT_EQ(ResourceType_Series, StringToResourceType("SeRiEs")); + ASSERT_EQ(ResourceType_Instance, StringToResourceType("INStance")); + ASSERT_EQ(ResourceType_Instance, StringToResourceType("IMagE")); + ASSERT_THROW(StringToResourceType("heLLo"), OrthancException); + ASSERT_EQ(2047, StringToMetadata("2047")); ASSERT_THROW(StringToMetadata("Ceci est un test"), OrthancException); ASSERT_THROW(RegisterUserMetadata(128, ""), OrthancException); // too low (< 1024)