Mercurial > hg > orthanc
comparison OrthancServer/OrthancMoveRequestHandler.cpp @ 3301:6ce10c3b1eb7
Fix issue #131 (C-MOVE failure due to duplicate StudyInstanceUID in the database)
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Sun, 24 Feb 2019 08:51:15 +0100 |
parents | 8ea7c4546c3a |
children | 94f4a18a79cc |
comparison
equal
deleted
inserted
replaced
3300:5b7f56cf4a38 | 3301:6ce10c3b1eb7 |
---|---|
62 std::auto_ptr<DicomUserConnection> connection_; | 62 std::auto_ptr<DicomUserConnection> connection_; |
63 | 63 |
64 public: | 64 public: |
65 SynchronousMove(ServerContext& context, | 65 SynchronousMove(ServerContext& context, |
66 const std::string& targetAet, | 66 const std::string& targetAet, |
67 const std::string& publicId, | 67 const std::vector<std::string>& publicIds, |
68 const std::string& originatorAet, | 68 const std::string& originatorAet, |
69 uint16_t originatorId) : | 69 uint16_t originatorId) : |
70 context_(context), | 70 context_(context), |
71 localAet_(context.GetDefaultLocalApplicationEntityTitle()), | 71 localAet_(context.GetDefaultLocalApplicationEntityTitle()), |
72 position_(0), | 72 position_(0), |
73 originatorAet_(originatorAet), | 73 originatorAet_(originatorAet), |
74 originatorId_(originatorId) | 74 originatorId_(originatorId) |
75 { | 75 { |
76 LOG(INFO) << "Sending resource " << publicId << " to modality \"" | |
77 << targetAet << "\" in synchronous mode"; | |
78 | |
79 std::list<std::string> tmp; | |
80 context_.GetIndex().GetChildInstances(tmp, publicId); | |
81 | |
82 instances_.reserve(tmp.size()); | |
83 for (std::list<std::string>::iterator it = tmp.begin(); it != tmp.end(); ++it) | |
84 { | |
85 instances_.push_back(*it); | |
86 } | |
87 | |
88 { | 76 { |
89 OrthancConfiguration::ReaderLock lock; | 77 OrthancConfiguration::ReaderLock lock; |
90 remote_ = lock.GetConfiguration().GetModalityUsingAet(targetAet); | 78 remote_ = lock.GetConfiguration().GetModalityUsingAet(targetAet); |
79 } | |
80 | |
81 for (size_t i = 0; i < publicIds.size(); i++) | |
82 { | |
83 LOG(INFO) << "Sending resource " << publicIds[i] << " to modality \"" | |
84 << targetAet << "\" in synchronous mode"; | |
85 | |
86 std::list<std::string> tmp; | |
87 context_.GetIndex().GetChildInstances(tmp, publicIds[i]); | |
88 | |
89 instances_.reserve(tmp.size()); | |
90 for (std::list<std::string>::iterator it = tmp.begin(); it != tmp.end(); ++it) | |
91 { | |
92 instances_.push_back(*it); | |
93 } | |
91 } | 94 } |
92 } | 95 } |
93 | 96 |
94 virtual unsigned int GetSubOperationCount() const | 97 virtual unsigned int GetSubOperationCount() const |
95 { | 98 { |
129 size_t countInstances_; | 132 size_t countInstances_; |
130 | 133 |
131 public: | 134 public: |
132 AsynchronousMove(ServerContext& context, | 135 AsynchronousMove(ServerContext& context, |
133 const std::string& targetAet, | 136 const std::string& targetAet, |
134 const std::string& publicId, | 137 const std::vector<std::string>& publicIds, |
135 const std::string& originatorAet, | 138 const std::string& originatorAet, |
136 uint16_t originatorId) : | 139 uint16_t originatorId) : |
137 context_(context), | 140 context_(context), |
138 job_(new DicomModalityStoreJob(context)), | 141 job_(new DicomModalityStoreJob(context)), |
139 position_(0) | 142 position_(0) |
140 { | 143 { |
141 LOG(INFO) << "Sending resource " << publicId << " to modality \"" | |
142 << targetAet << "\" in asynchronous mode"; | |
143 | |
144 job_->SetDescription("C-MOVE"); | 144 job_->SetDescription("C-MOVE"); |
145 job_->SetPermissive(true); | 145 job_->SetPermissive(true); |
146 job_->SetLocalAet(context.GetDefaultLocalApplicationEntityTitle()); | 146 job_->SetLocalAet(context.GetDefaultLocalApplicationEntityTitle()); |
147 | 147 |
148 { | 148 { |
152 | 152 |
153 if (originatorId != 0) | 153 if (originatorId != 0) |
154 { | 154 { |
155 job_->SetMoveOriginator(originatorAet, originatorId); | 155 job_->SetMoveOriginator(originatorAet, originatorId); |
156 } | 156 } |
157 | 157 |
158 std::list<std::string> tmp; | 158 for (size_t i = 0; i < publicIds.size(); i++) |
159 context_.GetIndex().GetChildInstances(tmp, publicId); | 159 { |
160 | 160 LOG(INFO) << "Sending resource " << publicIds[i] << " to modality \"" |
161 countInstances_ = tmp.size(); | 161 << targetAet << "\" in asynchronous mode"; |
162 | 162 |
163 job_->Reserve(tmp.size()); | 163 std::list<std::string> tmp; |
164 | 164 context_.GetIndex().GetChildInstances(tmp, publicIds[i]); |
165 for (std::list<std::string>::iterator it = tmp.begin(); it != tmp.end(); ++it) | 165 |
166 { | 166 countInstances_ = tmp.size(); |
167 job_->AddInstance(*it); | 167 |
168 job_->Reserve(job_->GetCommandsCount() + tmp.size()); | |
169 | |
170 for (std::list<std::string>::iterator it = tmp.begin(); it != tmp.end(); ++it) | |
171 { | |
172 job_->AddInstance(*it); | |
173 } | |
168 } | 174 } |
169 } | 175 } |
170 | 176 |
171 virtual unsigned int GetSubOperationCount() const | 177 virtual unsigned int GetSubOperationCount() const |
172 { | 178 { |
190 } | 196 } |
191 }; | 197 }; |
192 } | 198 } |
193 | 199 |
194 | 200 |
195 bool OrthancMoveRequestHandler::LookupIdentifier(std::string& publicId, | 201 bool OrthancMoveRequestHandler::LookupIdentifiers(std::vector<std::string>& publicIds, |
196 ResourceType level, | 202 ResourceType level, |
197 const DicomMap& input) | 203 const DicomMap& input) |
198 { | 204 { |
199 DicomTag tag(0, 0); // Dummy initialization | 205 DicomTag tag(0, 0); // Dummy initialization |
200 | 206 |
201 switch (level) | 207 switch (level) |
202 { | 208 { |
230 if (value.IsNull() || | 236 if (value.IsNull() || |
231 value.IsBinary()) | 237 value.IsBinary()) |
232 { | 238 { |
233 return false; | 239 return false; |
234 } | 240 } |
235 | |
236 const std::string& content = value.GetContent(); | |
237 | |
238 std::vector<std::string> ids; | |
239 context_.GetIndex().LookupIdentifierExact(ids, level, tag, content); | |
240 | |
241 if (ids.size() != 1) | |
242 { | |
243 return false; | |
244 } | |
245 else | 241 else |
246 { | 242 { |
247 publicId = ids[0]; | 243 const std::string& content = value.GetContent(); |
244 context_.GetIndex().LookupIdentifierExact(publicIds, level, tag, content); | |
248 return true; | 245 return true; |
249 } | 246 } |
250 } | 247 } |
251 | 248 |
252 | 249 |
253 static IMoveRequestIterator* CreateIterator(ServerContext& context, | 250 static IMoveRequestIterator* CreateIterator(ServerContext& context, |
254 const std::string& targetAet, | 251 const std::string& targetAet, |
255 const std::string& publicId, | 252 const std::vector<std::string>& publicIds, |
256 const std::string& originatorAet, | 253 const std::string& originatorAet, |
257 uint16_t originatorId) | 254 uint16_t originatorId) |
258 { | 255 { |
256 if (publicIds.empty()) | |
257 { | |
258 throw OrthancException(ErrorCode_BadRequest, | |
259 "C-MOVE request matching no resource stored in Orthanc"); | |
260 } | |
261 | |
259 bool synchronous; | 262 bool synchronous; |
260 | 263 |
261 { | 264 { |
262 OrthancConfiguration::ReaderLock lock; | 265 OrthancConfiguration::ReaderLock lock; |
263 synchronous = lock.GetConfiguration().GetBooleanParameter("SynchronousCMove", true); | 266 synchronous = lock.GetConfiguration().GetBooleanParameter("SynchronousCMove", true); |
264 } | 267 } |
265 | 268 |
266 if (synchronous) | 269 if (synchronous) |
267 { | 270 { |
268 return new SynchronousMove(context, targetAet, publicId, originatorAet, originatorId); | 271 return new SynchronousMove(context, targetAet, publicIds, originatorAet, originatorId); |
269 } | 272 } |
270 else | 273 else |
271 { | 274 { |
272 return new AsynchronousMove(context, targetAet, publicId, originatorAet, originatorId); | 275 return new AsynchronousMove(context, targetAet, publicIds, originatorAet, originatorId); |
273 } | 276 } |
274 } | 277 } |
275 | 278 |
276 | 279 |
277 IMoveRequestIterator* OrthancMoveRequestHandler::Handle(const std::string& targetAet, | 280 IMoveRequestIterator* OrthancMoveRequestHandler::Handle(const std::string& targetAet, |
312 // does not follow the DICOM standard. This is for instance the | 315 // does not follow the DICOM standard. This is for instance the |
313 // behavior of Tudor DICOM. Try and automatically deduce the | 316 // behavior of Tudor DICOM. Try and automatically deduce the |
314 // query level: Start from the instance level, going up to the | 317 // query level: Start from the instance level, going up to the |
315 // patient level until a valid DICOM identifier is found. | 318 // patient level until a valid DICOM identifier is found. |
316 | 319 |
317 std::string publicId; | 320 std::vector<std::string> publicIds; |
318 | 321 |
319 if (LookupIdentifier(publicId, ResourceType_Instance, input) || | 322 if (LookupIdentifiers(publicIds, ResourceType_Instance, input) || |
320 LookupIdentifier(publicId, ResourceType_Series, input) || | 323 LookupIdentifiers(publicIds, ResourceType_Series, input) || |
321 LookupIdentifier(publicId, ResourceType_Study, input) || | 324 LookupIdentifiers(publicIds, ResourceType_Study, input) || |
322 LookupIdentifier(publicId, ResourceType_Patient, input)) | 325 LookupIdentifiers(publicIds, ResourceType_Patient, input)) |
323 { | 326 { |
324 return CreateIterator(context_, targetAet, publicId, originatorAet, originatorId); | 327 return CreateIterator(context_, targetAet, publicIds, originatorAet, originatorId); |
325 } | 328 } |
326 else | 329 else |
327 { | 330 { |
328 // No identifier is present in the request. | 331 // No identifier is present in the request. |
329 throw OrthancException(ErrorCode_BadRequest); | 332 throw OrthancException(ErrorCode_BadRequest, "Invalid fields in a C-MOVE request"); |
330 } | 333 } |
331 } | 334 } |
332 | 335 |
333 assert(levelTmp != NULL); | 336 assert(levelTmp != NULL); |
334 ResourceType level = StringToResourceType(levelTmp->GetContent().c_str()); | 337 ResourceType level = StringToResourceType(levelTmp->GetContent().c_str()); |
336 | 339 |
337 /** | 340 /** |
338 * Lookup for the resource to be sent. | 341 * Lookup for the resource to be sent. |
339 **/ | 342 **/ |
340 | 343 |
341 std::string publicId; | 344 std::vector<std::string> publicIds; |
342 | 345 |
343 if (LookupIdentifier(publicId, level, input)) | 346 if (LookupIdentifiers(publicIds, level, input)) |
344 { | 347 { |
345 return CreateIterator(context_, targetAet, publicId, originatorAet, originatorId); | 348 return CreateIterator(context_, targetAet, publicIds, originatorAet, originatorId); |
346 } | 349 } |
347 else | 350 else |
348 { | 351 { |
349 throw OrthancException(ErrorCode_BadRequest); | 352 throw OrthancException(ErrorCode_BadRequest, "Invalid fields in a C-MOVE request"); |
350 } | 353 } |
351 } | 354 } |
352 } | 355 } |