Mercurial > hg > orthanc
comparison OrthancServer/ServerIndex.cpp @ 269:f6fdf5abe751
recycling up and running
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 07 Dec 2012 14:46:44 +0100 |
parents | 4bc02e2254ec |
children | e6a4c4329481 |
comparison
equal
deleted
inserted
replaced
268:4bc02e2254ec | 269:f6fdf5abe751 |
---|---|
193 } | 193 } |
194 } | 194 } |
195 | 195 |
196 | 196 |
197 ServerIndex::ServerIndex(ServerContext& context, | 197 ServerIndex::ServerIndex(ServerContext& context, |
198 const std::string& dbPath) : mutex_() | 198 const std::string& dbPath) : |
199 maximumStorageSize_(0), | |
200 maximumPatients_(0) | |
199 { | 201 { |
200 listener_.reset(new Internals::ServerIndexListener(context)); | 202 listener_.reset(new Internals::ServerIndexListener(context)); |
201 | 203 |
202 if (dbPath == ":memory:") | 204 if (dbPath == ":memory:") |
203 { | 205 { |
215 { | 217 { |
216 } | 218 } |
217 | 219 |
218 db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_)); | 220 db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_)); |
219 } | 221 } |
222 | |
223 // Initial recycling if the parameters have changed since the last | |
224 // execution of Orthanc | |
225 StandaloneRecycling(); | |
220 | 226 |
221 unsigned int sleep; | 227 unsigned int sleep; |
222 try | 228 try |
223 { | 229 { |
224 std::string sleepString = db_->GetGlobalProperty(GlobalProperty_FlushSleep); | 230 std::string sleepString = db_->GetGlobalProperty(GlobalProperty_FlushSleep); |
507 | 513 |
508 std::string parent = db_->GetPublicId(parentId); | 514 std::string parent = db_->GetPublicId(parentId); |
509 | 515 |
510 switch (type) | 516 switch (type) |
511 { | 517 { |
512 case ResourceType_Study: | 518 case ResourceType_Study: |
513 result["ParentPatient"] = parent; | 519 result["ParentPatient"] = parent; |
514 break; | 520 break; |
515 | 521 |
516 case ResourceType_Series: | 522 case ResourceType_Series: |
517 result["ParentStudy"] = parent; | 523 result["ParentStudy"] = parent; |
518 break; | 524 break; |
519 | 525 |
520 case ResourceType_Instance: | 526 case ResourceType_Instance: |
521 result["ParentSeries"] = parent; | 527 result["ParentSeries"] = parent; |
522 break; | 528 break; |
523 | 529 |
524 default: | 530 default: |
525 throw OrthancException(ErrorCode_InternalError); | 531 throw OrthancException(ErrorCode_InternalError); |
526 } | 532 } |
527 } | 533 } |
528 | 534 |
529 // List the children resources | 535 // List the children resources |
530 std::list<std::string> children; | 536 std::list<std::string> children; |
540 c.append(*it); | 546 c.append(*it); |
541 } | 547 } |
542 | 548 |
543 switch (type) | 549 switch (type) |
544 { | 550 { |
551 case ResourceType_Patient: | |
552 result["Studies"] = c; | |
553 break; | |
554 | |
555 case ResourceType_Study: | |
556 result["Series"] = c; | |
557 break; | |
558 | |
559 case ResourceType_Series: | |
560 result["Instances"] = c; | |
561 break; | |
562 | |
563 default: | |
564 throw OrthancException(ErrorCode_InternalError); | |
565 } | |
566 } | |
567 | |
568 // Set the resource type | |
569 switch (type) | |
570 { | |
545 case ResourceType_Patient: | 571 case ResourceType_Patient: |
546 result["Studies"] = c; | 572 result["Type"] = "Patient"; |
547 break; | 573 break; |
548 | 574 |
549 case ResourceType_Study: | 575 case ResourceType_Study: |
550 result["Series"] = c; | 576 result["Type"] = "Study"; |
551 break; | 577 break; |
552 | 578 |
553 case ResourceType_Series: | 579 case ResourceType_Series: |
554 result["Instances"] = c; | 580 { |
581 result["Type"] = "Series"; | |
582 result["Status"] = ToString(GetSeriesStatus(id)); | |
583 | |
584 int i; | |
585 if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) | |
586 result["ExpectedNumberOfInstances"] = i; | |
587 else | |
588 result["ExpectedNumberOfInstances"] = Json::nullValue; | |
589 | |
555 break; | 590 break; |
591 } | |
592 | |
593 case ResourceType_Instance: | |
594 { | |
595 result["Type"] = "Instance"; | |
596 | |
597 FileInfo attachment; | |
598 if (!db_->LookupAttachment(attachment, id, FileContentType_Dicom)) | |
599 { | |
600 throw OrthancException(ErrorCode_InternalError); | |
601 } | |
602 | |
603 result["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize()); | |
604 result["FileUuid"] = attachment.GetUuid(); | |
605 | |
606 int i; | |
607 if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) | |
608 result["IndexInSeries"] = i; | |
609 else | |
610 result["IndexInSeries"] = Json::nullValue; | |
611 | |
612 break; | |
613 } | |
556 | 614 |
557 default: | 615 default: |
558 throw OrthancException(ErrorCode_InternalError); | 616 throw OrthancException(ErrorCode_InternalError); |
559 } | |
560 } | |
561 | |
562 // Set the resource type | |
563 switch (type) | |
564 { | |
565 case ResourceType_Patient: | |
566 result["Type"] = "Patient"; | |
567 break; | |
568 | |
569 case ResourceType_Study: | |
570 result["Type"] = "Study"; | |
571 break; | |
572 | |
573 case ResourceType_Series: | |
574 { | |
575 result["Type"] = "Series"; | |
576 result["Status"] = ToString(GetSeriesStatus(id)); | |
577 | |
578 int i; | |
579 if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) | |
580 result["ExpectedNumberOfInstances"] = i; | |
581 else | |
582 result["ExpectedNumberOfInstances"] = Json::nullValue; | |
583 | |
584 break; | |
585 } | |
586 | |
587 case ResourceType_Instance: | |
588 { | |
589 result["Type"] = "Instance"; | |
590 | |
591 FileInfo attachment; | |
592 if (!db_->LookupAttachment(attachment, id, FileContentType_Dicom)) | |
593 { | |
594 throw OrthancException(ErrorCode_InternalError); | |
595 } | |
596 | |
597 result["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize()); | |
598 result["FileUuid"] = attachment.GetUuid(); | |
599 | |
600 int i; | |
601 if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) | |
602 result["IndexInSeries"] = i; | |
603 else | |
604 result["IndexInSeries"] = Json::nullValue; | |
605 | |
606 break; | |
607 } | |
608 | |
609 default: | |
610 throw OrthancException(ErrorCode_InternalError); | |
611 } | 617 } |
612 | 618 |
613 // Record the remaining information | 619 // Record the remaining information |
614 result["ID"] = publicId; | 620 result["ID"] = publicId; |
615 MainDicomTagsToJson(result, id); | 621 MainDicomTagsToJson(result, id); |
696 DicomMap map; | 702 DicomMap map; |
697 db_->GetMainDicomTags(map, currentId); | 703 db_->GetMainDicomTags(map, currentId); |
698 | 704 |
699 switch (currentType) | 705 switch (currentType) |
700 { | 706 { |
701 case ResourceType_Patient: | 707 case ResourceType_Patient: |
702 patientId = map.GetValue(DICOM_TAG_PATIENT_ID).AsString(); | 708 patientId = map.GetValue(DICOM_TAG_PATIENT_ID).AsString(); |
703 done = true; | 709 done = true; |
704 break; | 710 break; |
705 | 711 |
706 case ResourceType_Study: | 712 case ResourceType_Study: |
707 studyInstanceUid = map.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString(); | 713 studyInstanceUid = map.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString(); |
708 currentType = ResourceType_Patient; | 714 currentType = ResourceType_Patient; |
709 break; | 715 break; |
710 | 716 |
711 case ResourceType_Series: | 717 case ResourceType_Series: |
712 seriesInstanceUid = map.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString(); | 718 seriesInstanceUid = map.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString(); |
713 currentType = ResourceType_Study; | 719 currentType = ResourceType_Study; |
714 break; | 720 break; |
715 | 721 |
716 case ResourceType_Instance: | 722 case ResourceType_Instance: |
717 sopInstanceUid = map.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString(); | 723 sopInstanceUid = map.GetValue(DICOM_TAG_SOP_INSTANCE_UID).AsString(); |
718 currentType = ResourceType_Series; | 724 currentType = ResourceType_Series; |
719 break; | 725 break; |
720 | 726 |
721 default: | 727 default: |
722 throw OrthancException(ErrorCode_InternalError); | 728 throw OrthancException(ErrorCode_InternalError); |
723 } | 729 } |
724 | 730 |
725 // If we have not reached the Patient level, find the parent of | 731 // If we have not reached the Patient level, find the parent of |
726 // the current resource | 732 // the current resource |
727 if (!done) | 733 if (!done) |
758 } | 764 } |
759 | 765 |
760 | 766 |
761 bool ServerIndex::IsRecyclingNeeded(uint64_t instanceSize) | 767 bool ServerIndex::IsRecyclingNeeded(uint64_t instanceSize) |
762 { | 768 { |
769 if (maximumStorageSize_ != 0) | |
770 { | |
771 uint64_t currentSize = db_->GetTotalCompressedSize(); | |
772 if (currentSize + instanceSize > maximumStorageSize_) | |
773 { | |
774 return true; | |
775 } | |
776 } | |
777 | |
778 if (maximumPatients_ != 0) | |
779 { | |
780 uint64_t patientCount = db_->GetResourceCount(ResourceType_Patient); | |
781 if (patientCount > maximumPatients_) | |
782 { | |
783 return true; | |
784 } | |
785 } | |
786 | |
763 return false; | 787 return false; |
764 } | 788 } |
765 | 789 |
766 | 790 |
767 void ServerIndex::Recycle(uint64_t instanceSize, | 791 void ServerIndex::Recycle(uint64_t instanceSize, |
770 if (!IsRecyclingNeeded(instanceSize)) | 794 if (!IsRecyclingNeeded(instanceSize)) |
771 { | 795 { |
772 return; | 796 return; |
773 } | 797 } |
774 | 798 |
775 | 799 // Check whether other DICOM instances from this patient are |
776 //throw OrthancException(ErrorCode_FullStorage); | 800 // already stored |
801 int64_t patientToAvoid; | |
802 ResourceType type; | |
803 bool hasPatientToAvoid = db_->LookupResource(newPatientId, patientToAvoid, type); | |
804 | |
805 if (hasPatientToAvoid && type != ResourceType_Patient) | |
806 { | |
807 throw OrthancException(ErrorCode_InternalError); | |
808 } | |
809 | |
810 // Iteratively select patient to remove until there is enough | |
811 // space in the DICOM store | |
812 int64_t patientToRecycle; | |
813 while (true) | |
814 { | |
815 // If other instances of this patient are already in the store, | |
816 // we must avoid to recycle them | |
817 bool ok = hasPatientToAvoid ? | |
818 db_->SelectPatientToRecycle(patientToRecycle, patientToAvoid) : | |
819 db_->SelectPatientToRecycle(patientToRecycle); | |
820 | |
821 if (!ok) | |
822 { | |
823 throw OrthancException(ErrorCode_FullStorage); | |
824 } | |
825 | |
826 LOG(INFO) << "Recycling one patient"; | |
827 db_->DeleteResource(patientToRecycle); | |
828 | |
829 if (!IsRecyclingNeeded(instanceSize)) | |
830 { | |
831 // OK, we're done | |
832 break; | |
833 } | |
834 } | |
777 } | 835 } |
836 | |
837 void ServerIndex::SetMaximumPatientCount(unsigned int count) | |
838 { | |
839 boost::mutex::scoped_lock lock(mutex_); | |
840 maximumPatients_ = count; | |
841 StandaloneRecycling(); | |
842 } | |
843 | |
844 void ServerIndex::SetMaximumStorageSize(uint64_t size) | |
845 { | |
846 boost::mutex::scoped_lock lock(mutex_); | |
847 maximumStorageSize_ = size; | |
848 StandaloneRecycling(); | |
849 } | |
850 | |
851 void ServerIndex::StandaloneRecycling() | |
852 { | |
853 // WARNING: No mutex here, do not include this as a public method | |
854 std::auto_ptr<SQLite::Transaction> t(db_->StartTransaction()); | |
855 t->Begin(); | |
856 Recycle(0, ""); | |
857 t->Commit(); | |
858 listener_->CommitFilesToRemove(); | |
859 } | |
778 } | 860 } |