comparison OrthancServer/DicomProtocol/DicomUserConnection.cpp @ 1805:f08978b1f45b worklists

c-find scu for modality worklists
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 20 Nov 2015 17:56:31 +0100
parents ec66a16aa398
children 3dcf5c0734c9
comparison
equal deleted inserted replaced
1803:d093f998a83b 1805:f08978b1f45b
351 namespace 351 namespace
352 { 352 {
353 struct FindPayload 353 struct FindPayload
354 { 354 {
355 DicomFindAnswers* answers; 355 DicomFindAnswers* answers;
356 std::string level; 356 const char* level;
357 bool isWorklist;
357 }; 358 };
358 } 359 }
359 360
360 361
361 static void FindCallback( 362 static void FindCallback(
369 { 370 {
370 FindPayload& payload = *reinterpret_cast<FindPayload*>(callbackData); 371 FindPayload& payload = *reinterpret_cast<FindPayload*>(callbackData);
371 372
372 if (responseIdentifiers != NULL) 373 if (responseIdentifiers != NULL)
373 { 374 {
374 DicomMap m; 375 if (payload.isWorklist)
375 FromDcmtkBridge::Convert(m, *responseIdentifiers);
376
377 if (!m.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
378 { 376 {
379 m.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, payload.level); 377 ParsedDicomFile answer(*responseIdentifiers);
378 payload.answers->Add(answer);
380 } 379 }
381 380 else
382 payload.answers->Add(m); 381 {
382 DicomMap m;
383 FromDcmtkBridge::Convert(m, *responseIdentifiers);
384
385 if (!m.HasTag(DICOM_TAG_QUERY_RETRIEVE_LEVEL))
386 {
387 m.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, payload.level);
388 }
389
390 payload.answers->Add(m);
391 }
383 } 392 }
384 } 393 }
385 394
386 395
387 static void FixFindQuery(DicomMap& fixedQuery, 396 static void FixFindQuery(DicomMap& fixedQuery,
472 return ToDcmtkBridge::Convert(fields); 481 return ToDcmtkBridge::Convert(fields);
473 } 482 }
474 } 483 }
475 484
476 485
486 static void ExecuteFind(DicomFindAnswers& answers,
487 T_ASC_Association* association,
488 DcmDataset* dataset,
489 const char* sopClass,
490 bool isWorklist,
491 const char* level,
492 uint32_t dimseTimeout)
493 {
494 assert(isWorklist ^ (level != NULL));
495
496 FindPayload payload;
497 payload.answers = &answers;
498 payload.level = level;
499 payload.isWorklist = isWorklist;
500
501 // Figure out which of the accepted presentation contexts should be used
502 int presID = ASC_findAcceptedPresentationContextID(association, sopClass);
503 if (presID == 0)
504 {
505 throw OrthancException(ErrorCode_DicomFindUnavailable);
506 }
507
508 T_DIMSE_C_FindRQ request;
509 memset(&request, 0, sizeof(request));
510 request.MessageID = association->nextMsgID++;
511 strcpy(request.AffectedSOPClassUID, sopClass);
512 request.DataSetType = DIMSE_DATASET_PRESENT;
513 request.Priority = DIMSE_PRIORITY_MEDIUM;
514
515 T_DIMSE_C_FindRSP response;
516 DcmDataset* statusDetail = NULL;
517 OFCondition cond = DIMSE_findUser(association, presID, &request, dataset,
518 FindCallback, &payload,
519 /*opt_blockMode*/ DIMSE_BLOCKING,
520 /*opt_dimse_timeout*/ dimseTimeout,
521 &response, &statusDetail);
522
523 if (statusDetail)
524 {
525 delete statusDetail;
526 }
527
528 Check(cond);
529 }
530
531
477 void DicomUserConnection::Find(DicomFindAnswers& result, 532 void DicomUserConnection::Find(DicomFindAnswers& result,
478 ResourceType level, 533 ResourceType level,
479 const DicomMap& originalFields) 534 const DicomMap& originalFields)
480 { 535 {
481 DicomMap fields; 536 DicomMap fields;
482 FixFindQuery(fields, level, originalFields); 537 FixFindQuery(fields, level, originalFields);
483 538
484 CheckIsOpen(); 539 CheckIsOpen();
485 540
486 FindPayload payload;
487 payload.answers = &result;
488
489 std::auto_ptr<DcmDataset> dataset(ConvertQueryFields(fields, manufacturer_)); 541 std::auto_ptr<DcmDataset> dataset(ConvertQueryFields(fields, manufacturer_));
490 542 const char* clevel = NULL;
491 const char* sopClass; 543 const char* sopClass = NULL;
544
492 switch (level) 545 switch (level)
493 { 546 {
494 case ResourceType_Patient: 547 case ResourceType_Patient:
495 payload.level = "PATIENT"; 548 clevel = "PATIENT";
496 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT"); 549 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT");
497 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel; 550 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel;
498 break; 551 break;
499 552
500 case ResourceType_Study: 553 case ResourceType_Study:
501 payload.level = "STUDY"; 554 clevel = "STUDY";
502 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY"); 555 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY");
503 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; 556 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
504 break; 557 break;
505 558
506 case ResourceType_Series: 559 case ResourceType_Series:
507 payload.level = "SERIES"; 560 clevel = "SERIES";
508 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES"); 561 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES");
509 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; 562 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
510 break; 563 break;
511 564
512 case ResourceType_Instance: 565 case ResourceType_Instance:
513 payload.level = "INSTANCE"; 566 clevel = "INSTANCE";
514 if (manufacturer_ == ModalityManufacturer_ClearCanvas || 567 if (manufacturer_ == ModalityManufacturer_ClearCanvas ||
515 manufacturer_ == ModalityManufacturer_Dcm4Chee) 568 manufacturer_ == ModalityManufacturer_Dcm4Chee)
516 { 569 {
517 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>. 570 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>.
518 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J 571 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J
563 616
564 default: 617 default:
565 throw OrthancException(ErrorCode_ParameterOutOfRange); 618 throw OrthancException(ErrorCode_ParameterOutOfRange);
566 } 619 }
567 620
568 // Figure out which of the accepted presentation contexts should be used 621 assert(clevel != NULL && sopClass != NULL);
569 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); 622 ExecuteFind(result, pimpl_->assoc_, dataset.get(), sopClass, false, clevel, pimpl_->dimseTimeout_);
570 if (presID == 0)
571 {
572 throw OrthancException(ErrorCode_DicomFindUnavailable);
573 }
574
575 T_DIMSE_C_FindRQ request;
576 memset(&request, 0, sizeof(request));
577 request.MessageID = pimpl_->assoc_->nextMsgID++;
578 strcpy(request.AffectedSOPClassUID, sopClass);
579 request.DataSetType = DIMSE_DATASET_PRESENT;
580 request.Priority = DIMSE_PRIORITY_MEDIUM;
581
582 T_DIMSE_C_FindRSP response;
583 DcmDataset* statusDetail = NULL;
584 OFCondition cond = DIMSE_findUser(pimpl_->assoc_, presID, &request, dataset.get(),
585 FindCallback, &payload,
586 /*opt_blockMode*/ DIMSE_BLOCKING,
587 /*opt_dimse_timeout*/ pimpl_->dimseTimeout_,
588 &response, &statusDetail);
589
590 if (statusDetail)
591 {
592 delete statusDetail;
593 }
594
595 Check(cond);
596 } 623 }
597 624
598 625
599 void DicomUserConnection::MoveInternal(const std::string& targetAet, 626 void DicomUserConnection::MoveInternal(const std::string& targetAet,
600 ResourceType level, 627 ResourceType level,
683 710
684 storageSOPClasses_.clear(); 711 storageSOPClasses_.clear();
685 defaultStorageSOPClasses_.clear(); 712 defaultStorageSOPClasses_.clear();
686 713
687 // Copy the short list of storage SOP classes from DCMTK, making 714 // Copy the short list of storage SOP classes from DCMTK, making
688 // room for the 4 SOP classes reserved for C-ECHO, C-FIND, C-MOVE. 715 // room for the 5 SOP classes reserved for C-ECHO, C-FIND, C-MOVE at (**).
689 716
690 std::set<std::string> uncommon; 717 std::set<std::string> uncommon;
691 uncommon.insert(UID_BlendingSoftcopyPresentationStateStorage); 718 uncommon.insert(UID_BlendingSoftcopyPresentationStateStorage);
692 uncommon.insert(UID_GrayscaleSoftcopyPresentationStateStorage); 719 uncommon.insert(UID_GrayscaleSoftcopyPresentationStateStorage);
693 uncommon.insert(UID_ColorSoftcopyPresentationStateStorage); 720 uncommon.insert(UID_ColorSoftcopyPresentationStateStorage);
694 uncommon.insert(UID_PseudoColorSoftcopyPresentationStateStorage); 721 uncommon.insert(UID_PseudoColorSoftcopyPresentationStateStorage);
722 uncommon.insert(UID_XAXRFGrayscaleSoftcopyPresentationStateStorage);
695 723
696 // Add the storage syntaxes for C-STORE 724 // Add the storage syntaxes for C-STORE
697 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++) 725 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++)
698 { 726 {
699 if (uncommon.find(dcmShortSCUStorageSOPClassUIDs[i]) == uncommon.end()) 727 if (uncommon.find(dcmShortSCUStorageSOPClassUIDs[i]) == uncommon.end())
719 SetTimeout(10); 747 SetTimeout(10);
720 pimpl_->net_ = NULL; 748 pimpl_->net_ = NULL;
721 pimpl_->params_ = NULL; 749 pimpl_->params_ = NULL;
722 pimpl_->assoc_ = NULL; 750 pimpl_->assoc_ = NULL;
723 751
724 // SOP classes for C-ECHO, C-FIND and C-MOVE 752 // SOP classes for C-ECHO, C-FIND and C-MOVE (**)
725 reservedStorageSOPClasses_.push_back(UID_VerificationSOPClass); 753 reservedStorageSOPClasses_.push_back(UID_VerificationSOPClass);
726 reservedStorageSOPClasses_.push_back(UID_FINDPatientRootQueryRetrieveInformationModel); 754 reservedStorageSOPClasses_.push_back(UID_FINDPatientRootQueryRetrieveInformationModel);
727 reservedStorageSOPClasses_.push_back(UID_FINDStudyRootQueryRetrieveInformationModel); 755 reservedStorageSOPClasses_.push_back(UID_FINDStudyRootQueryRetrieveInformationModel);
728 reservedStorageSOPClasses_.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel); 756 reservedStorageSOPClasses_.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel);
757 reservedStorageSOPClasses_.push_back(UID_FINDModalityWorklistInformationModel);
729 758
730 ResetStorageSOPClasses(); 759 ResetStorageSOPClasses();
731 } 760 }
732 761
733 DicomUserConnection::~DicomUserConnection() 762 DicomUserConnection::~DicomUserConnection()
1099 storageSOPClasses_.insert(sop); 1128 storageSOPClasses_.insert(sop);
1100 1129
1101 CheckStorageSOPClassesInvariant(); 1130 CheckStorageSOPClassesInvariant();
1102 } 1131 }
1103 1132
1133
1134 void DicomUserConnection::FindWorklist(DicomFindAnswers& result,
1135 ParsedDicomFile& query)
1136 {
1137 CheckIsOpen();
1138
1139 DcmDataset* dataset = query.GetDcmtkObject().getDataset();
1140 const char* sopClass = UID_FINDModalityWorklistInformationModel;
1141
1142 ExecuteFind(result, pimpl_->assoc_, dataset, sopClass, true, NULL, pimpl_->dimseTimeout_);
1143 }
1104 } 1144 }