# HG changeset patch # User Alain Mazy # Date 1655801745 -7200 # Node ID afa427f6544489020620451d74bef0bda9dc1a23 # Parent 559b35d18ef79e4b9af63ba5a0540761d39bc767 Added an Asynchronous mode to /modalities/../move diff -r 559b35d18ef7 -r afa427f65444 NEWS --- a/NEWS Wed Jun 15 15:40:07 2022 +0200 +++ b/NEWS Tue Jun 21 10:55:45 2022 +0200 @@ -17,6 +17,11 @@ DicomControlUserConnection::SetupPresentationContexts() * Improved HttpClient error logging (add method + url) + +* Added an Asynchronous mode to /modalities/../move. + + + Version 1.11.0 (2022-05-09) =========================== diff -r 559b35d18ef7 -r afa427f65444 OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp --- a/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp Wed Jun 15 15:40:07 2022 +0200 +++ b/OrthancFramework/Sources/DicomNetworking/DicomControlUserConnection.cpp Tue Jun 21 10:55:45 2022 +0200 @@ -362,27 +362,7 @@ DcmDataset* dataset = query->GetDcmtkObject().getDataset(); const char* sopClass = UID_MOVEStudyRootQueryRetrieveInformationModel; - switch (level) - { - case ResourceType_Patient: - DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, "PATIENT"); - break; - - case ResourceType_Study: - DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, "STUDY"); - break; - - case ResourceType_Series: - DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, "SERIES"); - break; - - case ResourceType_Instance: - DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, "IMAGE"); - break; - - default: - throw OrthancException(ErrorCode_ParameterOutOfRange); - } + DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, ResourceTypeToDicomQueryRetrieveLevel(level)); // Figure out which of the accepted presentation contexts should be used int presID = ASC_findAcceptedPresentationContextID(&association_->GetDcmtkAssociation(), sopClass); @@ -519,32 +499,26 @@ DcmDataset* dataset = query->GetDcmtkObject().getDataset(); assert(dataset != NULL); - const char* clevel = NULL; + const char* clevel = ResourceTypeToDicomQueryRetrieveLevel(level); const char* sopClass = NULL; + DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, clevel); + switch (level) { case ResourceType_Patient: - clevel = "PATIENT"; - DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, "PATIENT"); sopClass = UID_FINDPatientRootQueryRetrieveInformationModel; break; case ResourceType_Study: - clevel = "STUDY"; - DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, "STUDY"); sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; break; case ResourceType_Series: - clevel = "SERIES"; - DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, "SERIES"); sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; break; case ResourceType_Instance: - clevel = "IMAGE"; - DU_putStringDOElement(dataset, DCM_QueryRetrieveLevel, "IMAGE"); sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; break; diff -r 559b35d18ef7 -r afa427f65444 OrthancFramework/Sources/Enumerations.cpp --- a/OrthancFramework/Sources/Enumerations.cpp Wed Jun 15 15:40:07 2022 +0200 +++ b/OrthancFramework/Sources/Enumerations.cpp Tue Jun 21 10:55:45 2022 +0200 @@ -1314,6 +1314,27 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); } + const char* ResourceTypeToDicomQueryRetrieveLevel(ResourceType type) + { + if (type == ResourceType_Patient) + { + return "PATIENT"; + } + else if (type == ResourceType_Study) + { + return "STUDY"; + } + else if (type == ResourceType_Series) + { + return "SERIES"; + } + else if (type == ResourceType_Instance) + { + return "IMAGE"; + } + + throw OrthancException(ErrorCode_ParameterOutOfRange); + } ImageFormat StringToImageFormat(const char* format) { diff -r 559b35d18ef7 -r afa427f65444 OrthancFramework/Sources/Enumerations.h --- a/OrthancFramework/Sources/Enumerations.h Wed Jun 15 15:40:07 2022 +0200 +++ b/OrthancFramework/Sources/Enumerations.h Tue Jun 21 10:55:45 2022 +0200 @@ -862,6 +862,9 @@ bool IsResourceLevelAboveOrEqual(ResourceType level, ResourceType reference); +ORTHANC_PUBLIC + const char* ResourceTypeToDicomQueryRetrieveLevel(ResourceType type); + ORTHANC_PUBLIC DicomModule GetModule(ResourceType type); diff -r 559b35d18ef7 -r afa427f65444 OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp Wed Jun 15 15:40:07 2022 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestModalities.cpp Tue Jun 21 10:55:45 2022 +0200 @@ -1517,6 +1517,7 @@ { if (call.IsDocumentation()) { + OrthancRestApi::DocumentSubmitCommandsJob(call); call.GetDocumentation() .SetTag("Networking") .SetSummary("Trigger C-MOVE SCU") @@ -1534,12 +1535,12 @@ "Target AET that will be used by the remote DICOM modality as a target for its C-STORE SCU " "commands, defaults to `DicomAet` configuration option in order to do a simple query/retrieve", false) .SetRequestField(KEY_TIMEOUT, RestApiCallDocumentation::Type_Number, - "Timeout for the C-STORE command, in seconds", false) + "Timeout for the C-MOVE command, in seconds", false) .SetUriArgument("id", "Identifier of the modality of interest"); return; } - const ServerContext& context = OrthancRestApi::GetContext(call); + ServerContext& context = OrthancRestApi::GetContext(call); Json::Value request; @@ -1564,21 +1565,33 @@ const RemoteModalityParameters source = MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - DicomAssociationParameters params(localAet, source); - InjectAssociationTimeout(params, request); // Handles KEY_TIMEOUT + std::unique_ptr job(new DicomMoveScuJob(context)); - DicomControlUserConnection connection(params); + job->SetQueryFormat(DicomToJsonFormat_Short); + + // QueryAccessor query(call); + job->SetTargetAet(targetAet); + job->SetLocalAet(localAet); + job->SetRemoteModality(source); + + if (request.isMember(KEY_TIMEOUT)) + { + job->SetTimeout(SerializationToolbox::ReadUnsignedInteger(request, KEY_TIMEOUT)); + } for (Json::Value::ArrayIndex i = 0; i < request[KEY_RESOURCES].size(); i++) { DicomMap resource; FromDcmtkBridge::FromJson(resource, request[KEY_RESOURCES][i], "Resources elements"); - - connection.Move(targetAet, level, resource); + + resource.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, std::string(ResourceTypeToDicomQueryRetrieveLevel(level)), false); + + job->AddQuery(resource); } - // Move has succeeded - call.GetOutput().AnswerBuffer("{}", MimeType_Json); + OrthancRestApi::GetApi(call).SubmitCommandsJob + (call, job.release(), true /* synchronous by default */, request); + return; } diff -r 559b35d18ef7 -r afa427f65444 OrthancServer/Sources/ServerJobs/DicomMoveScuJob.cpp --- a/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.cpp Wed Jun 15 15:40:07 2022 +0200 +++ b/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.cpp Tue Jun 21 10:55:45 2022 +0200 @@ -107,7 +107,8 @@ 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; @@ -122,6 +123,13 @@ 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) diff -r 559b35d18ef7 -r afa427f65444 OrthancServer/Sources/ServerJobs/DicomMoveScuJob.h --- a/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.h Wed Jun 15 15:40:07 2022 +0200 +++ b/OrthancServer/Sources/ServerJobs/DicomMoveScuJob.h Tue Jun 21 10:55:45 2022 +0200 @@ -51,7 +51,7 @@ public: explicit DicomMoveScuJob(ServerContext& context) : context_(context), - query_(true /* this is for worklists */), + query_(false /* this is not for worklists */), queryFormat_(DicomToJsonFormat_Short) { } @@ -61,6 +61,8 @@ void AddFindAnswer(const DicomMap& answer); + void AddQuery(const DicomMap& query); + void AddFindAnswer(QueryRetrieveHandler& query, size_t i);