# HG changeset patch # User Sebastien Jodogne # Date 1368626225 -7200 # Node ID 28ba732749199433ebcd16f96efaf5f79b83a042 # Parent ccf3a0a43dacf734fc0baad107bf974d92e519a4 registration of user-defined metadata diff -r ccf3a0a43dac -r 28ba73274919 Core/EnumerationDictionary.h --- a/Core/EnumerationDictionary.h Wed May 15 14:54:58 2013 +0200 +++ b/Core/EnumerationDictionary.h Wed May 15 15:57:05 2013 +0200 @@ -55,6 +55,24 @@ public: void Add(Enumeration value, const std::string& str) { + // Check if these values are free + if (enumerationToString_.find(value) != enumerationToString_.end() || + stringToEnumeration_.find(str) != stringToEnumeration_.end()) + { + throw OrthancException(ErrorCode_BadRequest); + } + + // Prevent the registration of a number + try + { + boost::lexical_cast(str); + throw OrthancException(ErrorCode_BadRequest); + } + catch (boost::bad_lexical_cast) + { + // OK, the string is not a number + } + enumerationToString_[value] = str; stringToEnumeration_[str] = value; stringToEnumeration_[boost::lexical_cast(static_cast(value))] = value; @@ -62,6 +80,15 @@ Enumeration Translate(const std::string& str) const { + try + { + int value = boost::lexical_cast(str); + return static_cast(value); + } + catch (boost::bad_lexical_cast) + { + } + typename StringToEnumeration::const_iterator found = stringToEnumeration_.find(str); @@ -82,7 +109,7 @@ if (found == enumerationToString_.end()) { - throw OrthancException(ErrorCode_InexistentItem); + throw OrthancException(ErrorCode_ParameterOutOfRange); } else { diff -r ccf3a0a43dac -r 28ba73274919 OrthancServer/OrthancInitialization.cpp --- a/OrthancServer/OrthancInitialization.cpp Wed May 15 14:54:58 2013 +0200 +++ b/OrthancServer/OrthancInitialization.cpp Wed May 15 15:57:05 2013 +0200 @@ -34,6 +34,7 @@ #include "../Core/OrthancException.h" #include "../Core/Toolbox.h" +#include "ServerEnumerations.h" #include #include @@ -118,12 +119,51 @@ } + static void RegisterUserMetadata() + { + if (configuration_->isMember("UserMetadata")) + { + const Json::Value& parameter = (*configuration_) ["UserMetadata"]; + + Json::Value::Members members = parameter.getMemberNames(); + for (size_t i = 0; i < members.size(); i++) + { + std::string info = "\"" + members[i] + "\" = " + parameter[members[i]].toStyledString(); + LOG(INFO) << "Registering user-specific metadata: " << info; + + if (!parameter[members[i]].asBool()) + { + LOG(ERROR) << "Not a number in this user-specific metadata: " << info; + throw OrthancException(ErrorCode_BadParameterType); + } + + int metadata = parameter[members[i]].asInt(); + + try + { + RegisterUserMetadata(metadata, members[i]); + } + catch (OrthancException e) + { + LOG(ERROR) << "Cannot register this user-specific metadata: " << info; + throw e; + } + } + } + } + + void OrthancInitialize(const char* configurationFile) { boost::mutex::scoped_lock lock(globalMutex_); + + InitializeServerEnumerations(); defaultDirectory_ = boost::filesystem::current_path(); ReadGlobalConfiguration(configurationFile); + curl_global_init(CURL_GLOBAL_ALL); + + RegisterUserMetadata(); } diff -r ccf3a0a43dac -r 28ba73274919 OrthancServer/ServerEnumerations.cpp --- a/OrthancServer/ServerEnumerations.cpp Wed May 15 14:54:58 2013 +0200 +++ b/OrthancServer/ServerEnumerations.cpp Wed May 15 15:57:05 2013 +0200 @@ -32,9 +32,54 @@ #include "ServerEnumerations.h" #include "../Core/OrthancException.h" +#include "../Core/EnumerationDictionary.h" + +#include namespace Orthanc { + static boost::mutex enumerationsMutex_; + static Toolbox::EnumerationDictionary dictMetadataType_; + + void InitializeServerEnumerations() + { + boost::mutex::scoped_lock lock(enumerationsMutex_); + + dictMetadataType_.Add(MetadataType_Instance_IndexInSeries, "IndexInSeries"); + dictMetadataType_.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate"); + dictMetadataType_.Add(MetadataType_Instance_RemoteAet, "RemoteAET"); + dictMetadataType_.Add(MetadataType_Series_ExpectedNumberOfInstances, "ExpectedNumberOfInstances"); + dictMetadataType_.Add(MetadataType_ModifiedFrom, "ModifiedFrom"); + dictMetadataType_.Add(MetadataType_AnonymizedFrom, "AnonymizedFrom"); + dictMetadataType_.Add(MetadataType_LastUpdate, "LastUpdate"); + } + + void RegisterUserMetadata(int metadata, + const std::string name) + { + boost::mutex::scoped_lock lock(enumerationsMutex_); + + if (metadata < static_cast(MetadataType_StartUser) || + metadata > static_cast(MetadataType_EndUser)) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + dictMetadataType_.Add(static_cast(metadata), name); + } + + const char* EnumerationToString(MetadataType type) + { + boost::mutex::scoped_lock lock(enumerationsMutex_); + return dictMetadataType_.Translate(type).c_str(); + } + + MetadataType StringToMetadata(const std::string& str) + { + boost::mutex::scoped_lock lock(enumerationsMutex_); + return dictMetadataType_.Translate(str); + } + const char* EnumerationToString(ResourceType type) { switch (type) diff -r ccf3a0a43dac -r 28ba73274919 OrthancServer/ServerEnumerations.h --- a/OrthancServer/ServerEnumerations.h Wed May 15 14:54:58 2013 +0200 +++ b/OrthancServer/ServerEnumerations.h Wed May 15 15:57:05 2013 +0200 @@ -81,7 +81,11 @@ MetadataType_Series_ExpectedNumberOfInstances = 4, MetadataType_ModifiedFrom = 5, MetadataType_AnonymizedFrom = 6, - MetadataType_LastUpdate = 7 + MetadataType_LastUpdate = 7, + + // Make sure that the value "65535" can be stored into this enumeration + MetadataType_StartUser = 1024, + MetadataType_EndUser = 65535 }; enum ChangeType @@ -99,11 +103,20 @@ ChangeType_ModifiedPatient = 11 }; + void InitializeServerEnumerations(); + + void RegisterUserMetadata(int metadata, + const std::string name); + std::string GetBasePath(ResourceType type, const std::string& publicId); + MetadataType StringToMetadata(const std::string& str); + const char* EnumerationToString(ResourceType type); + const char* EnumerationToString(MetadataType type); + const char* EnumerationToString(SeriesStatus status); const char* EnumerationToString(StoreStatus status); diff -r ccf3a0a43dac -r 28ba73274919 Resources/Configuration.json --- a/Resources/Configuration.json Wed May 15 14:54:58 2013 +0200 +++ b/Resources/Configuration.json Wed May 15 15:57:05 2013 +0200 @@ -1,104 +1,110 @@ { - /** - * General configuration of Orthanc - **/ + /** + * General configuration of Orthanc + **/ + + // The logical name of this instance of Orthanc. This one is + // displayed in Orthanc Explorer and at the URI "/system". + "Name" : "MyOrthanc", - // The logical name of this instance of Orthanc. This one is - // displayed in Orthanc Explorer and at the URI "/system". - "Name" : "MyOrthanc", + // Path to the directory that holds the heavyweight files + // (i.e. the raw DICOM instances) + "StorageDirectory" : "OrthancStorage", - // Path to the directory that holds the heavyweight files - // (i.e. the raw DICOM instances) - "StorageDirectory" : "OrthancStorage", + // Path to the directory that holds the SQLite index (if unset, + // the value of StorageDirectory is used). This index could be + // stored on a RAM-drive or a SSD device for performance reasons. + "IndexDirectory" : "OrthancStorage", - // Path to the directory that holds the SQLite index (if unset, - // the value of StorageDirectory is used). This index could be - // stored on a RAM-drive or a SSD device for performance reasons. - "IndexDirectory" : "OrthancStorage", + // Enable the transparent compression of the DICOM instances + "StorageCompression" : false, - // Enable the transparent compression of the DICOM instances - "StorageCompression" : false, + // Maximum size of the storage in MB (a value of "0" indicates no + // limit on the storage size) + "MaximumStorageSize" : 0, - // Maximum size of the storage in MB (a value of "0" indicates no - // limit on the storage size) - "MaximumStorageSize" : 0, + // Maximum number of patients that can be stored at a given time + // in the storage (a value of "0" indicates no limit on the number + // of patients) + "MaximumPatientCount" : 0, + + // List of paths to the custom Lua scripts to load into this + // instance of Orthanc + "LuaScripts" : [ + ], - // Maximum number of patients that can be stored at a given time - // in the storage (a value of "0" indicates no limit on the number - // of patients) - "MaximumPatientCount" : 0, - - // List of paths to the custom Lua scripts to load into this - // instance of Orthanc - "LuaScripts" : [ - ], + // Dictionary of the user-specific metadata. Each entry must map a + // number between 1024 and 65535 to an unique string. + "UserMetadata" : { + // "Sample" : 1024 + }, - /** - * Configuration of the HTTP server - **/ + /** + * Configuration of the HTTP server + **/ - // HTTP port for the REST services and for the GUI - "HttpPort" : 8042, + // HTTP port for the REST services and for the GUI + "HttpPort" : 8042, - /** - * Configuration of the DICOM server - **/ + /** + * Configuration of the DICOM server + **/ - // The DICOM Application Entity Title - "DicomAet" : "ORTHANC", + // The DICOM Application Entity Title + "DicomAet" : "ORTHANC", - // Check whether the called AET corresponds during a DICOM request - "DicomCheckCalledAet" : false, + // Check whether the called AET corresponds during a DICOM request + "DicomCheckCalledAet" : false, - // The DICOM port - "DicomPort" : 4242, + // The DICOM port + "DicomPort" : 4242, - /** - * Security-related options for the HTTP server - **/ + /** + * Security-related options for the HTTP server + **/ - // Whether remote hosts can connect to the HTTP server - "RemoteAccessAllowed" : false, + // Whether remote hosts can connect to the HTTP server + "RemoteAccessAllowed" : false, - // Whether or not SSL is enabled - "SslEnabled" : false, + // Whether or not SSL is enabled + "SslEnabled" : false, - // Path to the SSL certificate (meaningful only if SSL is enabled) - "SslCertificate" : "certificate.pem", + // Path to the SSL certificate (meaningful only if SSL is enabled) + "SslCertificate" : "certificate.pem", - // Whether or not the password protection is enabled - "AuthenticationEnabled" : false, + // Whether or not the password protection is enabled + "AuthenticationEnabled" : false, - // The list of the registered users. Because Orthanc uses HTTP - // Basic Authentication, the passwords are stored as plain text. - "RegisteredUsers" : { - "alice" : "alicePassword" - }, + // The list of the registered users. Because Orthanc uses HTTP + // Basic Authentication, the passwords are stored as plain text. + "RegisteredUsers" : { + // "alice" : "alicePassword" + }, - /** - * Network topology - **/ + /** + * Network topology + **/ - // The list of the known DICOM modalities - "DicomModalities" : { - /** - * Uncommenting the following line would enable Orthanc to - * connect to an instance of the "storescp" open-source DICOM - * store (shipped in the DCMTK distribution) started by the - * command line "storescp 2000". - **/ - // "sample" : [ "STORESCP", "localhost", 2000 ] - }, + // The list of the known DICOM modalities + "DicomModalities" : { + /** + * Uncommenting the following line would enable Orthanc to + * connect to an instance of the "storescp" open-source DICOM + * store (shipped in the DCMTK distribution) started by the + * command line "storescp 2000". + **/ + // "sample" : [ "STORESCP", "localhost", 2000 ] + }, - // The list of the known Orthanc peers (currently unused) - "OrthancPeers" : { - } + // The list of the known Orthanc peers (currently unused) + "OrthancPeers" : { + } } diff -r ccf3a0a43dac -r 28ba73274919 UnitTests/main.cpp --- a/UnitTests/main.cpp Wed May 15 14:54:58 2013 +0200 +++ b/UnitTests/main.cpp Wed May 15 15:57:05 2013 +0200 @@ -353,14 +353,19 @@ { Toolbox::EnumerationDictionary d; - ASSERT_THROW(d.Translate("2"), OrthancException); ASSERT_THROW(d.Translate("ReceptionDate"), OrthancException); + ASSERT_EQ(MetadataType_ModifiedFrom, d.Translate("5")); d.Add(MetadataType_Instance_ReceptionDate, "ReceptionDate"); ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("ReceptionDate")); ASSERT_EQ(MetadataType_Instance_ReceptionDate, d.Translate("2")); ASSERT_EQ("ReceptionDate", d.Translate(MetadataType_Instance_ReceptionDate)); + + ASSERT_THROW(d.Add(MetadataType_Instance_ReceptionDate, "Hello"), OrthancException); + ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "ReceptionDate"), OrthancException); // already used + ASSERT_THROW(d.Add(MetadataType_ModifiedFrom, "1024"), OrthancException); // cannot register numbers + d.Add(MetadataType_ModifiedFrom, "ModifiedFrom"); // ok } @@ -377,6 +382,17 @@ ASSERT_STREQ("Success", EnumerationToString(StoreStatus_Success)); ASSERT_STREQ("CompletedSeries", EnumerationToString(ChangeType_CompletedSeries)); + + ASSERT_STREQ("IndexInSeries", EnumerationToString(MetadataType_Instance_IndexInSeries)); + ASSERT_STREQ("LastUpdate", EnumerationToString(MetadataType_LastUpdate)); + + ASSERT_EQ(2047, StringToMetadata("2047")); + ASSERT_THROW(StringToMetadata("Ceci est un test"), OrthancException); + ASSERT_THROW(RegisterUserMetadata(128, ""), OrthancException); // too low (< 1024) + ASSERT_THROW(RegisterUserMetadata(128000, ""), OrthancException); // too high (> 65535) + RegisterUserMetadata(2047, "Ceci est un test"); + ASSERT_EQ(2047, StringToMetadata("2047")); + ASSERT_EQ(2047, StringToMetadata("Ceci est un test")); }