changeset 1859:4e7c318a3f69

C-FIND SCP will return tags with sequence value representation
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 02 Dec 2015 11:22:05 +0100
parents 36ab170733d6
children c7d70f659190
files NEWS OrthancServer/DicomProtocol/IFindRequestHandler.h OrthancServer/Internals/FindScp.cpp OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/OrthancFindRequestHandler.h OrthancServer/OrthancRestApi/OrthancRestResources.cpp OrthancServer/ServerContext.cpp OrthancServer/ServerToolbox.cpp OrthancServer/ServerToolbox.h Plugins/Engine/OrthancPlugins.cpp UnitTestsSources/FromDcmtkTests.cpp
diffstat 11 files changed, 121 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Tue Dec 01 16:57:03 2015 +0100
+++ b/NEWS	Wed Dec 02 11:22:05 2015 +0100
@@ -4,8 +4,8 @@
 Major
 -----
 
-* Experimental support of DICOM C-Find SCP for modality worklists through plugins
-* Support of DICOM C-Find SCU for modality worklists ("/modalities/{dicom}/find-worklist")
+* Experimental support of DICOM C-FIND SCP for modality worklists through plugins
+* Support of DICOM C-FIND SCU for modality worklists ("/modalities/{dicom}/find-worklist")
 
 REST API
 --------
@@ -64,15 +64,16 @@
 Maintenance
 -----------
 
-* Full indexation of the patient/study tags to speed up searches and C-Find
+* Full indexation of the patient/study tags to speed up searches and C-FIND
 * Many refactorings, notably of the searching features and of the image decoding
-* C-Move SCP for studies using AccessionNumber tag
-* Fix issue 4 (C-Store Association not renegotiated on Specific-to-specific transfer syntax change)
+* C-MOVE SCP for studies using AccessionNumber tag
+* Fix issue 4 (C-STORE Association not renegotiated on Specific-to-specific transfer syntax change)
 * Fix formatting of multipart HTTP answers
 * "--logdir" flag creates a single log file instead of 3 separate files for errors/warnings/infos
 * "--errors" flag lists the error codes that could be returned by Orthanc
 * Under Windows, the exit status of Orthanc corresponds to the encountered error code
 * New "AgfaImpax", "EFilm2" and "Vitrea" modality manufacturers
+* C-FIND SCP will return tags with sequence value representation
 * Upgrade to Boost 1.59.0 for static builds
 
 
@@ -148,7 +149,7 @@
 -------
 
 * The configuration can be splitted into several files stored inside the same folder
-* Custom setting of the local AET during C-Store SCU (both in Lua and in the REST API)
+* Custom setting of the local AET during C-STORE SCU (both in Lua and in the REST API)
 * Many code refactorings
 
 Lua
@@ -167,9 +168,9 @@
 Fixes
 -----
 
-* Fix compatibility issues for C-Find SCU to Siemens Syngo.Via modalities SCP
+* Fix compatibility issues for C-FIND SCU to Siemens Syngo.Via modalities SCP
 * Fix issue 15 (Lua scripts making HTTP requests)
-* Fix issue 35 (Characters in PatientID string are not protected for C-Find)
+* Fix issue 35 (Characters in PatientID string are not protected for C-FIND)
 * Fix issue 37 (Hyphens trigger range query even if datatype does not support ranges)
 
 
@@ -180,7 +181,7 @@
 -----
 
 * DICOM Query/Retrieve available from Orthanc Explorer
-* C-Move SCU and C-Find SCU are accessible through the REST API
+* C-MOVE SCU and C-FIND SCU are accessible through the REST API
 * "?expand" flag for URIs "/patients", "/studies" and "/series"
 * "/tools/find" URI to search for DICOM resources from REST
 * Support of FreeBSD
@@ -190,15 +191,15 @@
 -----
 
 * Speed-up in Orthanc Explorer for large amount of images
-* Speed-up of the C-Find SCP server of Orthanc
+* Speed-up of the C-FIND SCP server of Orthanc
 * Allow replacing PatientID/StudyInstanceUID/SeriesInstanceUID from Lua scripts
-* Option "CaseSensitivePN" to enable case-insensitive C-Find SCP
+* Option "CaseSensitivePN" to enable case-insensitive C-FIND SCP
 
 Fixes
 -----
 
 * Prevent freeze on C-FIND if no DICOM tag is to be returned
