view SQLite/Plugins/IndexPlugin.cpp @ 206:6dcf57074dd4

starting OrthancPluginDatabaseBackendV3 wrapper
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 19 Mar 2021 10:11:17 +0100
parents 2089d4071408
children d9ef3f16e6a2
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
 * Copyright (C) 2017-2021 Osimis S.A., Belgium
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero 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
 * Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 **/


#include "SQLiteIndex.h"
#include "../../Framework/Plugins/PluginInitialization.h"

#include <Compatibility.h>  // For std::unique_ptr<>
#include <Logging.h>

static std::unique_ptr<OrthancDatabases::SQLiteIndex> backend_;



#if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE)         // Macro introduced in Orthanc 1.3.1
#  if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 10, 0)

namespace OrthancDatabases
{
  class Output : public IDatabaseBackendOutput
  {
  private:
    _OrthancPluginDatabaseAnswerType            answerType_;
    std::list<std::string>                      strings_;
    
    std::vector<OrthancPluginAttachment>        attachments_;
    std::vector<OrthancPluginChange>            changes_;
    std::vector<OrthancPluginDicomTag>          tags_;
    std::vector<OrthancPluginExportedResource>  exported_;
    std::vector<OrthancPluginDatabaseEvent>     events_;
    
    const char* StoreString(const std::string& s)
    {
      strings_.push_back(s);
      return strings_.back().c_str();
    }

    void SetupAnswerType(_OrthancPluginDatabaseAnswerType type)
    {
      if (answerType_ == _OrthancPluginDatabaseAnswerType_None)
      {
        answerType_ = type;
      }
      else if (answerType_ != type)
      {
        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
      }
    }
    
  public:
    Output() :
      answerType_(_OrthancPluginDatabaseAnswerType_None)
    {
    }
    
    
    void Clear()
    {
      answerType_ = _OrthancPluginDatabaseAnswerType_None;
      strings_.clear();
      
      attachments_.clear();
      changes_.clear();
      tags_.clear();
      exported_.clear();
      events_.clear();
    }


    static OrthancPluginErrorCode ReadAnswersCount(OrthancPluginDatabaseTransaction* transaction,
                                                   uint32_t* target /* out */)
    {
      const Output& that = *reinterpret_cast<const Output*>(transaction);

      size_t size;
      
      switch (that.answerType_)
      {
        case _OrthancPluginDatabaseAnswerType_None:
          size = 0;
          break;
        
        case _OrthancPluginDatabaseAnswerType_Attachment:
          size = that.attachments_.size();
          break;
        
        case _OrthancPluginDatabaseAnswerType_Change:
          size = that.changes_.size();
          break;
        
        case _OrthancPluginDatabaseAnswerType_DicomTag:
          size = that.tags_.size();
          break;
        
        case _OrthancPluginDatabaseAnswerType_ExportedResource:
          size = that.exported_.size();
          break;
        
        default:
          return OrthancPluginErrorCode_InternalError;
      }

      *target = static_cast<uint32_t>(size);
      return OrthancPluginErrorCode_Success;
    }


    static OrthancPluginErrorCode ReadAnswerAttachment(OrthancPluginDatabaseTransaction* transaction,
                                                       OrthancPluginAttachment* target /* out */,
                                                       uint32_t index)
    {
      const Output& that = *reinterpret_cast<const Output*>(transaction);

      if (index < that.attachments_.size())
      {
        *target = that.attachments_[index];
        return OrthancPluginErrorCode_Success;        
      }
      else
      {
        return OrthancPluginErrorCode_ParameterOutOfRange;        
      }
    }


    static OrthancPluginErrorCode ReadAnswerChange(OrthancPluginDatabaseTransaction* transaction,
                                                       OrthancPluginChange* target /* out */,
                                                       uint32_t index)
    {
      const Output& that = *reinterpret_cast<const Output*>(transaction);

      if (index < that.changes_.size())
      {
        *target = that.changes_[index];
        return OrthancPluginErrorCode_Success;        
      }
      else
      {
        return OrthancPluginErrorCode_ParameterOutOfRange;        
      }
    }


    static OrthancPluginErrorCode ReadAnswerDicomTag(OrthancPluginDatabaseTransaction* transaction,
                                                     uint16_t* group,
                                                     uint16_t* element,
                                                     const char** value,
                                                     uint32_t index)
    {
      const Output& that = *reinterpret_cast<const Output*>(transaction);

      if (index < that.tags_.size())
      {
        const OrthancPluginDicomTag& tag = that.tags_[index];
        *group = tag.group;
        *element = tag.element;
        *value = tag.value;
        return OrthancPluginErrorCode_Success;        
      }
      else
      {
        return OrthancPluginErrorCode_ParameterOutOfRange;        
      }
    }


