Mercurial > hg > orthanc
view OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp @ 5882:1c562a107a80 find-refactoring
make the DB HK thread resilient to DB disconnection
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Thu, 28 Nov 2024 11:59:51 +0100 |
parents | 1c549458ea5d |
children |
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-2023 Osimis S.A., Belgium * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, 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 "../../Sources/PrecompiledHeadersServer.h" #include "OrthancPluginDatabaseV4.h" #if ORTHANC_ENABLE_PLUGINS != 1 # error The plugin support is disabled #endif #include "../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" #include "../../../OrthancFramework/Sources/Logging.h" #include "../../../OrthancFramework/Sources/OrthancException.h" #include "../../Sources/Database/Compatibility/GenericFind.h" #include "../../Sources/Database/ResourcesContent.h" #include "../../Sources/Database/VoidDatabaseListener.h" #include "../../Sources/ServerToolbox.h" #include "PluginsEnumerations.h" #include "../../Sources/Database/MainDicomTagsRegistry.h" #include "OrthancDatabasePlugin.pb.h" // Auto-generated file #include <cassert> namespace Orthanc { static void CheckSuccess(PluginsErrorDictionary& errorDictionary, OrthancPluginErrorCode code) { if (code != OrthancPluginErrorCode_Success) { errorDictionary.LogError(code, true); throw OrthancException(static_cast<ErrorCode>(code)); } } static ResourceType Convert(DatabasePluginMessages::ResourceType type) { switch (type) { case DatabasePluginMessages::RESOURCE_PATIENT: return ResourceType_Patient; case DatabasePluginMessages::RESOURCE_STUDY: return ResourceType_Study; case DatabasePluginMessages::RESOURCE_SERIES: return ResourceType_Series; case DatabasePluginMessages::RESOURCE_INSTANCE: return ResourceType_Instance; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } static DatabasePluginMessages::ResourceType Convert(ResourceType type) { switch (type) { case ResourceType_Patient: return DatabasePluginMessages::RESOURCE_PATIENT; case ResourceType_Study: return DatabasePluginMessages::RESOURCE_STUDY; case ResourceType_Series: return DatabasePluginMessages::RESOURCE_SERIES; case ResourceType_Instance: return DatabasePluginMessages::RESOURCE_INSTANCE; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } static FileInfo Convert(const DatabasePluginMessages::FileInfo& source) { return FileInfo(source.uuid(), static_cast<FileContentType>(source.content_type()), source.uncompressed_size(), source.uncompressed_hash(), static_cast<CompressionType>(source.compression_type()), source.compressed_size(), source.compressed_hash()); } static ServerIndexChange Convert(const DatabasePluginMessages::ServerIndexChange& source) { return ServerIndexChange(source.seq(), static_cast<ChangeType>(source.change_type()), Convert(source.resource_type()), source.public_id(), source.date()); } static ExportedResource Convert(const DatabasePluginMessages::ExportedResource& source) { return ExportedResource(source.seq(), Convert(source.resource_type()), source.public_id(), source.modality(), source.date(), source.patient_id(), source.study_instance_uid(), source.series_instance_uid(), source.sop_instance_uid()); } static void Convert(DatabasePluginMessages::DatabaseConstraint& target, const DatabaseDicomTagConstraint& source) { target.set_level(Convert(source.GetLevel())); target.set_tag_group(source.GetTag().GetGroup()); target.set_tag_element(source.GetTag().GetElement()); target.set_is_identifier_tag(source.IsIdentifier()); target.set_is_case_sensitive(source.IsCaseSensitive()); target.set_is_mandatory(source.IsMandatory()); target.mutable_values()->Reserve(source.GetValuesCount()); for (size_t j = 0; j < source.GetValuesCount(); j++) { target.add_values(source.GetValue(j)); } switch (source.GetConstraintType()) { case ConstraintType_Equal: target.set_type(DatabasePluginMessages::CONSTRAINT_EQUAL); break; case ConstraintType_SmallerOrEqual: target.set_type(DatabasePluginMessages::CONSTRAINT_SMALLER_OR_EQUAL); break; case ConstraintType_GreaterOrEqual: target.set_type(DatabasePluginMessages::CONSTRAINT_GREATER_OR_EQUAL); break; case ConstraintType_Wildcard: target.set_type(DatabasePluginMessages::CONSTRAINT_WILDCARD); break; case ConstraintType_List: target.set_type(DatabasePluginMessages::CONSTRAINT_LIST); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } static void Convert(DatabasePluginMessages::DatabaseMetadataConstraint& target, const DatabaseMetadataConstraint& source) { target.set_metadata(source.GetMetadata()); target.set_is_case_sensitive(source.IsCaseSensitive()); target.set_is_mandatory(source.IsMandatory()); target.mutable_values()->Reserve(source.GetValuesCount()); for (size_t j = 0; j < source.GetValuesCount(); j++) { target.add_values(source.GetValue(j)); } switch (source.GetConstraintType()) { case ConstraintType_Equal: target.set_type(DatabasePluginMessages::CONSTRAINT_EQUAL); break; case ConstraintType_SmallerOrEqual: target.set_type(DatabasePluginMessages::CONSTRAINT_SMALLER_OR_EQUAL); break; case ConstraintType_GreaterOrEqual: target.set_type(DatabasePluginMessages::CONSTRAINT_GREATER_OR_EQUAL); break; case ConstraintType_Wildcard: target.set_type(DatabasePluginMessages::CONSTRAINT_WILDCARD); break; case ConstraintType_List: target.set_type(DatabasePluginMessages::CONSTRAINT_LIST); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } static void Convert(DatabasePluginMessages::Find_Request_Ordering& target, const FindRequest::Ordering& source) { switch (source.GetKeyType()) { case FindRequest::KeyType_DicomTag: { ResourceType tagLevel; DicomTagType tagType; MainDicomTagsRegistry registry; registry.LookupTag(tagLevel, tagType, source.GetDicomTag()); target.set_key_type(DatabasePluginMessages::ORDERING_KEY_TYPE_DICOM_TAG); target.set_tag_group(source.GetDicomTag().GetGroup()); target.set_tag_element(source.GetDicomTag().GetElement()); target.set_is_identifier_tag(tagType == DicomTagType_Identifier); target.set_tag_level(Convert(tagLevel)); }; break; case FindRequest::KeyType_Metadata: target.set_key_type(DatabasePluginMessages::ORDERING_KEY_TYPE_METADATA); target.set_metadata(source.GetMetadataType()); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } switch (source.GetDirection()) { case FindRequest::OrderingDirection_Ascending: target.set_direction(DatabasePluginMessages::ORDERING_DIRECTION_ASC); break; case FindRequest::OrderingDirection_Descending: target.set_direction(DatabasePluginMessages::ORDERING_DIRECTION_DESC); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } static DatabasePluginMessages::LabelsConstraintType Convert(LabelsConstraint constraint) { switch (constraint) { case LabelsConstraint_All: return DatabasePluginMessages::LABELS_CONSTRAINT_ALL; case LabelsConstraint_Any: return DatabasePluginMessages::LABELS_CONSTRAINT_ANY; case LabelsConstraint_None: return DatabasePluginMessages::LABELS_CONSTRAINT_NONE; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } } static void Convert(DatabasePluginMessages::Find_Request_ChildrenSpecification& target, const FindRequest::ChildrenSpecification& source) { target.set_retrieve_identifiers(source.IsRetrieveIdentifiers()); target.set_retrieve_count(source.IsRetrieveCount()); for (std::set<MetadataType>::const_iterator it = source.GetMetadata().begin(); it != source.GetMetadata().end(); ++it) { target.add_retrieve_metadata(*it); } for (std::set<DicomTag>::const_iterator it = source.GetMainDicomTags().begin(); it != source.GetMainDicomTags().end(); ++it) { DatabasePluginMessages::Find_Request_Tag* tag = target.add_retrieve_main_dicom_tags(); tag->set_group(it->GetGroup()); tag->set_element(it->GetElement()); } } static void Convert(FindResponse::Resource& target, ResourceType level, const DatabasePluginMessages::Find_Response_ResourceContent& source) { for (int i = 0; i < source.main_dicom_tags().size(); i++) { target.AddStringDicomTag(level, source.main_dicom_tags(i).group(), source.main_dicom_tags(i).element(), source.main_dicom_tags(i).value()); } for (int i = 0; i < source.metadata().size(); i++) { target.AddMetadata(level, static_cast<MetadataType>(source.metadata(i).key()), source.metadata(i).value(), source.metadata(i).revision()); } } static void Convert(FindResponse::Resource& target, ResourceType level, const DatabasePluginMessages::Find_Response_ChildrenContent& source) { for (int i = 0; i < source.identifiers().size(); i++) { target.AddChildIdentifier(level, source.identifiers(i)); } target.SetChildrenCount(level, source.count()); for (int i = 0; i < source.main_dicom_tags().size(); i++) { const DicomTag tag(source.main_dicom_tags(i).group(), source.main_dicom_tags(i).element()); for (int j = 0; j < source.main_dicom_tags(i).values().size(); j++) { target.AddChildrenMainDicomTagValue(level, tag, source.main_dicom_tags(i).values(j)); } } for (int i = 0; i < source.metadata().size(); i++) { MetadataType key = static_cast<MetadataType>(source.metadata(i).key()); for (int j = 0; j < source.metadata(i).values().size(); j++) { target.AddChildrenMetadataValue(level, key, source.metadata(i).values(j)); } } } static void Execute(DatabasePluginMessages::Response& response, const OrthancPluginDatabaseV4& database, const DatabasePluginMessages::Request& request) { std::string requestSerialized; request.SerializeToString(&requestSerialized); OrthancPluginMemoryBuffer64 responseSerialized; CheckSuccess(database.GetErrorDictionary(), database.GetDefinition().operations( &responseSerialized, database.GetDefinition().backend, requestSerialized.empty() ? NULL : requestSerialized.c_str(), requestSerialized.size())); bool success = response.ParseFromArray(responseSerialized.data, responseSerialized.size); if (responseSerialized.size > 0) { free(responseSerialized.data); } if (!success) { throw OrthancException(ErrorCode_DatabasePlugin, "Cannot unserialize protobuf originating from the database plugin"); } } static void ExecuteDatabase(DatabasePluginMessages::DatabaseResponse& response, const OrthancPluginDatabaseV4& database, DatabasePluginMessages::DatabaseOperation operation, const DatabasePluginMessages::DatabaseRequest& request) { DatabasePluginMessages::Request fullRequest; fullRequest.set_type(DatabasePluginMessages::REQUEST_DATABASE); fullRequest.mutable_database_request()->CopyFrom(request); fullRequest.mutable_database_request()->set_operation(operation); DatabasePluginMessages::Response fullResponse; Execute(fullResponse, database, fullRequest); response.CopyFrom(fullResponse.database_response()); } class OrthancPluginDatabaseV4::Transaction : public IDatabaseWrapper::ITransaction, public IDatabaseWrapper::ICompatibilityTransaction { private: OrthancPluginDatabaseV4& database_; IDatabaseListener& listener_; void* transaction_; void ExecuteTransaction(DatabasePluginMessages::TransactionResponse& response, DatabasePluginMessages::TransactionOperation operation, const DatabasePluginMessages::TransactionRequest& request) { DatabasePluginMessages::Request fullRequest; fullRequest.set_type(DatabasePluginMessages::REQUEST_TRANSACTION); fullRequest.mutable_transaction_request()->CopyFrom(request); fullRequest.mutable_transaction_request()->set_transaction(reinterpret_cast<intptr_t>(transaction_)); fullRequest.mutable_transaction_request()->set_operation(operation); DatabasePluginMessages::Response fullResponse; Execute(fullResponse, database_, fullRequest); response.CopyFrom(fullResponse.transaction_response()); } void ExecuteTransaction(DatabasePluginMessages::TransactionResponse& response, DatabasePluginMessages::TransactionOperation operation) { DatabasePluginMessages::TransactionRequest request; // Ignored ExecuteTransaction(response, operation, request); } void ExecuteTransaction(DatabasePluginMessages::TransactionOperation operation, const DatabasePluginMessages::TransactionRequest& request) { DatabasePluginMessages::TransactionResponse response; // Ignored ExecuteTransaction(response, operation, request); } void ExecuteTransaction(DatabasePluginMessages::TransactionOperation operation) { DatabasePluginMessages::TransactionResponse response; // Ignored DatabasePluginMessages::TransactionRequest request; // Ignored ExecuteTransaction(response, operation, request); } void ListLabelsInternal(std::set<std::string>& target, bool isSingleResource, int64_t resource) { if (database_.GetDatabaseCapabilities().HasLabelsSupport()) { DatabasePluginMessages::TransactionRequest request; request.mutable_list_labels()->set_single_resource(isSingleResource); request.mutable_list_labels()->set_id(resource); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LIST_LABELS, request); target.clear(); for (int i = 0; i < response.list_labels().labels().size(); i++) { target.insert(response.list_labels().labels(i)); } } else { // This method shouldn't have been called throw OrthancException(ErrorCode_InternalError); } } public: Transaction(OrthancPluginDatabaseV4& database, IDatabaseListener& listener, TransactionType type) : database_(database), listener_(listener), transaction_(NULL) { DatabasePluginMessages::DatabaseRequest request; switch (type) { case TransactionType_ReadOnly: request.mutable_start_transaction()->set_type(DatabasePluginMessages::TRANSACTION_READ_ONLY); break; case TransactionType_ReadWrite: request.mutable_start_transaction()->set_type(DatabasePluginMessages::TRANSACTION_READ_WRITE); break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } DatabasePluginMessages::DatabaseResponse response; ExecuteDatabase(response, database, DatabasePluginMessages::OPERATION_START_TRANSACTION, request); transaction_ = reinterpret_cast<void*>(response.start_transaction().transaction()); if (transaction_ == NULL) { throw OrthancException(ErrorCode_NullPointer); } } virtual ~Transaction() { try { DatabasePluginMessages::DatabaseRequest request; request.mutable_finalize_transaction()->set_transaction(reinterpret_cast<intptr_t>(transaction_)); DatabasePluginMessages::DatabaseResponse response; ExecuteDatabase(response, database_, DatabasePluginMessages::OPERATION_FINALIZE_TRANSACTION, request); } catch (OrthancException& e) { // Destructors must not throw exceptions LOG(ERROR) << "Cannot finalize the database engine: " << e.What(); } } void* GetTransactionObject() { return transaction_; } virtual void Rollback() ORTHANC_OVERRIDE { ExecuteTransaction(DatabasePluginMessages::OPERATION_ROLLBACK); } virtual void Commit(int64_t fileSizeDelta) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_commit()->set_file_size_delta(fileSizeDelta); ExecuteTransaction(DatabasePluginMessages::OPERATION_COMMIT, request); } virtual void AddAttachment(int64_t id, const FileInfo& attachment, int64_t revision) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_add_attachment()->set_id(id); request.mutable_add_attachment()->mutable_attachment()->set_uuid(attachment.GetUuid()); request.mutable_add_attachment()->mutable_attachment()->set_content_type(attachment.GetContentType()); request.mutable_add_attachment()->mutable_attachment()->set_uncompressed_size(attachment.GetUncompressedSize()); request.mutable_add_attachment()->mutable_attachment()->set_uncompressed_hash(attachment.GetUncompressedMD5()); request.mutable_add_attachment()->mutable_attachment()->set_compression_type(attachment.GetCompressionType()); request.mutable_add_attachment()->mutable_attachment()->set_compressed_size(attachment.GetCompressedSize()); request.mutable_add_attachment()->mutable_attachment()->set_compressed_hash(attachment.GetCompressedMD5()); request.mutable_add_attachment()->set_revision(revision); ExecuteTransaction(DatabasePluginMessages::OPERATION_ADD_ATTACHMENT, request); } virtual void ClearChanges() ORTHANC_OVERRIDE { ExecuteTransaction(DatabasePluginMessages::OPERATION_CLEAR_CHANGES); } virtual void ClearExportedResources() ORTHANC_OVERRIDE { ExecuteTransaction(DatabasePluginMessages::OPERATION_CLEAR_EXPORTED_RESOURCES); } virtual void DeleteAttachment(int64_t id, FileContentType attachment) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_delete_attachment()->set_id(id); request.mutable_delete_attachment()->set_type(attachment); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_DELETE_ATTACHMENT, request); listener_.SignalAttachmentDeleted(Convert(response.delete_attachment().deleted_attachment())); } virtual void DeleteMetadata(int64_t id, MetadataType type) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_delete_metadata()->set_id(id); request.mutable_delete_metadata()->set_type(type); ExecuteTransaction(DatabasePluginMessages::OPERATION_DELETE_METADATA, request); } virtual void DeleteResource(int64_t id) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_delete_resource()->set_id(id); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_DELETE_RESOURCE, request); for (int i = 0; i < response.delete_resource().deleted_attachments().size(); i++) { listener_.SignalAttachmentDeleted(Convert(response.delete_resource().deleted_attachments(i))); } for (int i = 0; i < response.delete_resource().deleted_resources().size(); i++) { listener_.SignalResourceDeleted(Convert(response.delete_resource().deleted_resources(i).level()), response.delete_resource().deleted_resources(i).public_id()); } if (response.delete_resource().is_remaining_ancestor()) { listener_.SignalRemainingAncestor(Convert(response.delete_resource().remaining_ancestor().level()), response.delete_resource().remaining_ancestor().public_id()); } } virtual void GetAllMetadata(std::map<MetadataType, std::string>& target, int64_t id) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_all_metadata()->set_id(id); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_METADATA, request); target.clear(); for (int i = 0; i < response.get_all_metadata().metadata().size(); i++) { MetadataType key = static_cast<MetadataType>(response.get_all_metadata().metadata(i).type()); if (target.find(key) == target.end()) { target[key] = response.get_all_metadata().metadata(i).value(); } else { throw OrthancException(ErrorCode_DatabasePlugin); } } } virtual void GetAllPublicIds(std::list<std::string>& target, ResourceType resourceType) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_all_public_ids()->set_resource_type(Convert(resourceType)); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_PUBLIC_IDS, request); target.clear(); for (int i = 0; i < response.get_all_public_ids().ids().size(); i++) { target.push_back(response.get_all_public_ids().ids(i)); } } virtual void GetAllPublicIdsCompatibility(std::list<std::string>& target, ResourceType resourceType, int64_t since, uint32_t limit) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_all_public_ids_with_limits()->set_resource_type(Convert(resourceType)); request.mutable_get_all_public_ids_with_limits()->set_since(since); request.mutable_get_all_public_ids_with_limits()->set_limit(limit); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_ALL_PUBLIC_IDS_WITH_LIMITS, request); target.clear(); for (int i = 0; i < response.get_all_public_ids_with_limits().ids().size(); i++) { target.push_back(response.get_all_public_ids_with_limits().ids(i)); } } virtual void GetChanges(std::list<ServerIndexChange>& target /*out*/, bool& done /*out*/, int64_t since, uint32_t limit) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_changes()->set_since(since); request.mutable_get_changes()->set_limit(limit); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHANGES, request); done = response.get_changes().done(); target.clear(); for (int i = 0; i < response.get_changes().changes().size(); i++) { target.push_back(Convert(response.get_changes().changes(i))); } } virtual void GetChangesExtended(std::list<ServerIndexChange>& target /*out*/, bool& done /*out*/, int64_t since, int64_t to, uint32_t limit, const std::set<ChangeType>& changeTypes) ORTHANC_OVERRIDE { assert(database_.GetDatabaseCapabilities().HasExtendedChanges()); DatabasePluginMessages::TransactionRequest request; DatabasePluginMessages::TransactionResponse response; request.mutable_get_changes_extended()->set_since(since); request.mutable_get_changes_extended()->set_limit(limit); request.mutable_get_changes_extended()->set_to(to); for (std::set<ChangeType>::const_iterator it = changeTypes.begin(); it != changeTypes.end(); ++it) { request.mutable_get_changes_extended()->add_change_type(*it); } ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHANGES_EXTENDED, request); done = response.get_changes_extended().done(); target.clear(); for (int i = 0; i < response.get_changes_extended().changes().size(); i++) { target.push_back(Convert(response.get_changes_extended().changes(i))); } } virtual void GetChildrenInternalId(std::list<int64_t>& target, int64_t id) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_children_internal_id()->set_id(id); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHILDREN_INTERNAL_ID, request); target.clear(); for (int i = 0; i < response.get_children_internal_id().ids().size(); i++) { target.push_back(response.get_children_internal_id().ids(i)); } } virtual void GetChildrenPublicId(std::list<std::string>& target, int64_t id) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_children_public_id()->set_id(id); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHILDREN_PUBLIC_ID, request); target.clear(); for (int i = 0; i < response.get_children_public_id().ids().size(); i++) { target.push_back(response.get_children_public_id().ids(i)); } } virtual void GetExportedResources(std::list<ExportedResource>& target /*out*/, bool& done /*out*/, int64_t since, uint32_t limit) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_exported_resources()->set_since(since); request.mutable_get_exported_resources()->set_limit(limit); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_EXPORTED_RESOURCES, request); done = response.get_exported_resources().done(); target.clear(); for (int i = 0; i < response.get_exported_resources().resources().size(); i++) { target.push_back(Convert(response.get_exported_resources().resources(i))); } } virtual void GetLastChange(std::list<ServerIndexChange>& target /*out*/) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_LAST_CHANGE); target.clear(); if (response.get_last_change().found()) { target.push_back(Convert(response.get_last_change().change())); } } virtual void GetLastExportedResource(std::list<ExportedResource>& target /*out*/) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_LAST_EXPORTED_RESOURCE); target.clear(); if (response.get_last_exported_resource().found()) { target.push_back(Convert(response.get_last_exported_resource().resource())); } } virtual void GetMainDicomTags(DicomMap& target, int64_t id) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_main_dicom_tags()->set_id(id); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_MAIN_DICOM_TAGS, request); target.Clear(); for (int i = 0; i < response.get_main_dicom_tags().tags().size(); i++) { const DatabasePluginMessages::GetMainDicomTags_Response_Tag& tag = response.get_main_dicom_tags().tags(i); if (tag.group() > 0xffffu || tag.element() > 0xffffu) { throw OrthancException(ErrorCode_ParameterOutOfRange); } else { target.SetValue(tag.group(), tag.element(), tag.value(), false); } } } virtual std::string GetPublicId(int64_t resourceId) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_public_id()->set_id(resourceId); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_PUBLIC_ID, request); return response.get_public_id().id(); } virtual uint64_t GetResourcesCount(ResourceType resourceType) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_resources_count()->set_type(Convert(resourceType)); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_RESOURCES_COUNT, request); return response.get_resources_count().count(); } virtual ResourceType GetResourceType(int64_t resourceId) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_resource_type()->set_id(resourceId); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_RESOURCE_TYPE, request); return Convert(response.get_resource_type().type()); } virtual uint64_t GetTotalCompressedSize() ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_TOTAL_COMPRESSED_SIZE); return response.get_total_compressed_size().size(); } virtual uint64_t GetTotalUncompressedSize() ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_TOTAL_UNCOMPRESSED_SIZE); return response.get_total_uncompressed_size().size(); } virtual bool IsProtectedPatient(int64_t internalId) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_is_protected_patient()->set_patient_id(internalId); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_IS_PROTECTED_PATIENT, request); return response.is_protected_patient().protected_patient(); } virtual void ListAvailableAttachments(std::set<FileContentType>& target, int64_t id) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_list_available_attachments()->set_id(id); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LIST_AVAILABLE_ATTACHMENTS, request); target.clear(); for (int i = 0; i < response.list_available_attachments().attachments().size(); i++) { FileContentType attachment = static_cast<FileContentType>(response.list_available_attachments().attachments(i)); if (target.find(attachment) == target.end()) { target.insert(attachment); } else { throw OrthancException(ErrorCode_DatabasePlugin); } } } virtual void LogChange(ChangeType changeType, ResourceType resourceType, int64_t internalId, const std::string& /* publicId - unused */, const std::string& date) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_log_change()->set_change_type(changeType); request.mutable_log_change()->set_resource_type(Convert(resourceType)); request.mutable_log_change()->set_resource_id(internalId); request.mutable_log_change()->set_date(date); ExecuteTransaction(DatabasePluginMessages::OPERATION_LOG_CHANGE, request); } virtual void LogExportedResource(const ExportedResource& resource) ORTHANC_OVERRIDE { // TODO: "seq" is ignored, could be simplified in "ExportedResource" DatabasePluginMessages::TransactionRequest request; request.mutable_log_exported_resource()->set_resource_type(Convert(resource.GetResourceType())); request.mutable_log_exported_resource()->set_public_id(resource.GetPublicId()); request.mutable_log_exported_resource()->set_modality(resource.GetModality()); request.mutable_log_exported_resource()->set_date(resource.GetDate()); request.mutable_log_exported_resource()->set_patient_id(resource.GetPatientId()); request.mutable_log_exported_resource()->set_study_instance_uid(resource.GetStudyInstanceUid()); request.mutable_log_exported_resource()->set_series_instance_uid(resource.GetSeriesInstanceUid()); request.mutable_log_exported_resource()->set_sop_instance_uid(resource.GetSopInstanceUid()); ExecuteTransaction(DatabasePluginMessages::OPERATION_LOG_EXPORTED_RESOURCE, request); } virtual bool LookupAttachment(FileInfo& attachment, int64_t& revision, int64_t id, FileContentType contentType) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_lookup_attachment()->set_id(id); request.mutable_lookup_attachment()->set_content_type(contentType); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_ATTACHMENT, request); if (response.lookup_attachment().found()) { attachment = Convert(response.lookup_attachment().attachment()); revision = response.lookup_attachment().revision(); return true; } else { return false; } } virtual bool LookupGlobalProperty(std::string& target, GlobalProperty property, bool shared) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_lookup_global_property()->set_server_id(shared ? "" : database_.GetServerIdentifier()); request.mutable_lookup_global_property()->set_property(property); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_GLOBAL_PROPERTY, request); if (response.lookup_global_property().found()) { target = response.lookup_global_property().value(); return true; } else { return false; } } virtual int64_t IncrementGlobalProperty(GlobalProperty property, int64_t increment, bool shared) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_increment_global_property()->set_server_id(shared ? "" : database_.GetServerIdentifier()); request.mutable_increment_global_property()->set_property(property); request.mutable_increment_global_property()->set_increment(increment); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_INCREMENT_GLOBAL_PROPERTY, request); return response.increment_global_property().new_value(); } virtual void UpdateAndGetStatistics(int64_t& patientsCount, int64_t& studiesCount, int64_t& seriesCount, int64_t& instancesCount, int64_t& compressedSize, int64_t& uncompressedSize) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_UPDATE_AND_GET_STATISTICS); patientsCount = response.update_and_get_statistics().patients_count(); studiesCount = response.update_and_get_statistics().studies_count(); seriesCount = response.update_and_get_statistics().series_count(); instancesCount = response.update_and_get_statistics().instances_count(); compressedSize = response.update_and_get_statistics().total_compressed_size(); uncompressedSize = response.update_and_get_statistics().total_uncompressed_size(); } virtual void PerformDbHousekeeping() ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_PERFORM_DB_HOUSEKEEPING); } virtual bool LookupMetadata(std::string& target, int64_t& revision, int64_t id, MetadataType type) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_lookup_metadata()->set_id(id); request.mutable_lookup_metadata()->set_metadata_type(type); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_METADATA, request); if (response.lookup_metadata().found()) { target = response.lookup_metadata().value(); revision = response.lookup_metadata().revision(); return true; } else { return false; } } virtual bool LookupParent(int64_t& parentId, int64_t resourceId) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_lookup_parent()->set_id(resourceId); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_PARENT, request); if (response.lookup_parent().found()) { parentId = response.lookup_parent().parent(); return true; } else { return false; } } virtual bool LookupResource(int64_t& id, ResourceType& type, const std::string& publicId) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_lookup_resource()->set_public_id(publicId); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_RESOURCE, request); if (response.lookup_resource().found()) { id = response.lookup_resource().internal_id(); type = Convert(response.lookup_resource().type()); return true; } else { return false; } } virtual bool SelectPatientToRecycle(int64_t& internalId) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_SELECT_PATIENT_TO_RECYCLE); if (response.select_patient_to_recycle().found()) { internalId = response.select_patient_to_recycle().patient_id(); return true; } else { return false; } } virtual bool SelectPatientToRecycle(int64_t& internalId, int64_t patientIdToAvoid) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_select_patient_to_recycle_with_avoid()->set_patient_id_to_avoid(patientIdToAvoid); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_SELECT_PATIENT_TO_RECYCLE_WITH_AVOID, request); if (response.select_patient_to_recycle_with_avoid().found()) { internalId = response.select_patient_to_recycle_with_avoid().patient_id(); return true; } else { return false; } } virtual void SetGlobalProperty(GlobalProperty property, bool shared, const std::string& value) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_set_global_property()->set_server_id(shared ? "" : database_.GetServerIdentifier()); request.mutable_set_global_property()->set_property(property); request.mutable_set_global_property()->set_value(value); ExecuteTransaction(DatabasePluginMessages::OPERATION_SET_GLOBAL_PROPERTY, request); } virtual void ClearMainDicomTags(int64_t id) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_clear_main_dicom_tags()->set_id(id); ExecuteTransaction(DatabasePluginMessages::OPERATION_CLEAR_MAIN_DICOM_TAGS, request); } virtual void SetMetadata(int64_t id, MetadataType type, const std::string& value, int64_t revision) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_set_metadata()->set_id(id); request.mutable_set_metadata()->set_metadata_type(type); request.mutable_set_metadata()->set_value(value); request.mutable_set_metadata()->set_revision(revision); ExecuteTransaction(DatabasePluginMessages::OPERATION_SET_METADATA, request); } virtual void SetProtectedPatient(int64_t internalId, bool isProtected) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_set_protected_patient()->set_patient_id(internalId); request.mutable_set_protected_patient()->set_protected_patient(isProtected); ExecuteTransaction(DatabasePluginMessages::OPERATION_SET_PROTECTED_PATIENT, request); } virtual bool IsDiskSizeAbove(uint64_t threshold) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_is_disk_size_above()->set_threshold(threshold); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_IS_DISK_SIZE_ABOVE, request); return response.is_disk_size_above().result(); } virtual void ApplyLookupResources(std::list<std::string>& resourcesId, std::list<std::string>* instancesId, // Can be NULL if not needed const DatabaseDicomTagConstraints& lookup, ResourceType queryLevel, const std::set<std::string>& labels, LabelsConstraint labelsConstraint, uint32_t limit) ORTHANC_OVERRIDE { if (!database_.GetDatabaseCapabilities().HasLabelsSupport() && !labels.empty()) { throw OrthancException(ErrorCode_InternalError); } DatabasePluginMessages::TransactionRequest request; request.mutable_lookup_resources()->set_query_level(Convert(queryLevel)); request.mutable_lookup_resources()->set_limit(limit); request.mutable_lookup_resources()->set_retrieve_instances_ids(instancesId != NULL); request.mutable_lookup_resources()->mutable_lookup()->Reserve(lookup.GetSize()); for (size_t i = 0; i < lookup.GetSize(); i++) { Convert(*request.mutable_lookup_resources()->add_lookup(), lookup.GetConstraint(i)); } for (std::set<std::string>::const_iterator it = labels.begin(); it != labels.end(); ++it) { request.mutable_lookup_resources()->add_labels(*it); } request.mutable_lookup_resources()->set_labels_constraint(Convert(labelsConstraint)); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_RESOURCES, request); for (int i = 0; i < response.lookup_resources().resources_ids().size(); i++) { resourcesId.push_back(response.lookup_resources().resources_ids(i)); } if (instancesId != NULL) { if (response.lookup_resources().resources_ids().size() != response.lookup_resources().instances_ids().size()) { throw OrthancException(ErrorCode_DatabasePlugin); } else { for (int i = 0; i < response.lookup_resources().instances_ids().size(); i++) { instancesId->push_back(response.lookup_resources().instances_ids(i)); } } } } virtual bool CreateInstance(CreateInstanceResult& result, /* out */ int64_t& instanceId, /* out */ const std::string& patient, const std::string& study, const std::string& series, const std::string& instance) ORTHANC_OVERRIDE { // TODO: "CreateInstanceResult" => constructor and getters DatabasePluginMessages::TransactionRequest request; request.mutable_create_instance()->set_patient(patient); request.mutable_create_instance()->set_study(study); request.mutable_create_instance()->set_series(series); request.mutable_create_instance()->set_instance(instance); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_CREATE_INSTANCE, request); instanceId = response.create_instance().instance_id(); if (response.create_instance().is_new_instance()) { result.isNewPatient_ = response.create_instance().is_new_patient(); result.isNewStudy_ = response.create_instance().is_new_study(); result.isNewSeries_ = response.create_instance().is_new_series(); result.patientId_ = response.create_instance().patient_id(); result.studyId_ = response.create_instance().study_id(); result.seriesId_ = response.create_instance().series_id(); return true; } else { return false; } } virtual void SetResourcesContent(const ResourcesContent& content) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_set_resources_content()->mutable_tags()->Reserve(content.GetListTags().size()); for (ResourcesContent::ListTags::const_iterator it = content.GetListTags().begin(); it != content.GetListTags().end(); ++it) { DatabasePluginMessages::SetResourcesContent_Request_Tag* tag = request.mutable_set_resources_content()->add_tags(); tag->set_resource_id(it->GetResourceId()); tag->set_is_identifier(it->IsIdentifier()); tag->set_group(it->GetTag().GetGroup()); tag->set_element(it->GetTag().GetElement()); tag->set_value(it->GetValue()); } request.mutable_set_resources_content()->mutable_metadata()->Reserve(content.GetListMetadata().size()); for (ResourcesContent::ListMetadata::const_iterator it = content.GetListMetadata().begin(); it != content.GetListMetadata().end(); ++it) { DatabasePluginMessages::SetResourcesContent_Request_Metadata* metadata = request.mutable_set_resources_content()->add_metadata(); metadata->set_resource_id(it->GetResourceId()); metadata->set_metadata(it->GetType()); metadata->set_value(it->GetValue()); } ExecuteTransaction(DatabasePluginMessages::OPERATION_SET_RESOURCES_CONTENT, request); } virtual void GetChildrenMetadata(std::list<std::string>& target, int64_t resourceId, MetadataType metadata) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_get_children_metadata()->set_id(resourceId); request.mutable_get_children_metadata()->set_metadata(metadata); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_CHILDREN_METADATA, request); for (int i = 0; i < response.get_children_metadata().values().size(); i++) { target.push_back(response.get_children_metadata().values(i)); } } virtual int64_t GetLastChangeIndex() ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_GET_LAST_CHANGE_INDEX); return response.get_last_change_index().result(); } virtual bool LookupResourceAndParent(int64_t& id, ResourceType& type, std::string& parentPublicId, const std::string& publicId) ORTHANC_OVERRIDE { DatabasePluginMessages::TransactionRequest request; request.mutable_lookup_resource_and_parent()->set_public_id(publicId); DatabasePluginMessages::TransactionResponse response; ExecuteTransaction(response, DatabasePluginMessages::OPERATION_LOOKUP_RESOURCE_AND_PARENT, request); if (response.lookup_resource_and_parent().found()) { id = response.lookup_resource_and_parent().id(); type = Convert(response.lookup_resource_and_parent().type()); switch (type) { case ResourceType_Patient: if (!response.lookup_resource_and_parent().parent_public_id().empty()) { throw OrthancException(ErrorCode_DatabasePlugin); } break; case ResourceType_Study: case ResourceType_Series: case ResourceType_Instance: if (response.lookup_resource_and_parent().parent_public_id().empty()) { throw OrthancException(ErrorCode_DatabasePlugin); } else { parentPublicId = response.lookup_resource_and_parent().parent_public_id(); } break; default: throw OrthancException(ErrorCode_ParameterOutOfRange); } return true; } else { return false; } } virtual void AddLabel(int64_t resource, const std::string& label) ORTHANC_OVERRIDE { if (database_.GetDatabaseCapabilities().HasLabelsSupport()) { DatabasePluginMessages::TransactionRequest request; request.mutable_add_label()->set_id(resource); request.mutable_add_label()->set_label(label); ExecuteTransaction(DatabasePluginMessages::OPERATION_ADD_LABEL, request); } else { // This method shouldn't have been called throw OrthancException(ErrorCode_InternalError); } } virtual void RemoveLabel(int64_t resource, const std::string& label) ORTHANC_OVERRIDE { if (database_.GetDatabaseCapabilities().HasLabelsSupport()) { DatabasePluginMessages::TransactionRequest request; request.mutable_remove_label()->set_id(resource); request.mutable_remove_label()->set_label(label); ExecuteTransaction(DatabasePluginMessages::OPERATION_REMOVE_LABEL, request); } else { // This method shouldn't have been called throw OrthancException(ErrorCode_InternalError); } } virtual void ListLabels(std::set<std::string>& target, int64_t resource) ORTHANC_OVERRIDE { ListLabelsInternal(target, true, resource); } virtual void ListAllLabels(std::set<std::string>& target) ORTHANC_OVERRIDE { ListLabelsInternal(target, false, -1); } virtual void ExecuteCount(uint64_t& count, const FindRequest& request, const Capabilities& capabilities) ORTHANC_OVERRIDE { if (capabilities.HasFindSupport()) { DatabasePluginMessages::TransactionRequest dbRequest; dbRequest.mutable_find()->set_level(Convert(request.GetLevel())); if (request.GetOrthancIdentifiers().HasPatientId()) { dbRequest.mutable_find()->set_orthanc_id_patient(request.GetOrthancIdentifiers().GetPatientId()); } if (request.GetOrthancIdentifiers().HasStudyId()) { dbRequest.mutable_find()->set_orthanc_id_study(request.GetOrthancIdentifiers().GetStudyId()); } if (request.GetOrthancIdentifiers().HasSeriesId()) { dbRequest.mutable_find()->set_orthanc_id_series(request.GetOrthancIdentifiers().GetSeriesId()); } if (request.GetOrthancIdentifiers().HasInstanceId()) { dbRequest.mutable_find()->set_orthanc_id_instance(request.GetOrthancIdentifiers().GetInstanceId()); } for (size_t i = 0; i < request.GetDicomTagConstraints().GetSize(); i++) { Convert(*dbRequest.mutable_find()->add_dicom_tag_constraints(), request.GetDicomTagConstraints().GetConstraint(i)); } for (std::deque<DatabaseMetadataConstraint*>::const_iterator it = request.GetMetadataConstraint().begin(); it != request.GetMetadataConstraint().end(); ++it) { Convert(*dbRequest.mutable_find()->add_metadata_constraints(), *(*it)); } for (std::set<std::string>::const_iterator it = request.GetLabels().begin(); it != request.GetLabels().end(); ++it) { dbRequest.mutable_find()->add_labels(*it); } dbRequest.mutable_find()->set_labels_constraint(Convert(request.GetLabelsConstraint())); DatabasePluginMessages::TransactionResponse dbResponse; ExecuteTransaction(dbResponse, DatabasePluginMessages::OPERATION_COUNT_RESOURCES, dbRequest); count = dbResponse.count_resources().count(); } else { throw OrthancException(ErrorCode_NotImplemented); } } virtual void ExecuteFind(FindResponse& response, const FindRequest& request, const Capabilities& capabilities) ORTHANC_OVERRIDE { if (capabilities.HasFindSupport()) { DatabasePluginMessages::TransactionRequest dbRequest; dbRequest.mutable_find()->set_level(Convert(request.GetLevel())); if (request.GetOrthancIdentifiers().HasPatientId()) { dbRequest.mutable_find()->set_orthanc_id_patient(request.GetOrthancIdentifiers().GetPatientId()); } if (request.GetOrthancIdentifiers().HasStudyId()) { dbRequest.mutable_find()->set_orthanc_id_study(request.GetOrthancIdentifiers().GetStudyId()); } if (request.GetOrthancIdentifiers().HasSeriesId()) { dbRequest.mutable_find()->set_orthanc_id_series(request.GetOrthancIdentifiers().GetSeriesId()); } if (request.GetOrthancIdentifiers().HasInstanceId()) { dbRequest.mutable_find()->set_orthanc_id_instance(request.GetOrthancIdentifiers().GetInstanceId()); } for (size_t i = 0; i < request.GetDicomTagConstraints().GetSize(); i++) { Convert(*dbRequest.mutable_find()->add_dicom_tag_constraints(), request.GetDicomTagConstraints().GetConstraint(i)); } for (std::deque<DatabaseMetadataConstraint*>::const_iterator it = request.GetMetadataConstraint().begin(); it != request.GetMetadataConstraint().end(); ++it) { Convert(*dbRequest.mutable_find()->add_metadata_constraints(), *(*it)); } for (std::deque<FindRequest::Ordering*>::const_iterator it = request.GetOrdering().begin(); it != request.GetOrdering().end(); ++it) { Convert(*dbRequest.mutable_find()->add_ordering(), *(*it)); } if (request.HasLimits()) { dbRequest.mutable_find()->mutable_limits()->set_since(request.GetLimitsSince()); dbRequest.mutable_find()->mutable_limits()->set_count(request.GetLimitsCount()); } for (std::set<std::string>::const_iterator it = request.GetLabels().begin(); it != request.GetLabels().end(); ++it) { dbRequest.mutable_find()->add_labels(*it); } dbRequest.mutable_find()->set_labels_constraint(Convert(request.GetLabelsConstraint())); dbRequest.mutable_find()->set_retrieve_main_dicom_tags(request.IsRetrieveMainDicomTags()); dbRequest.mutable_find()->set_retrieve_metadata(request.IsRetrieveMetadata()); dbRequest.mutable_find()->set_retrieve_labels(request.IsRetrieveLabels()); dbRequest.mutable_find()->set_retrieve_attachments(request.IsRetrieveAttachments()); dbRequest.mutable_find()->set_retrieve_parent_identifier(request.IsRetrieveParentIdentifier()); if (request.GetLevel() == ResourceType_Instance) { dbRequest.mutable_find()->set_retrieve_one_instance_metadata_and_attachments(false); } else { dbRequest.mutable_find()->set_retrieve_one_instance_metadata_and_attachments(request.IsRetrieveOneInstanceMetadataAndAttachments()); } if (request.GetLevel() == ResourceType_Study || request.GetLevel() == ResourceType_Series || request.GetLevel() == ResourceType_Instance) { dbRequest.mutable_find()->mutable_parent_patient()->set_retrieve_main_dicom_tags(request.GetParentSpecification(ResourceType_Patient).IsRetrieveMainDicomTags()); dbRequest.mutable_find()->mutable_parent_patient()->set_retrieve_metadata(request.GetParentSpecification(ResourceType_Patient).IsRetrieveMetadata()); } if (request.GetLevel() == ResourceType_Series || request.GetLevel() == ResourceType_Instance) { dbRequest.mutable_find()->mutable_parent_study()->set_retrieve_main_dicom_tags(request.GetParentSpecification(ResourceType_Study).IsRetrieveMainDicomTags()); dbRequest.mutable_find()->mutable_parent_study()->set_retrieve_metadata(request.GetParentSpecification(ResourceType_Study).IsRetrieveMetadata()); } if (request.GetLevel() == ResourceType_Instance) { dbRequest.mutable_find()->mutable_parent_series()->set_retrieve_main_dicom_tags(request.GetParentSpecification(ResourceType_Series).IsRetrieveMainDicomTags()); dbRequest.mutable_find()->mutable_parent_series()->set_retrieve_metadata(request.GetParentSpecification(ResourceType_Series).IsRetrieveMetadata()); } if (request.GetLevel() == ResourceType_Patient) { Convert(*dbRequest.mutable_find()->mutable_children_studies(), request.GetChildrenSpecification(ResourceType_Study)); } if (request.GetLevel() == ResourceType_Patient || request.GetLevel() == ResourceType_Study) { Convert(*dbRequest.mutable_find()->mutable_children_series(), request.GetChildrenSpecification(ResourceType_Series)); } if (request.GetLevel() == ResourceType_Patient || request.GetLevel() == ResourceType_Study || request.GetLevel() == ResourceType_Series) { Convert(*dbRequest.mutable_find()->mutable_children_instances(), request.GetChildrenSpecification(ResourceType_Instance)); } DatabasePluginMessages::TransactionResponse dbResponse; ExecuteTransaction(dbResponse, DatabasePluginMessages::OPERATION_FIND, dbRequest); for (int i = 0; i < dbResponse.find().size(); i++) { const DatabasePluginMessages::Find_Response& source = dbResponse.find(i); std::unique_ptr<FindResponse::Resource> target( new FindResponse::Resource(request.GetLevel(), source.internal_id(), source.public_id())); if (request.IsRetrieveParentIdentifier()) { target->SetParentIdentifier(source.parent_public_id()); } for (int i = 0; i < source.labels().size(); i++) { target->AddLabel(source.labels(i)); } if (source.attachments().size() != source.attachments_revisions().size()) { throw OrthancException(ErrorCode_DatabasePlugin); } for (int i = 0; i < source.attachments().size(); i++) { target->AddAttachment(Convert(source.attachments(i)), source.attachments_revisions(i)); } Convert(*target, ResourceType_Patient, source.patient_content()); if (request.GetLevel() == ResourceType_Study || request.GetLevel() == ResourceType_Series || request.GetLevel() == ResourceType_Instance) { Convert(*target, ResourceType_Study, source.study_content()); } if (request.GetLevel() == ResourceType_Series || request.GetLevel() == ResourceType_Instance) { Convert(*target, ResourceType_Series, source.series_content()); } if (request.GetLevel() == ResourceType_Instance) { Convert(*target, ResourceType_Instance, source.instance_content()); } if (request.GetLevel() == ResourceType_Patient) { Convert(*target, ResourceType_Study, source.children_studies_content()); } if (request.GetLevel() == ResourceType_Patient || request.GetLevel() == ResourceType_Study) { Convert(*target, ResourceType_Series, source.children_series_content()); } if (request.GetLevel() == ResourceType_Patient || request.GetLevel() == ResourceType_Study || request.GetLevel() == ResourceType_Series) { Convert(*target, ResourceType_Instance, source.children_instances_content()); } if (request.GetLevel() != ResourceType_Instance && request.IsRetrieveOneInstanceMetadataAndAttachments()) { std::map<MetadataType, std::string> metadata; for (int i = 0; i < source.one_instance_metadata().size(); i++) { MetadataType key = static_cast<MetadataType>(source.one_instance_metadata(i).key()); if (metadata.find(key) == metadata.end()) { metadata[key] = source.one_instance_metadata(i).value(); } else { throw OrthancException(ErrorCode_DatabasePlugin); } } std::map<FileContentType, FileInfo> attachments; for (int i = 0; i < source.one_instance_attachments().size(); i++) { FileInfo info(Convert(source.one_instance_attachments(i))); if (attachments.find(info.GetContentType()) == attachments.end()) { attachments[info.GetContentType()] = info; } else { throw OrthancException(ErrorCode_DatabasePlugin); } } target->SetOneInstanceMetadataAndAttachments(source.one_instance_public_id(), metadata, attachments); } response.Add(target.release()); } } else { throw OrthancException(ErrorCode_NotImplemented); } } virtual void ExecuteFind(std::list<std::string>& identifiers, const Capabilities& capabilities, const FindRequest& request) ORTHANC_OVERRIDE { if (capabilities.HasFindSupport()) { // The integrated version of "ExecuteFind()" should have been called throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { Compatibility::GenericFind find(*this, *this); find.ExecuteFind(identifiers, capabilities, request); } } virtual void ExecuteExpand(FindResponse& response, const Capabilities& capabilities, const FindRequest& request, const std::string& identifier) ORTHANC_OVERRIDE { if (capabilities.HasFindSupport()) { // The integrated version of "ExecuteFind()" should have been called throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { Compatibility::GenericFind find(*this, *this); find.ExecuteExpand(response, capabilities, request, identifier); } } }; OrthancPluginDatabaseV4::OrthancPluginDatabaseV4(SharedLibrary& library, PluginsErrorDictionary& errorDictionary, const _OrthancPluginRegisterDatabaseBackendV4& database, const std::string& serverIdentifier) : library_(library), errorDictionary_(errorDictionary), definition_(database), serverIdentifier_(serverIdentifier), open_(false), databaseVersion_(0) { CLOG(INFO, PLUGINS) << "Identifier of this Orthanc server for the global properties " << "of the custom database: \"" << serverIdentifier << "\""; if (definition_.backend == NULL || definition_.operations == NULL || definition_.finalize == NULL) { throw OrthancException(ErrorCode_NullPointer); } } OrthancPluginDatabaseV4::~OrthancPluginDatabaseV4() { definition_.finalize(definition_.backend); } static void AddIdentifierTags(DatabasePluginMessages::Open::Request& request, ResourceType level) { const DicomTag* tags = NULL; size_t size; ServerToolbox::LoadIdentifiers(tags, size, level); if (tags == NULL || size == 0) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } for (size_t i = 0; i < size; i++) { DatabasePluginMessages::Open_Request_IdentifierTag* tag = request.add_identifier_tags(); tag->set_level(Convert(level)); tag->set_group(tags[i].GetGroup()); tag->set_element(tags[i].GetElement()); tag->set_name(FromDcmtkBridge::GetTagName(tags[i], "")); } } void OrthancPluginDatabaseV4::Open() { if (open_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } { DatabasePluginMessages::DatabaseRequest request; AddIdentifierTags(*request.mutable_open(), ResourceType_Patient); AddIdentifierTags(*request.mutable_open(), ResourceType_Study); AddIdentifierTags(*request.mutable_open(), ResourceType_Series); AddIdentifierTags(*request.mutable_open(), ResourceType_Instance); DatabasePluginMessages::DatabaseResponse response; ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_OPEN, request); } { DatabasePluginMessages::DatabaseRequest request; DatabasePluginMessages::DatabaseResponse response; ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_GET_SYSTEM_INFORMATION, request); const ::Orthanc::DatabasePluginMessages::GetSystemInformation_Response& systemInfo = response.get_system_information(); databaseVersion_ = systemInfo.database_version(); dbCapabilities_.SetFlushToDisk(systemInfo.supports_flush_to_disk()); dbCapabilities_.SetRevisionsSupport(systemInfo.supports_revisions()); dbCapabilities_.SetLabelsSupport(systemInfo.supports_labels()); dbCapabilities_.SetAtomicIncrementGlobalProperty(systemInfo.supports_increment_global_property()); dbCapabilities_.SetHasUpdateAndGetStatistics(systemInfo.has_update_and_get_statistics()); dbCapabilities_.SetMeasureLatency(systemInfo.has_measure_latency()); dbCapabilities_.SetHasExtendedChanges(systemInfo.has_extended_changes()); dbCapabilities_.SetHasFindSupport(systemInfo.supports_find()); dbCapabilities_.SetHasDbHousekeeping(systemInfo.has_db_housekeeping()); } open_ = true; } void OrthancPluginDatabaseV4::Close() { if (!open_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { DatabasePluginMessages::DatabaseRequest request; DatabasePluginMessages::DatabaseResponse response; ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_CLOSE, request); } } void OrthancPluginDatabaseV4::FlushToDisk() { if (!open_ || !GetDatabaseCapabilities().HasFlushToDisk()) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { DatabasePluginMessages::DatabaseRequest request; DatabasePluginMessages::DatabaseResponse response; ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_FLUSH_TO_DISK, request); } } IDatabaseWrapper::ITransaction* OrthancPluginDatabaseV4::StartTransaction(TransactionType type, IDatabaseListener& listener) { if (!open_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return new Transaction(*this, listener, type); } } unsigned int OrthancPluginDatabaseV4::GetDatabaseVersion() { if (!open_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return databaseVersion_; } } void OrthancPluginDatabaseV4::Upgrade(unsigned int targetVersion, IStorageArea& storageArea) { if (!open_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { VoidDatabaseListener listener; Transaction transaction(*this, listener, TransactionType_ReadWrite); try { DatabasePluginMessages::DatabaseRequest request; request.mutable_upgrade()->set_target_version(targetVersion); request.mutable_upgrade()->set_storage_area(reinterpret_cast<intptr_t>(&storageArea)); request.mutable_upgrade()->set_transaction(reinterpret_cast<intptr_t>(transaction.GetTransactionObject())); DatabasePluginMessages::DatabaseResponse response; ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_UPGRADE, request); transaction.Commit(0); } catch (OrthancException& e) { transaction.Rollback(); throw; } } } uint64_t OrthancPluginDatabaseV4::MeasureLatency() { if (!open_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { DatabasePluginMessages::DatabaseRequest request; DatabasePluginMessages::DatabaseResponse response; ExecuteDatabase(response, *this, DatabasePluginMessages::OPERATION_MEASURE_LATENCY, request); return response.measure_latency().latency_us(); } } const IDatabaseWrapper::Capabilities OrthancPluginDatabaseV4::GetDatabaseCapabilities() const { if (!open_) { throw OrthancException(ErrorCode_BadSequenceOfCalls); } else { return dbCapabilities_; } } bool OrthancPluginDatabaseV4::HasIntegratedFind() const { return dbCapabilities_.HasFindSupport(); } }