Mercurial > hg > orthanc
comparison OrthancServer/ServerIndex.cpp @ 3090:31244604f617 db-changes
starting optimization of SeriesIndex::GetSeriesStatus()
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sat, 05 Jan 2019 12:17:30 +0100 |
parents | df1b17be20f6 |
children | 476cba12c2b0 |
comparison
equal
deleted
inserted
replaced
3089:fb8ee0786b1e | 3090:31244604f617 |
---|---|
497 | 497 |
498 LOG(INFO) << "Stopping the database flushing thread"; | 498 LOG(INFO) << "Stopping the database flushing thread"; |
499 } | 499 } |
500 | 500 |
501 | 501 |
502 static void ComputeExpectedNumberOfInstances(ResourcesContent& target, | 502 static bool ComputeExpectedNumberOfInstances(int64_t& target, |
503 int64_t series, | |
504 const DicomMap& dicomSummary) | 503 const DicomMap& dicomSummary) |
505 { | 504 { |
506 try | 505 try |
507 { | 506 { |
508 const DicomValue* value; | 507 const DicomValue* value; |
509 const DicomValue* value2; | 508 const DicomValue* value2; |
510 | 509 |
511 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL && | 510 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL && |
512 (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS)) != NULL) | 511 !value->IsNull() && |
512 !value->IsBinary() && | |
513 (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS)) != NULL && | |
514 !value2->IsNull() && | |
515 !value2->IsBinary()) | |
513 { | 516 { |
514 // Patch for series with temporal positions thanks to Will Ryder | 517 // Patch for series with temporal positions thanks to Will Ryder |
515 int64_t imagesInAcquisition = boost::lexical_cast<int64_t>(value->GetContent()); | 518 int64_t imagesInAcquisition = boost::lexical_cast<int64_t>(value->GetContent()); |
516 int64_t countTemporalPositions = boost::lexical_cast<int64_t>(value2->GetContent()); | 519 int64_t countTemporalPositions = boost::lexical_cast<int64_t>(value2->GetContent()); |
517 std::string expected = boost::lexical_cast<std::string>(imagesInAcquisition * countTemporalPositions); | 520 target = imagesInAcquisition * countTemporalPositions; |
518 target.AddMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, expected); | 521 return (target > 0); |
519 } | 522 } |
520 | 523 |
521 else if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL && | 524 else if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_SLICES)) != NULL && |
522 (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TIME_SLICES)) != NULL) | 525 !value->IsNull() && |
526 !value->IsBinary() && | |
527 (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TIME_SLICES)) != NULL && | |
528 !value2->IsBinary() && | |
529 !value2->IsNull()) | |
523 { | 530 { |
524 // Support of Cardio-PET images | 531 // Support of Cardio-PET images |
525 int64_t numberOfSlices = boost::lexical_cast<int64_t>(value->GetContent()); | 532 int64_t numberOfSlices = boost::lexical_cast<int64_t>(value->GetContent()); |
526 int64_t numberOfTimeSlices = boost::lexical_cast<int64_t>(value2->GetContent()); | 533 int64_t numberOfTimeSlices = boost::lexical_cast<int64_t>(value2->GetContent()); |
527 std::string expected = boost::lexical_cast<std::string>(numberOfSlices * numberOfTimeSlices); | 534 target = numberOfSlices * numberOfTimeSlices; |
528 target.AddMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, expected); | 535 return (target > 0); |
529 } | 536 } |
530 | 537 |
531 else if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL) | 538 else if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_CARDIAC_NUMBER_OF_IMAGES)) != NULL && |
532 { | 539 !value->IsNull() && |
533 target.AddMetadata(series, MetadataType_Series_ExpectedNumberOfInstances, value->GetContent()); | 540 !value->IsBinary()) |
541 { | |
542 target = boost::lexical_cast<int64_t>(value->GetContent()); | |
543 return (target > 0); | |
534 } | 544 } |
535 } | 545 } |
536 catch (OrthancException&) | 546 catch (OrthancException&) |
537 { | 547 { |
538 } | 548 } |
539 catch (boost::bad_lexical_cast&) | 549 catch (boost::bad_lexical_cast&) |
540 { | 550 { |
541 } | 551 } |
552 | |
553 return false; | |
542 } | 554 } |
543 | 555 |
544 | 556 |
545 | 557 |
546 | 558 |
702 boost::mutex::scoped_lock lock(mutex_); | 714 boost::mutex::scoped_lock lock(mutex_); |
703 | 715 |
704 const DicomMap& dicomSummary = instanceToStore.GetSummary(); | 716 const DicomMap& dicomSummary = instanceToStore.GetSummary(); |
705 const ServerIndex::MetadataMap& metadata = instanceToStore.GetMetadata(); | 717 const ServerIndex::MetadataMap& metadata = instanceToStore.GetMetadata(); |
706 | 718 |
719 int64_t expectedInstances; | |
720 const bool hasExpectedInstances = | |
721 ComputeExpectedNumberOfInstances(expectedInstances, dicomSummary); | |
722 | |
707 instanceMetadata.clear(); | 723 instanceMetadata.clear(); |
708 | 724 |
709 const std::string hashPatient = instanceToStore.GetHasher().HashPatient(); | 725 const std::string hashPatient = instanceToStore.GetHasher().HashPatient(); |
710 const std::string hashStudy = instanceToStore.GetHasher().HashStudy(); | 726 const std::string hashStudy = instanceToStore.GetHasher().HashStudy(); |
711 const std::string hashSeries = instanceToStore.GetHasher().HashSeries(); | 727 const std::string hashSeries = instanceToStore.GetHasher().HashSeries(); |
744 return StoreStatus_AlreadyStored; | 760 return StoreStatus_AlreadyStored; |
745 } | 761 } |
746 } | 762 } |
747 | 763 |
748 | 764 |
749 // Warn about the creation of new resources. The order must be from instance to patient. | 765 // Warn about the creation of new resources. The order must be |
766 // from instance to patient. | |
767 | |
768 // NB: In theory, could be sped up by grouping the underlying | |
769 // calls to "db_.LogChange()". However, this would only have an | |
770 // impact when new patient/study/series get created, which | |
771 // occurs far less often that creating new instances. The | |
772 // positive impact looks marginal in practice. | |
750 SignalNewResource(ChangeType_NewInstance, ResourceType_Instance, hashInstance, instanceId); | 773 SignalNewResource(ChangeType_NewInstance, ResourceType_Instance, hashInstance, instanceId); |
751 | 774 |
752 if (status.isNewSeries_) | 775 if (status.isNewSeries_) |
753 { | 776 { |
754 SignalNewResource(ChangeType_NewSeries, ResourceType_Series, hashSeries, status.seriesId_); | 777 SignalNewResource(ChangeType_NewSeries, ResourceType_Series, hashSeries, status.seriesId_); |
841 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); | 864 std::string now = SystemToolbox::GetNowIsoString(true /* use UTC time (not local time) */); |
842 content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now); | 865 content.AddMetadata(status.seriesId_, MetadataType_LastUpdate, now); |
843 content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now); | 866 content.AddMetadata(status.studyId_, MetadataType_LastUpdate, now); |
844 content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now); | 867 content.AddMetadata(status.patientId_, MetadataType_LastUpdate, now); |
845 | 868 |
846 | 869 if (status.isNewSeries_ && |
870 hasExpectedInstances) | |
871 { | |
872 content.AddMetadata(status.seriesId_, MetadataType_Series_ExpectedNumberOfInstances, | |
873 boost::lexical_cast<std::string>(expectedInstances)); | |
874 } | |
875 | |
876 | |
847 // Attach the auto-computed metadata for the instance level, | 877 // Attach the auto-computed metadata for the instance level, |
848 // reflecting these additions into the input metadata map | 878 // reflecting these additions into the input metadata map |
849 SetInstanceMetadata(content, instanceMetadata, instanceId, | 879 SetInstanceMetadata(content, instanceMetadata, instanceId, |
850 MetadataType_Instance_ReceptionDate, now); | 880 MetadataType_Instance_ReceptionDate, now); |
851 SetInstanceMetadata(content, instanceMetadata, instanceId, MetadataType_Instance_RemoteAet, | 881 SetInstanceMetadata(content, instanceMetadata, instanceId, MetadataType_Instance_RemoteAet, |
907 MetadataType_Instance_IndexInSeries, value->GetContent()); | 937 MetadataType_Instance_IndexInSeries, value->GetContent()); |
908 } | 938 } |
909 } | 939 } |
910 | 940 |
911 | 941 |
912 // Check whether the series of this new instance is now completed | |
913 if (status.isNewSeries_) | |
914 { | |
915 ComputeExpectedNumberOfInstances(content, status.seriesId_, dicomSummary); | |
916 } | |
917 | |
918 | |
919 db_.SetResourcesContent(content); | 942 db_.SetResourcesContent(content); |
920 } | 943 } |
921 | 944 |
922 | 945 |
946 // Check whether the series of this new instance is now completed | |
923 // TODO - SPEED THIS UP | 947 // TODO - SPEED THIS UP |
924 SeriesStatus seriesStatus = GetSeriesStatus(status.seriesId_); | 948 SeriesStatus seriesStatus = GetSeriesStatus(status.seriesId_); |
925 if (seriesStatus == SeriesStatus_Complete) | 949 if (seriesStatus == SeriesStatus_Complete) |
926 { | 950 { |
927 LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries); | 951 LogChange(status.seriesId_, ChangeType_CompletedSeries, ResourceType_Series, hashSeries); |
963 target["CountSeries"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Series)); | 987 target["CountSeries"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Series)); |
964 target["CountInstances"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Instance)); | 988 target["CountInstances"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Instance)); |
965 } | 989 } |
966 | 990 |
967 | 991 |
992 | |
993 SeriesStatus ServerIndex::GetSeriesStatus(int64_t id, | |
994 int64_t expectedNumberOfInstances) | |
995 { | |
996 // Loop over the instances of this series | |
997 std::list<int64_t> children; | |
998 db_.GetChildrenInternalId(children, id); | |
999 | |
1000 std::set<int64_t> instances; | |
1001 for (std::list<int64_t>::const_iterator | |
1002 it = children.begin(); it != children.end(); ++it) | |
1003 { | |
1004 // Get the index of this instance in the series | |
1005 int64_t index; | |
1006 if (!GetMetadataAsInteger(index, *it, MetadataType_Instance_IndexInSeries)) | |
1007 { | |
1008 return SeriesStatus_Unknown; | |
1009 } | |
1010 | |
1011 if (!(index > 0 && index <= expectedNumberOfInstances)) | |
1012 { | |
1013 // Out-of-range instance index | |
1014 return SeriesStatus_Inconsistent; | |
1015 } | |
1016 | |
1017 if (instances.find(index) != instances.end()) | |
1018 { | |
1019 // Twice the same instance index | |
1020 return SeriesStatus_Inconsistent; | |
1021 } | |
1022 | |
1023 instances.insert(index); | |
1024 } | |
1025 | |
1026 if (static_cast<int64_t>(instances.size()) == expectedNumberOfInstances) | |
1027 { | |
1028 return SeriesStatus_Complete; | |
1029 } | |
1030 else | |
1031 { | |
1032 return SeriesStatus_Missing; | |
1033 } | |
1034 } | |
1035 | |
968 | 1036 |
969 SeriesStatus ServerIndex::GetSeriesStatus(int64_t id) | 1037 SeriesStatus ServerIndex::GetSeriesStatus(int64_t id) |
970 { | 1038 { |
971 // Get the expected number of instances in this series (from the metadata) | 1039 // Get the expected number of instances in this series (from the metadata) |
972 int64_t expected; | 1040 int64_t expected; |
973 if (!GetMetadataAsInteger(expected, id, MetadataType_Series_ExpectedNumberOfInstances)) | 1041 if (!GetMetadataAsInteger(expected, id, MetadataType_Series_ExpectedNumberOfInstances)) |
974 { | 1042 { |
975 return SeriesStatus_Unknown; | 1043 return SeriesStatus_Unknown; |
976 } | 1044 } |
977 | |
978 // Loop over the instances of this series | |
979 std::list<int64_t> children; | |
980 db_.GetChildrenInternalId(children, id); | |
981 | |
982 std::set<int64_t> instances; | |
983 for (std::list<int64_t>::const_iterator | |
984 it = children.begin(); it != children.end(); ++it) | |
985 { | |
986 // Get the index of this instance in the series | |
987 int64_t index; | |
988 if (!GetMetadataAsInteger(index, *it, MetadataType_Instance_IndexInSeries)) | |
989 { | |
990 return SeriesStatus_Unknown; | |
991 } | |
992 | |
993 if (!(index > 0 && index <= expected)) | |
994 { | |
995 // Out-of-range instance index | |
996 return SeriesStatus_Inconsistent; | |
997 } | |
998 | |
999 if (instances.find(index) != instances.end()) | |
1000 { | |
1001 // Twice the same instance index | |
1002 return SeriesStatus_Inconsistent; | |
1003 } | |
1004 | |
1005 instances.insert(index); | |
1006 } | |
1007 | |
1008 if (static_cast<int64_t>(instances.size()) == expected) | |
1009 { | |
1010 return SeriesStatus_Complete; | |
1011 } | |
1012 else | 1045 else |
1013 { | 1046 { |
1014 return SeriesStatus_Missing; | 1047 return GetSeriesStatus(id, expected); |
1015 } | 1048 } |
1016 } | 1049 } |
1017 | 1050 |
1018 | 1051 |
1019 void ServerIndex::MainDicomTagsToJson(Json::Value& target, | 1052 void ServerIndex::MainDicomTagsToJson(Json::Value& target, |
1040 target["MainDicomTags"] = Json::objectValue; | 1073 target["MainDicomTags"] = Json::objectValue; |
1041 FromDcmtkBridge::ToJson(target["MainDicomTags"], tags, true); | 1074 FromDcmtkBridge::ToJson(target["MainDicomTags"], tags, true); |
1042 } | 1075 } |
1043 } | 1076 } |
1044 | 1077 |
1078 | |
1045 bool ServerIndex::LookupResource(Json::Value& result, | 1079 bool ServerIndex::LookupResource(Json::Value& result, |
1046 const std::string& publicId, | 1080 const std::string& publicId, |
1047 ResourceType expectedType) | 1081 ResourceType expectedType) |
1048 { | 1082 { |
1049 result = Json::objectValue; | 1083 result = Json::objectValue; |
1138 result["Type"] = "Series"; | 1172 result["Type"] = "Series"; |
1139 result["Status"] = EnumerationToString(GetSeriesStatus(id)); | 1173 result["Status"] = EnumerationToString(GetSeriesStatus(id)); |
1140 | 1174 |
1141 int64_t i; | 1175 int64_t i; |
1142 if (GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) | 1176 if (GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) |
1177 { | |
1143 result["ExpectedNumberOfInstances"] = static_cast<int>(i); | 1178 result["ExpectedNumberOfInstances"] = static_cast<int>(i); |
1179 } | |
1144 else | 1180 else |
1181 { | |
1145 result["ExpectedNumberOfInstances"] = Json::nullValue; | 1182 result["ExpectedNumberOfInstances"] = Json::nullValue; |
1183 } | |
1146 | 1184 |
1147 break; | 1185 break; |
1148 } | 1186 } |
1149 | 1187 |
1150 case ResourceType_Instance: | 1188 case ResourceType_Instance: |