changeset 1802:138664eb59de worklists

sample worklist plugin
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 20 Nov 2015 15:54:07 +0100
parents 2c60c357ee3e
children d093f998a83b
files OrthancServer/main.cpp Plugins/Engine/OrthancPlugins.cpp Plugins/Include/orthanc/OrthancCPlugin.h Plugins/Samples/ModalityWorklists/CMakeLists.txt Plugins/Samples/ModalityWorklists/Plugin.cpp Plugins/Samples/ModalityWorklists/WorklistsDatabase/Generate.py Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist1.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist10.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist2.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist3.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist4.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist5.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist6.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist7.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist8.wl Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist9.wl
diffstat 16 files changed, 318 insertions(+), 82 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/main.cpp	Fri Nov 20 14:33:41 2015 +0100
+++ b/OrthancServer/main.cpp	Fri Nov 20 15:54:07 2015 +0100
@@ -52,14 +52,9 @@
 #include "../Plugins/Engine/OrthancPlugins.h"
 #include "FromDcmtkBridge.h"
 
-#include "Search/HierarchicalMatcher.h"
-
 using namespace Orthanc;
 
 
-
-
-
 class OrthancStoreRequestHandler : public IStoreRequestHandler
 {
 private:
@@ -95,66 +90,6 @@
 
 
 
-
-class OrthancWorklistRequestHandler : public IWorklistRequestHandler
-{
-private:
-  ServerContext& server_;
-
-public:
-  OrthancWorklistRequestHandler(ServerContext& context) :
-    server_(context)
-  {
-  }
-
-  virtual void Handle(DicomFindAnswers& answers,
-                      ParsedDicomFile& query,
-                      const std::string& remoteIp,
-                      const std::string& remoteAet,
-                      const std::string& calledAet)
-  {
-    LOG(WARNING) << "Worklist Find query from " << remoteAet << " to " << calledAet;
-
-    bool caseSensitivePN = Configuration::GetGlobalBoolParameter("CaseSensitivePN", false);
-    HierarchicalMatcher matcher(query, caseSensitivePN);
-
-    boost::filesystem::path source("/tmp/worklists/db/ORTHANCTEST");
-    boost::filesystem::directory_iterator end;
-
-    try
-    {
-      for (boost::filesystem::directory_iterator it(source); it != end; ++it)
-      {
-        if (is_regular_file(it->status()))
-        {
-          std::string extension = boost::filesystem::extension(it->path());
-          Toolbox::ToLowerCase(extension);
-
-          if (extension == ".wl")
-          {
-            std::string s;
-            Toolbox::ReadFile(s, it->path().string());
-            ParsedDicomFile f(s);
-
-            if (matcher.Match(f))
-            {
-              std::auto_ptr<ParsedDicomFile> e(matcher.Extract(f));
-              answers.Add(*e);
-            }
-          }
-        }
-      }
-    }
-    catch (boost::filesystem::filesystem_error&)
-    {
-      LOG(ERROR) << "Inexistent folder while scanning for worklists: " << source;
-    }
-
-    answers.SetComplete(true);  // All the worklists have been returned
-  }
-};
-
-
 class MyDicomServerFactory : 
   public IStoreRequestHandlerFactory,
   public IFindRequestHandlerFactory, 
--- a/Plugins/Engine/OrthancPlugins.cpp	Fri Nov 20 14:33:41 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.cpp	Fri Nov 20 15:54:07 2015 +0100
@@ -363,6 +363,14 @@
       Reset();
     }
 
+    void GetQueryDicom(OrthancPluginMemoryBuffer& target) const
+    {
+      assert(currentQuery_ != NULL);
+      std::string dicom;
+      currentQuery_->SaveToMemoryBuffer(dicom);
+      CopyToMemoryBuffer(target, dicom.c_str(), dicom.size());
+    }
+
     bool IsMatch(const void* dicom,
                  size_t size) const
     {
@@ -371,12 +379,14 @@
       return matcher_->Match(f);
     }
 
