Mercurial > hg > orthanc
changeset 714:6a1dbba0cca7
new implementation of C-Find handler
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 14 Feb 2014 11:26:12 +0100 |
parents | 9d1973813d8b |
children | 78239878727a |
files | OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/ServerIndex.cpp OrthancServer/ServerIndex.h |
diffstat | 3 files changed, 213 insertions(+), 75 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancServer/OrthancFindRequestHandler.cpp Fri Feb 14 09:21:23 2014 +0100 +++ b/OrthancServer/OrthancFindRequestHandler.cpp Fri Feb 14 11:26:12 2014 +0100 @@ -286,90 +286,152 @@ } - static bool LookupCandidateResourcesInternal(/* out */ std::list<std::string>& resources, - /* in */ ServerIndex& index, - /* in */ ResourceType level, - /* in */ const DicomMap& query, - /* in */ DicomTag tag) + namespace { - if (query.HasTag(tag)) + class CandidateResources { - const DicomValue& value = query.GetValue(tag); - if (!value.IsNull()) + private: + ServerIndex& index_; + ModalityManufacturer manufacturer_; + ResourceType level_; + bool isFilterApplied_; + std::set<std::string> filtered_; + + static void ListToSet(std::set<std::string>& target, + const std::list<std::string>& source) { - std::string str = query.GetValue(tag).AsString(); - if (!IsWildcard(str)) + for (std::list<std::string>::const_iterator + it = source.begin(); it != source.end(); it++) { - index.LookupTagValue(resources, tag, str/*, level*/); - return true; + target.insert(*it); } } - } - - return false; - } - - static void LookupCandidateResources(/* out */ std::list<std::string>& resources, - /* in */ ServerIndex& index, - /* in */ ResourceType level, - /* in */ const DicomMap& query, - /* in */ ModalityManufacturer manufacturer) - { - // TODO : Speed up using full querying against the MainDicomTags. + void ApplyExactFilter(const DicomTag& tag, const std::string& value) + { + LOG(INFO) << "Applying exact filter on tag " + << FromDcmtkBridge::GetName(tag) << " (value: " << value << ")"; - resources.clear(); - - bool done = false; + std::list<std::string> resources; + index_.LookupTagValue(resources, tag, value, level_); - switch (level) - { - case ResourceType_Patient: - done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_PATIENT_ID); - break; + if (isFilterApplied_) + { + std::set<std::string> s; + ListToSet(s, resources); - case ResourceType_Study: - done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_STUDY_INSTANCE_UID); - break; + std::set<std::string> tmp = filtered_; + filtered_.clear(); - case ResourceType_Series: - done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_SERIES_INSTANCE_UID); - break; - - case ResourceType_Instance: - if (manufacturer == ModalityManufacturer_MedInria) - { - std::list<std::string> series; - - if (LookupCandidateResourcesInternal(series, index, ResourceType_Series, query, DICOM_TAG_SERIES_INSTANCE_UID) && - series.size() == 1) + for (std::set<std::string>::const_iterator + it = tmp.begin(); it != tmp.end(); it++) { - index.GetChildInstances(resources, series.front()); - done = true; - } + if (s.find(*it) != s.end()) + { + filtered_.insert(*it); + } + } } else { - done = LookupCandidateResourcesInternal(resources, index, level, query, DICOM_TAG_SOP_INSTANCE_UID); + assert(filtered_.empty()); + isFilterApplied_ = true; + ListToSet(filtered_, resources); + } + } + + public: + CandidateResources(ServerIndex& index, + ModalityManufacturer manufacturer) : + index_(index), + manufacturer_(manufacturer), + level_(ResourceType_Patient), + isFilterApplied_(false) + { + } + + ResourceType GetLevel() const + { + return level_; + } + + void GoDown() + { + assert(level_ != ResourceType_Instance); + + if (isFilterApplied_) + { + std::set<std::string> tmp = filtered_; + + filtered_.clear(); + + for (std::set<std::string>::const_iterator + it = tmp.begin(); it != tmp.end(); it++) + { + std::list<std::string> children; + index_.GetChildren(children, *it); + ListToSet(filtered_, children); + } } - break; + switch (level_) + { + case ResourceType_Patient: + level_ = ResourceType_Study; + break; + + case ResourceType_Study: + level_ = ResourceType_Series; + break; - default: - break; - } + case ResourceType_Series: + level_ = ResourceType_Instance; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + } + + void Flatten(std::list<std::string>& resources) const + { + resources.clear(); - if (!done) - { - Json::Value allResources; - index.GetAllUuids(allResources, level); - assert(allResources.type() == Json::arrayValue); + if (isFilterApplied_) + { + for (std::set<std::string>::const_iterator + it = filtered_.begin(); it != filtered_.end(); it++) + { + resources.push_back(*it); + } + } + else + { + Json::Value tmp; + index_.GetAllUuids(tmp, level_); + for (Json::Value::ArrayIndex i = 0; i < tmp.size(); i++) + { + resources.push_back(tmp[i].asString()); + } + } + } - for (Json::Value::ArrayIndex i = 0; i < allResources.size(); i++) + void ApplyFilter(const DicomTag& tag, const DicomMap& query) { - resources.push_back(allResources[i].asString()); + if (query.HasTag(tag)) + { + const DicomValue& value = query.GetValue(tag); + if (!value.IsNull()) + { + std::string value = query.GetValue(tag).AsString(); + if (!IsWildcard(value)) + { + ApplyExactFilter(tag, value); + } + } + } } - } + }; } @@ -406,19 +468,26 @@ ResourceType level = StringToResourceType(levelTmp->AsString().c_str()); - switch (manufacturer) + if (level != ResourceType_Patient && + level != ResourceType_Study && + level != ResourceType_Series && + level != ResourceType_Instance) { - case ModalityManufacturer_MedInria: - // MedInria makes FIND requests at the instance level before starting MOVE - break; + throw OrthancException(ErrorCode_NotImplemented); + } + - default: - if (level != ResourceType_Patient && - level != ResourceType_Study && - level != ResourceType_Series) - { - throw OrthancException(ErrorCode_NotImplemented); - } + DicomArray query(input); + LOG(INFO) << "DICOM C-Find request at level: " << EnumerationToString(level); + + for (size_t i = 0; i < query.GetSize(); i++) + { + if (!query.GetElement(i).GetValue().IsNull()) + { + LOG(INFO) << " " << query.GetElement(i).GetTag() + << " " << FromDcmtkBridge::GetName(query.GetElement(i).GetTag()) + << " = " << query.GetElement(i).GetValue().AsString(); + } } @@ -429,9 +498,45 @@ * for each of them. **/ + CandidateResources candidates(context_.GetIndex(), manufacturer); + + for (;;) + { + switch (candidates.GetLevel()) + { + case ResourceType_Patient: + candidates.ApplyFilter(DICOM_TAG_PATIENT_ID, input); + break; + + case ResourceType_Study: + candidates.ApplyFilter(DICOM_TAG_STUDY_INSTANCE_UID, input); + candidates.ApplyFilter(DICOM_TAG_ACCESSION_NUMBER, input); + break; + + case ResourceType_Series: + candidates.ApplyFilter(DICOM_TAG_SERIES_INSTANCE_UID, input); + break; + + case ResourceType_Instance: + candidates.ApplyFilter(DICOM_TAG_SOP_INSTANCE_UID, input); + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } + + if (candidates.GetLevel() == level) + { + break; + } + + candidates.GoDown(); + } + std::list<std::string> resources; - LookupCandidateResources(resources, context_.GetIndex(), level, input, manufacturer); + candidates.Flatten(resources); + LOG(INFO) << "Number of candidate resources after exact filtering: " << resources.size(); /** * Apply filtering on modalities for studies, if asked (this is an @@ -454,7 +559,6 @@ * Loop over all the resources for this query level. **/ - DicomArray query(input); for (std::list<std::string>::const_iterator resource = resources.begin(); resource != resources.end(); ++resource) {
--- a/OrthancServer/ServerIndex.cpp Fri Feb 14 09:21:23 2014 +0100 +++ b/OrthancServer/ServerIndex.cpp Fri Feb 14 11:26:12 2014 +0100 @@ -1107,6 +1107,37 @@ } + void ServerIndex::GetChildren(std::list<std::string>& result, + const std::string& publicId) + { + result.clear(); + + boost::mutex::scoped_lock lock(mutex_); + + ResourceType type; + int64_t resource; + if (!db_->LookupResource(publicId, resource, type)) + { + throw OrthancException(ErrorCode_UnknownResource); + } + + if (type == ResourceType_Instance) + { + // An instance cannot have a child + throw OrthancException(ErrorCode_BadParameterType); + } + + std::list<int64_t> tmp; + db_->GetChildrenInternalId(tmp, resource); + + for (std::list<int64_t>::const_iterator + it = tmp.begin(); it != tmp.end(); ++it) + { + result.push_back(db_->GetPublicId(*it)); + } + } + + void ServerIndex::GetChildInstances(std::list<std::string>& result, const std::string& publicId) {
--- a/OrthancServer/ServerIndex.h Fri Feb 14 09:21:23 2014 +0100 +++ b/OrthancServer/ServerIndex.h Fri Feb 14 11:26:12 2014 +0100 @@ -163,6 +163,9 @@ void SetProtectedPatient(const std::string& publicId, bool isProtected); + void GetChildren(std::list<std::string>& result, + const std::string& publicId); + void GetChildInstances(std::list<std::string>& result, const std::string& publicId);