# HG changeset patch # User Sebastien Jodogne # Date 1443537108 -7200 # Node ID 5360cdba70d8abc5edd230f8d390e42438ccfb19 # Parent d3ba98d6b6e96f551a194213ae1c9ee6c3cab329 New function "OrthancPluginRegisterDictionaryTag()" to declare DICOM tags diff -r d3ba98d6b6e9 -r 5360cdba70d8 NEWS --- a/NEWS Tue Sep 29 15:13:34 2015 +0200 +++ b/NEWS Tue Sep 29 16:31:48 2015 +0200 @@ -8,6 +8,7 @@ ------- * New function "OrthancPluginRegisterErrorCode()" to declare custom error codes +* New function "OrthancPluginRegisterDictionaryTag()" to declare DICOM tags Maintenance ----------- diff -r d3ba98d6b6e9 -r 5360cdba70d8 OrthancServer/FromDcmtkBridge.cpp --- a/OrthancServer/FromDcmtkBridge.cpp Tue Sep 29 15:13:34 2015 +0200 +++ b/OrthancServer/FromDcmtkBridge.cpp Tue Sep 29 16:31:48 2015 +0200 @@ -157,43 +157,74 @@ #endif + namespace + { + class DictionaryLocker + { + private: + DcmDataDictionary& dictionary_; + + public: + DictionaryLocker() : dictionary_(dcmDataDict.wrlock()) + { + } + + ~DictionaryLocker() + { + dcmDataDict.unlock(); + } + + DcmDataDictionary& operator*() + { + return dictionary_; + } + + DcmDataDictionary* operator->() + { + return &dictionary_; + } + }; + } + + void FromDcmtkBridge::InitializeDictionary() { /* Disable "gethostbyaddr" (which results in memory leaks) and use raw IP addresses */ dcmDisableGethostbyaddr.set(OFTrue); - dcmDataDict.clear(); - DcmDataDictionary& d = dcmDataDict.wrlock(); + { + DictionaryLocker locker; + + locker->clear(); #if DCMTK_USE_EMBEDDED_DICTIONARIES == 1 - LOG(WARNING) << "Loading the embedded dictionaries"; - /** - * Do not load DICONDE dictionary, it breaks the other tags. The - * command "strace storescu 2>&1 |grep dic" shows that DICONDE - * dictionary is not loaded by storescu. - **/ - //LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_DICONDE); + LOG(WARNING) << "Loading the embedded dictionaries"; + /** + * Do not load DICONDE dictionary, it breaks the other tags. The + * command "strace storescu 2>&1 |grep dic" shows that DICONDE + * dictionary is not loaded by storescu. + **/ + //LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICONDE); - LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_DICOM); - LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_PRIVATE); + LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_DICOM); + LoadEmbeddedDictionary(*locker, EmbeddedResources::DICTIONARY_PRIVATE); #elif defined(__linux) || defined(__FreeBSD_kernel__) - std::string path = DCMTK_DICTIONARY_DIR; + std::string path = DCMTK_DICTIONARY_DIR; - const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE); - if (env != NULL) - { - path = std::string(env); - } + const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE); + if (env != NULL) + { + path = std::string(env); + } - LoadExternalDictionary(d, path, "dicom.dic"); - LoadExternalDictionary(d, path, "private.dic"); + LoadExternalDictionary(*locker, path, "dicom.dic"); + LoadExternalDictionary(*locker, path, "private.dic"); #else #error Support your platform here #endif - - dcmDataDict.unlock(); + } /* make sure data dictionary is loaded */ if (!dcmDataDict.isDictionaryLoaded()) @@ -214,6 +245,45 @@ } + void FromDcmtkBridge::RegisterDictionaryTag(const DicomTag& tag, + const DcmEVR& vr, + const std::string& name, + unsigned int minMultiplicity, + unsigned int maxMultiplicity) + { + if (minMultiplicity < 1) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + if (maxMultiplicity == 0) + { + maxMultiplicity = DcmVariableVM; + } + else if (maxMultiplicity < minMultiplicity) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + + std::auto_ptr entry(new DcmDictEntry(tag.GetGroup(), + tag.GetElement(), + vr, name.c_str(), + static_cast(minMultiplicity), + static_cast(maxMultiplicity), + NULL /* version */, + OFTrue /* doCopyString */, + NULL /* private creator */)); + + entry->setGroupRangeRestriction(DcmDictRange_Unspecified); + entry->setElementRangeRestriction(DcmDictRange_Unspecified); + + { + DictionaryLocker locker; + locker->addEntry(entry.release()); + } + } + + Encoding FromDcmtkBridge::DetectEncoding(DcmDataset& dataset) { // By default, Latin1 encoding is assumed diff -r d3ba98d6b6e9 -r 5360cdba70d8 OrthancServer/FromDcmtkBridge.h --- a/OrthancServer/FromDcmtkBridge.h Tue Sep 29 15:13:34 2015 +0200 +++ b/OrthancServer/FromDcmtkBridge.h Tue Sep 29 16:31:48 2015 +0200 @@ -46,6 +46,12 @@ public: static void InitializeDictionary(); + static void RegisterDictionaryTag(const DicomTag& tag, + const DcmEVR& vr, + const std::string& name, + unsigned int minMultiplicity, + unsigned int maxMultiplicity); + static Encoding DetectEncoding(DcmDataset& dataset); static void Convert(DicomMap& target, DcmDataset& dataset); diff -r d3ba98d6b6e9 -r 5360cdba70d8 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Tue Sep 29 15:13:34 2015 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Tue Sep 29 16:31:48 2015 +0200 @@ -43,6 +43,7 @@ #include "../../Core/Logging.h" #include "../../Core/OrthancException.h" #include "../../Core/Toolbox.h" +#include "../../OrthancServer/FromDcmtkBridge.h" #include "../../OrthancServer/OrthancInitialization.h" #include "../../OrthancServer/ServerContext.h" #include "../../OrthancServer/ServerToolbox.h" @@ -297,6 +298,7 @@ sizeof(int32_t) != sizeof(OrthancPluginChangeType) || sizeof(int32_t) != sizeof(OrthancPluginImageFormat) || sizeof(int32_t) != sizeof(OrthancPluginCompressionType) || + sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation) || sizeof(int32_t) != sizeof(_OrthancPluginDatabaseAnswerType)) { /* Sanity check of the compiler */ @@ -1697,6 +1699,16 @@ return true; } + case _OrthancPluginService_RegisterDictionaryTag: + { + const _OrthancPluginRegisterDictionaryTag& p = + *reinterpret_cast(parameters); + FromDcmtkBridge::RegisterDictionaryTag(DicomTag(p.group, p.element), + Plugins::Convert(p.vr), p.name, + p.minMultiplicity, p.maxMultiplicity); + return true; + } + default: { // This service is unknown to the Orthanc plugin engine diff -r d3ba98d6b6e9 -r 5360cdba70d8 Plugins/Engine/PluginsEnumerations.cpp --- a/Plugins/Engine/PluginsEnumerations.cpp Tue Sep 29 15:13:34 2015 +0200 +++ b/Plugins/Engine/PluginsEnumerations.cpp Tue Sep 29 16:31:48 2015 +0200 @@ -186,5 +186,97 @@ return FileContentType_Unknown; } } + + + + DcmEVR Convert(OrthancPluginValueRepresentation vr) + { + switch (vr) + { + case OrthancPluginValueRepresentation_AE: + return EVR_AE; + + case OrthancPluginValueRepresentation_AS: + return EVR_AS; + + case OrthancPluginValueRepresentation_AT: + return EVR_AT; + + case OrthancPluginValueRepresentation_CS: + return EVR_CS; + + case OrthancPluginValueRepresentation_DA: + return EVR_DA; + + case OrthancPluginValueRepresentation_DS: + return EVR_DS; + + case OrthancPluginValueRepresentation_DT: + return EVR_DT; + + case OrthancPluginValueRepresentation_FD: + return EVR_FD; + + case OrthancPluginValueRepresentation_FL: + return EVR_FL; + + case OrthancPluginValueRepresentation_IS: + return EVR_IS; + + case OrthancPluginValueRepresentation_LO: + return EVR_LO; + + case OrthancPluginValueRepresentation_LT: + return EVR_LT; + + case OrthancPluginValueRepresentation_OB: + return EVR_OB; + + case OrthancPluginValueRepresentation_OF: + return EVR_OF; + + case OrthancPluginValueRepresentation_OW: + return EVR_OW; + + case OrthancPluginValueRepresentation_PN: + return EVR_PN; + + case OrthancPluginValueRepresentation_SH: + return EVR_SH; + + case OrthancPluginValueRepresentation_SL: + return EVR_SL; + + case OrthancPluginValueRepresentation_SQ: + return EVR_SQ; + + case OrthancPluginValueRepresentation_SS: + return EVR_SS; + + case OrthancPluginValueRepresentation_ST: + return EVR_ST; + + case OrthancPluginValueRepresentation_TM: + return EVR_TM; + + case OrthancPluginValueRepresentation_UI: + return EVR_UI; + + case OrthancPluginValueRepresentation_UL: + return EVR_UL; + + case OrthancPluginValueRepresentation_UN: + return EVR_UN; + + case OrthancPluginValueRepresentation_US: + return EVR_US; + + case OrthancPluginValueRepresentation_UT: + return EVR_UT; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } } } diff -r d3ba98d6b6e9 -r 5360cdba70d8 Plugins/Engine/PluginsEnumerations.h --- a/Plugins/Engine/PluginsEnumerations.h Tue Sep 29 15:13:34 2015 +0200 +++ b/Plugins/Engine/PluginsEnumerations.h Tue Sep 29 16:31:48 2015 +0200 @@ -37,6 +37,8 @@ #include "../Include/orthanc/OrthancCPlugin.h" #include "../../OrthancServer/ServerEnumerations.h" +#include + namespace Orthanc { namespace Plugins @@ -52,6 +54,8 @@ OrthancPluginContentType Convert(FileContentType type); FileContentType Convert(OrthancPluginContentType type); + + DcmEVR Convert(OrthancPluginValueRepresentation vr); } } diff -r d3ba98d6b6e9 -r 5360cdba70d8 Plugins/Include/orthanc/OrthancCPlugin.h --- a/Plugins/Include/orthanc/OrthancCPlugin.h Tue Sep 29 15:13:34 2015 +0200 +++ b/Plugins/Include/orthanc/OrthancCPlugin.h Tue Sep 29 16:31:48 2015 +0200 @@ -384,6 +384,7 @@ _OrthancPluginService_GetErrorDescription = 17, _OrthancPluginService_CallHttpClient = 18, _OrthancPluginService_RegisterErrorCode = 19, + _OrthancPluginService_RegisterDictionaryTag = 20, /* Registration of callbacks */ _OrthancPluginService_RegisterRestCallback = 1000, @@ -599,6 +600,43 @@ } OrthancPluginImageFormat; + /** + * The value representations present in the DICOM standard (version 2013). + * @ingroup Toolbox + **/ + typedef enum + { + OrthancPluginValueRepresentation_AE = 1, /*!< Application Entity */ + OrthancPluginValueRepresentation_AS = 2, /*!< Age String */ + OrthancPluginValueRepresentation_AT = 3, /*!< Attribute Tag */ + OrthancPluginValueRepresentation_CS = 4, /*!< Code String */ + OrthancPluginValueRepresentation_DA = 5, /*!< Date */ + OrthancPluginValueRepresentation_DS = 6, /*!< Decimal String */ + OrthancPluginValueRepresentation_DT = 7, /*!< Date Time */ + OrthancPluginValueRepresentation_FD = 8, /*!< Floating Point Double */ + OrthancPluginValueRepresentation_FL = 9, /*!< Floating Point Single */ + OrthancPluginValueRepresentation_IS = 10, /*!< Integer String */ + OrthancPluginValueRepresentation_LO = 11, /*!< Long String */ + OrthancPluginValueRepresentation_LT = 12, /*!< Long Text */ + OrthancPluginValueRepresentation_OB = 13, /*!< Other Byte String */ + OrthancPluginValueRepresentation_OF = 14, /*!< Other Float String */ + OrthancPluginValueRepresentation_OW = 15, /*!< Other Word String */ + OrthancPluginValueRepresentation_PN = 16, /*!< Person Name */ + OrthancPluginValueRepresentation_SH = 17, /*!< Short String */ + OrthancPluginValueRepresentation_SL = 18, /*!< Signed Long */ + OrthancPluginValueRepresentation_SQ = 19, /*!< Sequence of Items */ + OrthancPluginValueRepresentation_SS = 20, /*!< Signed Short */ + OrthancPluginValueRepresentation_ST = 21, /*!< Short Text */ + OrthancPluginValueRepresentation_TM = 22, /*!< Time */ + OrthancPluginValueRepresentation_UI = 23, /*!< Unique Identifier (UID) */ + OrthancPluginValueRepresentation_UL = 24, /*!< Unsigned Long */ + OrthancPluginValueRepresentation_UN = 25, /*!< Unknown */ + OrthancPluginValueRepresentation_US = 26, /*!< Unsigned Short */ + OrthancPluginValueRepresentation_UT = 27, /*!< Unlimited Text */ + + _OrthancPluginValueRepresentation_INTERNAL = 0x7fffffff + } OrthancPluginValueRepresentation; + /** * @brief A memory buffer allocated by the core system of Orthanc. @@ -810,7 +848,8 @@ sizeof(int32_t) != sizeof(OrthancPluginResourceType) || sizeof(int32_t) != sizeof(OrthancPluginChangeType) || sizeof(int32_t) != sizeof(OrthancPluginCompressionType) || - sizeof(int32_t) != sizeof(OrthancPluginImageFormat)) + sizeof(int32_t) != sizeof(OrthancPluginImageFormat) || + sizeof(int32_t) != sizeof(OrthancPluginValueRepresentation)) { /* Mismatch in the size of the enumerations */ return 0; @@ -3677,6 +3716,56 @@ } + + typedef struct + { + uint16_t group; + uint16_t element; + OrthancPluginValueRepresentation vr; + const char* name; + uint32_t minMultiplicity; + uint32_t maxMultiplicity; + } _OrthancPluginRegisterDictionaryTag; + + /** + * @brief Register a new tag into the DICOM dictionary. + * + * This function declares a new tag in the dictionary of DICOM tags + * that is known to Orthanc. This function should be used in the + * OrthancPluginInitialize() callback. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @param group The group of the tag. + * @param element The element of the tag. + * @param vr The value representation of the tag. + * @param name The nickname of the tag. + * @param minMultiplicity The minimum multiplicity of the tag (must be above 0). + * @param maxMultiplicity The maximum multiplicity of the tag. A value of 0 means + * an arbitrary multiplicity ("n"). + * @return 0 if success, other value if error. + * @ingroup Toolbox + **/ + ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginRegisterDictionaryTag( + OrthancPluginContext* context, + uint16_t group, + uint16_t element, + OrthancPluginValueRepresentation vr, + const char* name, + uint32_t minMultiplicity, + uint32_t maxMultiplicity) + { + _OrthancPluginRegisterDictionaryTag params; + params.group = group; + params.element = element; + params.vr = vr; + params.name = name; + params.minMultiplicity = minMultiplicity; + params.maxMultiplicity = maxMultiplicity; + + return context->InvokeService(context, _OrthancPluginService_RegisterDictionaryTag, ¶ms); + } + + #ifdef __cplusplus } #endif diff -r d3ba98d6b6e9 -r 5360cdba70d8 Plugins/Samples/Basic/Plugin.c --- a/Plugins/Samples/Basic/Plugin.c Tue Sep 29 15:13:34 2015 +0200 +++ b/Plugins/Samples/Basic/Plugin.c Tue Sep 29 16:31:48 2015 +0200 @@ -405,6 +405,9 @@ customError = OrthancPluginRegisterErrorCode(context, 4, 402, "Hello world"); + OrthancPluginRegisterDictionaryTag(context, 0x0014, 0x1020, OrthancPluginValueRepresentation_DA, + "ValidationExpiryDate", 1, 1); + return 0; }