Mercurial > hg > orthanc
changeset 5904:bed6a8ba5431 get-scu
refactoring Move and Get jobs
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Thu, 05 Dec 2024 12:02:33 +0100 |
parents | 63ea301075ef |
children | dad78aa9141e |
files | OrthancServer/CMakeLists.txt OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp OrthancServer/Sources/ServerJobs/DicomGetScuJob.h OrthancServer/Sources/ServerJobs/DicomMoveScuJob.cpp OrthancServer/Sources/ServerJobs/DicomMoveScuJob.h OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.h |
diffstat | 8 files changed, 394 insertions(+), 532 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancServer/CMakeLists.txt Thu Dec 05 09:30:05 2024 +0100 +++ b/OrthancServer/CMakeLists.txt Thu Dec 05 12:02:33 2024 +0100 @@ -130,6 +130,7 @@ ${CMAKE_SOURCE_DIR}/Sources/ServerJobs/ArchiveJob.cpp ${CMAKE_SOURCE_DIR}/Sources/ServerJobs/CleaningInstancesJob.cpp ${CMAKE_SOURCE_DIR}/Sources/ServerJobs/DicomModalityStoreJob.cpp + ${CMAKE_SOURCE_DIR}/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp ${CMAKE_SOURCE_DIR}/Sources/ServerJobs/DicomGetScuJob.cpp ${CMAKE_SOURCE_DIR}/Sources/ServerJobs/DicomMoveScuJob.cpp ${CMAKE_SOURCE_DIR}/Sources/ServerJobs/LuaJobManager.cpp
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp Thu Dec 05 09:30:05 2024 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp Thu Dec 05 12:02:33 2024 +0100 @@ -1014,91 +1014,62 @@ retrieveMethod = context.GetDefaultDicomRetrieveMethod(); } + std::unique_ptr<DicomRetrieveScuBaseJob> job; + + switch (retrieveMethod) { case RetrieveMethod_Move: { - std::unique_ptr<DicomMoveScuJob> job(new DicomMoveScuJob(context)); - job->SetQueryFormat(OrthancRestApi::GetDicomFormat(body, DicomToJsonFormat_Short)); - - job->SetTargetAet(targetAet); - job->SetLocalAet(query.GetHandler().GetLocalAet()); - job->SetRemoteModality(query.GetHandler().GetRemoteModality()); - - // TODO: refactor in a base class for DicomGetScuJob and DicomMoveScuJob - if (timeout >= 0) - { - // New in Orthanc 1.7.0 - job->SetTimeout(static_cast<uint32_t>(timeout)); - } - else if (query.GetHandler().HasTimeout()) - { - // New in Orthanc 1.9.1 - job->SetTimeout(query.GetHandler().GetTimeout()); - } + job.reset(new DicomMoveScuJob(context)); + (dynamic_cast<DicomMoveScuJob*>(job.get()))->SetTargetAet(targetAet); LOG(WARNING) << "Driving C-Move SCU on remote modality " << query.GetHandler().GetRemoteModality().GetApplicationEntityTitle() << " to target modality " << targetAet; - - // TODO: refactor in a base class for DicomGetScuJob and DicomMoveScuJob - if (allAnswers) - { - for (size_t i = 0; i < query.GetHandler().GetAnswersCount(); i++) - { - job->AddFindAnswer(query.GetHandler(), i); - } - } - else - { - job->AddFindAnswer(query.GetHandler(), index); - } - - OrthancRestApi::GetApi(call).SubmitCommandsJob - (call, job.release(), true /* synchronous by default */, body); - }; break; case RetrieveMethod_Get: { - std::unique_ptr<DicomGetScuJob> job(new DicomGetScuJob(context)); - job->SetQueryFormat(OrthancRestApi::GetDicomFormat(body, DicomToJsonFormat_Short)); - job->SetRemoteModality(query.GetHandler().GetRemoteModality()); - - // TODO: refactor in a base class for DicomGetScuJob and DicomMoveScuJob - if (timeout >= 0) - { - // New in Orthanc 1.7.0 - job->SetTimeout(static_cast<uint32_t>(timeout)); - } - else if (query.GetHandler().HasTimeout()) - { - // New in Orthanc 1.9.1 - job->SetTimeout(query.GetHandler().GetTimeout()); - } + job.reset(new DicomGetScuJob(context)); LOG(WARNING) << "Driving C-Get SCU on remote modality " << query.GetHandler().GetRemoteModality().GetApplicationEntityTitle(); - - // TODO: refactor in a base class for DicomGetScuJob and DicomMoveScuJob - if (allAnswers) - { - for (size_t i = 0; i < query.GetHandler().GetAnswersCount(); i++) - { - job->AddFindAnswer(query.GetHandler(), i); - } - } - else - { - job->AddFindAnswer(query.GetHandler(), index); - } - - OrthancRestApi::GetApi(call).SubmitCommandsJob - (call, job.release(), true /* synchronous by default */, body); - }; break; default: throw OrthancException(ErrorCode_NotImplemented); } + + job->SetQueryFormat(OrthancRestApi::GetDicomFormat(body, DicomToJsonFormat_Short)); + + job->SetLocalAet(query.GetHandler().GetLocalAet()); + job->SetRemoteModality(query.GetHandler().GetRemoteModality()); + + if (timeout >= 0) + { + // New in Orthanc 1.7.0 + job->SetTimeout(static_cast<uint32_t>(timeout)); + } + else if (query.GetHandler().HasTimeout()) + { + // New in Orthanc 1.9.1 + job->SetTimeout(query.GetHandler().GetTimeout()); + } + + if (allAnswers) + { + for (size_t i = 0; i < query.GetHandler().GetAnswersCount(); i++) + { + job->AddFindAnswer(query.GetHandler(), i); + } + } + else + { + job->AddFindAnswer(query.GetHandler(), index); + } + + OrthancRestApi::GetApi(call).SubmitCommandsJob + (call, job.release(), true /* synchronous by default */, body); + }
--- a/OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp Thu Dec 05 09:30:05 2024 +0100 +++ b/OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp Thu Dec 05 12:02:33 2024 +0100 @@ -37,52 +37,6 @@ namespace Orthanc { - class DicomGetScuJob::Command : public SetOfCommandsJob::ICommand - { - private: - DicomGetScuJob& that_; - std::unique_ptr<DicomMap> findAnswer_; - - public: - Command(DicomGetScuJob& that, - const DicomMap& findAnswer) : - that_(that), - findAnswer_(findAnswer.Clone()) - { - } - - virtual bool Execute(const std::string& jobId) ORTHANC_OVERRIDE - { - that_.Retrieve(*findAnswer_); - return true; - } - - virtual void Serialize(Json::Value& target) const ORTHANC_OVERRIDE - { - findAnswer_->Serialize(target); - } - }; - - - class DicomGetScuJob::Unserializer : - public SetOfCommandsJob::ICommandUnserializer - { - private: - DicomGetScuJob& that_; - - public: - explicit Unserializer(DicomGetScuJob& that) : - that_(that) - { - } - - virtual ICommand* Unserialize(const Json::Value& source) const ORTHANC_OVERRIDE - { - DicomMap findAnswer; - findAnswer.Unserialize(source); - return new Command(that_, findAnswer); - } - }; static uint16_t InstanceReceivedHandler(void* callbackContext, @@ -149,32 +103,6 @@ connection_->Get(findAnswer, InstanceReceivedHandler, &context_); } - // this method is used to implement the retrieve part of a Q&R - // it keeps only the main dicom tags from the C-Find answer - void DicomGetScuJob::AddFindAnswer(const DicomMap& answer) - { - DicomMap item; - item.CopyTagIfExists(answer, DICOM_TAG_QUERY_RETRIEVE_LEVEL); - item.CopyTagIfExists(answer, DICOM_TAG_PATIENT_ID); - item.CopyTagIfExists(answer, DICOM_TAG_STUDY_INSTANCE_UID); - item.CopyTagIfExists(answer, DICOM_TAG_SERIES_INSTANCE_UID); - item.CopyTagIfExists(answer, DICOM_TAG_SOP_INSTANCE_UID); - item.CopyTagIfExists(answer, DICOM_TAG_ACCESSION_NUMBER); - - query_.Add(item); - query_.GetAnswer(query_.GetSize() - 1).Remove(DICOM_TAG_SPECIFIC_CHARACTER_SET); // Remove the "SpecificCharacterSet" (0008,0005) tag that is automatically added if creating a ParsedDicomFile object from a DicomMap - - AddCommand(new Command(*this, answer)); - } - - void DicomGetScuJob::AddFindAnswer(QueryRetrieveHandler& query, - size_t i) - { - DicomMap answer; - query.GetAnswer(answer, i); - AddFindAnswer(answer); - } - void DicomGetScuJob::AddResourceToRetrieve(ResourceType level, const std::string& dicomId) { // TODO-GET: when retrieving a single series, one must provide the StudyInstanceUID too @@ -210,124 +138,4 @@ AddCommand(new Command(*this, item)); } - void DicomGetScuJob::SetLocalAet(const std::string& aet) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - parameters_.SetLocalApplicationEntityTitle(aet); - } - } - - - void DicomGetScuJob::SetRemoteModality(const RemoteModalityParameters& remote) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - parameters_.SetRemoteModality(remote); - } - } - - - void DicomGetScuJob::SetTimeout(uint32_t seconds) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - parameters_.SetTimeout(seconds); - } - } - - - void DicomGetScuJob::Stop(JobStopReason reason) - { - connection_.reset(); - } - - - void DicomGetScuJob::SetQueryFormat(DicomToJsonFormat format) // TODO-GET: is this usefull ? - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - queryFormat_ = format; - } - } - - - void DicomGetScuJob::GetPublicContent(Json::Value& value) - { - SetOfCommandsJob::GetPublicContent(value); - - value[LOCAL_AET] = parameters_.GetLocalApplicationEntityTitle(); - value["RemoteAet"] = parameters_.GetRemoteModality().GetApplicationEntityTitle(); - - value[QUERY] = Json::objectValue; - // query_.ToJson(value[QUERY], queryFormat_); - } - - - DicomGetScuJob::DicomGetScuJob(ServerContext& context, - const Json::Value& serialized) : - SetOfCommandsJob(new Unserializer(*this), serialized), - context_(context), - parameters_(DicomAssociationParameters::UnserializeJob(serialized)), - // targetAet_(SerializationToolbox::ReadString(serialized, TARGET_AET)), - query_(true), - queryFormat_(DicomToJsonFormat_Short) - { - if (serialized.isMember(QUERY)) - { - const Json::Value& query = serialized[QUERY]; - if (query.type() == Json::arrayValue) - { - for (Json::Value::ArrayIndex i = 0; i < query.size(); i++) - { - DicomMap item; - FromDcmtkBridge::FromJson(item, query[i]); - // AddToQuery(query_, item); - } - } - } - - if (serialized.isMember(QUERY_FORMAT)) - { - queryFormat_ = StringToDicomToJsonFormat(SerializationToolbox::ReadString(serialized, QUERY_FORMAT)); - } - } - - - bool DicomGetScuJob::Serialize(Json::Value& target) - { - if (!SetOfCommandsJob::Serialize(target)) - { - return false; - } - else - { - parameters_.SerializeJob(target); - // target[TARGET_AET] = targetAet_; - - // "Short" is for compatibility with Orthanc <= 1.9.4 - target[QUERY] = Json::objectValue; - // query_.ToJson(target[QUERY], DicomToJsonFormat_Short); - - target[QUERY_FORMAT] = EnumerationToString(queryFormat_); - - return true; - } - } }
--- a/OrthancServer/Sources/ServerJobs/DicomGetScuJob.h Thu Dec 05 09:30:05 2024 +0100 +++ b/OrthancServer/Sources/ServerJobs/DicomGetScuJob.h Thu Dec 05 12:02:33 2024 +0100 @@ -25,76 +25,35 @@ #include "../../../OrthancFramework/Sources/Compatibility.h" #include "../../../OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.h" -#include "../../../OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h" - +#include "DicomRetrieveScuBaseJob.h" #include "../QueryRetrieveHandler.h" namespace Orthanc { class ServerContext; - class DicomGetScuJob : public SetOfCommandsJob + class DicomGetScuJob : public DicomRetrieveScuBaseJob { private: - class Command; - class Unserializer; - ServerContext& context_; - DicomAssociationParameters parameters_; - DicomFindAnswers query_; - DicomToJsonFormat queryFormat_; // New in 1.9.5 - - std::unique_ptr<DicomControlUserConnection> connection_; - - void Retrieve(const DicomMap& findAnswer); + virtual void Retrieve(const DicomMap& findAnswer) ORTHANC_OVERRIDE; public: explicit DicomGetScuJob(ServerContext& context) : - context_(context), - query_(false /* this is not for worklists */), - queryFormat_(DicomToJsonFormat_Short) + DicomRetrieveScuBaseJob(context) { } DicomGetScuJob(ServerContext& context, - const Json::Value& serialized); + const Json::Value& serialized); - void AddFindAnswer(const DicomMap& answer); - - // void AddQuery(const DicomMap& query); - - void AddFindAnswer(QueryRetrieveHandler& query, - size_t i); void AddResourceToRetrieve(ResourceType level, const std::string& dicomId); - const DicomAssociationParameters& GetParameters() const - { - return parameters_; - } - - void SetLocalAet(const std::string& aet); - - void SetRemoteModality(const RemoteModalityParameters& remote); - - void SetTimeout(uint32_t timeout); - - void SetQueryFormat(DicomToJsonFormat format); - - DicomToJsonFormat GetQueryFormat() const - { - return queryFormat_; - } - - virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE; virtual void GetJobType(std::string& target) ORTHANC_OVERRIDE { target = "DicomGetScu"; } - - virtual void GetPublicContent(Json::Value& value) ORTHANC_OVERRIDE; - - virtual bool Serialize(Json::Value& target) ORTHANC_OVERRIDE; }; }
--- a/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.cpp Thu Dec 05 09:30:05 2024 +0100 +++ b/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.cpp Thu Dec 05 12:02:33 2024 +0100 @@ -36,52 +36,7 @@ namespace Orthanc { - class DicomMoveScuJob::Command : public SetOfCommandsJob::ICommand - { - private: - DicomMoveScuJob& that_; - std::unique_ptr<DicomMap> findAnswer_; - public: - Command(DicomMoveScuJob& that, - const DicomMap& findAnswer) : - that_(that), - findAnswer_(findAnswer.Clone()) - { - } - - virtual bool Execute(const std::string& jobId) ORTHANC_OVERRIDE - { - that_.Retrieve(*findAnswer_); - return true; - } - - virtual void Serialize(Json::Value& target) const ORTHANC_OVERRIDE - { - findAnswer_->Serialize(target); - } - }; - - - class DicomMoveScuJob::Unserializer : - public SetOfCommandsJob::ICommandUnserializer - { - private: - DicomMoveScuJob& that_; - - public: - explicit Unserializer(DicomMoveScuJob& that) : - that_(that) - { - } - - virtual ICommand* Unserialize(const Json::Value& source) const ORTHANC_OVERRIDE - { - DicomMap findAnswer; - findAnswer.Unserialize(source); - return new Command(that_, findAnswer); - } - }; void DicomMoveScuJob::Retrieve(const DicomMap& findAnswer) @@ -95,64 +50,6 @@ } - static void AddToQuery(DicomFindAnswers& query, - const DicomMap& item) - { - query.Add(item); - - /** - * Compatibility with Orthanc <= 1.9.4: Remove the - * "SpecificCharacterSet" (0008,0005) tag that is automatically - * added if creating a ParsedDicomFile object from a DicomMap. - **/ - query.GetAnswer(query.GetSize() - 1).Remove(DICOM_TAG_SPECIFIC_CHARACTER_SET); - } - - // this method is used to implement the retrieve part of a Q&R - // it keeps only the main dicom tags from the C-Find answer - void DicomMoveScuJob::AddFindAnswer(const DicomMap& answer) - { - DicomMap item; - item.CopyTagIfExists(answer, DICOM_TAG_QUERY_RETRIEVE_LEVEL); - item.CopyTagIfExists(answer, DICOM_TAG_PATIENT_ID); - item.CopyTagIfExists(answer, DICOM_TAG_STUDY_INSTANCE_UID); - item.CopyTagIfExists(answer, DICOM_TAG_SERIES_INSTANCE_UID); - item.CopyTagIfExists(answer, DICOM_TAG_SOP_INSTANCE_UID); - item.CopyTagIfExists(answer, DICOM_TAG_ACCESSION_NUMBER); - AddToQuery(query_, item); - - AddCommand(new Command(*this, answer)); - } - - // this method is used to implement a C-Move - // it keeps all tags from the C-Move query - void DicomMoveScuJob::AddQuery(const DicomMap& query) - { - AddToQuery(query_, query); - AddCommand(new Command(*this, query)); - } - - void DicomMoveScuJob::AddFindAnswer(QueryRetrieveHandler& query, - size_t i) - { - DicomMap answer; - query.GetAnswer(answer, i); - AddFindAnswer(answer); - } - - - void DicomMoveScuJob::SetLocalAet(const std::string& aet) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - parameters_.SetLocalApplicationEntityTitle(aet); - } - } - void DicomMoveScuJob::SetTargetAet(const std::string& aet) { @@ -167,109 +64,33 @@ } - void DicomMoveScuJob::SetRemoteModality(const RemoteModalityParameters& remote) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - parameters_.SetRemoteModality(remote); - } - } - - - void DicomMoveScuJob::SetTimeout(uint32_t seconds) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - parameters_.SetTimeout(seconds); - } - } - - - void DicomMoveScuJob::Stop(JobStopReason reason) - { - connection_.reset(); - } - - - void DicomMoveScuJob::SetQueryFormat(DicomToJsonFormat format) - { - if (IsStarted()) - { - throw OrthancException(ErrorCode_BadSequenceOfCalls); - } - else - { - queryFormat_ = format; - } - } void DicomMoveScuJob::GetPublicContent(Json::Value& value) { - SetOfCommandsJob::GetPublicContent(value); + DicomRetrieveScuBaseJob::GetPublicContent(value); - value[LOCAL_AET] = parameters_.GetLocalApplicationEntityTitle(); - value["RemoteAet"] = parameters_.GetRemoteModality().GetApplicationEntityTitle(); - - value[QUERY] = Json::objectValue; - query_.ToJson(value[QUERY], queryFormat_); + value[TARGET_AET] = targetAet_; } DicomMoveScuJob::DicomMoveScuJob(ServerContext& context, const Json::Value& serialized) : - SetOfCommandsJob(new Unserializer(*this), serialized), - context_(context), - parameters_(DicomAssociationParameters::UnserializeJob(serialized)), - targetAet_(SerializationToolbox::ReadString(serialized, TARGET_AET)), - query_(true), - queryFormat_(DicomToJsonFormat_Short) + DicomRetrieveScuBaseJob(context, serialized), + targetAet_(SerializationToolbox::ReadString(serialized, TARGET_AET)) { - if (serialized.isMember(QUERY)) - { - const Json::Value& query = serialized[QUERY]; - if (query.type() == Json::arrayValue) - { - for (Json::Value::ArrayIndex i = 0; i < query.size(); i++) - { - DicomMap item; - FromDcmtkBridge::FromJson(item, query[i]); - AddToQuery(query_, item); - } - } - } - - if (serialized.isMember(QUERY_FORMAT)) - { - queryFormat_ = StringToDicomToJsonFormat(SerializationToolbox::ReadString(serialized, QUERY_FORMAT)); - } } bool DicomMoveScuJob::Serialize(Json::Value& target) { - if (!SetOfCommandsJob::Serialize(target)) + if (!DicomRetrieveScuBaseJob::Serialize(target)) { return false; } else { - parameters_.SerializeJob(target); target[TARGET_AET] = targetAet_; - - // "Short" is for compatibility with Orthanc <= 1.9.4 - target[QUERY] = Json::objectValue; - query_.ToJson(target[QUERY], DicomToJsonFormat_Short); - - target[QUERY_FORMAT] = EnumerationToString(queryFormat_); return true; }
--- a/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.h Thu Dec 05 09:30:05 2024 +0100 +++ b/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.h Thu Dec 05 12:02:33 2024 +0100 @@ -25,7 +25,8 @@ #include "../../../OrthancFramework/Sources/Compatibility.h" #include "../../../OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.h" -#include "../../../OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h" +// #include "../../../OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h" +#include "DicomRetrieveScuBaseJob.h" #include "../QueryRetrieveHandler.h" @@ -33,51 +34,22 @@ { class ServerContext; - class DicomMoveScuJob : public SetOfCommandsJob + class DicomMoveScuJob : public DicomRetrieveScuBaseJob { private: - class Command; - class Unserializer; + std::string targetAet_; - ServerContext& context_; - DicomAssociationParameters parameters_; - std::string targetAet_; - DicomFindAnswers query_; - DicomToJsonFormat queryFormat_; // New in 1.9.5 - - std::unique_ptr<DicomControlUserConnection> connection_; - - void Retrieve(const DicomMap& findAnswer); + virtual void Retrieve(const DicomMap& findAnswer) ORTHANC_OVERRIDE; public: explicit DicomMoveScuJob(ServerContext& context) : - context_(context), - query_(false /* this is not for worklists */), - queryFormat_(DicomToJsonFormat_Short) + DicomRetrieveScuBaseJob(context) { } DicomMoveScuJob(ServerContext& context, const Json::Value& serialized); - void AddFindAnswer(const DicomMap& answer); - - void AddQuery(const DicomMap& query); - - void AddFindAnswer(QueryRetrieveHandler& query, - size_t i); - - const DicomAssociationParameters& GetParameters() const - { - return parameters_; - } - - void SetLocalAet(const std::string& aet); - - void SetRemoteModality(const RemoteModalityParameters& remote); - - void SetTimeout(uint32_t timeout); - const std::string& GetTargetAet() const { return targetAet_; @@ -85,15 +57,6 @@ void SetTargetAet(const std::string& aet); - void SetQueryFormat(DicomToJsonFormat format); - - DicomToJsonFormat GetQueryFormat() const - { - return queryFormat_; - } - - virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE; - virtual void GetJobType(std::string& target) ORTHANC_OVERRIDE { target = "DicomMoveScu";
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.cpp Thu Dec 05 12:02:33 2024 +0100 @@ -0,0 +1,205 @@ +/** + * 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 "DicomGetScuJob.h" + +#include "../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h" +#include "../../../OrthancFramework/Sources/SerializationToolbox.h" +#include "../ServerContext.h" +#include <dcmtk/dcmnet/dimse.h> +#include <algorithm> + +static const char* const LOCAL_AET = "LocalAet"; +static const char* const QUERY = "Query"; +static const char* const QUERY_FORMAT = "QueryFormat"; // New in 1.9.5 +static const char* const REMOTE = "Remote"; +static const char* const TIMEOUT = "Timeout"; + +namespace Orthanc +{ + + static void AddToQuery(DicomFindAnswers& query, + const DicomMap& item) + { + query.Add(item); + + /** + * Compatibility with Orthanc <= 1.9.4: Remove the + * "SpecificCharacterSet" (0008,0005) tag that is automatically + * added if creating a ParsedDicomFile object from a DicomMap. + **/ + query.GetAnswer(query.GetSize() - 1).Remove(DICOM_TAG_SPECIFIC_CHARACTER_SET); + } + + // this method is used to implement the retrieve part of a Q&R + // it keeps only the main dicom tags from the C-Find answer + void DicomRetrieveScuBaseJob::AddFindAnswer(const DicomMap& answer) + { + DicomMap item; + item.CopyTagIfExists(answer, DICOM_TAG_QUERY_RETRIEVE_LEVEL); + item.CopyTagIfExists(answer, DICOM_TAG_PATIENT_ID); + item.CopyTagIfExists(answer, DICOM_TAG_STUDY_INSTANCE_UID); + item.CopyTagIfExists(answer, DICOM_TAG_SERIES_INSTANCE_UID); + item.CopyTagIfExists(answer, DICOM_TAG_SOP_INSTANCE_UID); + item.CopyTagIfExists(answer, DICOM_TAG_ACCESSION_NUMBER); + AddToQuery(query_, item); + + AddCommand(new Command(*this, answer)); + } + + void DicomRetrieveScuBaseJob::AddFindAnswer(QueryRetrieveHandler& query, + size_t i) + { + DicomMap answer; + query.GetAnswer(answer, i); + AddFindAnswer(answer); + } + + // this method is used to implement a C-Move + // it keeps all tags from the C-Move query + void DicomRetrieveScuBaseJob::AddQuery(const DicomMap& query) + { + AddToQuery(query_, query); + AddCommand(new Command(*this, query)); + } + + + void DicomRetrieveScuBaseJob::SetLocalAet(const std::string& aet) + { + if (IsStarted()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + parameters_.SetLocalApplicationEntityTitle(aet); + } + } + + + void DicomRetrieveScuBaseJob::SetRemoteModality(const RemoteModalityParameters& remote) + { + if (IsStarted()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + parameters_.SetRemoteModality(remote); + } + } + + + void DicomRetrieveScuBaseJob::SetTimeout(uint32_t seconds) + { + if (IsStarted()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + parameters_.SetTimeout(seconds); + } + } + + + void DicomRetrieveScuBaseJob::Stop(JobStopReason reason) + { + connection_.reset(); + } + + + void DicomRetrieveScuBaseJob::SetQueryFormat(DicomToJsonFormat format) + { + if (IsStarted()) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else + { + queryFormat_ = format; + } + } + + + void DicomRetrieveScuBaseJob::GetPublicContent(Json::Value& value) + { + SetOfCommandsJob::GetPublicContent(value); + + value[LOCAL_AET] = parameters_.GetLocalApplicationEntityTitle(); + value["RemoteAet"] = parameters_.GetRemoteModality().GetApplicationEntityTitle(); + + value[QUERY] = Json::objectValue; + query_.ToJson(value[QUERY], queryFormat_); + } + + + DicomRetrieveScuBaseJob::DicomRetrieveScuBaseJob(ServerContext& context, + const Json::Value& serialized) : + SetOfCommandsJob(new Unserializer(*this), serialized), + context_(context), + parameters_(DicomAssociationParameters::UnserializeJob(serialized)), + query_(true), + queryFormat_(DicomToJsonFormat_Short) + { + if (serialized.isMember(QUERY)) + { + const Json::Value& query = serialized[QUERY]; + if (query.type() == Json::arrayValue) + { + for (Json::Value::ArrayIndex i = 0; i < query.size(); i++) + { + DicomMap item; + FromDcmtkBridge::FromJson(item, query[i]); + AddToQuery(query_, item); + } + } + } + + if (serialized.isMember(QUERY_FORMAT)) + { + queryFormat_ = StringToDicomToJsonFormat(SerializationToolbox::ReadString(serialized, QUERY_FORMAT)); + } + } + + + bool DicomRetrieveScuBaseJob::Serialize(Json::Value& target) + { + if (!SetOfCommandsJob::Serialize(target)) + { + return false; + } + else + { + parameters_.SerializeJob(target); + + // "Short" is for compatibility with Orthanc <= 1.9.4 + target[QUERY] = Json::objectValue; + query_.ToJson(target[QUERY], DicomToJsonFormat_Short); + + target[QUERY_FORMAT] = EnumerationToString(queryFormat_); + + return true; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancServer/Sources/ServerJobs/DicomRetrieveScuBaseJob.h Thu Dec 05 12:02:33 2024 +0100 @@ -0,0 +1,134 @@ +/** + * 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/>. + **/ + +#pragma once + +#include "../../../OrthancFramework/Sources/Compatibility.h" +#include "../../../OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.h" +#include "../../../OrthancFramework/Sources/JobsEngine/SetOfCommandsJob.h" + +#include "../QueryRetrieveHandler.h" + +namespace Orthanc +{ + class ServerContext; + + class DicomRetrieveScuBaseJob : public SetOfCommandsJob + { + protected: + class Command : public SetOfCommandsJob::ICommand + { + private: + DicomRetrieveScuBaseJob &that_; + std::unique_ptr<DicomMap> findAnswer_; + + public: + Command(DicomRetrieveScuBaseJob &that, + const DicomMap &findAnswer) : + that_(that), + findAnswer_(findAnswer.Clone()) + { + } + + virtual bool Execute(const std::string &jobId) ORTHANC_OVERRIDE + { + that_.Retrieve(*findAnswer_); + return true; + } + + virtual void Serialize(Json::Value &target) const ORTHANC_OVERRIDE + { + findAnswer_->Serialize(target); + } + }; + + class Unserializer : public SetOfCommandsJob::ICommandUnserializer + { + protected: + DicomRetrieveScuBaseJob &that_; + + public: + explicit Unserializer(DicomRetrieveScuBaseJob &that) : + that_(that) + { + } + + virtual ICommand *Unserialize(const Json::Value &source) const ORTHANC_OVERRIDE + { + DicomMap findAnswer; + findAnswer.Unserialize(source); + return new Command(that_, findAnswer); + } + }; + + ServerContext &context_; + DicomAssociationParameters parameters_; + DicomFindAnswers query_; + DicomToJsonFormat queryFormat_; // New in 1.9.5 + + std::unique_ptr<DicomControlUserConnection> connection_; + + virtual void Retrieve(const DicomMap &findAnswer) = 0; + + explicit DicomRetrieveScuBaseJob(ServerContext &context) : + context_(context), + query_(false /* this is not for worklists */), + queryFormat_(DicomToJsonFormat_Short) + { + } + + DicomRetrieveScuBaseJob(ServerContext &context, + const Json::Value &serialized); + + public: + void AddFindAnswer(const DicomMap &answer); + + void AddQuery(const DicomMap& query); + + void AddFindAnswer(QueryRetrieveHandler &query, + size_t i); + + const DicomAssociationParameters &GetParameters() const + { + return parameters_; + } + + void SetLocalAet(const std::string &aet); + + void SetRemoteModality(const RemoteModalityParameters &remote); + + void SetTimeout(uint32_t timeout); + + void SetQueryFormat(DicomToJsonFormat format); + + DicomToJsonFormat GetQueryFormat() const + { + return queryFormat_; + } + + virtual void Stop(JobStopReason reason) ORTHANC_OVERRIDE; + + virtual void GetPublicContent(Json::Value &value) ORTHANC_OVERRIDE; + + virtual bool Serialize(Json::Value &target) ORTHANC_OVERRIDE; + }; +}