Mercurial > hg > orthanc
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 } |