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 }