view Plugins/Samples/ModalityWorklists/Plugin.cpp @ 2204:0158f2de8cad

Fix handling of worklist SCP with ReferencedStudySequence and ReferencedPatientSequence
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 08 Dec 2016 14:46:13 +0100
parents 7a05144cb919
children 395522e46b2b
line wrap: on
line source

/**
 * Orthanc - A Lightweight, RESTful DICOM Store
 * Copyright (C) 2012-2016 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>
#include <algorithm>

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 (OrthancPluginWorklistIsMatch(context_, query, dicom.c_str(), dicom.size()))
  {
    // This DICOM file matches the worklist query, add it to the answers
    return OrthancPluginWorklistAddAnswer
      (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 (OrthancPluginWorklistGetDicomQuery(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);
}
                          

static void ToLowerCase(std::string& s)
{
  std::transform(s.begin(), s.end(), s.begin(), tolower);
}


OrthancPluginErrorCode Callback(OrthancPluginWorklistAnswers*     answers,
                                const OrthancPluginWorklistQuery* query,
                                const char*                       remoteAet,
                                const char*                       calledAet)
{
  namespace fs = boost::filesystem;  

  Json::Value json;

  if (!GetQueryDicom(json, query))
  {
    return OrthancPluginErrorCode_InternalError;
  }

  {
    std::string msg = ("Received worklist query from remote modality " + 
                       std::string(remoteAet) + ":\n" + json.toStyledString());
    OrthancPluginLogInfo(context_, msg.c_str());
  }

  fs::path source(folder_);
  fs::directory_iterator end;

  try
  {
    for (fs::directory_iterator it(source); it != end; ++it)
    {
      fs::file_type type(it->status().type());

      if (type == fs::regular_file ||
          type == fs::reparse_file)   // cf. BitBucket issue #11
      {
        std::string extension = fs::extension(it->path());
        ToLowerCase(extension);

        if (extension == ".wl")
        {
          OrthancPluginErrorCode error = MatchWorklist(answers, query, it->path().string());
          if (error)
          {
            OrthancPluginLogError(context_, "Error while adding an answer to a worklist request");
            return error;
          }
        }
      }
    }
  }
  catch (fs::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_, "Sample worklist plugin is initializing");
    OrthancPluginSetDescription(context_, "Serve DICOM modality worklists from a folder with Orthanc.");

    /* 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 of the worklist server");
      return -1;
    }

    bool enabled = false;

    if (configuration.isMember("Worklists"))
    {
      const Json::Value& config = configuration["Worklists"];
      if (!config.isMember("Enable") ||
          config["Enable"].type() != Json::booleanValue)
      {
        OrthancPluginLogError(context_, "The configuration option \"Worklists.Enable\" must contain a Boolean");
        return -1;
      }
      else
      {
        enabled = config["Enable"].asBool();
        if (enabled)
        {
          if (!config.isMember("Database") ||
              config["Database"].type() != Json::stringValue)
          {
            OrthancPluginLogError(context_, "The configuration option \"Worklists.Database\" must contain a path");
            return -1;
          }

          folder_ = config["Database"].asString();
        }
        else
        {
          OrthancPluginLogWarning(context_, "Worklists server is disabled by the configuration file");
        }
      }
    }
    else
    {
      OrthancPluginLogWarning(context_, "Worklists server is disabled, no suitable configuration section was provided");
    }

    if (enabled)
    {
      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 "worklists";
  }


  ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion()
  {
    return MODALITY_WORKLISTS_VERSION;
  }
}