-    void GetQueryDicom(OrthancPluginMemoryBuffer& target) const
+    void AddAnswer(OrthancPluginWorklistAnswers* answers,
+                   const void* dicom,
+                   size_t size) const
     {
-      assert(currentQuery_ != NULL);
-      std::string dicom;
-      currentQuery_->SaveToMemoryBuffer(dicom);
-      CopyToMemoryBuffer(target, dicom.c_str(), dicom.size());
+      assert(matcher_.get() != NULL);
+      ParsedDicomFile f(dicom, size);
+      std::auto_ptr<ParsedDicomFile> summary(matcher_->Extract(f));
+      reinterpret_cast<DicomFindAnswers*>(answers)->Add(*summary);
     }
   };
 
@@ -1926,9 +1936,7 @@
       {
         const _OrthancPluginWorklistAnswersOperation& p =
           *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
-        
-        ParsedDicomFile answer(p.dicom, p.size);
-        reinterpret_cast<DicomFindAnswers*>(p.answers)->Add(answer);
+        reinterpret_cast<const WorklistHandler*>(p.query)->AddAnswer(p.answers, p.dicom, p.size);
         return true;
       }
 
@@ -1936,7 +1944,6 @@
       {
         const _OrthancPluginWorklistAnswersOperation& p =
           *reinterpret_cast<const _OrthancPluginWorklistAnswersOperation*>(parameters);
-
         reinterpret_cast<DicomFindAnswers*>(p.answers)->SetComplete(false);
         return true;
       }
--- a/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Nov 20 14:33:41 2015 +0100
+++ b/Plugins/Include/orthanc/OrthancCPlugin.h	Fri Nov 20 15:54:07 2015 +0100
@@ -4103,19 +4103,22 @@
   
   typedef struct
   {
-    OrthancPluginWorklistAnswers*  answers;
-    const void*                    dicom;
-    uint32_t                       size;
+    OrthancPluginWorklistAnswers*      answers;
+    const OrthancPluginWorklistQuery*  query;
+    const void*                        dicom;
+    uint32_t                           size;
   } _OrthancPluginWorklistAnswersOperation;
 