-* Fix slow C-Store SCP on recent versions of Linux, if
+* Fix slow C-STORE SCP on recent versions of Linux, if
   USE_SYSTEM_DCMTK is set to OFF (http://forum.dcmtk.org/viewtopic.php?f=1&t=4009)
 * Fix issue 30 (QR response missing "Query/Retrieve Level" (008,0052))
 * Fix issue 32 (Cyrillic symbols): Introduction of the "Windows1251" encoding
@@ -269,7 +270,7 @@
 
 * "/instances-tags" to get the tags of all the child instances of a
   patient/study/series with a single REST call (bulk tags retrieval)
-* Configuration/Lua to select the accepted C-Store SCP transfer syntaxes
+* Configuration/Lua to select the accepted C-STORE SCP transfer syntaxes
 * Fix reporting of errors in Orthanc Explorer when sending images to peers/modalities
 * Installation of plugin SDK in CMake
 
@@ -380,7 +381,7 @@
 Version 0.7.5 (2014/05/08)
 ==========================
 
-* Dynamic negotiation of SOP classes for C-Store SCU
+* Dynamic negotiation of SOP classes for C-STORE SCU
 * Creation of DICOM instances using the REST API
 * Embedding of images within DICOM instances
 * Adding/removal/modification of remote modalities/peers through REST
@@ -427,8 +428,8 @@
 ==========================
 
 * Support of Query/Retrieve from medInria
-* Accept more transfer syntaxes for C-Store SCP and SCU (notably JPEG)
-* Create the meta-header when receiving files through C-Store SCP
+* Accept more transfer syntaxes for C-STORE SCP and SCU (notably JPEG)
+* Create the meta-header when receiving files through C-STORE SCP
 * Fixes and improvements thanks to the static analyzer cppcheck
 
 
@@ -471,7 +472,7 @@
 ==========================
 
 * Detection of stable patients/studies/series
-* C-Find SCU at the instance level
+* C-FIND SCU at the instance level
 * Link from modified to original resource in Orthanc Explorer
 * Fix of issue #8
 * Anonymization of the medical alerts tag (0010,2000)
@@ -581,7 +582,7 @@
 * The patient/study/series/instances are now indexed by SHA-1 digests
   of their DICOM Instance IDs (and not by UUIDs anymore): The same
   DICOM objects are thus always identified by the same Orthanc IDs
-* Log of exported instances through DICOM C-Store SCU ("/exported" URI)
+* Log of exported instances through DICOM C-STORE SCU ("/exported" URI)
 * Full refactoring of the DB schema and of the REST API
 * Introduction of generic classes for REST APIs (in Core/RestApi)
 
--- a/OrthancServer/DicomProtocol/IFindRequestHandler.h	Tue Dec 01 16:57:03 2015 +0100
+++ b/OrthancServer/DicomProtocol/IFindRequestHandler.h	Wed Dec 02 11:22:05 2015 +0100
@@ -45,6 +45,7 @@
 
     virtual void Handle(DicomFindAnswers& answers,
                         const DicomMap& input,
+                        const std::list<DicomTag>& sequencesToReturn,
                         const std::string& remoteIp,
                         const std::string& remoteAet,
                         const std::string& calledAet) = 0;
--- a/OrthancServer/Internals/FindScp.cpp	Tue Dec 01 16:57:03 2015 +0100
+++ b/OrthancServer/Internals/FindScp.cpp	Wed Dec 02 11:22:05 2015 +0100
@@ -148,9 +148,30 @@
           {
             if (data.findHandler_ != NULL)
             {
+              std::list<DicomTag> sequencesToReturn;
+
+              for (unsigned long i = 0; i < requestIdentifiers->card(); i++)
+              {
+                DcmElement* element = requestIdentifiers->getElement(i);
+                if (element && !element->isLeaf())
+                {
+                  const DicomTag tag(FromDcmtkBridge::Convert(element->getTag()));
+
+                  DcmSequenceOfItems& sequence = dynamic_cast<DcmSequenceOfItems&>(*element);
+                  if (sequence.card() != 0)
+                  {
+                    LOG(WARNING) << "Orthanc only supports sequence matching on worklists, "
+                                 << "ignoring C-FIND SCU constraint on tag (" << tag.Format() 
+                                 << ") " << FromDcmtkBridge::GetName(tag);
+                  }
+
+                  sequencesToReturn.push_back(tag);
+                }
+              }
+
               DicomMap input;
               FromDcmtkBridge::Convert(input, *requestIdentifiers);
-              data.findHandler_->Handle(data.answers_, input,
+              data.findHandler_->Handle(data.answers_, input, sequencesToReturn,
                                         *data.remoteIp_, *data.remoteAet_,
                                         *data.calledAet_);
               ok = true;
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Tue Dec 01 16:57:03 2015 +0100
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Wed Dec 02 11:22:05 2015 +0100
@@ -47,19 +47,21 @@
 {
   static void AddAnswer(DicomFindAnswers& answers,
                         const Json::Value& resource,
-                        const DicomArray& query)
+                        const DicomArray& query,
+                        const std::list<DicomTag>& sequencesToReturn)
   {
     DicomMap result;
 
     for (size_t i = 0; i < query.GetSize(); i++)
     {
-      // Fix issue 30 (QR response missing "Query/Retrieve Level" (008,0052))
       if (query.GetElement(i).GetTag() == DICOM_TAG_QUERY_RETRIEVE_LEVEL)
       {
+        // Fix issue 30 on Google Code (QR response missing "Query/Retrieve Level" (008,0052))
         result.SetValue(query.GetElement(i).GetTag(), query.GetElement(i).GetValue());
       }
       else if (query.GetElement(i).GetTag() == DICOM_TAG_SPECIFIC_CHARACTER_SET)
       {
+        // Do not include the encoding, this is handled by class ParsedDicomFile
       }
       else
       {
@@ -77,13 +79,46 @@
       }
     }
 
-    if (result.GetSize() == 0)
+    if (result.GetSize() == 0 &&
+        sequencesToReturn.empty())
     {
       LOG(WARNING) << "The C-FIND request does not return any DICOM tag";
     }
+    else if (sequencesToReturn.empty())
+    {
+      answers.Add(result);
+    }
     else
     {
-      answers.Add(result);
+      ParsedDicomFile dicom(result);
+
+      for (std::list<DicomTag>::const_iterator tag = sequencesToReturn.begin();
+           tag != sequencesToReturn.end(); ++tag)
+      {
+        std::cout << tag->Format();
+
+        const Json::Value& source = resource[tag->Format()];
+
+        if (source.type() == Json::objectValue &&
+            source.isMember("Type") &&
+            source.isMember("Value") &&
+            source["Type"].asString() == "Sequence" &&
+            source["Value"].type() == Json::arrayValue)
+        {
+          Json::Value content = Json::arrayValue;
+
+          for (Json::Value::ArrayIndex i = 0; i < source["Value"].size(); i++)
+          {
+            Json::Value item;
+            Toolbox::SimplifyTags(item, source["Value"][i], DicomToJsonFormat_Short);
+            content.append(item);
+          }
+
+          dicom.Replace(*tag, content, false);
+        }
+      }
+
+      answers.Add(dicom);
     }
   }
 
@@ -126,6 +161,7 @@
 
   void OrthancFindRequestHandler::Handle(DicomFindAnswers& answers,
                                          const DicomMap& input,
+                                         const std::list<DicomTag>& sequencesToReturn,
                                          const std::string& remoteIp,
                                          const std::string& remoteAet,
                                          const std::string& calledAet)
@@ -181,6 +217,14 @@
       }
     }
 
+    for (std::list<DicomTag>::const_iterator it = sequencesToReturn.begin();
+         it != sequencesToReturn.end(); ++it)
+    {
+      LOG(INFO) << "  (" << it->Format()
+                << ")  " << FromDcmtkBridge::GetName(*it)
+                << " : sequence tag whose content will be copied";
+    }
+
 
     /**
      * Build up the query object.
@@ -255,7 +299,7 @@
         }
         else
         {
-          AddAnswer(answers, dicom, query);
+          AddAnswer(answers, dicom, query, sequencesToReturn);
         }
       }
     }
--- a/OrthancServer/OrthancFindRequestHandler.h	Tue Dec 01 16:57:03 2015 +0100
+++ b/OrthancServer/OrthancFindRequestHandler.h	Wed Dec 02 11:22:05 2015 +0100
@@ -62,6 +62,7 @@
 
     virtual void Handle(DicomFindAnswers& answers,
                         const DicomMap& input,
+                        const std::list<DicomTag>& sequencesToReturn,
                         const std::string& remoteIp,
                         const std::string& remoteAet,
                         const std::string& calledAet);
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Tue Dec 01 16:57:03 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Dec 02 11:22:05 2015 +0100
@@ -209,7 +209,7 @@
       context.ReadJson(full, publicId);
 
       Json::Value simplified;
-      Toolbox::SimplifyTags(simplified, full);
+      Toolbox::SimplifyTags(simplified, full, DicomToJsonFormat_Simple);
       call.GetOutput().AnswerJson(simplified);
     }
     else
@@ -928,7 +928,7 @@
       if (simplify)
       {
         Json::Value simplified;
-        Toolbox::SimplifyTags(simplified, sharedTags);
+        Toolbox::SimplifyTags(simplified, sharedTags, DicomToJsonFormat_Simple);
         call.GetOutput().AnswerJson(simplified);
       }
       else
@@ -995,7 +995,7 @@
     if (simplify)
     {
       Json::Value simplified;
-      Toolbox::SimplifyTags(simplified, result);
+      Toolbox::SimplifyTags(simplified, result, DicomToJsonFormat_Simple);
       call.GetOutput().AnswerJson(simplified);
     }
     else
@@ -1201,7 +1201,7 @@
       if (simplify)
       {
         Json::Value simplified;
-        Toolbox::SimplifyTags(simplified, full);
+        Toolbox::SimplifyTags(simplified, full, DicomToJsonFormat_Simple);
         result[*it] = simplified;
       }
       else
@@ -1295,7 +1295,7 @@
     if (simplify)
     {
       Json::Value simplified;
-      Toolbox::SimplifyTags(simplified, header);
+      Toolbox::SimplifyTags(simplified, header, DicomToJsonFormat_Simple);
       call.GetOutput().AnswerJson(simplified);
     }
     else
--- a/OrthancServer/ServerContext.cpp	Tue Dec 01 16:57:03 2015 +0100
+++ b/OrthancServer/ServerContext.cpp	Wed Dec 02 11:22:05 2015 +0100
@@ -189,7 +189,7 @@
       resultPublicId = hasher.HashInstance();
 
       Json::Value simplifiedTags;
-      Toolbox::SimplifyTags(simplifiedTags, dicom.GetJson());
+      Toolbox::SimplifyTags(simplifiedTags, dicom.GetJson(), DicomToJsonFormat_Simple);
 
       // Test if the instance must be filtered out
       bool accepted = true;
--- a/OrthancServer/ServerToolbox.cpp	Tue Dec 01 16:57:03 2015 +0100
+++ b/OrthancServer/ServerToolbox.cpp	Wed Dec 02 11:22:05 2015 +0100
@@ -47,7 +47,8 @@
   namespace Toolbox
   {
     void SimplifyTags(Json::Value& target,
-                      const Json::Value& source)
+                      const Json::Value& source,
+                      DicomToJsonFormat format)
     {
       assert(source.isObject());
 
@@ -57,9 +58,23 @@
       for (size_t i = 0; i < members.size(); i++)
       {
         const Json::Value& v = source[members[i]];
-        const std::string& name = v["Name"].asString();
         const std::string& type = v["Type"].asString();
 
+        std::string name;
+        switch (format)
+        {
+          case DicomToJsonFormat_Simple:
+            name = v["Name"].asString();
+            break;
+
+          case DicomToJsonFormat_Short:
+            name = members[i];
+            break;
+
+          default:
+            throw OrthancException(ErrorCode_ParameterOutOfRange);
+        }
+
         if (type == "String")
         {
           target[name] = v["Value"].asString();
@@ -78,7 +93,7 @@
           for (Json::Value::ArrayIndex i = 0; i < array.size(); i++)
           {
             Json::Value c;
-            SimplifyTags(c, array[i]);
+            SimplifyTags(c, array[i], format);
             children.append(c);
           }
 
--- a/OrthancServer/ServerToolbox.h	Tue Dec 01 16:57:03 2015 +0100
+++ b/OrthancServer/ServerToolbox.h	Wed Dec 02 11:22:05 2015 +0100
@@ -42,7 +42,8 @@
   namespace Toolbox
   {
     void SimplifyTags(Json::Value& target,
-                      const Json::Value& source);
+                      const Json::Value& source,
+                      DicomToJsonFormat format);
 
     void LogMissingRequiredTag(const DicomMap& summary);
 
--- a/Plugins/Engine/OrthancPlugins.cpp	Tue Dec 01 16:57:03 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.cpp	Wed Dec 02 11:22:05 2015 +0100
@@ -1187,7 +1187,7 @@
         else
         {
           Json::Value simplified;
-          Toolbox::SimplifyTags(simplified, instance.GetJson());
+          Toolbox::SimplifyTags(simplified, instance.GetJson(), DicomToJsonFormat_Simple);
           s = writer.write(simplified);
         }
 
--- a/UnitTestsSources/FromDcmtkTests.cpp	Tue Dec 01 16:57:03 2015 +0100
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Wed Dec 02 11:22:05 2015 +0100
@@ -395,7 +395,7 @@
       FromDcmtkBridge::ToJson(b, *element, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0, Encoding_Ascii);
 
       Json::Value c;
-      Toolbox::SimplifyTags(c, b);
+      Toolbox::SimplifyTags(c, b, DicomToJsonFormat_Simple);
 
       a[1]["PatientName"] = "Hello2";  // To remove the Data URI Scheme encoding
       ASSERT_EQ(0, c["ReferencedStudySequence"].compare(a));
@@ -474,7 +474,7 @@
     f.ToJson(b, DicomToJsonFormat_Full, DicomToJsonFlags_Default, 0);
 
     Json::Value c;
-    Toolbox::SimplifyTags(c, b);
+    Toolbox::SimplifyTags(c, b, DicomToJsonFormat_Simple);
 
     ASSERT_EQ(0, c["ReferencedPatientSequence"].compare(a));
     ASSERT_NE(0, c["ReferencedStudySequence"].compare(a));  // Because Data URI Scheme decoding was enabled