Mercurial > hg > orthanc
changeset 1802:138664eb59de worklists
sample worklist plugin
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'), + ])