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: