Mercurial > hg > orthanc
comparison OrthancServer/OrthancRestApi/OrthancRestModalities.cpp @ 2982:94c8222c52b7
New URIs to launch new C-FIND to explore the hierarchy of a C-FIND answer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 07 Dec 2018 18:03:56 +0100 |
parents | bbfd95a0c429 |
children | b1ba0a8311b5 |
comparison
equal
deleted
inserted
replaced
2980:63b724c7b046 | 2982:94c8222c52b7 |
---|---|
45 #include "../ServerToolbox.h" | 45 #include "../ServerToolbox.h" |
46 | 46 |
47 | 47 |
48 namespace Orthanc | 48 namespace Orthanc |
49 { | 49 { |
50 static const char* const KEY_LEVEL = "Level"; | |
51 static const char* const KEY_QUERY = "Query"; | |
52 static const char* const KEY_RESOURCES = "Resources"; | |
53 | |
54 | |
50 static RemoteModalityParameters MyGetModalityUsingSymbolicName(const std::string& name) | 55 static RemoteModalityParameters MyGetModalityUsingSymbolicName(const std::string& name) |
51 { | 56 { |
52 OrthancConfiguration::ReaderLock lock; | 57 OrthancConfiguration::ReaderLock lock; |
53 return lock.GetConfiguration().GetModalityUsingSymbolicName(name); | 58 return lock.GetConfiguration().GetModalityUsingSymbolicName(name); |
54 } | 59 } |
406 | 411 |
407 /*************************************************************************** | 412 /*************************************************************************** |
408 * DICOM C-Find and C-Move SCU => Recommended since Orthanc 0.9.0 | 413 * DICOM C-Find and C-Move SCU => Recommended since Orthanc 0.9.0 |
409 ***************************************************************************/ | 414 ***************************************************************************/ |
410 | 415 |
416 static void AnswerQueryHandler(RestApiPostCall& call, | |
417 std::auto_ptr<QueryRetrieveHandler>& handler) | |
418 { | |
419 ServerContext& context = OrthancRestApi::GetContext(call); | |
420 | |
421 if (handler.get() == NULL) | |
422 { | |
423 throw OrthancException(ErrorCode_NullPointer); | |
424 } | |
425 | |
426 handler->Run(); | |
427 | |
428 std::string s = context.GetQueryRetrieveArchive().Add(handler.release()); | |
429 Json::Value result = Json::objectValue; | |
430 result["ID"] = s; | |
431 result["Path"] = "/queries/" + s; | |
432 | |
433 call.GetOutput().AnswerJson(result); | |
434 } | |
435 | |
436 | |
411 static void DicomQuery(RestApiPostCall& call) | 437 static void DicomQuery(RestApiPostCall& call) |
412 { | 438 { |
413 ServerContext& context = OrthancRestApi::GetContext(call); | 439 ServerContext& context = OrthancRestApi::GetContext(call); |
414 Json::Value request; | 440 Json::Value request; |
415 | 441 |
416 if (call.ParseJsonRequest(request) && | 442 if (call.ParseJsonRequest(request) && |
417 request.type() == Json::objectValue && | 443 request.type() == Json::objectValue && |
418 request.isMember("Level") && request["Level"].type() == Json::stringValue && | 444 request.isMember(KEY_LEVEL) && request[KEY_LEVEL].type() == Json::stringValue && |
419 (!request.isMember("Query") || request["Query"].type() == Json::objectValue)) | 445 (!request.isMember(KEY_QUERY) || request[KEY_QUERY].type() == Json::objectValue)) |
420 { | 446 { |
421 std::auto_ptr<QueryRetrieveHandler> handler(new QueryRetrieveHandler(context)); | 447 std::auto_ptr<QueryRetrieveHandler> handler(new QueryRetrieveHandler(context)); |
422 | 448 |
423 handler->SetModality(call.GetUriComponent("id", "")); | 449 handler->SetModality(call.GetUriComponent("id", "")); |
424 handler->SetLevel(StringToResourceType(request["Level"].asCString())); | 450 handler->SetLevel(StringToResourceType(request[KEY_LEVEL].asCString())); |
425 | 451 |
426 if (request.isMember("Query")) | 452 if (request.isMember(KEY_QUERY)) |
427 { | 453 { |
428 Json::Value::Members tags = request["Query"].getMemberNames(); | 454 std::map<DicomTag, std::string> query; |
429 for (size_t i = 0; i < tags.size(); i++) | 455 SerializationToolbox::ReadMapOfTags(query, request, KEY_QUERY); |
456 | |
457 for (std::map<DicomTag, std::string>::const_iterator | |
458 it = query.begin(); it != query.end(); ++it) | |
430 { | 459 { |
431 handler->SetQuery(FromDcmtkBridge::ParseTag(tags[i].c_str()), | 460 handler->SetQuery(it->first, it->second); |
432 request["Query"][tags[i]].asString()); | |
433 } | 461 } |
434 } | 462 } |
435 | 463 |
436 handler->Run(); | 464 AnswerQueryHandler(call, handler); |
437 | |
438 std::string s = context.GetQueryRetrieveArchive().Add(handler.release()); | |
439 Json::Value result = Json::objectValue; | |
440 result["ID"] = s; | |
441 result["Path"] = "/queries/" + s; | |
442 call.GetOutput().AnswerJson(result); | |
443 } | 465 } |
444 } | 466 } |
445 | 467 |
446 | 468 |
447 static void ListQueries(RestApiGetCall& call) | 469 static void ListQueries(RestApiGetCall& call) |
467 class QueryAccessor | 489 class QueryAccessor |
468 { | 490 { |
469 private: | 491 private: |
470 ServerContext& context_; | 492 ServerContext& context_; |
471 SharedArchive::Accessor accessor_; | 493 SharedArchive::Accessor accessor_; |
472 QueryRetrieveHandler& handler_; | 494 QueryRetrieveHandler* handler_; |
473 | 495 |
474 public: | 496 public: |
475 QueryAccessor(RestApiCall& call) : | 497 QueryAccessor(RestApiCall& call) : |
476 context_(OrthancRestApi::GetContext(call)), | 498 context_(OrthancRestApi::GetContext(call)), |
477 accessor_(context_.GetQueryRetrieveArchive(), call.GetUriComponent("id", "")), | 499 accessor_(context_.GetQueryRetrieveArchive(), call.GetUriComponent("id", "")), |
478 handler_(dynamic_cast<QueryRetrieveHandler&>(accessor_.GetItem())) | 500 handler_(NULL) |
479 { | 501 { |
502 if (accessor_.IsValid()) | |
503 { | |
504 handler_ = &dynamic_cast<QueryRetrieveHandler&>(accessor_.GetItem()); | |
505 } | |
506 else | |
507 { | |
508 throw OrthancException(ErrorCode_UnknownResource); | |
509 } | |
480 } | 510 } |
481 | 511 |
482 QueryRetrieveHandler& GetHandler() const | 512 QueryRetrieveHandler& GetHandler() const |
483 { | 513 { |
484 return handler_; | 514 assert(handler_ != NULL); |
515 return *handler_; | |
485 } | 516 } |
486 }; | 517 }; |
487 | 518 |
488 static void AnswerDicomMap(RestApiCall& call, | 519 static void AnswerDicomMap(RestApiCall& call, |
489 const DicomMap& value, | 520 const DicomMap& value, |
650 size_t index = boost::lexical_cast<size_t>(call.GetUriComponent("index", "")); | 681 size_t index = boost::lexical_cast<size_t>(call.GetUriComponent("index", "")); |
651 | 682 |
652 DicomMap map; | 683 DicomMap map; |
653 query.GetHandler().GetAnswer(map, index); | 684 query.GetHandler().GetAnswer(map, index); |
654 | 685 |
655 RestApi::AutoListChildren(call); | 686 Json::Value answer = Json::arrayValue; |
656 } | 687 answer.append("content"); |
657 | 688 answer.append("retrieve"); |
658 | 689 |
690 switch (query.GetHandler().GetLevel()) | |
691 { | |
692 case ResourceType_Patient: | |
693 answer.append("query-study"); | |
694 | |
695 case ResourceType_Study: | |
696 answer.append("query-series"); | |
697 | |
698 case ResourceType_Series: | |
699 answer.append("query-instances"); | |
700 break; | |
701 | |
702 default: | |
703 break; | |
704 } | |
705 | |
706 call.GetOutput().AnswerJson(answer); | |
707 } | |
708 | |
709 | |
710 template <ResourceType CHILDREN_LEVEL> | |
711 static void AnswerQueryChildren(RestApiPostCall& call) | |
712 { | |
713 // New in Orthanc 1.4.3 | |
714 assert(CHILDREN_LEVEL == ResourceType_Study || | |
715 CHILDREN_LEVEL == ResourceType_Series || | |
716 CHILDREN_LEVEL == ResourceType_Instance); | |
717 | |
718 ServerContext& context = OrthancRestApi::GetContext(call); | |
719 | |
720 std::auto_ptr<QueryRetrieveHandler> handler(new QueryRetrieveHandler(context)); | |
721 | |
722 { | |
723 const QueryAccessor parent(call); | |
724 const ResourceType level = parent.GetHandler().GetLevel(); | |
725 | |
726 const size_t index = boost::lexical_cast<size_t>(call.GetUriComponent("index", "")); | |
727 | |
728 Json::Value request; | |
729 | |
730 if (index >= parent.GetHandler().GetAnswersCount()) | |
731 { | |
732 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
733 } | |
734 else if (CHILDREN_LEVEL == ResourceType_Study && | |
735 level != ResourceType_Patient) | |
736 { | |
737 throw OrthancException(ErrorCode_UnknownResource); | |
738 } | |
739 else if (CHILDREN_LEVEL == ResourceType_Series && | |
740 level != ResourceType_Patient && | |
741 level != ResourceType_Study) | |
742 { | |
743 throw OrthancException(ErrorCode_UnknownResource); | |
744 } | |
745 else if (CHILDREN_LEVEL == ResourceType_Instance && | |
746 level != ResourceType_Patient && | |
747 level != ResourceType_Study && | |
748 level != ResourceType_Series) | |
749 { | |
750 throw OrthancException(ErrorCode_UnknownResource); | |
751 } | |
752 else if (!call.ParseJsonRequest(request)) | |
753 { | |
754 throw OrthancException(ErrorCode_BadFileFormat, "Must provide a JSON object"); | |
755 } | |
756 else | |
757 { | |
758 handler->SetModality(parent.GetHandler().GetModalitySymbolicName()); | |
759 handler->SetLevel(CHILDREN_LEVEL); | |
760 | |
761 if (request.isMember(KEY_QUERY)) | |
762 { | |
763 std::map<DicomTag, std::string> query; | |
764 SerializationToolbox::ReadMapOfTags(query, request, KEY_QUERY); | |
765 | |
766 for (std::map<DicomTag, std::string>::const_iterator | |
767 it = query.begin(); it != query.end(); ++it) | |
768 { | |
769 handler->SetQuery(it->first, it->second); | |
770 } | |
771 } | |
772 } | |
773 } | |
774 | |
775 AnswerQueryHandler(call, handler); | |
776 } | |
777 | |
659 | 778 |
660 | 779 |
661 /*************************************************************************** | 780 /*************************************************************************** |
662 * DICOM C-Store SCU | 781 * DICOM C-Store SCU |
663 ***************************************************************************/ | 782 ***************************************************************************/ |
699 resources = &request; | 818 resources = &request; |
700 } | 819 } |
701 else | 820 else |
702 { | 821 { |
703 if (request.type() != Json::objectValue || | 822 if (request.type() != Json::objectValue || |
704 !request.isMember("Resources")) | 823 !request.isMember(KEY_RESOURCES)) |
705 { | 824 { |
706 return false; | 825 return false; |
707 } | 826 } |
708 | 827 |
709 resources = &request["Resources"]; | 828 resources = &request[KEY_RESOURCES]; |
710 if (!resources->isArray()) | 829 if (!resources->isArray()) |
711 { | 830 { |
712 return false; | 831 return false; |
713 } | 832 } |
714 | 833 |
792 { | 911 { |
793 ServerContext& context = OrthancRestApi::GetContext(call); | 912 ServerContext& context = OrthancRestApi::GetContext(call); |
794 | 913 |
795 Json::Value request; | 914 Json::Value request; |
796 | 915 |
797 static const char* RESOURCES = "Resources"; | |
798 static const char* LEVEL = "Level"; | |
799 | |
800 if (!call.ParseJsonRequest(request) || | 916 if (!call.ParseJsonRequest(request) || |
801 request.type() != Json::objectValue || | 917 request.type() != Json::objectValue || |
802 !request.isMember(RESOURCES) || | 918 !request.isMember(KEY_RESOURCES) || |
803 !request.isMember(LEVEL) || | 919 !request.isMember(KEY_LEVEL) || |
804 request[RESOURCES].type() != Json::arrayValue || | 920 request[KEY_RESOURCES].type() != Json::arrayValue || |
805 request[LEVEL].type() != Json::stringValue) | 921 request[KEY_LEVEL].type() != Json::stringValue) |
806 { | 922 { |
807 throw OrthancException(ErrorCode_BadFileFormat); | 923 throw OrthancException(ErrorCode_BadFileFormat); |
808 } | 924 } |
809 | 925 |
810 ResourceType level = StringToResourceType(request["Level"].asCString()); | 926 ResourceType level = StringToResourceType(request[KEY_LEVEL].asCString()); |
811 | 927 |
812 std::string localAet = Toolbox::GetJsonStringField | 928 std::string localAet = Toolbox::GetJsonStringField |
813 (request, "LocalAet", context.GetDefaultLocalApplicationEntityTitle()); | 929 (request, "LocalAet", context.GetDefaultLocalApplicationEntityTitle()); |
814 std::string targetAet = Toolbox::GetJsonStringField | 930 std::string targetAet = Toolbox::GetJsonStringField |
815 (request, "TargetAet", context.GetDefaultLocalApplicationEntityTitle()); | 931 (request, "TargetAet", context.GetDefaultLocalApplicationEntityTitle()); |
818 MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); | 934 MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); |
819 | 935 |
820 DicomUserConnection connection(localAet, source); | 936 DicomUserConnection connection(localAet, source); |
821 connection.Open(); | 937 connection.Open(); |
822 | 938 |
823 for (Json::Value::ArrayIndex i = 0; i < request[RESOURCES].size(); i++) | 939 for (Json::Value::ArrayIndex i = 0; i < request[KEY_RESOURCES].size(); i++) |
824 { | 940 { |
825 DicomMap resource; | 941 DicomMap resource; |
826 FromDcmtkBridge::FromJson(resource, request[RESOURCES][i]); | 942 FromDcmtkBridge::FromJson(resource, request[KEY_RESOURCES][i]); |
827 | 943 |
828 connection.Move(targetAet, level, resource); | 944 connection.Move(targetAet, level, resource); |
829 } | 945 } |
830 | 946 |
831 // Move has succeeded | 947 // Move has succeeded |
1073 { | 1189 { |
1074 const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); | 1190 const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle(); |
1075 RemoteModalityParameters remote = | 1191 RemoteModalityParameters remote = |
1076 MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); | 1192 MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); |
1077 | 1193 |
1078 std::auto_ptr<ParsedDicomFile> query(ParsedDicomFile::CreateFromJson(json, static_cast<DicomFromJsonFlags>(0))); | 1194 std::auto_ptr<ParsedDicomFile> query |
1195 (ParsedDicomFile::CreateFromJson(json, static_cast<DicomFromJsonFlags>(0))); | |
1079 | 1196 |
1080 DicomFindAnswers answers(true); | 1197 DicomFindAnswers answers(true); |
1081 | 1198 |
1082 { | 1199 { |
1083 DicomUserConnection connection(localAet, remote); | 1200 DicomUserConnection connection(localAet, remote); |
1114 Register("/queries/{id}", ListQueryOperations); | 1231 Register("/queries/{id}", ListQueryOperations); |
1115 Register("/queries/{id}/answers", ListQueryAnswers); | 1232 Register("/queries/{id}/answers", ListQueryAnswers); |
1116 Register("/queries/{id}/answers/{index}", ListQueryAnswerOperations); | 1233 Register("/queries/{id}/answers/{index}", ListQueryAnswerOperations); |
1117 Register("/queries/{id}/answers/{index}/content", GetQueryOneAnswer); | 1234 Register("/queries/{id}/answers/{index}/content", GetQueryOneAnswer); |
1118 Register("/queries/{id}/answers/{index}/retrieve", RetrieveOneAnswer); | 1235 Register("/queries/{id}/answers/{index}/retrieve", RetrieveOneAnswer); |
1236 Register("/queries/{id}/answers/{index}/query-instances", | |
1237 AnswerQueryChildren<ResourceType_Instance>); | |
1238 Register("/queries/{id}/answers/{index}/query-series", | |
1239 AnswerQueryChildren<ResourceType_Series>); | |
1240 Register("/queries/{id}/answers/{index}/query-studies", | |
1241 AnswerQueryChildren<ResourceType_Study>); | |
1119 Register("/queries/{id}/level", GetQueryLevel); | 1242 Register("/queries/{id}/level", GetQueryLevel); |
1120 Register("/queries/{id}/modality", GetQueryModality); | 1243 Register("/queries/{id}/modality", GetQueryModality); |
1121 Register("/queries/{id}/query", GetQueryArguments); | 1244 Register("/queries/{id}/query", GetQueryArguments); |
1122 Register("/queries/{id}/retrieve", RetrieveAllAnswers); | 1245 Register("/queries/{id}/retrieve", RetrieveAllAnswers); |
1123 | 1246 |