comparison OrthancServer/Sources/ServerContext.cpp @ 4796:94616af363ec filter-store-instance

added ReceivedCStoreInstanceFilter lua callback + OrthancPluginRegisterIncomingCStoreInstanceFilter in sdk
author Alain Mazy <am@osimis.io>
date Fri, 01 Oct 2021 18:36:45 +0200
parents 51ec061516c9
children 0bd98c52474b
comparison
equal deleted inserted replaced
4795:22d5b611dea7 4796:94616af363ec
55 #include "ServerJobs/OrthancJobUnserializer.h" 55 #include "ServerJobs/OrthancJobUnserializer.h"
56 #include "ServerToolbox.h" 56 #include "ServerToolbox.h"
57 #include "StorageCommitmentReports.h" 57 #include "StorageCommitmentReports.h"
58 58
59 #include <dcmtk/dcmdata/dcfilefo.h> 59 #include <dcmtk/dcmdata/dcfilefo.h>
60 #include <dcmtk/dcmnet/dimse.h>
60 61
61 62
62 static size_t DICOM_CACHE_SIZE = 128 * 1024 * 1024; // 128 MB 63 static size_t DICOM_CACHE_SIZE = 128 * 1024 * 1024; // 128 MB
63 64
64 65
96 transferSyntax != DicomTransferSyntax_HEVCMain10ProfileLevel5_1 && 97 transferSyntax != DicomTransferSyntax_HEVCMain10ProfileLevel5_1 &&
97 98
98 // Do not try to transcode special transfer syntaxes 99 // Do not try to transcode special transfer syntaxes
99 transferSyntax != DicomTransferSyntax_RFC2557MimeEncapsulation && 100 transferSyntax != DicomTransferSyntax_RFC2557MimeEncapsulation &&
100 transferSyntax != DicomTransferSyntax_XML); 101 transferSyntax != DicomTransferSyntax_XML);
102 }
103
104
105 ServerContext::StoreResult::StoreResult() :
106 status_(StoreStatus_Failure),
107 cstoreStatusCode_(0)
108 {
101 } 109 }
102 110
103 111
104 void ServerContext::ChangeThread(ServerContext* that, 112 void ServerContext::ChangeThread(ServerContext* that,
105 unsigned int sleepDelay) 113 unsigned int sleepDelay)
487 StorageAccessor accessor(area_, GetMetricsRegistry()); 495 StorageAccessor accessor(area_, GetMetricsRegistry());
488 accessor.Remove(fileUuid, type); 496 accessor.Remove(fileUuid, type);
489 } 497 }
490 498
491 499
492 StoreStatus ServerContext::StoreAfterTranscoding(std::string& resultPublicId, 500 ServerContext::StoreResult ServerContext::StoreAfterTranscoding(std::string& resultPublicId,
493 DicomInstanceToStore& dicom, 501 DicomInstanceToStore& dicom,
494 StoreInstanceMode mode) 502 StoreInstanceMode mode)
495 { 503 {
496 bool overwrite; 504 bool overwrite;
497 switch (mode) 505 switch (mode)
498 { 506 {
499 case StoreInstanceMode_Default: 507 case StoreInstanceMode_Default:
536 544
537 Json::Value simplifiedTags; 545 Json::Value simplifiedTags;
538 Toolbox::SimplifyDicomAsJson(simplifiedTags, dicomAsJson, DicomToJsonFormat_Human); 546 Toolbox::SimplifyDicomAsJson(simplifiedTags, dicomAsJson, DicomToJsonFormat_Human);
539 547
540 // Test if the instance must be filtered out 548 // Test if the instance must be filtered out
541 bool accepted = true; 549 StoreResult result;
542 550
543 { 551 {
544 boost::shared_lock<boost::shared_mutex> lock(listenersMutex_); 552 boost::shared_lock<boost::shared_mutex> lock(listenersMutex_);
545 553
546 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) 554 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it)
547 { 555 {
548 try 556 try
549 { 557 {
550 if (!it->GetListener().FilterIncomingInstance(dicom, simplifiedTags)) 558 if (!it->GetListener().FilterIncomingInstance(dicom, simplifiedTags))
551 { 559 {
552 accepted = false; 560 result.SetStatus(StoreStatus_FilteredOut);
561 result.SetCStoreStatusCode(STATUS_Success); // to keep backward compatibility, we still return 'success'
553 break; 562 break;
554 } 563 }
564
565 if (dicom.GetOrigin().GetRequestOrigin() == Orthanc::RequestOrigin_DicomProtocol)
566 {
567 uint16_t filterResult = it->GetListener().FilterIncomingCStoreInstance(dicom, simplifiedTags);
568 if (it->GetListener().FilterIncomingCStoreInstance(dicom, simplifiedTags) != 0x0000)
569 {
570 result.SetStatus(StoreStatus_FilteredOut);
571 result.SetCStoreStatusCode(filterResult);
572 break;
573 }
574 }
575
555 } 576 }
556 catch (OrthancException& e) 577 catch (OrthancException& e)
557 { 578 {
558 LOG(ERROR) << "Error in the " << it->GetDescription() 579 LOG(ERROR) << "Error in the " << it->GetDescription()
559 << " callback while receiving an instance: " << e.What() 580 << " callback while receiving an instance: " << e.What()
561 throw; 582 throw;
562 } 583 }
563 } 584 }
564 } 585 }
565 586
566 if (!accepted) 587 if (result.GetStatus() == StoreStatus_FilteredOut)
567 { 588 {
568 LOG(INFO) << "An incoming instance has been discarded by the filter"; 589 LOG(INFO) << "An incoming instance has been discarded by the filter";
569 return StoreStatus_FilteredOut; 590 return result;
570 } 591 }
571 592
572 // Remove the file from the DicomCache (useful if 593 // Remove the file from the DicomCache (useful if
573 // "OverwriteInstances" is set to "true") 594 // "OverwriteInstances" is set to "true")
574 dicomCache_.Invalidate(resultPublicId); 595 dicomCache_.Invalidate(resultPublicId);
593 attachments.push_back(dicomUntilPixelData); 614 attachments.push_back(dicomUntilPixelData);
594 } 615 }
595 616
596 typedef std::map<MetadataType, std::string> InstanceMetadata; 617 typedef std::map<MetadataType, std::string> InstanceMetadata;
597 InstanceMetadata instanceMetadata; 618 InstanceMetadata instanceMetadata;
598 StoreStatus status = index_.Store( 619 result.SetStatus(index_.Store(
599 instanceMetadata, summary, attachments, dicom.GetMetadata(), dicom.GetOrigin(), overwrite, 620 instanceMetadata, summary, attachments, dicom.GetMetadata(), dicom.GetOrigin(), overwrite,
600 hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset); 621 hasTransferSyntax, transferSyntax, hasPixelDataOffset, pixelDataOffset));
601 622
602 // Only keep the metadata for the "instance" level 623 // Only keep the metadata for the "instance" level
603 dicom.ClearMetadata(); 624 dicom.ClearMetadata();
604 625
605 for (InstanceMetadata::const_iterator it = instanceMetadata.begin(); 626 for (InstanceMetadata::const_iterator it = instanceMetadata.begin();
606 it != instanceMetadata.end(); ++it) 627 it != instanceMetadata.end(); ++it)
607 { 628 {
608 dicom.AddMetadata(ResourceType_Instance, it->first, it->second); 629 dicom.AddMetadata(ResourceType_Instance, it->first, it->second);
609 } 630 }
610 631
611 if (status != StoreStatus_Success) 632 if (result.GetStatus() != StoreStatus_Success)
612 { 633 {
613 accessor.Remove(dicomInfo); 634 accessor.Remove(dicomInfo);
614 635
615 if (dicomUntilPixelData.IsValid()) 636 if (dicomUntilPixelData.IsValid())
616 { 637 {
617 accessor.Remove(dicomUntilPixelData); 638 accessor.Remove(dicomUntilPixelData);
618 } 639 }
619 } 640 }
620 641
621 switch (status) 642 switch (result.GetStatus())
622 { 643 {
623 case StoreStatus_Success: 644 case StoreStatus_Success:
624 LOG(INFO) << "New instance stored"; 645 LOG(INFO) << "New instance stored";
625 break; 646 break;
626 647
635 default: 656 default:
636 // This should never happen 657 // This should never happen
637 break; 658 break;
638 } 659 }
639 660
640 if (status == StoreStatus_Success || 661 if (result.GetStatus() == StoreStatus_Success ||
641 status == StoreStatus_AlreadyStored) 662 result.GetStatus() == StoreStatus_AlreadyStored)
642 { 663 {
643 boost::shared_lock<boost::shared_mutex> lock(listenersMutex_); 664 boost::shared_lock<boost::shared_mutex> lock(listenersMutex_);
644 665
645 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it) 666 for (ServerListeners::iterator it = listeners_.begin(); it != listeners_.end(); ++it)
646 { 667 {
655 << " (code " << e.GetErrorCode() << ")"; 676 << " (code " << e.GetErrorCode() << ")";
656 } 677 }
657 } 678 }
658 } 679 }
659 680
660 return status; 681 return result;
661 } 682 }
662 catch (OrthancException& e) 683 catch (OrthancException& e)
663 { 684 {
664 if (e.GetErrorCode() == ErrorCode_InexistentTag) 685 if (e.GetErrorCode() == ErrorCode_InexistentTag)
665 { 686 {
669 throw; 690 throw;
670 } 691 }
671 } 692 }
672 693
673 694
674 StoreStatus ServerContext::Store(std::string& resultPublicId, 695 ServerContext::StoreResult ServerContext::Store(std::string& resultPublicId,
675 DicomInstanceToStore& dicom, 696 DicomInstanceToStore& dicom,
676 StoreInstanceMode mode) 697 StoreInstanceMode mode)
677 { 698 {
678 if (!isIngestTranscoding_) 699 if (!isIngestTranscoding_)
679 { 700 {
680 // No automated transcoding. This was the only path in Orthanc <= 1.6.1. 701 // No automated transcoding. This was the only path in Orthanc <= 1.6.1.
681 return StoreAfterTranscoding(resultPublicId, dicom, mode); 702 return StoreAfterTranscoding(resultPublicId, dicom, mode);
731 std::unique_ptr<ParsedDicomFile> tmp(transcoded.ReleaseAsParsedDicomFile()); 752 std::unique_ptr<ParsedDicomFile> tmp(transcoded.ReleaseAsParsedDicomFile());
732 753
733 std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*tmp)); 754 std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromParsedDicomFile(*tmp));
734 toStore->SetOrigin(dicom.GetOrigin()); 755 toStore->SetOrigin(dicom.GetOrigin());
735 756
736 StoreStatus ok = StoreAfterTranscoding(resultPublicId, *toStore, mode); 757 StoreResult result = StoreAfterTranscoding(resultPublicId, *toStore, mode);
737 assert(resultPublicId == tmp->GetHasher().HashInstance()); 758 assert(resultPublicId == tmp->GetHasher().HashInstance());
738 759
739 return ok; 760 return result;
740 } 761 }
741 else 762 else
742 { 763 {
743 // Cannot transcode => store the original file 764 // Cannot transcode => store the original file
744 return StoreAfterTranscoding(resultPublicId, dicom, mode); 765 return StoreAfterTranscoding(resultPublicId, dicom, mode);