Orthanc/OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.cpp
2025-06-23 19:07:37 +05:30

232 lines
7.1 KiB
C++

/**
* 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-2025 Orthanc Team SRL, Belgium
* Copyright (C) 2021-2025 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 "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)
{
}
virtual unsigned int GetSubOperationCount() const ORTHANC_OVERRIDE
{
return 1;
}
virtual Status DoNext() ORTHANC_OVERRIDE
{
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 j = 0; j < response.size(); j++)
{
if (response[j].type() != Json::stringValue)
{
throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
}
else
{
publicIds.insert(response[j].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);
}