Mercurial > hg > orthanc
comparison Core/DicomNetworking/DicomUserConnection.cpp @ 3215:4924972bce77
more verbose errors in DicomUserConnection
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 11 Feb 2019 06:46:19 +0100 |
parents | c4e1977e5ed7 |
children | 63f59ad9381a |
comparison
equal
deleted
inserted
replaced
3214:2a688e8b3c49 | 3215:4924972bce77 |
---|---|
163 uint16_t moveOriginatorID); | 163 uint16_t moveOriginatorID); |
164 }; | 164 }; |
165 | 165 |
166 | 166 |
167 static void Check(const OFCondition& cond, | 167 static void Check(const OFCondition& cond, |
168 const std::string& aet) | 168 const std::string& aet, |
169 const std::string& command) | |
169 { | 170 { |
170 if (cond.bad()) | 171 if (cond.bad()) |
171 { | 172 { |
172 // Reformat the error message from DCMTK by turning multiline | 173 // Reformat the error message from DCMTK by turning multiline |
173 // errors into a single line | 174 // errors into a single line |
203 | 204 |
204 if (isMultiline) | 205 if (isMultiline) |
205 { | 206 { |
206 info += ")"; | 207 info += ")"; |
207 } | 208 } |
208 | 209 |
209 throw OrthancException(ErrorCode_NetworkProtocol, | 210 throw OrthancException(ErrorCode_NetworkProtocol, |
210 "DicomUserConnection to AET \"" + aet + "\": " + info); | 211 "DicomUserConnection - " + command + |
212 " to AET \"" + aet + "\": " + info); | |
211 } | 213 } |
212 } | 214 } |
213 | 215 |
214 void DicomUserConnection::PImpl::CheckIsOpen() const | 216 void DicomUserConnection::PImpl::CheckIsOpen() const |
215 { | 217 { |
233 const char* asPreferred[], | 235 const char* asPreferred[], |
234 std::vector<const char*>& asFallback, | 236 std::vector<const char*>& asFallback, |
235 const std::string& aet) | 237 const std::string& aet) |
236 { | 238 { |
237 Check(ASC_addPresentationContext(params, presentationContextId, | 239 Check(ASC_addPresentationContext(params, presentationContextId, |
238 sopClass.c_str(), asPreferred, 1), aet); | 240 sopClass.c_str(), asPreferred, 1), |
241 aet, "initializing"); | |
239 presentationContextId += 2; | 242 presentationContextId += 2; |
240 | 243 |
241 if (asFallback.size() > 0) | 244 if (asFallback.size() > 0) |
242 { | 245 { |
243 Check(ASC_addPresentationContext(params, presentationContextId, | 246 Check(ASC_addPresentationContext(params, presentationContextId, |
244 sopClass.c_str(), &asFallback[0], asFallback.size()), aet); | 247 sopClass.c_str(), &asFallback[0], asFallback.size()), |
248 aet, "initializing"); | |
245 presentationContextId += 2; | 249 presentationContextId += 2; |
246 } | 250 } |
247 } | 251 } |
248 | 252 |
249 | 253 |
306 DicomUserConnection& connection, | 310 DicomUserConnection& connection, |
307 const std::string& moveOriginatorAET, | 311 const std::string& moveOriginatorAET, |
308 uint16_t moveOriginatorID) | 312 uint16_t moveOriginatorID) |
309 { | 313 { |
310 DcmFileFormat dcmff; | 314 DcmFileFormat dcmff; |
311 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength), connection.remoteAet_); | 315 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength), |
316 connection.remoteAet_, "C-STORE"); | |
312 | 317 |
313 // Determine the storage SOP class UID for this instance | 318 // Determine the storage SOP class UID for this instance |
314 static const DcmTagKey DCM_SOP_CLASS_UID(0x0008, 0x0016); | 319 static const DcmTagKey DCM_SOP_CLASS_UID(0x0008, 0x0016); |
315 OFString sopClassUid; | 320 OFString sopClassUid; |
316 if (dcmff.getDataset()->findAndGetOFString(DCM_SOP_CLASS_UID, sopClassUid).good()) | 321 if (dcmff.getDataset()->findAndGetOFString(DCM_SOP_CLASS_UID, sopClassUid).good()) |
378 if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sizeof(sopClass), sopInstance, sizeof(sopInstance))) | 383 if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sizeof(sopClass), sopInstance, sizeof(sopInstance))) |
379 #else | 384 #else |
380 if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sopInstance)) | 385 if (!DU_findSOPClassAndInstanceInDataSet(dcmff.getDataset(), sopClass, sopInstance)) |
381 #endif | 386 #endif |
382 { | 387 { |
383 throw OrthancException(ErrorCode_NoSopClassOrInstance); | 388 throw OrthancException(ErrorCode_NoSopClassOrInstance, |
389 "Unable to determine the SOP class/instance for C-STORE with AET " + | |
390 connection.remoteAet_); | |
384 } | 391 } |
385 | 392 |
386 // Figure out which of the accepted presentation contexts should be used | 393 // Figure out which of the accepted presentation contexts should be used |
387 int presID = ASC_findAcceptedPresentationContextID(assoc_, sopClass); | 394 int presID = ASC_findAcceptedPresentationContextID(assoc_, sopClass); |
388 if (presID == 0) | 395 if (presID == 0) |
389 { | 396 { |
390 const char *modalityName = dcmSOPClassUIDToModality(sopClass); | 397 const char *modalityName = dcmSOPClassUIDToModality(sopClass); |
391 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass); | 398 if (modalityName == NULL) modalityName = dcmFindNameOfUID(sopClass); |
392 if (!modalityName) modalityName = "unknown SOP class"; | 399 if (modalityName == NULL) modalityName = "unknown SOP class"; |
393 throw OrthancException(ErrorCode_NoPresentationContext); | 400 throw OrthancException(ErrorCode_NoPresentationContext, |
401 "Unable to determine the accepted presentation contexts for C-STORE with AET " + | |
402 connection.remoteAet_ + " (" + std::string(modalityName) + ")"); | |
394 } | 403 } |
395 | 404 |
396 // Prepare the transmission of data | 405 // Prepare the transmission of data |
397 T_DIMSE_C_StoreRQ request; | 406 T_DIMSE_C_StoreRQ request; |
398 memset(&request, 0, sizeof(request)); | 407 memset(&request, 0, sizeof(request)); |
416 T_DIMSE_C_StoreRSP rsp; | 425 T_DIMSE_C_StoreRSP rsp; |
417 DcmDataset* statusDetail = NULL; | 426 DcmDataset* statusDetail = NULL; |
418 Check(DIMSE_storeUser(assoc_, presID, &request, | 427 Check(DIMSE_storeUser(assoc_, presID, &request, |
419 NULL, dcmff.getDataset(), /*progressCallback*/ NULL, NULL, | 428 NULL, dcmff.getDataset(), /*progressCallback*/ NULL, NULL, |
420 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ dimseTimeout_, | 429 /*opt_blockMode*/ DIMSE_BLOCKING, /*opt_dimse_timeout*/ dimseTimeout_, |
421 &rsp, &statusDetail, NULL), connection.remoteAet_); | 430 &rsp, &statusDetail, NULL), |
431 connection.remoteAet_, "C-STORE"); | |
422 | 432 |
423 if (statusDetail != NULL) | 433 if (statusDetail != NULL) |
424 { | 434 { |
425 delete statusDetail; | 435 delete statusDetail; |
426 } | 436 } |
607 | 617 |
608 // Figure out which of the accepted presentation contexts should be used | 618 // Figure out which of the accepted presentation contexts should be used |
609 int presID = ASC_findAcceptedPresentationContextID(association, sopClass); | 619 int presID = ASC_findAcceptedPresentationContextID(association, sopClass); |
610 if (presID == 0) | 620 if (presID == 0) |
611 { | 621 { |
612 throw OrthancException(ErrorCode_DicomFindUnavailable); | 622 throw OrthancException(ErrorCode_DicomFindUnavailable, |
623 "Remote AET is " + remoteAet); | |
613 } | 624 } |
614 | 625 |
615 T_DIMSE_C_FindRQ request; | 626 T_DIMSE_C_FindRQ request; |
616 memset(&request, 0, sizeof(request)); | 627 memset(&request, 0, sizeof(request)); |
617 request.MessageID = association->nextMsgID++; | 628 request.MessageID = association->nextMsgID++; |
638 if (statusDetail) | 649 if (statusDetail) |
639 { | 650 { |
640 delete statusDetail; | 651 delete statusDetail; |
641 } | 652 } |
642 | 653 |
643 Check(cond, remoteAet); | 654 Check(cond, remoteAet, "C-FIND"); |
644 } | 655 } |
645 | 656 |
646 | 657 |
647 void DicomUserConnection::Find(DicomFindAnswers& result, | 658 void DicomUserConnection::Find(DicomFindAnswers& result, |
648 ResourceType level, | 659 ResourceType level, |
811 | 822 |
812 // Figure out which of the accepted presentation contexts should be used | 823 // Figure out which of the accepted presentation contexts should be used |
813 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); | 824 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); |
814 if (presID == 0) | 825 if (presID == 0) |
815 { | 826 { |
816 throw OrthancException(ErrorCode_DicomMoveUnavailable); | 827 throw OrthancException(ErrorCode_DicomMoveUnavailable, |
828 "Remote AET is " + remoteAet_); | |
817 } | 829 } |
818 | 830 |
819 T_DIMSE_C_MoveRQ request; | 831 T_DIMSE_C_MoveRQ request; |
820 memset(&request, 0, sizeof(request)); | 832 memset(&request, 0, sizeof(request)); |
821 request.MessageID = pimpl_->assoc_->nextMsgID++; | 833 request.MessageID = pimpl_->assoc_->nextMsgID++; |
842 if (responseIdentifiers) | 854 if (responseIdentifiers) |
843 { | 855 { |
844 delete responseIdentifiers; | 856 delete responseIdentifiers; |
845 } | 857 } |
846 | 858 |
847 Check(cond, remoteAet_); | 859 Check(cond, remoteAet_, "C-MOVE"); |
848 } | 860 } |
849 | 861 |
850 | 862 |
851 void DicomUserConnection::ResetStorageSOPClasses() | 863 void DicomUserConnection::ResetStorageSOPClasses() |
852 { | 864 { |
981 { | 993 { |
982 if (remoteHost_ != host) | 994 if (remoteHost_ != host) |
983 { | 995 { |
984 if (host.size() > HOST_NAME_MAX - 10) | 996 if (host.size() > HOST_NAME_MAX - 10) |
985 { | 997 { |
986 throw OrthancException(ErrorCode_ParameterOutOfRange); | 998 throw OrthancException(ErrorCode_ParameterOutOfRange, |
999 "Invalid host name (too long): " + host); | |
987 } | 1000 } |
988 | 1001 |
989 Close(); | 1002 Close(); |
990 remoteHost_ = host; | 1003 remoteHost_ = host; |
991 } | 1004 } |
1011 LOG(INFO) << "Opening a DICOM SCU connection from AET \"" << GetLocalApplicationEntityTitle() | 1024 LOG(INFO) << "Opening a DICOM SCU connection from AET \"" << GetLocalApplicationEntityTitle() |
1012 << "\" to AET \"" << GetRemoteApplicationEntityTitle() << "\" on host " | 1025 << "\" to AET \"" << GetRemoteApplicationEntityTitle() << "\" on host " |
1013 << GetRemoteHost() << ":" << GetRemotePort() | 1026 << GetRemoteHost() << ":" << GetRemotePort() |
1014 << " (manufacturer: " << EnumerationToString(GetRemoteManufacturer()) << ")"; | 1027 << " (manufacturer: " << EnumerationToString(GetRemoteManufacturer()) << ")"; |
1015 | 1028 |
1016 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ pimpl_->acseTimeout_, &pimpl_->net_), remoteAet_); | 1029 Check(ASC_initializeNetwork(NET_REQUESTOR, 0, /*opt_acse_timeout*/ pimpl_->acseTimeout_, &pimpl_->net_), remoteAet_, "connecting"); |
1017 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU), remoteAet_); | 1030 Check(ASC_createAssociationParameters(&pimpl_->params_, /*opt_maxReceivePDULength*/ ASC_DEFAULTMAXPDU), remoteAet_, "connecting"); |
1018 | 1031 |
1019 // Set this application's title and the called application's title in the params | 1032 // Set this application's title and the called application's title in the params |
1020 Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), remoteAet_.c_str(), NULL), remoteAet_); | 1033 Check(ASC_setAPTitles(pimpl_->params_, localAet_.c_str(), remoteAet_.c_str(), NULL), |
1034 remoteAet_, "connecting"); | |
1021 | 1035 |
1022 // Set the network addresses of the local and remote entities | 1036 // Set the network addresses of the local and remote entities |
1023 char localHost[HOST_NAME_MAX]; | 1037 char localHost[HOST_NAME_MAX]; |
1024 gethostname(localHost, HOST_NAME_MAX - 1); | 1038 gethostname(localHost, HOST_NAME_MAX - 1); |
1025 | 1039 |
1030 #else | 1044 #else |
1031 snprintf | 1045 snprintf |
1032 #endif | 1046 #endif |
1033 (remoteHostAndPort, HOST_NAME_MAX - 1, "%s:%d", remoteHost_.c_str(), remotePort_); | 1047 (remoteHostAndPort, HOST_NAME_MAX - 1, "%s:%d", remoteHost_.c_str(), remotePort_); |
1034 | 1048 |
1035 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, remoteHostAndPort), remoteAet_); | 1049 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, remoteHostAndPort), |
1050 remoteAet_, "connecting"); | |
1036 | 1051 |
1037 // Set various options | 1052 // Set various options |
1038 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false), remoteAet_); | 1053 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false), |
1054 remoteAet_, "connecting"); | |
1039 | 1055 |
1040 SetupPresentationContexts(preferredTransferSyntax_); | 1056 SetupPresentationContexts(preferredTransferSyntax_); |
1041 | 1057 |
1042 // Do the association | 1058 // Do the association |
1043 Check(ASC_requestAssociation(pimpl_->net_, pimpl_->params_, &pimpl_->assoc_), remoteAet_); | 1059 Check(ASC_requestAssociation(pimpl_->net_, pimpl_->params_, &pimpl_->assoc_), |
1060 remoteAet_, "connecting"); | |
1044 | 1061 |
1045 if (ASC_countAcceptedPresentationContexts(pimpl_->params_) == 0) | 1062 if (ASC_countAcceptedPresentationContexts(pimpl_->params_) == 0) |
1046 { | 1063 { |
1047 throw OrthancException(ErrorCode_NoPresentationContext); | 1064 throw OrthancException(ErrorCode_NoPresentationContext, |
1065 "Unable to negotiate a presentation context with AET " + | |
1066 remoteAet_); | |
1048 } | 1067 } |
1049 } | 1068 } |
1050 | 1069 |
1051 void DicomUserConnection::Close() | 1070 void DicomUserConnection::Close() |
1052 { | 1071 { |
1116 CheckIsOpen(); | 1135 CheckIsOpen(); |
1117 DIC_US status; | 1136 DIC_US status; |
1118 Check(DIMSE_echoUser(pimpl_->assoc_, pimpl_->assoc_->nextMsgID++, | 1137 Check(DIMSE_echoUser(pimpl_->assoc_, pimpl_->assoc_->nextMsgID++, |
1119 /*opt_blockMode*/ DIMSE_BLOCKING, | 1138 /*opt_blockMode*/ DIMSE_BLOCKING, |
1120 /*opt_dimse_timeout*/ pimpl_->dimseTimeout_, | 1139 /*opt_dimse_timeout*/ pimpl_->dimseTimeout_, |
1121 &status, NULL), remoteAet_); | 1140 &status, NULL), remoteAet_, "C-ECHO"); |
1122 return status == STATUS_Success; | 1141 return status == STATUS_Success; |
1123 } | 1142 } |
1124 | 1143 |
1125 | 1144 |
1126 static void TestAndCopyTag(DicomMap& result, | 1145 static void TestAndCopyTag(DicomMap& result, |