changeset 1657:5360cdba70d8

New function "OrthancPluginRegisterDictionaryTag()" to declare DICOM tags
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Sep 2015 16:31:48 +0200
parents d3ba98d6b6e9
children 54bafe0e7e7b
files NEWS OrthancServer/FromDcmtkBridge.cpp OrthancServer/FromDcmtkBridge.h Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/PluginsEnumerations.cpp Plugins/Engine/PluginsEnumerations.h Plugins/Include/orthanc/OrthancCPlugin.h Plugins/Samples/Basic/Plugin.c
diffstat 8 files changed, 299 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- 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
 -----------
--- 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<DcmDictEntry>  entry(new DcmDictEntry(tag.GetGroup(),
+                                                        tag.GetElement(),
+                                                        vr, name.c_str(),
+                                                        static_cast<int>(minMultiplicity),
+                                                        static_cast<int>(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
--- 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);
--- 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<const _OrthancPluginRegisterDictionaryTag*>(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
--- 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);
+      }
+    }
   }
 }
--- 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 <dcmtk/dcmdata/dcvr.h>
+
 namespace Orthanc
 {
   namespace Plugins
@@ -52,6 +54,8 @@
     OrthancPluginContentType Convert(FileContentType type);
 
     FileContentType Convert(OrthancPluginContentType type);
+
+    DcmEVR Convert(OrthancPluginValueRepresentation vr);
   }
 }
 
--- 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 ("<tt>n</tt>").
+   * @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, &params);
+  }
+
+
 #ifdef  __cplusplus
 }
 #endif
--- 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;
 }