Mercurial > hg > orthanc
comparison OrthancServer/DicomProtocol/DicomUserConnection.cpp @ 1364:111e23bb4904 query-retrieve
integration mainline->query-retrieve
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 21 May 2015 16:58:30 +0200 |
parents | c2c28dd17e87 bba8a47922d1 |
children | a3559b66fba7 |
comparison
equal
deleted
inserted
replaced
953:f894be6e7cc1 | 1364:111e23bb4904 |
---|---|
1 /** | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, | 3 * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics |
4 * Belgium | 4 * Department, University Hospital of Liege, Belgium |
5 * | 5 * |
6 * This program is free software: you can redistribute it and/or | 6 * This program is free software: you can redistribute it and/or |
7 * modify it under the terms of the GNU General Public License as | 7 * modify it under the terms of the GNU General Public License as |
8 * published by the Free Software Foundation, either version 3 of the | 8 * published by the Free Software Foundation, either version 3 of the |
9 * License, or (at your option) any later version. | 9 * License, or (at your option) any later version. |
133 namespace Orthanc | 133 namespace Orthanc |
134 { | 134 { |
135 struct DicomUserConnection::PImpl | 135 struct DicomUserConnection::PImpl |
136 { | 136 { |
137 // Connection state | 137 // Connection state |
138 uint32_t dimseTimeout_; | |
139 uint32_t acseTimeout_; | |
138 T_ASC_Network* net_; | 140 T_ASC_Network* net_; |
139 T_ASC_Parameters* params_; | 141 T_ASC_Parameters* params_; |
140 T_ASC_Association* assoc_; | 142 T_ASC_Association* assoc_; |
141 | 143 |
142 bool IsOpen() const | 144 bool IsOpen() const |
152 | 154 |
153 static void Check(const OFCondition& cond) | 155 static void Check(const OFCondition& cond) |
154 { | 156 { |
155 if (cond.bad()) | 157 if (cond.bad()) |
156 { | 158 { |
157 throw OrthancException("DicomUserConnection: " + std::string(cond.text())); | 159 LOG(ERROR) << "DicomUserConnection: " << std::string(cond.text()); |
160 throw OrthancException(ErrorCode_NetworkProtocol); | |
158 } | 161 } |
159 } | 162 } |
160 | 163 |
161 void DicomUserConnection::PImpl::CheckIsOpen() const | 164 void DicomUserConnection::PImpl::CheckIsOpen() const |
162 { | 165 { |
163 if (!IsOpen()) | 166 if (!IsOpen()) |
164 { | 167 { |
165 throw OrthancException("DicomUserConnection: First open the connection"); | 168 LOG(ERROR) << "DicomUserConnection: First open the connection"; |
169 throw OrthancException(ErrorCode_NetworkProtocol); | |
166 } | 170 } |
167 } | 171 } |
168 | 172 |
169 | 173 |
170 void DicomUserConnection::CheckIsOpen() const | 174 void DicomUserConnection::CheckIsOpen() const |
321 // Finally conduct transmission of data | 325 // Finally conduct transmission of data |
322 T_DIMSE_C_StoreRSP rsp; | 326 T_DIMSE_C_StoreRSP rsp; |
323 DcmDataset* statusDetail = NULL; | 327 DcmDataset* statusDetail = NULL; |
324 Check(DIMSE_storeUser(assoc_, presID, &req, | 328 Check(DIMSE_storeUser(assoc_, presID, &req, |
325 NULL, dcmff.getDataset(), /*progressCallback*/ NULL, NULL, | 329 NULL, dcmff.getDataset(), /*progressCallback*/ NULL, NULL, |
326 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ 0, | 330 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ dimseTimeout_, |
327 &rsp, &statusDetail, NULL)); | 331 &rsp, &statusDetail, NULL)); |
328 | 332 |
329 if (statusDetail != NULL) | 333 if (statusDetail != NULL) |
330 { | 334 { |
331 delete statusDetail; | 335 delete statusDetail; |
448 | 452 |
449 // Figure out which of the accepted presentation contexts should be used | 453 // Figure out which of the accepted presentation contexts should be used |
450 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); | 454 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); |
451 if (presID == 0) | 455 if (presID == 0) |
452 { | 456 { |
453 throw OrthancException("DicomUserConnection: The C-FIND command is not supported by the distant AET"); | 457 throw OrthancException("DicomUserConnection: The C-FIND command is not supported by the remote AET"); |
454 } | 458 } |
455 | 459 |
456 T_DIMSE_C_FindRQ request; | 460 T_DIMSE_C_FindRQ request; |
457 memset(&request, 0, sizeof(request)); | 461 memset(&request, 0, sizeof(request)); |
458 request.MessageID = pimpl_->assoc_->nextMsgID++; | 462 request.MessageID = pimpl_->assoc_->nextMsgID++; |
462 | 466 |
463 T_DIMSE_C_FindRSP response; | 467 T_DIMSE_C_FindRSP response; |
464 DcmDataset* statusDetail = NULL; | 468 DcmDataset* statusDetail = NULL; |
465 OFCondition cond = DIMSE_findUser(pimpl_->assoc_, presID, &request, dataset.get(), | 469 OFCondition cond = DIMSE_findUser(pimpl_->assoc_, presID, &request, dataset.get(), |
466 FindCallback, &result, | 470 FindCallback, &result, |
467 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ 0, | 471 /*opt_blockMode*/ DIMSE_BLOCKING, |
472 /*opt_dimse_timeout*/ pimpl_->dimseTimeout_, | |
468 &response, &statusDetail); | 473 &response, &statusDetail); |
469 | 474 |
470 if (statusDetail) | 475 if (statusDetail) |
471 { | 476 { |
472 delete statusDetail; | 477 delete statusDetail; |
539 | 544 |
540 // Figure out which of the accepted presentation contexts should be used | 545 // Figure out which of the accepted presentation contexts should be used |
541 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); | 546 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); |
542 if (presID == 0) | 547 if (presID == 0) |
543 { | 548 { |
544 throw OrthancException("DicomUserConnection: The C-MOVE command is not supported by the distant AET"); | 549 throw OrthancException("DicomUserConnection: The C-MOVE command is not supported by the remote AET"); |
545 } | 550 } |
546 | 551 |
547 T_DIMSE_C_MoveRQ request; | 552 T_DIMSE_C_MoveRQ request; |
548 memset(&request, 0, sizeof(request)); | 553 memset(&request, 0, sizeof(request)); |
549 request.MessageID = pimpl_->assoc_->nextMsgID++; | 554 request.MessageID = pimpl_->assoc_->nextMsgID++; |
555 T_DIMSE_C_MoveRSP response; | 560 T_DIMSE_C_MoveRSP response; |
556 DcmDataset* statusDetail = NULL; | 561 DcmDataset* statusDetail = NULL; |
557 DcmDataset* responseIdentifiers = NULL; | 562 DcmDataset* responseIdentifiers = NULL; |
558 OFCondition cond = DIMSE_moveUser(pimpl_->assoc_, presID, &request, dataset.get(), | 563 OFCondition cond = DIMSE_moveUser(pimpl_->assoc_, presID, &request, dataset.get(), |
559 NULL, NULL, | 564 NULL, NULL, |
560 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ 0, | 565 /*opt_blockMode*/ DIMSE_BLOCKING, |
566 /*opt_dimse_timeout*/ pimpl_->dimseTimeout_, | |
561 pimpl_->net_, NULL, NULL, | 567 pimpl_->net_, NULL, NULL, |
562 &response, &statusDetail, &responseIdentifiers); | 568 &response, &statusDetail, &responseIdentifiers); |
563 | 569 |
564 if (statusDetail) | 570 if (statusDetail) |
565 { | 571 { |
606 | 612 |
607 DicomUserConnection::DicomUserConnection() : | 613 DicomUserConnection::DicomUserConnection() : |
608 pimpl_(new PImpl), | 614 pimpl_(new PImpl), |
609 preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX), | 615 preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX), |
610 localAet_("STORESCU"), | 616 localAet_("STORESCU"), |
611 distantAet_("ANY-SCP"), | 617 remoteAet_("ANY-SCP"), |
612 distantHost_("127.0.0.1") | 618 remoteHost_("127.0.0.1") |
613 { | 619 { |
614 distantPort_ = 104; | 620 remotePort_ = 104; |
615 manufacturer_ = ModalityManufacturer_Generic; | 621 manufacturer_ = ModalityManufacturer_Generic; |
616 | 622 |
623 SetTimeout(10); | |
617 pimpl_->net_ = NULL; | 624 pimpl_->net_ = NULL; |
618 pimpl_->params_ = NULL; | 625 pimpl_->params_ = NULL; |
619 pimpl_->assoc_ = NULL; | 626 pimpl_->assoc_ = NULL; |
620 | 627 |
621 // SOP classes for C-ECHO, C-FIND and C-MOVE | 628 // SOP classes for C-ECHO, C-FIND and C-MOVE |
633 } | 640 } |
634 | 641 |
635 | 642 |
636 void DicomUserConnection::Connect(const RemoteModalityParameters& parameters) | 643 void DicomUserConnection::Connect(const RemoteModalityParameters& parameters) |
637 { | 644 { |
638 SetDistantApplicationEntityTitle(parameters.GetApplicationEntityTitle()); | 645 SetRemoteApplicationEntityTitle(parameters.GetApplicationEntityTitle()); |
639 SetDistantHost(parameters.GetHost()); | 646 SetRemoteHost(parameters.GetHost()); |
640 SetDistantPort(parameters.GetPort()); | 647 SetRemotePort(parameters.GetPort()); |
641 SetDistantManufacturer(parameters.GetManufacturer()); | 648 SetRemoteManufacturer(parameters.GetManufacturer()); |
642 } | 649 } |
643 | 650 |
644 | 651 |
645 void DicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet) | 652 void DicomUserConnection::SetLocalApplicationEntityTitle(const std::string& aet) |
646 { | 653 { |
649 Close(); | 656 Close(); |
650 localAet_ = aet; | 657 localAet_ = aet; |
651 } | 658 } |
652 } | 659 } |
653 | 660 |
654 void DicomUserConnection::SetDistantApplicationEntityTitle(const std::string& aet) | 661 void DicomUserConnection::SetRemoteApplicationEntityTitle(const std::string& aet) |
655 { | 662 { |
656 if (distantAet_ != aet) | 663 if (remoteAet_ != aet) |
657 { | 664 { |
658 Close(); | 665 Close(); |
659 distantAet_ = aet; | 666 remoteAet_ = aet; |
660 } | 667 } |
661 } | 668 } |
662 | 669 |
663 void DicomUserConnection::SetDistantManufacturer(ModalityManufacturer manufacturer) | 670 void DicomUserConnection::SetRemoteManufacturer(ModalityManufacturer manufacturer) |
664 { | 671 { |
665 if (manufacturer_ != manufacturer) | 672 if (manufacturer_ != manufacturer) |
666 { | 673 { |
667 Close(); | 674 Close(); |
668 manufacturer_ = manufacturer; | 675 manufacturer_ = manufacturer; |
682 preferredTransferSyntax_ = preferredTransferSyntax; | 689 preferredTransferSyntax_ = preferredTransferSyntax; |
683 } | 690 } |
684 } | 691 } |
685 | 692 |
686 | 693 |
687 void DicomUserConnection::SetDistantHost(const std::string& host) | 694 void DicomUserConnection::SetRemoteHost(const std::string& host) |
688 { | 695 { |
689 if (distantHost_ != host) | 696 if (remoteHost_ != host) |
690 { | 697 { |
691 if (host.size() > HOST_NAME_MAX - 10) | 698 if (host.size() > HOST_NAME_MAX - 10) |
692 { | 699 { |
693 throw OrthancException("Distant host name is too long"); | 700 throw OrthancException("Remote host name is too long"); |
694 } | 701 } |
695 | 702 |
696 Close(); | 703 Close(); |
697 distantHost_ = host; | 704 remoteHost_ = host; |
698 } | 705 } |
699 } | 706 } |
700 | 707 |
701 void DicomUserConnection::SetDistantPort(uint16_t port) | 708 void DicomUserConnection::SetRemotePort(uint16_t port) |
702 { | 709 { |
703 if (distantPort_ != port) | 710 if (remotePort_ != port) |
704 { | 711 { |
705 Close(); | 712 Close(); |
706 distantPort_ = port; | 713 remotePort_ = port; |
707 } | 714 } |
708 } | 715 } |
709 | 716 |
710 void DicomUserConnection::Open() | 717 void DicomUserConnection::Open() |
711 { | 718 { |
714 // Don't reopen the connection | 721 // Don't reopen the connection |
715 return; | 722 return; |
716 } | 723 } |
717 | 724 |
718 LOG(INFO) << "Opening a DICOM SCU connection from AET \"" << GetLocalApplicationEntityTitle() | 725 LOG(INFO) << "Opening a DICOM SCU connection from AET \"" << GetLocalApplicationEntityTitle() |
719 << "\" to AET \"" << GetDistantApplicationEntityTitle() << "\" on host " | 726 << "\" to AET \"" << GetRemoteApplicationEntityTitle() << "\" on host " |
720 << GetDistantHost() << ":" << GetDistantPort() | 727 << GetRemoteHost() << ":" << GetRemotePort() |
721 << " (manufacturer: " << EnumerationToString(GetDistantManufacturer()) << ")"; | 728 << " (manufacturer: " << EnumerationToString(GetRemoteManufacturer()) << ")"; |
722 | 729 |
723 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ 30, &pimpl_->net_)); | 730 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ pimpl_->acseTimeout_, &pimpl_->net_)); |
724 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU)); | 731 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU)); |
725 | 732 |
726 // Set this application's title and the called application's title in the params | 733 // Set this application's title and the called application's title in the params |
727 Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), distantAet_.c_str(), NULL)); | 734 Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), remoteAet_.c_str(), NULL)); |
728 | 735 |
729 // Set the network addresses of the local and distant entities | 736 // Set the network addresses of the local and remote entities |
730 char localHost[HOST_NAME_MAX]; | 737 char localHost[HOST_NAME_MAX]; |
731 gethostname(localHost, HOST_NAME_MAX - 1); | 738 gethostname(localHost, HOST_NAME_MAX - 1); |
732 | 739 |
733 char distantHostAndPort[HOST_NAME_MAX]; | 740 char remoteHostAndPort[HOST_NAME_MAX]; |
734 | 741 |
735 #ifdef _MSC_VER | 742 #ifdef _MSC_VER |
736 _snprintf | 743 _snprintf |
737 #else | 744 #else |
738 snprintf | 745 snprintf |
739 #endif | 746 #endif |
740 (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_); | 747 (remoteHostAndPort, HOST_NAME_MAX - 1, "%s:%d", remoteHost_.c_str(), remotePort_); |
741 | 748 |
742 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); | 749 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, remoteHostAndPort)); |
743 | 750 |
744 // Set various options | 751 // Set various options |
745 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); | 752 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); |
746 | 753 |
747 SetupPresentationContexts(preferredTransferSyntax_); | 754 SetupPresentationContexts(preferredTransferSyntax_); |
814 bool DicomUserConnection::Echo() | 821 bool DicomUserConnection::Echo() |
815 { | 822 { |
816 CheckIsOpen(); | 823 CheckIsOpen(); |
817 DIC_US status; | 824 DIC_US status; |
818 Check(DIMSE_echoUser(pimpl_->assoc_, pimpl_->assoc_->nextMsgID++, | 825 Check(DIMSE_echoUser(pimpl_->assoc_, pimpl_->assoc_->nextMsgID++, |
819 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ 0, | 826 /*opt_blockMode*/ DIMSE_BLOCKING, |
827 /*opt_dimse_timeout*/ pimpl_->dimseTimeout_, | |
820 &status, NULL)); | 828 &status, NULL)); |
821 return status == STATUS_Success; | 829 return status == STATUS_Success; |
822 } | 830 } |
823 | 831 |
824 | 832 |
861 map.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid); | 869 map.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, seriesUid); |
862 map.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid); | 870 map.SetValue(DICOM_TAG_SOP_INSTANCE_UID, instanceUid); |
863 Move(targetAet, map); | 871 Move(targetAet, map); |
864 } | 872 } |
865 | 873 |
866 void DicomUserConnection::SetConnectionTimeout(uint32_t seconds) | 874 |
867 { | 875 void DicomUserConnection::SetTimeout(uint32_t seconds) |
876 { | |
877 if (seconds <= 0) | |
878 { | |
879 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
880 } | |
881 | |
868 dcmConnectionTimeout.set(seconds); | 882 dcmConnectionTimeout.set(seconds); |
883 pimpl_->dimseTimeout_ = seconds; | |
884 pimpl_->acseTimeout_ = 10; | |
885 } | |
886 | |
887 | |
888 void DicomUserConnection::DisableTimeout() | |
889 { | |
890 /** | |
891 * Global timeout (seconds) for connecting to remote hosts. | |
892 * Default value is -1 which selects infinite timeout, i.e. blocking connect(). | |
893 */ | |
894 dcmConnectionTimeout.set(-1); | |
895 pimpl_->dimseTimeout_ = 0; | |
896 pimpl_->acseTimeout_ = 10; | |
869 } | 897 } |
870 | 898 |
871 | 899 |
872 void DicomUserConnection::CheckStorageSOPClassesInvariant() const | 900 void DicomUserConnection::CheckStorageSOPClassesInvariant() const |
873 { | 901 { |
912 } | 940 } |
913 else if (reservedStorageSOPClasses_.size() + storageSOPClasses_.size() + | 941 else if (reservedStorageSOPClasses_.size() + storageSOPClasses_.size() + |
914 defaultStorageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES) | 942 defaultStorageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES) |
915 { | 943 { |
916 // Make room in the default storage syntaxes | 944 // Make room in the default storage syntaxes |
917 assert(defaultStorageSOPClasses_.size() > 0); // Necessarily true because condition (*) is false | 945 assert(!defaultStorageSOPClasses_.empty()); // Necessarily true because condition (*) is false |
918 defaultStorageSOPClasses_.erase(*defaultStorageSOPClasses_.rbegin()); | 946 defaultStorageSOPClasses_.erase(*defaultStorageSOPClasses_.rbegin()); |
919 } | 947 } |
920 | 948 |
921 // Explicitly register the new storage syntax | 949 // Explicitly register the new storage syntax |
922 storageSOPClasses_.insert(sop); | 950 storageSOPClasses_.insert(sop); |