    static OrthancPluginErrorCode ReadAnswerExportedResource(OrthancPluginDatabaseTransaction* transaction,
                                                             OrthancPluginExportedResource* target /* out */,
                                                             uint32_t index)
    {
      const Output& that = *reinterpret_cast<const Output*>(transaction);

      if (index < that.exported_.size())
      {
        *target = that.exported_[index];
        return OrthancPluginErrorCode_Success;        
      }
      else
      {
        return OrthancPluginErrorCode_ParameterOutOfRange;        
      }
    }


    static OrthancPluginErrorCode ReadEventsCount(OrthancPluginDatabaseTransaction* transaction,
                                                  uint32_t* target /* out */)
    {
      const Output& that = *reinterpret_cast<const Output*>(transaction);
      *target = static_cast<uint32_t>(that.events_.size());
      return OrthancPluginErrorCode_Success;
    }

    
    static OrthancPluginErrorCode ReadEvent(OrthancPluginDatabaseTransaction* transaction,
                                            OrthancPluginDatabaseEvent* event /* out */,
                                            uint32_t index)
    {
      const Output& that = *reinterpret_cast<const Output*>(transaction);

      if (index < that.events_.size())
      {
        *event = that.events_[index];
        return OrthancPluginErrorCode_Success;
      }
      else
      {
        return OrthancPluginErrorCode_ParameterOutOfRange;
      }
    }

    
    virtual void SignalDeletedAttachment(const std::string& uuid,
                                         int32_t            contentType,
                                         uint64_t           uncompressedSize,
                                         const std::string& uncompressedHash,
                                         int32_t            compressionType,
                                         uint64_t           compressedSize,
                                         const std::string& compressedHash) ORTHANC_OVERRIDE
    {
      OrthancPluginDatabaseEvent event;
      event.type = OrthancPluginDatabaseEventType_DeletedAttachment;
      event.content.attachment.uuid = StoreString(uuid);
      event.content.attachment.contentType = contentType;
      event.content.attachment.uncompressedSize = uncompressedSize;
      event.content.attachment.uncompressedHash = StoreString(uncompressedHash);
      event.content.attachment.compressionType = compressionType;
      event.content.attachment.compressedSize = compressedSize;
      event.content.attachment.compressedHash = StoreString(compressedHash);
        
      events_.push_back(event);
    }
    
    
    virtual void SignalDeletedResource(const std::string& publicId,
                                       OrthancPluginResourceType resourceType) ORTHANC_OVERRIDE
    {
      OrthancPluginDatabaseEvent event;
      event.type = OrthancPluginDatabaseEventType_DeletedResource;
      event.content.resource.level = resourceType;
      event.content.resource.publicId = StoreString(publicId);
        
      events_.push_back(event);
    }
    

    virtual void SignalRemainingAncestor(const std::string& ancestorId,
                                         OrthancPluginResourceType ancestorType) ORTHANC_OVERRIDE
    {
      OrthancPluginDatabaseEvent event;
      event.type = OrthancPluginDatabaseEventType_RemainingAncestor;
      event.content.resource.level = ancestorType;
      event.content.resource.publicId = StoreString(ancestorId);
        
      events_.push_back(event);
    }
    
    
    virtual void AnswerAttachment(const std::string& uuid,
                                  int32_t            contentType,
                                  uint64_t           uncompressedSize,
                                  const std::string& uncompressedHash,
                                  int32_t            compressionType,
                                  uint64_t           compressedSize,
                                  const std::string& compressedHash) ORTHANC_OVERRIDE
    {
      SetupAnswerType(_OrthancPluginDatabaseAnswerType_Attachment);

      OrthancPluginAttachment attachment;
      attachment.uuid = StoreString(uuid);
      attachment.contentType = contentType;
      attachment.uncompressedSize = uncompressedSize;
      attachment.uncompressedHash = StoreString(uncompressedHash);
      attachment.compressionType = compressionType;
      attachment.compressedSize = compressedSize;
      attachment.compressedHash = StoreString(compressedHash);

      attachments_.push_back(attachment);
    }
    

    virtual void AnswerChange(int64_t                    seq,
                              int32_t                    changeType,
                              OrthancPluginResourceType  resourceType,
                              const std::string&         publicId,
                              const std::string&         date) ORTHANC_OVERRIDE
    {
      SetupAnswerType(_OrthancPluginDatabaseAnswerType_Change);

      OrthancPluginChange change;
      change.seq = seq;
      change.changeType = changeType;
      change.resourceType = resourceType;
      change.publicId = StoreString(publicId);
      change.date = StoreString(date);

      changes_.push_back(change);
    }
    

