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,