comparison OrthancFramework/Sources/DicomNetworking/DicomStoreUserConnection.cpp @ 5442:ac68a4383e51 debug-telemis

improved C-Store negotiation and logging
author Alain Mazy <am@osimis.io>
date Thu, 23 Nov 2023 16:59:16 +0100
parents 99fa307438e1
children 48b8dae6dc77
comparison
equal deleted inserted replaced
5440:99fa307438e1 5442:ac68a4383e51
251 bool DicomStoreUserConnection::NegotiatePresentationContext( 251 bool DicomStoreUserConnection::NegotiatePresentationContext(
252 uint8_t& presentationContextId, 252 uint8_t& presentationContextId,
253 const std::string& sopClassUid, 253 const std::string& sopClassUid,
254 DicomTransferSyntax transferSyntax, 254 DicomTransferSyntax transferSyntax,
255 bool hasPreferred, 255 bool hasPreferred,
256 DicomTransferSyntax preferred, 256 DicomTransferSyntax preferred)
257 bool alwaysRenegotiate,
258 bool enableLogs)
259 { 257 {
260 /** 258 /**
261 * Step 1: Check whether this presentation context is already 259 * Step 1: Check whether this presentation context is already
262 * available in the previously negotiated assocation. 260 * available in the previously negotiated assocation.
263 **/ 261 **/
264 262
265 if (LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax)) 263 if (LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax))
266 { 264 {
265 CLOG(INFO, DICOM) << "Found an accepted presentation context for SOPClassUID " << sopClassUid << " and transfer syntax " << GetTransferSyntaxUid(transferSyntax);
267 return true; 266 return true;
268 } 267 }
269 268
270 // The association must be re-negotiated 269 // The association must be re-negotiated
271 if (association_->IsOpen()) 270 if (association_->IsOpen())
272 { 271 {
273 if (enableLogs) 272 CLOG(INFO, DICOM) << "No accepted presentation context found, re-negotiating DICOM association with "
274 { 273 << parameters_.GetRemoteModality().GetApplicationEntityTitle()
275 CLOG(INFO, DICOM) << "Re-negotiating DICOM association with " 274 << " for SOPClassUID " << sopClassUid << " TransferSyntax =" << GetTransferSyntaxUid(transferSyntax);
276 << parameters_.GetRemoteModality().GetApplicationEntityTitle() 275
277 << " for SOPClassUID " << sopClassUid; 276 // Check if we know that the remote modality was
278 }
279
280 // Don't renegociate if we know that the remote modality was
281 // already proposed this individual transfer syntax (**) 277 // already proposed this individual transfer syntax (**)
282 if (!alwaysRenegotiate && 278 if (proposedOriginalClasses_.find(std::make_pair(sopClassUid, transferSyntax)) != proposedOriginalClasses_.end())
283 proposedOriginalClasses_.find(std::make_pair(sopClassUid, transferSyntax)) != proposedOriginalClasses_.end()) 279 {
284 { 280 CLOG(INFO, DICOM) << "The remote modality has already rejected SOP class UID \""
285 if (enableLogs) 281 << sopClassUid << "\" with transfer syntax \""
286 { 282 << GetTransferSyntaxUid(transferSyntax) << "\", but we will renegotiate anyway";
287 CLOG(INFO, DICOM) << "The remote modality has already rejected SOP class UID \"" 283 // always renegotiating since 1.12.2 // return false;
288 << sopClassUid << "\" with transfer syntax \"" 284 }
289 << GetTransferSyntaxUid(transferSyntax) << "\", don't renegotiate"; 285 }
290 } 286 else
291 287 {
292 return false; 288 CLOG(INFO, DICOM) << "Negotiating DICOM association with "
293 } 289 << parameters_.GetRemoteModality().GetApplicationEntityTitle()
290 << " for SOPClassUID " << sopClassUid << " TransferSyntax =" << GetTransferSyntaxUid(transferSyntax);
294 } 291 }
295 292
296 association_->ClearPresentationContexts(); 293 association_->ClearPresentationContexts();
297 proposedOriginalClasses_.clear(); 294 proposedOriginalClasses_.clear();
298 RegisterStorageClass(sopClassUid, transferSyntax); // (*) 295 RegisterStorageClass(sopClassUid, transferSyntax); // (*)
377 void DicomStoreUserConnection::Store(std::string& sopClassUid, 374 void DicomStoreUserConnection::Store(std::string& sopClassUid,
378 std::string& sopInstanceUid, 375 std::string& sopInstanceUid,
379 DcmFileFormat& dicom, 376 DcmFileFormat& dicom,
380 bool hasMoveOriginator, 377 bool hasMoveOriginator,
381 const std::string& moveOriginatorAET, 378 const std::string& moveOriginatorAET,
382 uint16_t moveOriginatorID, 379 uint16_t moveOriginatorID)
383 bool alwaysRenegotiate)
384 { 380 {
385 DicomTransferSyntax transferSyntax; 381 DicomTransferSyntax transferSyntax;
386 LookupParameters(sopClassUid, sopInstanceUid, transferSyntax, dicom); 382 LookupParameters(sopClassUid, sopInstanceUid, transferSyntax, dicom);
387 383
384 LOG(INFO) << "Performing C-Store on instance of SOPClassUID '" << sopClassUid << "'";
385
388 uint8_t presID; 386 uint8_t presID;
389 if (!NegotiatePresentationContext(presID, sopClassUid, transferSyntax, proposeUncompressedSyntaxes_, 387 if (!NegotiatePresentationContext(presID, sopClassUid, transferSyntax, proposeUncompressedSyntaxes_,
390 DicomTransferSyntax_LittleEndianExplicit, alwaysRenegotiate, true)) 388 DicomTransferSyntax_LittleEndianExplicit))
391 { 389 {
392 throw OrthancException(ErrorCode_NetworkProtocol, 390 throw OrthancException(ErrorCode_NetworkProtocol,
393 "No valid presentation context was negotiated for " 391 "No valid presentation context was negotiated for "
394 "SOP class UID [" + sopClassUid + "] and transfer " 392 "SOP class UID [" + sopClassUid + "] and transfer "
395 "syntax [" + GetTransferSyntaxUid(transferSyntax) + "] " 393 "syntax [" + GetTransferSyntaxUid(transferSyntax) + "] "
467 std::string& sopInstanceUid, 465 std::string& sopInstanceUid,
468 const void* buffer, 466 const void* buffer,
469 size_t size, 467 size_t size,
470 bool hasMoveOriginator, 468 bool hasMoveOriginator,
471 const std::string& moveOriginatorAET, 469 const std::string& moveOriginatorAET,
472 uint16_t moveOriginatorID, 470 uint16_t moveOriginatorID)
473 bool alwaysRenegotiate)
474 { 471 {
475 std::unique_ptr<DcmFileFormat> dicom( 472 std::unique_ptr<DcmFileFormat> dicom(
476 FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size)); 473 FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size));
477 474
478 if (dicom.get() == NULL) 475 if (dicom.get() == NULL)
479 { 476 {
480 throw OrthancException(ErrorCode_InternalError); 477 throw OrthancException(ErrorCode_InternalError);
481 } 478 }
482 479
483 Store(sopClassUid, sopInstanceUid, *dicom, hasMoveOriginator, moveOriginatorAET, moveOriginatorID, alwaysRenegotiate); 480 Store(sopClassUid, sopInstanceUid, *dicom, hasMoveOriginator, moveOriginatorAET, moveOriginatorID);
484 } 481 }
485 482
486 483
487 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1 484 #if ORTHANC_ENABLE_DCMTK_TRANSCODING == 1
488 void DicomStoreUserConnection::LookupTranscoding(std::set<DicomTransferSyntax>& acceptedSyntaxes, 485 void DicomStoreUserConnection::LookupTranscoding(std::set<DicomTransferSyntax>& acceptedSyntaxes,
489 const std::string& sopClassUid, 486 const std::string& sopClassUid,
490 DicomTransferSyntax sourceSyntax, 487 DicomTransferSyntax sourceSyntax,
491 bool hasPreferred, 488 bool hasPreferred,
492 DicomTransferSyntax preferred, 489 DicomTransferSyntax preferred)
493 bool alwaysRenegotiate)
494 { 490 {
495 acceptedSyntaxes.clear(); 491 acceptedSyntaxes.clear();
492 std::map<DicomTransferSyntax, uint8_t> contexts;
496 493
497 // Make sure a negotiation has already occurred for this transfer 494 // Make sure a negotiation has already occurred for this transfer
498 // syntax. We don't use the return code: Transcoding is possible 495 // syntax if we have not negotiated yet.
499 // even if the "sourceSyntax" is not supported. 496 // We don't use the return code: Transcoding is possible even if the "sourceSyntax" is not supported.
500 uint8_t presID; 497 if (!association_->IsOpen() || !association_->LookupAcceptedPresentationContext(contexts, sopClassUid))
501 NegotiatePresentationContext(presID, sopClassUid, sourceSyntax, hasPreferred, preferred, alwaysRenegotiate, false); 498 {
502 499 uint8_t presID;
503 std::map<DicomTransferSyntax, uint8_t> contexts; 500 NegotiatePresentationContext(presID, sopClassUid, sourceSyntax, hasPreferred, preferred);
501 }
502
504 if (association_->LookupAcceptedPresentationContext(contexts, sopClassUid)) 503 if (association_->LookupAcceptedPresentationContext(contexts, sopClassUid))
505 { 504 {
506 for (std::map<DicomTransferSyntax, uint8_t>::const_iterator 505 for (std::map<DicomTransferSyntax, uint8_t>::const_iterator
507 it = contexts.begin(); it != contexts.end(); ++it) 506 it = contexts.begin(); it != contexts.end(); ++it)
508 { 507 {
520 const void* buffer, 519 const void* buffer,
521 size_t size, 520 size_t size,
522 DicomTransferSyntax preferredTransferSyntax, 521 DicomTransferSyntax preferredTransferSyntax,
523 bool hasMoveOriginator, 522 bool hasMoveOriginator,
524 const std::string& moveOriginatorAET, 523 const std::string& moveOriginatorAET,
525 uint16_t moveOriginatorID, 524 uint16_t moveOriginatorID)
526 bool alwaysRenegotiate)
527 { 525 {
528 std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size)); 526 std::unique_ptr<DcmFileFormat> dicom(FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size));
529 if (dicom.get() == NULL || 527 if (dicom.get() == NULL ||
530 dicom->getDataset() == NULL) 528 dicom->getDataset() == NULL)
531 { 529 {
534 532
535 DicomTransferSyntax sourceSyntax; 533 DicomTransferSyntax sourceSyntax;
536 LookupParameters(sopClassUid, sopInstanceUid, sourceSyntax, *dicom); 534 LookupParameters(sopClassUid, sopInstanceUid, sourceSyntax, *dicom);
537 535
538 std::set<DicomTransferSyntax> accepted; 536 std::set<DicomTransferSyntax> accepted;
539 LookupTranscoding(accepted, sopClassUid, sourceSyntax, true, preferredTransferSyntax, alwaysRenegotiate); 537 LookupTranscoding(accepted, sopClassUid, sourceSyntax, true, preferredTransferSyntax);
540 538
541 if (accepted.size() == 0) 539 if (accepted.size() == 0)
542 { 540 {
543 throw OrthancException(ErrorCode_NoPresentationContext, "Cannot store instance of SOPClassUID " + 541 throw OrthancException(ErrorCode_NoPresentationContext, "Cannot C-Store an instance of SOPClassUID " +
544 sopClassUid + ", the destination has not accepted any TransferSyntax for this SOPClassUID."); 542 sopClassUid + ", the destination has not accepted any TransferSyntax for this SOPClassUID.");
545 } 543 }
546 544
547 if (accepted.find(sourceSyntax) != accepted.end()) 545 if (accepted.find(sourceSyntax) != accepted.end())
548 { 546 {
549 // No need for transcoding 547 // No need for transcoding
550 Store(sopClassUid, sopInstanceUid, *dicom, 548 Store(sopClassUid, sopInstanceUid, *dicom,
551 hasMoveOriginator, moveOriginatorAET, moveOriginatorID, alwaysRenegotiate); 549 hasMoveOriginator, moveOriginatorAET, moveOriginatorID);
552 } 550 }
553 else 551 else
554 { 552 {
555 // Transcoding is needed 553 // Transcoding is needed
556 IDicomTranscoder::DicomImage source; 554 IDicomTranscoder::DicomImage source;
561 559
562 IDicomTranscoder::DicomImage transcoded; 560 IDicomTranscoder::DicomImage transcoded;
563 bool success = false; 561 bool success = false;
564 bool isDestructiveCompressionAllowed = false; 562 bool isDestructiveCompressionAllowed = false;
565 std::set<DicomTransferSyntax> attemptedSyntaxes; 563 std::set<DicomTransferSyntax> attemptedSyntaxes;
564
565 LOG(INFO) << "Transcoding is required to C-Store an instance of SOPClassUID '" << sopClassUid << "', preferredTransferSyntax is " << GetTransferSyntaxUid(preferredTransferSyntax);
566 566
567 if (accepted.find(preferredTransferSyntax) != accepted.end()) 567 if (accepted.find(preferredTransferSyntax) != accepted.end())
568 { 568 {
569 // New in Orthanc 1.9.0: The preferred transfer syntax is 569 // New in Orthanc 1.9.0: The preferred transfer syntax is
570 // accepted by the remote modality => transcode to this syntax 570 // accepted by the remote modality => transcode to this syntax
635 throw OrthancException(ErrorCode_InternalError); 635 throw OrthancException(ErrorCode_InternalError);
636 } 636 }
637 else 637 else
638 { 638 {
639 Store(sopClassUid, sopInstanceUid, transcoded.GetParsed(), 639 Store(sopClassUid, sopInstanceUid, transcoded.GetParsed(),
640 hasMoveOriginator, moveOriginatorAET, moveOriginatorID, alwaysRenegotiate); 640 hasMoveOriginator, moveOriginatorAET, moveOriginatorID);
641 } 641 }
642 } 642 }
643 else 643 else
644 { 644 {
645 std::string s; 645 std::string s;
665 IDicomTranscoder& transcoder, 665 IDicomTranscoder& transcoder,
666 const void* buffer, 666 const void* buffer,
667 size_t size, 667 size_t size,
668 bool hasMoveOriginator, 668 bool hasMoveOriginator,
669 const std::string& moveOriginatorAET, 669 const std::string& moveOriginatorAET,
670 uint16_t moveOriginatorID, 670 uint16_t moveOriginatorID)
671 bool alwaysRenegotiate)
672 { 671 {
673 Transcode(sopClassUid, sopInstanceUid, transcoder, buffer, size, DicomTransferSyntax_LittleEndianExplicit, 672 Transcode(sopClassUid, sopInstanceUid, transcoder, buffer, size, DicomTransferSyntax_LittleEndianExplicit,
674 hasMoveOriginator, moveOriginatorAET, moveOriginatorID, alwaysRenegotiate); 673 hasMoveOriginator, moveOriginatorAET, moveOriginatorID);
675 } 674 }
676 #endif 675 #endif
677 } 676 }