Mercurial > hg > orthanc
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 } |