Mercurial > hg > orthanc
diff OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.cpp @ 5273:7cb1b851f5c8
Added a sample plugin bringing multitenant DICOM support through labels
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 14 Apr 2023 11:49:24 +0200 |
parents | |
children | 49477780e25a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.cpp Fri Apr 14 11:49:24 2023 +0200 @@ -0,0 +1,231 @@ +/** + * 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) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + **/ + + +#include "MoveRequestHandler.h" + +#include "PluginToolbox.h" + +#include "../../../../OrthancFramework/Sources/OrthancException.h" +#include "../../../../OrthancFramework/Sources/Toolbox.h" + +#include "../Common/OrthancPluginCppWrapper.h" + + +class MoveRequestHandler::Iterator : public Orthanc::IMoveRequestIterator +{ +private: + std::string targetModality_; + Json::Value body_; + bool done_; + +public: + Iterator(const std::string& targetModality, + const Json::Value& body) : + targetModality_(targetModality), + body_(body), + done_(false) + { + } + + unsigned int GetSubOperationCount() const + { + return 1; + } + + Status DoNext() + { + Json::Value answer; + + if (done_) + { + return Status_Failure; + } + else if (OrthancPlugins::RestApiPost(answer, "/modalities/" + targetModality_ + "/store", body_, false)) + { + done_ = true; + return Status_Success; + } + else + { + done_ = true; + return Status_Failure; + } + } +}; + + +void MoveRequestHandler::ExecuteLookup(std::set<std::string>& publicIds, + Orthanc::ResourceType level, + const Orthanc::DicomTag& tag, + const std::string& value) const +{ + std::vector<std::string> tokens; + Orthanc::Toolbox::TokenizeString(tokens, value, '\\'); + + for (size_t i = 0; i < tokens.size(); i++) + { + if (!tokens[i].empty()) + { + Json::Value request = Json::objectValue; + request["Level"] = Orthanc::EnumerationToString(level); + request["Query"][tag.Format()] = tokens[i]; + PluginToolbox::AddLabelsToFindRequest(request, labels_, constraint_); + + Json::Value response; + if (OrthancPlugins::RestApiPost(response, "/tools/find", request, false) && + response.type() == Json::arrayValue) + { + for (Json::Value::ArrayIndex i = 0; i < response.size(); i++) + { + if (response[i].type() != Json::stringValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + else + { + publicIds.insert(response[i].asString()); + } + } + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + } +} + + +void MoveRequestHandler::LookupIdentifiers(std::set<std::string>& publicIds, + Orthanc::ResourceType level, + const Orthanc::DicomMap& input) const +{ + std::string value; + + switch (level) + { + case Orthanc::ResourceType_Patient: + if (input.LookupStringValue(value, Orthanc::DICOM_TAG_PATIENT_ID, false) && + !value.empty()) + { + ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_PATIENT_ID, value); + } + break; + + case Orthanc::ResourceType_Study: + if (input.LookupStringValue(value, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) && + !value.empty()) + { + ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, value); + } + else if (input.LookupStringValue(value, Orthanc::DICOM_TAG_ACCESSION_NUMBER, false) && + !value.empty()) + { + ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_ACCESSION_NUMBER, value); + } + break; + + case Orthanc::ResourceType_Series: + if (input.LookupStringValue(value, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) && + !value.empty()) + { + ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, value); + } + break; + + case Orthanc::ResourceType_Instance: + if (input.LookupStringValue(value, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false) && + !value.empty()) + { + ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, value); + } + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } +} + + +Orthanc::IMoveRequestIterator* MoveRequestHandler::Handle(const std::string& targetAet, + const Orthanc::DicomMap& input, + const std::string& originatorIp, + const std::string& originatorAet, + const std::string& calledAet, + uint16_t originatorId) +{ + std::set<std::string> publicIds; + + std::string s; + if (input.LookupStringValue(s, Orthanc::DICOM_TAG_QUERY_RETRIEVE_LEVEL, false) && + !s.empty()) + { + LookupIdentifiers(publicIds, PluginToolbox::ParseQueryRetrieveLevel(s), input); + } + else + { + // The query level is not present in the C-Move request, which + // does not follow the DICOM standard. This is for instance the + // behavior of Tudor DICOM. Try and automatically deduce the + // query level: Start from the instance level, going up to the + // patient level until a valid DICOM identifier is found. + LookupIdentifiers(publicIds, Orthanc::ResourceType_Instance, input); + + if (publicIds.empty()) + { + LookupIdentifiers(publicIds, Orthanc::ResourceType_Series, input); + } + + if (publicIds.empty()) + { + LookupIdentifiers(publicIds, Orthanc::ResourceType_Study, input); + } + + if (publicIds.empty()) + { + LookupIdentifiers(publicIds, Orthanc::ResourceType_Patient, input); + } + } + + Json::Value resources = Json::arrayValue; + for (std::set<std::string>::const_iterator it = publicIds.begin(); it != publicIds.end(); ++it) + { + resources.append(*it); + } + + std::string targetName; + Orthanc::RemoteModalityParameters targetParameters; + if (!PluginToolbox::LookupAETitle(targetName, targetParameters, isStrictAet_, targetAet)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, "Unknown target AET: " + targetAet); + } + + Json::Value body; + body["CalledAet"] = calledAet; + body["MoveOriginatorAet"] = originatorAet; + body["MoveOriginatorID"] = originatorId; + body["Resources"] = resources; + body["Synchronous"] = isSynchronous_; + + return new Iterator(targetName, body); +}