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);