Mercurial > hg > orthanc
comparison OrthancServer/OrthancFindRequestHandler.cpp @ 1785:c131566b8252 dcmtk-3.6.1
integration mainline->dcmtk-3.6.1
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 18 Nov 2015 10:16:21 +0100 |
parents | e268412adcf1 |
children | 4f01c9d73f02 |
comparison
equal
deleted
inserted
replaced
1639:1b82bb0446d2 | 1785:c131566b8252 |
---|---|
31 | 31 |
32 | 32 |
33 #include "PrecompiledHeadersServer.h" | 33 #include "PrecompiledHeadersServer.h" |
34 #include "OrthancFindRequestHandler.h" | 34 #include "OrthancFindRequestHandler.h" |
35 | 35 |
36 #include "../Core/DicomFormat/DicomArray.h" | |
36 #include "../Core/Logging.h" | 37 #include "../Core/Logging.h" |
37 #include "../Core/DicomFormat/DicomArray.h" | 38 #include "FromDcmtkBridge.h" |
39 #include "OrthancInitialization.h" | |
40 #include "Search/LookupResource.h" | |
38 #include "ServerToolbox.h" | 41 #include "ServerToolbox.h" |
39 #include "OrthancInitialization.h" | |
40 #include "FromDcmtkBridge.h" | |
41 | |
42 #include "ResourceFinder.h" | |
43 #include "DicomFindQuery.h" | |
44 | 42 |
45 #include <boost/regex.hpp> | 43 #include <boost/regex.hpp> |
46 | 44 |
47 | 45 |
48 namespace Orthanc | 46 namespace Orthanc |
88 answers.Add(result); | 86 answers.Add(result); |
89 } | 87 } |
90 } | 88 } |
91 | 89 |
92 | 90 |
93 namespace | |
94 { | |
95 class CFindQuery : public DicomFindQuery | |
96 { | |
97 private: | |
98 DicomFindAnswers& answers_; | |
99 ServerIndex& index_; | |
100 const DicomArray& query_; | |
101 bool hasModalitiesInStudy_; | |
102 std::set<std::string> modalitiesInStudy_; | |
103 | |
104 public: | |
105 CFindQuery(DicomFindAnswers& answers, | |
106 ServerIndex& index, | |
107 const DicomArray& query) : | |
108 answers_(answers), | |
109 index_(index), | |
110 query_(query), | |
111 hasModalitiesInStudy_(false) | |
112 { | |
113 } | |
114 | |
115 void SetModalitiesInStudy(const std::string& value) | |
116 { | |
117 hasModalitiesInStudy_ = true; | |
118 | |
119 std::vector<std::string> tmp; | |
120 Toolbox::TokenizeString(tmp, value, '\\'); | |
121 | |
122 for (size_t i = 0; i < tmp.size(); i++) | |
123 { | |
124 modalitiesInStudy_.insert(tmp[i]); | |
125 } | |
126 } | |
127 | |
128 virtual bool HasMainDicomTagsFilter(ResourceType level) const | |
129 { | |
130 if (DicomFindQuery::HasMainDicomTagsFilter(level)) | |
131 { | |
132 return true; | |
133 } | |
134 | |
135 return (level == ResourceType_Study && | |
136 hasModalitiesInStudy_); | |
137 } | |
138 | |
139 virtual bool FilterMainDicomTags(const std::string& resourceId, | |
140 ResourceType level, | |
141 const DicomMap& mainTags) const | |
142 { | |
143 if (!DicomFindQuery::FilterMainDicomTags(resourceId, level, mainTags)) | |
144 { | |
145 return false; | |
146 } | |
147 | |
148 if (level != ResourceType_Study || | |
149 !hasModalitiesInStudy_) | |
150 { | |
151 return true; | |
152 } | |
153 | |
154 try | |
155 { | |
156 // We are considering a single study, and the | |
157 // "MODALITIES_IN_STUDY" tag is set in the C-Find. Check | |
158 // whether one of its child series matches one of the | |
159 // modalities. | |
160 | |
161 Json::Value study; | |
162 if (index_.LookupResource(study, resourceId, ResourceType_Study)) | |
163 { | |
164 // Loop over the series of the considered study. | |
165 for (Json::Value::ArrayIndex j = 0; j < study["Series"].size(); j++) | |
166 { | |
167 Json::Value series; | |
168 if (index_.LookupResource(series, study["Series"][j].asString(), ResourceType_Series)) | |
169 { | |
170 // Get the modality of this series | |
171 if (series["MainDicomTags"].isMember("Modality")) | |
172 { | |
173 std::string modality = series["MainDicomTags"]["Modality"].asString(); | |
174 if (modalitiesInStudy_.find(modality) != modalitiesInStudy_.end()) | |
175 { | |
176 // This series of the considered study matches one | |
177 // of the required modalities. Take the study into | |
178 // consideration for future filtering. | |
179 return true; | |
180 } | |
181 } | |
182 } | |
183 } | |
184 } | |
185 } | |
186 catch (OrthancException&) | |
187 { | |
188 // This resource has probably been deleted during the find request | |
189 } | |
190 | |
191 return false; | |
192 } | |
193 | |
194 virtual bool HasInstanceFilter() const | |
195 { | |
196 return true; | |
197 } | |
198 | |
199 virtual bool FilterInstance(const std::string& instanceId, | |
200 const Json::Value& content) const | |
201 { | |
202 bool ok = DicomFindQuery::FilterInstance(instanceId, content); | |
203 | |
204 if (ok) | |
205 { | |
206 // Add this resource to the answers | |
207 AddAnswer(answers_, content, query_); | |
208 } | |
209 | |
210 return ok; | |
211 } | |
212 }; | |
213 } | |
214 | |
215 | |
216 | |
217 bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers, | 91 bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers, |
218 const DicomMap& input, | 92 const DicomMap& input, |
219 const std::string& remoteIp, | 93 const std::string& remoteIp, |
220 const std::string& remoteAet) | 94 const std::string& remoteAet) |
221 { | 95 { |
238 /** | 112 /** |
239 * Retrieve the query level. | 113 * Retrieve the query level. |
240 **/ | 114 **/ |
241 | 115 |
242 const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL); | 116 const DicomValue* levelTmp = input.TestAndGetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL); |
243 if (levelTmp == NULL) | 117 if (levelTmp == NULL || |
118 levelTmp->IsNull() || | |
119 levelTmp->IsBinary()) | |
244 { | 120 { |
245 throw OrthancException(ErrorCode_BadRequest); | 121 throw OrthancException(ErrorCode_BadRequest); |
246 } | 122 } |
247 | 123 |
248 ResourceType level = StringToResourceType(levelTmp->AsString().c_str()); | 124 ResourceType level = StringToResourceType(levelTmp->GetContent().c_str()); |
249 | 125 |
250 if (level != ResourceType_Patient && | 126 if (level != ResourceType_Patient && |
251 level != ResourceType_Study && | 127 level != ResourceType_Study && |
252 level != ResourceType_Series && | 128 level != ResourceType_Series && |
253 level != ResourceType_Instance) | 129 level != ResourceType_Instance) |
263 { | 139 { |
264 if (!query.GetElement(i).GetValue().IsNull()) | 140 if (!query.GetElement(i).GetValue().IsNull()) |
265 { | 141 { |
266 LOG(INFO) << " " << query.GetElement(i).GetTag() | 142 LOG(INFO) << " " << query.GetElement(i).GetTag() |
267 << " " << FromDcmtkBridge::GetName(query.GetElement(i).GetTag()) | 143 << " " << FromDcmtkBridge::GetName(query.GetElement(i).GetTag()) |
268 << " = " << query.GetElement(i).GetValue().AsString(); | 144 << " = " << query.GetElement(i).GetValue().GetContent(); |
269 } | 145 } |
270 } | 146 } |
271 | 147 |
272 | 148 |
273 /** | 149 /** |
274 * Build up the query object. | 150 * Build up the query object. |
275 **/ | 151 **/ |
276 | 152 |
277 CFindQuery findQuery(answers, context_.GetIndex(), query); | 153 LookupResource finder(level); |
278 findQuery.SetLevel(level); | 154 |
279 | |
280 for (size_t i = 0; i < query.GetSize(); i++) | 155 for (size_t i = 0; i < query.GetSize(); i++) |
281 { | 156 { |
282 const DicomTag tag = query.GetElement(i).GetTag(); | 157 const DicomTag tag = query.GetElement(i).GetTag(); |
283 | 158 |
284 if (query.GetElement(i).GetValue().IsNull() || | 159 if (query.GetElement(i).GetValue().IsNull() || |
286 tag == DICOM_TAG_SPECIFIC_CHARACTER_SET) | 161 tag == DICOM_TAG_SPECIFIC_CHARACTER_SET) |
287 { | 162 { |
288 continue; | 163 continue; |
289 } | 164 } |
290 | 165 |
291 std::string value = query.GetElement(i).GetValue().AsString(); | 166 std::string value = query.GetElement(i).GetValue().GetContent(); |
292 if (value.size() == 0) | 167 if (value.size() == 0) |
293 { | 168 { |
294 // An empty string corresponds to a "*" wildcard constraint, so we ignore it | 169 // An empty string corresponds to a "*" wildcard constraint, so we ignore it |
295 continue; | 170 continue; |
296 } | 171 } |
297 | 172 |
298 if (tag == DICOM_TAG_MODALITIES_IN_STUDY) | 173 ValueRepresentation vr = FromDcmtkBridge::GetValueRepresentation(tag); |
299 { | 174 |
300 findQuery.SetModalitiesInStudy(value); | 175 // DICOM specifies that searches must be case sensitive, except |
301 } | 176 // for tags with a PN value representation |
302 else | 177 bool sensitive = true; |
303 { | 178 if (vr == ValueRepresentation_PatientName) |
304 findQuery.SetConstraint(tag, value, caseSensitivePN); | 179 { |
305 } | 180 sensitive = caseSensitivePN; |
181 } | |
182 | |
183 finder.AddDicomConstraint(tag, value, sensitive); | |
306 } | 184 } |
307 | 185 |
308 | 186 |
309 /** | 187 /** |
310 * Run the query. | 188 * Run the query. |
311 **/ | 189 **/ |
312 | 190 |
313 ResourceFinder finder(context_); | 191 size_t maxResults = (level == ResourceType_Instance) ? maxInstances_ : maxResults_; |
314 | 192 |
315 switch (level) | 193 std::vector<std::string> resources, instances; |
316 { | 194 context_.GetIndex().FindCandidates(resources, instances, finder); |
317 case ResourceType_Patient: | 195 |
318 case ResourceType_Study: | 196 assert(resources.size() == instances.size()); |
319 case ResourceType_Series: | 197 bool finished = true; |
320 finder.SetMaxResults(maxResults_); | 198 |
321 break; | 199 for (size_t i = 0; i < instances.size(); i++) |
322 | 200 { |
323 case ResourceType_Instance: | 201 Json::Value dicom; |
324 finder.SetMaxResults(maxInstances_); | 202 context_.ReadJson(dicom, instances[i]); |
325 break; | 203 |
326 | 204 if (finder.IsMatch(dicom)) |
327 default: | 205 { |
328 throw OrthancException(ErrorCode_InternalError); | 206 if (maxResults != 0 && |
329 } | 207 answers.GetSize() >= maxResults) |
330 | 208 { |
331 std::list<std::string> tmp; | 209 finished = false; |
332 bool finished = finder.Apply(tmp, findQuery); | 210 break; |
333 | 211 } |
334 LOG(INFO) << "Number of matching resources: " << tmp.size(); | 212 else |
213 { | |
214 AddAnswer(answers, dicom, query); | |
215 } | |
216 } | |
217 } | |
218 | |
219 LOG(INFO) << "Number of matching resources: " << answers.GetSize(); | |
335 | 220 |
336 return finished; | 221 return finished; |
337 } | 222 } |
338 } | 223 } |