Mercurial > hg > orthanc-databases
view Framework/Plugins/OrthancCppDatabasePlugin.h @ 74:a4e440e65c68 db-changes
rename
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 04 Jan 2019 17:24:30 +0100 |
parents | d40c5fecd160 |
children | 52c70007bb87 |
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-2019 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/>. **/ /** * NOTE: Until Orthanc 1.4.0, this file was part of the Orthanc source * distribution. This file is now part of "orthanc-databases", in * order to uncouple its evolution from the Orthanc core. **/ #pragma once #if HAS_ORTHANC_EXCEPTION != 1 # error HAS_ORTHANC_EXCEPTION must be set to 1 #endif #if ORTHANC_ENABLE_PLUGINS != 1 # error ORTHANC_ENABLE_PLUGINS must be set to 1 #endif #include <Core/OrthancException.h> #include <OrthancServer/Search/DatabaseConstraint.h> #define ORTHANC_PLUGINS_DATABASE_CATCH \ catch (::Orthanc::OrthancException& e) \ { \ return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); \ } \ catch (::std::runtime_error& e) \ { \ LogError(backend, e); \ return OrthancPluginErrorCode_DatabasePlugin; \ } \ catch (...) \ { \ backend->GetOutput().LogError("Native exception"); \ return OrthancPluginErrorCode_DatabasePlugin; \ } #include <stdexcept> #include <list> #include <string> namespace OrthancPlugins { /** * @ingroup Callbacks **/ class DatabaseBackendOutput : public boost::noncopyable { friend class DatabaseBackendAdapter; private: enum AllowedAnswers { AllowedAnswers_All, AllowedAnswers_None, AllowedAnswers_Attachment, AllowedAnswers_Change, AllowedAnswers_DicomTag, AllowedAnswers_ExportedResource, AllowedAnswers_MatchingResource }; OrthancPluginContext* context_; OrthancPluginDatabaseContext* database_; AllowedAnswers allowedAnswers_; void SetAllowedAnswers(AllowedAnswers allowed) { allowedAnswers_ = allowed; } public: DatabaseBackendOutput(OrthancPluginContext* context, OrthancPluginDatabaseContext* database) : context_(context), database_(database), allowedAnswers_(AllowedAnswers_All /* for unit tests */) { } OrthancPluginContext* GetContext() { return context_; } void LogError(const std::string& message) { OrthancPluginLogError(context_, message.c_str()); } void LogWarning(const std::string& message) { OrthancPluginLogWarning(context_, message.c_str()); } void LogInfo(const std::string& message) { OrthancPluginLogInfo(context_, message.c_str()); } 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) { OrthancPluginAttachment attachment; attachment.uuid = uuid.c_str(); attachment.contentType = contentType; attachment.uncompressedSize = uncompressedSize; attachment.uncompressedHash = uncompressedHash.c_str(); attachment.compressionType = compressionType; attachment.compressedSize = compressedSize; attachment.compressedHash = compressedHash.c_str(); OrthancPluginDatabaseSignalDeletedAttachment(context_, database_, &attachment); } void SignalDeletedResource(const std::string& publicId, OrthancPluginResourceType resourceType) { OrthancPluginDatabaseSignalDeletedResource(context_, database_, publicId.c_str(), resourceType); } void SignalRemainingAncestor(const std::string& ancestorId, OrthancPluginResourceType ancestorType) { OrthancPluginDatabaseSignalRemainingAncestor(context_, database_, ancestorId.c_str(), ancestorType); } 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) { if (allowedAnswers_ != AllowedAnswers_All && allowedAnswers_ != AllowedAnswers_Attachment) { throw std::runtime_error("Cannot answer with an attachment in the current state"); } OrthancPluginAttachment attachment; attachment.uuid = uuid.c_str(); attachment.contentType = contentType; attachment.uncompressedSize = uncompressedSize; attachment.uncompressedHash = uncompressedHash.c_str(); attachment.compressionType = compressionType; attachment.compressedSize = compressedSize; attachment.compressedHash = compressedHash.c_str(); OrthancPluginDatabaseAnswerAttachment(context_, database_, &attachment); } void AnswerChange(int64_t seq, int32_t changeType, OrthancPluginResourceType resourceType, const std::string& publicId, const std::string& date) { if (allowedAnswers_ != AllowedAnswers_All && allowedAnswers_ != AllowedAnswers_Change) { throw std::runtime_error("Cannot answer with a change in the current state"); } OrthancPluginChange change; change.seq = seq; change.changeType = changeType; change.resourceType = resourceType; change.publicId = publicId.c_str(); change.date = date.c_str(); OrthancPluginDatabaseAnswerChange(context_, database_, &change); } void AnswerDicomTag(uint16_t group, uint16_t element, const std::string& value) { if (allowedAnswers_ != AllowedAnswers_All && allowedAnswers_ != AllowedAnswers_DicomTag) { throw std::runtime_error("Cannot answer with a DICOM tag in the current state"); } OrthancPluginDicomTag tag; tag.group = group; tag.element = element; tag.value = value.c_str(); OrthancPluginDatabaseAnswerDicomTag(context_, database_, &tag); } 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) { if (allowedAnswers_ != AllowedAnswers_All && allowedAnswers_ != AllowedAnswers_ExportedResource) { throw std::runtime_error("Cannot answer with an exported resource in the current state"); } OrthancPluginExportedResource exported; exported.seq = seq; exported.resourceType = resourceType; exported.publicId = publicId.c_str(); exported.modality = modality.c_str(); exported.date = date.c_str(); exported.patientId = patientId.c_str(); exported.studyInstanceUid = studyInstanceUid.c_str(); exported.seriesInstanceUid = seriesInstanceUid.c_str(); exported.sopInstanceUid = sopInstanceUid.c_str(); OrthancPluginDatabaseAnswerExportedResource(context_, database_, &exported); } #if ORTHANC_PLUGINS_HAS_DATABASE_OPTIMIZATIONS_1 == 1 void AnswerMatchingResource(const std::string& resourceId) { if (allowedAnswers_ != AllowedAnswers_All && allowedAnswers_ != AllowedAnswers_MatchingResource) { throw std::runtime_error("Cannot answer with an exported resource in the current state"); } OrthancPluginMatchingResource match; match.resourceId = resourceId.c_str(); match.someInstanceId = NULL; OrthancPluginDatabaseAnswerMatchingResource(context_, database_, &match); } #endif #if ORTHANC_PLUGINS_HAS_DATABASE_OPTIMIZATIONS_1 == 1 void AnswerMatchingResource(const std::string& resourceId, const std::string& someInstanceId) { if (allowedAnswers_ != AllowedAnswers_All && allowedAnswers_ != AllowedAnswers_MatchingResource) { throw std::runtime_error("Cannot answer with an exported resource in the current state"); } OrthancPluginMatchingResource match; match.resourceId = resourceId.c_str(); match.someInstanceId = someInstanceId.c_str(); OrthancPluginDatabaseAnswerMatchingResource(context_, database_, &match); } #endif }; /** * @ingroup Callbacks **/ class IDatabaseBackend : public boost::noncopyable { friend class DatabaseBackendAdapter; private: DatabaseBackendOutput* output_; void Finalize() { if (output_ != NULL) { delete output_; output_ = NULL; } } protected: DatabaseBackendOutput& GetOutput() { return *output_; } public: IDatabaseBackend() : output_(NULL) { } virtual ~IDatabaseBackend() { Finalize(); } // This takes the ownership void RegisterOutput(DatabaseBackendOutput* output) { Finalize(); output_ = output; } virtual void Open() = 0; virtual void Close() = 0; virtual void AddAttachment(int64_t id, const OrthancPluginAttachment& attachment) = 0; virtual void AttachChild(int64_t parent, int64_t child) = 0; virtual void ClearChanges() = 0; virtual void ClearExportedResources() = 0; virtual int64_t CreateResource(const char* publicId, OrthancPluginResourceType type) = 0; virtual void DeleteAttachment(int64_t id, int32_t attachment) = 0; virtual void DeleteMetadata(int64_t id, int32_t metadataType) = 0; virtual void DeleteResource(int64_t id) = 0; virtual void GetAllInternalIds(std::list<int64_t>& target, OrthancPluginResourceType resourceType) = 0; virtual void GetAllPublicIds(std::list<std::string>& target, OrthancPluginResourceType resourceType) = 0; virtual void GetAllPublicIds(std::list<std::string>& target, OrthancPluginResourceType resourceType, uint64_t since, uint64_t limit) = 0; /* Use GetOutput().AnswerChange() */ virtual void GetChanges(bool& done /*out*/, int64_t since, uint32_t maxResults) = 0; virtual void GetChildrenInternalId(std::list<int64_t>& target /*out*/, int64_t id) = 0; virtual void GetChildrenPublicId(std::list<std::string>& target /*out*/, int64_t id) = 0; /* Use GetOutput().AnswerExportedResource() */ virtual void GetExportedResources(bool& done /*out*/, int64_t since, uint32_t maxResults) = 0; /* Use GetOutput().AnswerChange() */ virtual void GetLastChange() = 0; /* Use GetOutput().AnswerExportedResource() */ virtual void GetLastExportedResource() = 0; /* Use GetOutput().AnswerDicomTag() */ virtual void GetMainDicomTags(int64_t id) = 0; virtual std::string GetPublicId(int64_t resourceId) = 0; virtual uint64_t GetResourceCount(OrthancPluginResourceType resourceType) = 0; virtual OrthancPluginResourceType GetResourceType(int64_t resourceId) = 0; virtual uint64_t GetTotalCompressedSize() = 0; virtual uint64_t GetTotalUncompressedSize() = 0; virtual bool IsExistingResource(int64_t internalId) = 0; virtual bool IsProtectedPatient(int64_t internalId) = 0; virtual void ListAvailableMetadata(std::list<int32_t>& target /*out*/, int64_t id) = 0; virtual void ListAvailableAttachments(std::list<int32_t>& target /*out*/, int64_t id) = 0; virtual void LogChange(const OrthancPluginChange& change) = 0; virtual void LogExportedResource(const OrthancPluginExportedResource& resource) = 0; /* Use GetOutput().AnswerAttachment() */ virtual bool LookupAttachment(int64_t id, int32_t contentType) = 0; virtual bool LookupGlobalProperty(std::string& target /*out*/, int32_t property) = 0; virtual void LookupIdentifier(std::list<int64_t>& target /*out*/, OrthancPluginResourceType resourceType, uint16_t group, uint16_t element, OrthancPluginIdentifierConstraint constraint, const char* value) = 0; virtual void LookupIdentifierRange(std::list<int64_t>& target /*out*/, OrthancPluginResourceType resourceType, uint16_t group, uint16_t element, const char* start, const char* end) = 0; virtual bool LookupMetadata(std::string& target /*out*/, int64_t id, int32_t metadataType) = 0; virtual bool LookupParent(int64_t& parentId /*out*/, int64_t resourceId) = 0; virtual bool LookupResource(int64_t& id /*out*/, OrthancPluginResourceType& type /*out*/, const char* publicId) = 0; virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/) = 0; virtual bool SelectPatientToRecycle(int64_t& internalId /*out*/, int64_t patientIdToAvoid) = 0; virtual void SetGlobalProperty(int32_t property, const char* value) = 0; virtual void SetMainDicomTag(int64_t id, uint16_t group, uint16_t element, const char* value) = 0; virtual void SetIdentifierTag(int64_t id, uint16_t group, uint16_t element, const char* value) = 0; virtual void SetMetadata(int64_t id, int32_t metadataType, const char* value) = 0; virtual void SetProtectedPatient(int64_t internalId, bool isProtected) = 0; virtual void StartTransaction() = 0; virtual void RollbackTransaction() = 0; virtual void CommitTransaction() = 0; virtual uint32_t GetDatabaseVersion() = 0; /** * Upgrade the database to the specified version of the database * schema. The upgrade script is allowed to make calls to * OrthancPluginReconstructMainDicomTags(). **/ virtual void UpgradeDatabase(uint32_t targetVersion, OrthancPluginStorageArea* storageArea) = 0; virtual void ClearMainDicomTags(int64_t internalId) = 0; virtual bool HasCreateInstance() const = 0; #if ORTHANC_PLUGINS_HAS_DATABASE_OPTIMIZATIONS_1 == 1 virtual void LookupResources(const std::vector<Orthanc::DatabaseConstraint>& lookup, OrthancPluginResourceType queryLevel, uint32_t limit, bool requestSomeInstance) = 0; #endif #if ORTHANC_PLUGINS_HAS_DATABASE_OPTIMIZATIONS_1 == 1 virtual void CreateInstance(OrthancPluginCreateInstanceResult& result, const char* hashPatient, const char* hashStudy, const char* hashSeries, const char* hashInstance) { throw std::runtime_error("Not implemented"); } #endif }; /** * @brief Bridge between C and C++ database engines. * * Class creating the bridge between the C low-level primitives for * custom database engines, and the high-level IDatabaseBackend C++ * interface. * * @ingroup Callbacks **/ class DatabaseBackendAdapter { private: // This class cannot be instantiated DatabaseBackendAdapter() { } static void LogError(IDatabaseBackend* backend, const std::runtime_error& e) { backend->GetOutput().LogError("Exception in database back-end: " + std::string(e.what())); } static OrthancPluginErrorCode AddAttachment(void* payload, int64_t id, const OrthancPluginAttachment* attachment) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->AddAttachment(id, *attachment); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode AttachChild(void* payload, int64_t parent, int64_t child) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->AttachChild(parent, child); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode ClearChanges(void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->ClearChanges(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode ClearExportedResources(void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->ClearExportedResources(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode CreateResource(int64_t* id, void* payload, const char* publicId, OrthancPluginResourceType resourceType) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { *id = backend->CreateResource(publicId, resourceType); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode DeleteAttachment(void* payload, int64_t id, int32_t contentType) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->DeleteAttachment(id, contentType); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode DeleteMetadata(void* payload, int64_t id, int32_t metadataType) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->DeleteMetadata(id, metadataType); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode DeleteResource(void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->DeleteResource(id); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetAllInternalIds(OrthancPluginDatabaseContext* context, void* payload, OrthancPluginResourceType resourceType) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<int64_t> target; backend->GetAllInternalIds(target, resourceType); for (std::list<int64_t>::const_iterator it = target.begin(); it != target.end(); ++it) { OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, backend->GetOutput().database_, *it); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetAllPublicIds(OrthancPluginDatabaseContext* context, void* payload, OrthancPluginResourceType resourceType) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<std::string> ids; backend->GetAllPublicIds(ids, resourceType); for (std::list<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) { OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, backend->GetOutput().database_, it->c_str()); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetAllPublicIdsWithLimit(OrthancPluginDatabaseContext* context, void* payload, OrthancPluginResourceType resourceType, uint64_t since, uint64_t limit) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<std::string> ids; backend->GetAllPublicIds(ids, resourceType, since, limit); for (std::list<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) { OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, backend->GetOutput().database_, it->c_str()); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetChanges(OrthancPluginDatabaseContext* context, void* payload, int64_t since, uint32_t maxResult) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Change); try { bool done; backend->GetChanges(done, since, maxResult); if (done) { OrthancPluginDatabaseAnswerChangesDone(backend->GetOutput().context_, backend->GetOutput().database_); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetChildrenInternalId(OrthancPluginDatabaseContext* context, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<int64_t> target; backend->GetChildrenInternalId(target, id); for (std::list<int64_t>::const_iterator it = target.begin(); it != target.end(); ++it) { OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, backend->GetOutput().database_, *it); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetChildrenPublicId(OrthancPluginDatabaseContext* context, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<std::string> ids; backend->GetChildrenPublicId(ids, id); for (std::list<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) { OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, backend->GetOutput().database_, it->c_str()); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetExportedResources(OrthancPluginDatabaseContext* context, void* payload, int64_t since, uint32_t maxResult) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_ExportedResource); try { bool done; backend->GetExportedResources(done, since, maxResult); if (done) { OrthancPluginDatabaseAnswerExportedResourcesDone(backend->GetOutput().context_, backend->GetOutput().database_); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetLastChange(OrthancPluginDatabaseContext* context, void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Change); try { backend->GetLastChange(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetLastExportedResource(OrthancPluginDatabaseContext* context, void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_ExportedResource); try { backend->GetLastExportedResource(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetMainDicomTags(OrthancPluginDatabaseContext* context, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_DicomTag); try { backend->GetMainDicomTags(id); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetPublicId(OrthancPluginDatabaseContext* context, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::string s = backend->GetPublicId(id); OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, backend->GetOutput().database_, s.c_str()); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetResourceCount(uint64_t* target, void* payload, OrthancPluginResourceType resourceType) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { *target = backend->GetResourceCount(resourceType); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetResourceType(OrthancPluginResourceType* resourceType, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { *resourceType = backend->GetResourceType(id); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetTotalCompressedSize(uint64_t* target, void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { *target = backend->GetTotalCompressedSize(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetTotalUncompressedSize(uint64_t* target, void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { *target = backend->GetTotalUncompressedSize(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode IsExistingResource(int32_t* existing, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { *existing = backend->IsExistingResource(id); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode IsProtectedPatient(int32_t* isProtected, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { *isProtected = backend->IsProtectedPatient(id); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode ListAvailableMetadata(OrthancPluginDatabaseContext* context, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<int32_t> target; backend->ListAvailableMetadata(target, id); for (std::list<int32_t>::const_iterator it = target.begin(); it != target.end(); ++it) { OrthancPluginDatabaseAnswerInt32(backend->GetOutput().context_, backend->GetOutput().database_, *it); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode ListAvailableAttachments(OrthancPluginDatabaseContext* context, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<int32_t> target; backend->ListAvailableAttachments(target, id); for (std::list<int32_t>::const_iterator it = target.begin(); it != target.end(); ++it) { OrthancPluginDatabaseAnswerInt32(backend->GetOutput().context_, backend->GetOutput().database_, *it); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LogChange(void* payload, const OrthancPluginChange* change) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->LogChange(*change); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LogExportedResource(void* payload, const OrthancPluginExportedResource* exported) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->LogExportedResource(*exported); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LookupAttachment(OrthancPluginDatabaseContext* context, void* payload, int64_t id, int32_t contentType) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_Attachment); try { backend->LookupAttachment(id, contentType); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LookupGlobalProperty(OrthancPluginDatabaseContext* context, void* payload, int32_t property) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::string s; if (backend->LookupGlobalProperty(s, property)) { OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, backend->GetOutput().database_, s.c_str()); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LookupIdentifier3(OrthancPluginDatabaseContext* context, void* payload, OrthancPluginResourceType resourceType, const OrthancPluginDicomTag* tag, OrthancPluginIdentifierConstraint constraint) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<int64_t> target; backend->LookupIdentifier(target, resourceType, tag->group, tag->element, constraint, tag->value); for (std::list<int64_t>::const_iterator it = target.begin(); it != target.end(); ++it) { OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, backend->GetOutput().database_, *it); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LookupIdentifierRange(OrthancPluginDatabaseContext* context, void* payload, OrthancPluginResourceType resourceType, uint16_t group, uint16_t element, const char* start, const char* end) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::list<int64_t> target; backend->LookupIdentifierRange(target, resourceType, group, element, start, end); for (std::list<int64_t>::const_iterator it = target.begin(); it != target.end(); ++it) { OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, backend->GetOutput().database_, *it); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LookupMetadata(OrthancPluginDatabaseContext* context, void* payload, int64_t id, int32_t metadata) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { std::string s; if (backend->LookupMetadata(s, id, metadata)) { OrthancPluginDatabaseAnswerString(backend->GetOutput().context_, backend->GetOutput().database_, s.c_str()); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LookupParent(OrthancPluginDatabaseContext* context, void* payload, int64_t id) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { int64_t parent; if (backend->LookupParent(parent, id)) { OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, backend->GetOutput().database_, parent); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode LookupResource(OrthancPluginDatabaseContext* context, void* payload, const char* publicId) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { int64_t id; OrthancPluginResourceType type; if (backend->LookupResource(id, type, publicId)) { OrthancPluginDatabaseAnswerResource(backend->GetOutput().context_, backend->GetOutput().database_, id, type); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode SelectPatientToRecycle(OrthancPluginDatabaseContext* context, void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { int64_t id; if (backend->SelectPatientToRecycle(id)) { OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, backend->GetOutput().database_, id); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode SelectPatientToRecycle2(OrthancPluginDatabaseContext* context, void* payload, int64_t patientIdToAvoid) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { int64_t id; if (backend->SelectPatientToRecycle(id, patientIdToAvoid)) { OrthancPluginDatabaseAnswerInt64(backend->GetOutput().context_, backend->GetOutput().database_, id); } return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode SetGlobalProperty(void* payload, int32_t property, const char* value) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->SetGlobalProperty(property, value); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode SetMainDicomTag(void* payload, int64_t id, const OrthancPluginDicomTag* tag) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->SetMainDicomTag(id, tag->group, tag->element, tag->value); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode SetIdentifierTag(void* payload, int64_t id, const OrthancPluginDicomTag* tag) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->SetIdentifierTag(id, tag->group, tag->element, tag->value); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode SetMetadata(void* payload, int64_t id, int32_t metadata, const char* value) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->SetMetadata(id, metadata, value); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode SetProtectedPatient(void* payload, int64_t id, int32_t isProtected) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->SetProtectedPatient(id, (isProtected != 0)); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode StartTransaction(void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->StartTransaction(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode RollbackTransaction(void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->RollbackTransaction(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode CommitTransaction(void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->CommitTransaction(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode Open(void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->Open(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode Close(void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_None); try { backend->Close(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode GetDatabaseVersion(uint32_t* version, void* payload) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); try { *version = backend->GetDatabaseVersion(); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode UpgradeDatabase(void* payload, uint32_t targetVersion, OrthancPluginStorageArea* storageArea) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); try { backend->UpgradeDatabase(targetVersion, storageArea); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } static OrthancPluginErrorCode ClearMainDicomTags(void* payload, int64_t internalId) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); try { backend->ClearMainDicomTags(internalId); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } #if ORTHANC_PLUGINS_HAS_DATABASE_OPTIMIZATIONS_1 == 1 /* Use GetOutput().AnswerResource() */ static OrthancPluginErrorCode LookupResources( OrthancPluginDatabaseContext* context, void* payload, uint32_t constraintsCount, const OrthancPluginDatabaseConstraint* constraints, OrthancPluginResourceType queryLevel, uint32_t limit, uint8_t requestSomeInstance) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); backend->GetOutput().SetAllowedAnswers(DatabaseBackendOutput::AllowedAnswers_MatchingResource); try { std::vector<Orthanc::DatabaseConstraint> lookup; lookup.reserve(constraintsCount); for (uint32_t i = 0; i < constraintsCount; i++) { lookup.push_back(Orthanc::DatabaseConstraint(constraints[i])); } backend->LookupResources(lookup, queryLevel, limit, (requestSomeInstance != 0)); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } #endif #if ORTHANC_PLUGINS_HAS_DATABASE_OPTIMIZATIONS_1 == 1 static OrthancPluginErrorCode CreateInstance(OrthancPluginCreateInstanceResult* output, void* payload, const char* hashPatient, const char* hashStudy, const char* hashSeries, const char* hashInstance) { IDatabaseBackend* backend = reinterpret_cast<IDatabaseBackend*>(payload); try { backend->CreateInstance(*output, hashPatient, hashStudy, hashSeries, hashInstance); return OrthancPluginErrorCode_Success; } ORTHANC_PLUGINS_DATABASE_CATCH } #endif public: /** * Register a custom database back-end written in C++. * * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). * @param backend Your custom database engine. **/ static void Register(OrthancPluginContext* context, IDatabaseBackend& backend) { OrthancPluginDatabaseBackend params; memset(¶ms, 0, sizeof(params)); OrthancPluginDatabaseExtensions extensions; memset(&extensions, 0, sizeof(extensions)); params.addAttachment = AddAttachment; params.attachChild = AttachChild; params.clearChanges = ClearChanges; params.clearExportedResources = ClearExportedResources; params.createResource = CreateResource; params.deleteAttachment = DeleteAttachment; params.deleteMetadata = DeleteMetadata; params.deleteResource = DeleteResource; params.getAllPublicIds = GetAllPublicIds; params.getChanges = GetChanges; params.getChildrenInternalId = GetChildrenInternalId; params.getChildrenPublicId = GetChildrenPublicId; params.getExportedResources = GetExportedResources; params.getLastChange = GetLastChange; params.getLastExportedResource = GetLastExportedResource; params.getMainDicomTags = GetMainDicomTags; params.getPublicId = GetPublicId; params.getResourceCount = GetResourceCount; params.getResourceType = GetResourceType; params.getTotalCompressedSize = GetTotalCompressedSize; params.getTotalUncompressedSize = GetTotalUncompressedSize; params.isExistingResource = IsExistingResource; params.isProtectedPatient = IsProtectedPatient; params.listAvailableMetadata = ListAvailableMetadata; params.listAvailableAttachments = ListAvailableAttachments; params.logChange = LogChange; params.logExportedResource = LogExportedResource; params.lookupAttachment = LookupAttachment; params.lookupGlobalProperty = LookupGlobalProperty; params.lookupIdentifier = NULL; // Unused starting with Orthanc 0.9.5 (db v6) params.lookupIdentifier2 = NULL; // Unused starting with Orthanc 0.9.5 (db v6) params.lookupMetadata = LookupMetadata; params.lookupParent = LookupParent; params.lookupResource = LookupResource; params.selectPatientToRecycle = SelectPatientToRecycle; params.selectPatientToRecycle2 = SelectPatientToRecycle2; params.setGlobalProperty = SetGlobalProperty; params.setMainDicomTag = SetMainDicomTag; params.setIdentifierTag = SetIdentifierTag; params.setMetadata = SetMetadata; params.setProtectedPatient = SetProtectedPatient; params.startTransaction = StartTransaction; params.rollbackTransaction = RollbackTransaction; params.commitTransaction = CommitTransaction; params.open = Open; params.close = Close; extensions.getAllPublicIdsWithLimit = GetAllPublicIdsWithLimit; extensions.getDatabaseVersion = GetDatabaseVersion; extensions.upgradeDatabase = UpgradeDatabase; extensions.clearMainDicomTags = ClearMainDicomTags; extensions.getAllInternalIds = GetAllInternalIds; // New in Orthanc 0.9.5 (db v6) extensions.lookupIdentifier3 = LookupIdentifier3; // New in Orthanc 0.9.5 (db v6) bool performanceWarning = true; #if defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) // Macro introduced in Orthanc 1.3.1 # if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 4, 0) extensions.lookupIdentifierRange = LookupIdentifierRange; // New in Orthanc 1.4.0 # endif #endif #if ORTHANC_PLUGINS_HAS_DATABASE_OPTIMIZATIONS_1 == 1 extensions.lookupResources = LookupResources; // New in Orthanc 1.5.2 (fast lookup) if (backend.HasCreateInstance()) { extensions.createInstance = CreateInstance; // New in Orthanc 1.5.2 (fast create) } performanceWarning = false; #endif if (performanceWarning) { char info[1024]; sprintf(info, "Performance warning: The database index plugin was compiled " "against an old version of the Orthanc SDK (%d.%d.%d): " "Consider upgrading to version 1.5.2 of the Orthanc SDK", ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); OrthancPluginLogWarning(context, info); } OrthancPluginDatabaseContext* database = OrthancPluginRegisterDatabaseBackendV2(context, ¶ms, &extensions, &backend); if (!context) { throw std::runtime_error("Unable to register the database backend"); } backend.RegisterOutput(new DatabaseBackendOutput(context, database)); } }; }