    virtual void AnswerDicomTag(uint16_t group,
                                uint16_t element,
                                const std::string& value) ORTHANC_OVERRIDE
    {
      SetupAnswerType(_OrthancPluginDatabaseAnswerType_DicomTag);

      OrthancPluginDicomTag tag;
      tag.group = group;
      tag.element = element;
      tag.value = StoreString(value);

      tags_.push_back(tag);      
    }
    

    virtual void AnswerExportedResource(int64_t                    seq,
                                        OrthancPluginResourceType  resourceType,
                                        const std::string&         publicId,
                                        const std::string&         modality,
                                        const std::string&         date,
                                        const std::string&         patientId,
                                        const std::string&         studyInstanceUid,
                                        const std::string&         seriesInstanceUid,
                                        const std::string&         sopInstanceUid) ORTHANC_OVERRIDE
    {
      SetupAnswerType(_OrthancPluginDatabaseAnswerType_ExportedResource);

      OrthancPluginExportedResource exported;
      exported.seq = seq;
      exported.resourceType = resourceType;
      exported.publicId = StoreString(publicId);
      exported.modality = StoreString(modality);
      exported.date = StoreString(date);
      exported.patientId = StoreString(patientId);
      exported.studyInstanceUid = StoreString(studyInstanceUid);
      exported.seriesInstanceUid = StoreString(seriesInstanceUid);
      exported.sopInstanceUid = StoreString(sopInstanceUid);
  
      exported_.push_back(exported);
    }

    
    virtual void AnswerMatchingResource(const std::string& resourceId) ORTHANC_OVERRIDE
    {

    }
    
    
    virtual void AnswerMatchingResource(const std::string& resourceId,
                                        const std::string& someInstanceId) ORTHANC_OVERRIDE
    {

    }
  };


  class Factory : public IDatabaseBackendOutput::IFactory
  {
  public:
    Factory()
    {
    }

    virtual IDatabaseBackendOutput* CreateOutput()
    {
      return new Output;
    }
  };

  
  static void Register()
  {
    OrthancPluginDatabaseBackendV3 backend;
    memset(&backend, 0, sizeof(backend));

    backend.readAnswersCount = Output::ReadAnswersCount;
    backend.readAnswerAttachment = Output::ReadAnswerAttachment;
    backend.readAnswerChange = Output::ReadAnswerChange;
    backend.readAnswerDicomTag = Output::ReadAnswerDicomTag;
    backend.readAnswerExportedResource = Output::ReadAnswerExportedResource;
    
    backend.readEventsCount = Output::ReadEventsCount;
    backend.readEvent = Output::ReadEvent;
  }
}

#  endif
#endif



extern "C"
{
  ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
  {
    if (!OrthancDatabases::InitializePlugin(context, "SQLite", true))
    {
      return -1;
    }

#if 0
    OrthancPlugins::OrthancConfiguration configuration;

    if (!configuration.IsSection("SQLite"))
    {
      LOG(WARNING) << "No available configuration for the SQLite index plugin";
      return 0;
    }

    OrthancPlugins::OrthancConfiguration sqlite;
    configuration.GetSection(sqlite, "SQLite");

    bool enable;
    if (!sqlite.LookupBooleanValue(enable, "EnableIndex") ||
        !enable)
    {
      LOG(WARNING) << "The SQLite index is currently disabled, set \"EnableIndex\" "
                   << "to \"true\" in the \"SQLite\" section of the configuration file of Orthanc";
      return 0;
    }
#endif

    try
    {
      /* Create the database back-end */
      backend_.reset(new OrthancDatabases::SQLiteIndex(context, "index.db"));  // TODO parameter

      /* Register the SQLite index into Orthanc */
      OrthancDatabases::DatabaseBackendAdapterV2::Register(context, *backend_);
    }
    catch (Orthanc::OrthancException& e)
    {
      LOG(ERROR) << e.What();
      return -1;
    }
    catch (...)
    {
      LOG(ERROR) << "Native exception while initializing the plugin";
      return -1;
    }

    return 0;
  }


  ORTHANC_PLUGINS_API void OrthancPluginFinalize()
  {
    LOG(WARNING) << "SQLite index is finalizing";
    backend_.reset(NULL);
  }


  ORTHANC_PLUGINS_API const char* OrthancPluginGetName()
  {
    return "sqlite-index";
  }


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