-  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginWorklistAnswersOperation(
-    OrthancPluginContext*          context,
-    OrthancPluginWorklistAnswers*  answers,
-    const void*                    dicom,
-    uint32_t                       size)
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode  OrthancPluginWorklistAddWorklistAnswer(
+    OrthancPluginContext*             context,
+    OrthancPluginWorklistAnswers*     answers,
+    const OrthancPluginWorklistQuery* query,
+    const void*                       dicom,
+    uint32_t                          size)
   {
     _OrthancPluginWorklistAnswersOperation params;
     params.answers = answers;
+    params.query = query;
     params.dicom = dicom;
     params.size = size;
 
@@ -4129,6 +4132,7 @@
   {
     _OrthancPluginWorklistAnswersOperation params;
     params.answers = answers;
+    params.query = NULL;
     params.dicom = NULL;
     params.size = 0;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/ModalityWorklists/CMakeLists.txt	Fri Nov 20 15:54:07 2015 +0100
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(SampleModalityWorklists)
+
+SET(SAMPLE_MODALITY_WORKLISTS_VERSION "0.0" CACHE STRING "Version of the plugin")
+SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)")
+SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
+
+SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp")
+SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of boost")
+
+set(SAMPLES_ROOT ${CMAKE_SOURCE_DIR}/..)
+include(${CMAKE_SOURCE_DIR}/../Common/OrthancPlugins.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/JsonCppConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake)
+
+add_library(SampleModalityWorklists SHARED 
+  Plugin.cpp
+  ${JSONCPP_SOURCES}
+  ${BOOST_SOURCES}
+  )
+
+message("Setting the version of the plugin to ${SAMPLE_MODALITY_WORKLISTS_VERSION}")
+add_definitions(
+  -DSAMPLE_MODALITY_WORKLISTS_VERSION="${SAMPLE_MODALITY_WORKLISTS_VERSION}"
+  -DDEFAULT_WORKLISTS_FOLDER="${CMAKE_SOURCE_DIR}/WorklistsDatabase"
+  )
+
+set_target_properties(SampleModalityWorklists PROPERTIES 
+  VERSION ${SAMPLE_MODALITY_WORKLISTS_VERSION} 
+  SOVERSION ${SAMPLE_MODALITY_WORKLISTS_VERSION})
+
+install(
+  TARGETS SampleModalityWorklists
+  RUNTIME DESTINATION lib    # Destination for Windows
+  LIBRARY DESTINATION share/orthanc/plugins    # Destination for Linux
+  )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/ModalityWorklists/Plugin.cpp	Fri Nov 20 15:54:07 2015 +0100
@@ -0,0 +1,234 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital 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.
+ * 
+ * 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 <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include <orthanc/OrthancCPlugin.h>
+
+#include <boost/filesystem.hpp>
+#include <json/value.h>
+#include <json/reader.h>
+#include <string.h>
+#include <iostream>
+
+static OrthancPluginContext* context_ = NULL;
+static std::string folder_;
+
+
+static bool ReadFile(std::string& result,
+                     const std::string& path)
+{
+  OrthancPluginMemoryBuffer tmp;
+  if (OrthancPluginReadFile(context_, &tmp, path.c_str()))
+  {
+    return false;
+  }
+  else
+  {
+    result.assign(reinterpret_cast<const char*>(tmp.data), tmp.size);
+    OrthancPluginFreeMemoryBuffer(context_, &tmp);
+    return true;
+  }
+}
+
+
+/**
+ * This is the main function for matching a DICOM worklist against a query.
+ **/
+static OrthancPluginErrorCode  MatchWorklist(OrthancPluginWorklistAnswers*     answers,
+                                             const OrthancPluginWorklistQuery* query,
+                                             const std::string& path)
+{
+  std::string dicom;
+  if (!ReadFile(dicom, path))
+  {
+    // Cannot read this file, ignore this error
+    return OrthancPluginErrorCode_Success;
+  }
+
+  if (OrthancPluginIsWorklistMatch(context_, query, dicom.c_str(), dicom.size()))
+  {
+    // This DICOM file matches the worklist query, add it to the answers
+    return OrthancPluginWorklistAddWorklistAnswer
+      (context_, answers, query, dicom.c_str(), dicom.size());
+  }
+  else
+  {
+    // This DICOM file does not match
+    return OrthancPluginErrorCode_Success;
+  }
+}
+
+
+
+static bool ConvertToJson(Json::Value& result,
+                          char* content)
+{
+  if (content == NULL)
+  {
+    return false;
+  }
+  else
+  {
+    Json::Reader reader;
+    bool success = reader.parse(content, content + strlen(content), result);
+    OrthancPluginFreeString(context_, content);
+    return success;
+  }
+}
+
+
+
+static bool GetQueryDicom(Json::Value& value,
+                          const OrthancPluginWorklistQuery* query)
+{
+  OrthancPluginMemoryBuffer dicom;
+  if (OrthancPluginGetWorklistQueryDicom(context_, &dicom, query))
+  {
+    return false;
+  }
+
+  char* json = OrthancPluginDicomBufferToJson(context_, reinterpret_cast<const char*>(dicom.data),
+                                              dicom.size, 
+                                              OrthancPluginDicomToJsonFormat_Short, 
+                                              static_cast<OrthancPluginDicomToJsonFlags>(0), 0);
+  OrthancPluginFreeMemoryBuffer(context_, &dicom);
+
+  return ConvertToJson(value, json);
+}
+                          
+
+OrthancPluginErrorCode Callback(OrthancPluginWorklistAnswers*     answers,
+                                const OrthancPluginWorklistQuery* query,
+                                const char*                       remoteAet,
+                                const char*                       calledAet)
+{
+  Json::Value json;
+
+  if (!GetQueryDicom(json, query))
+  {
+    return OrthancPluginErrorCode_InternalError;
+  }
+
+  std::cout << "Received worklist query from remote modality " << remoteAet 
+            << ":" << std::endl << json.toStyledString();
+
+  boost::filesystem::path source(folder_);
+  boost::filesystem::directory_iterator end;
+
+  try
+  {
+    for (boost::filesystem::directory_iterator it(source); it != end; ++it)
+    {
+      if (is_regular_file(it->status()))
+      {
+        std::string extension = boost::filesystem::extension(it->path());
+        if (!strcasecmp(".wl", extension.c_str()))
+        {
+          OrthancPluginErrorCode error = MatchWorklist(answers, query, it->path().string());
+          if (error)
+          {
+            OrthancPluginLogError(context_, "Error while adding an answer to a worklist request");
+            return error;
+          }
+        }
+      }
+    }
+  }
+  catch (boost::filesystem::filesystem_error&)
+  {
+    std::string description = std::string("Inexistent folder while scanning for worklists: ") + source.string();
+    OrthancPluginLogError(context_, description.c_str());
+    return OrthancPluginErrorCode_DirectoryExpected;
+  }
+
+  // Uncomment the following line if too many answers are to be returned
+  // OrthancPluginMarkWorklistAnswersIncomplete(context_, answers);
+
+  return OrthancPluginErrorCode_Success;
+}
+
+
+extern "C"
+{
+  ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c)
+  {
+    context_ = c;
+    OrthancPluginLogWarning(context_, "Storage plugin is initializing");
+
+    /* Check the version of the Orthanc core */
+    if (OrthancPluginCheckVersion(c) == 0)
+    {
+      char info[1024];
+      sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
+              context_->orthancVersion,
+              ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
+              ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
+              ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
+      OrthancPluginLogError(context_, info);
+      return -1;
+    }
+
+    Json::Value configuration;
+    if (!ConvertToJson(configuration, OrthancPluginGetConfiguration(context_)))
+    {
+      OrthancPluginLogError(context_, "Cannot access the configuration");
+      return -1;
+    }
+
+    if (configuration.isMember("WorklistsFolder"))
+    {
+      if (configuration["WorklistsFolder"].type() != Json::stringValue)
+      {
+        OrthancPluginLogError(context_, "The configuration option \"WorklistsFolder\" must be a string");
+        return -1;
+      }
+
+      folder_ = configuration["WorklistsFolder"].asString();
+    }
+    else
+    {
+      folder_ = DEFAULT_WORKLISTS_FOLDER;
+    }
+
+    std::string message = "The database of worklists will be read from folder: " + folder_;
+    OrthancPluginLogWarning(context_, message.c_str());
+
+    OrthancPluginRegisterWorklistCallback(context_, Callback);
+
+    return 0;
+  }
+
+
+  ORTHANC_PLUGINS_API void OrthancPluginFinalize()
+  {
+    OrthancPluginLogWarning(context_, "Sample worklist plugin is finalizing");
+  }
+
+
+  ORTHANC_PLUGINS_API const char* OrthancPluginGetName()
+  {
+    return "sample-worklists";
+  }
+
+
+  ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion()
+  {
+    return SAMPLE_MODALITY_WORKLISTS_VERSION;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/ModalityWorklists/WorklistsDatabase/Generate.py	Fri Nov 20 15:54:07 2015 +0100
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import os
+import subprocess
+
+SOURCE = '/home/jodogne/Downloads/dcmtk-3.6.0/dcmwlm/data/wlistdb/OFFIS/'
+TARGET = os.path.abspath(os.path.dirname(__file__))
+
+for f in os.listdir(SOURCE):
+    ext = os.path.splitext(f)
+
+    if ext[1].lower() == '.dump':
+        subprocess.check_call([
+            'dump2dcm',
+            '-g',
+            '-q',
+            os.path.join(SOURCE, f),
+            os.path.join(TARGET, ext[0].lower() + '.wl'),
+        ])
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist1.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist10.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist2.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist3.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist4.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist5.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist6.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist7.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist8.wl has changed
Binary file Plugins/Samples/ModalityWorklists/WorklistsDatabase/wklist9.wl has changed