Mercurial > hg > orthanc
comparison OrthancServer/OrthancRestApi/OrthancRestModalities.cpp @ 2583:1b6a6d80b6f2 jobs
OrthancPeerStoreJob
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 14 May 2018 20:43:16 +0200 |
parents | 055d7d4a823f |
children | 38b5045f2bff |
comparison
equal
deleted
inserted
replaced
2582:b3da733d984c | 2583:1b6a6d80b6f2 |
---|---|
46 | 46 |
47 | 47 |
48 | 48 |
49 namespace Orthanc | 49 namespace Orthanc |
50 { | 50 { |
51 class InstancesIteratorJob : public IJob | 51 class SetOfInstancesJob : public IJob |
52 { | 52 { |
53 private: | 53 private: |
54 bool started_; | 54 bool started_; |
55 std::vector<std::string> instances_; | 55 std::vector<std::string> instances_; |
56 bool permissive_; | |
56 size_t position_; | 57 size_t position_; |
58 std::set<std::string> failedInstances_; | |
59 | |
60 protected: | |
61 virtual bool HandleInstance(const std::string& instance) = 0; | |
57 | 62 |
58 public: | 63 public: |
59 InstancesIteratorJob() : | 64 SetOfInstancesJob() : |
60 started_(false), | 65 started_(false), |
66 permissive_(false), | |
61 position_(0) | 67 position_(0) |
62 { | 68 { |
63 } | 69 } |
64 | 70 |
65 void Reserve(size_t size) | 71 void Reserve(size_t size) |
88 else | 94 else |
89 { | 95 { |
90 instances_.push_back(instance); | 96 instances_.push_back(instance); |
91 } | 97 } |
92 } | 98 } |
93 | 99 |
100 bool IsPermissive() const | |
101 { | |
102 return permissive_; | |
103 } | |
104 | |
105 void SetPermissive(bool permissive) | |
106 { | |
107 if (IsStarted()) | |
108 { | |
109 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
110 } | |
111 else | |
112 { | |
113 permissive_ = permissive; | |
114 } | |
115 } | |
116 | |
117 virtual void SignalResubmit() | |
118 { | |
119 if (started_) | |
120 { | |
121 position_ = 0; | |
122 failedInstances_.clear(); | |
123 } | |
124 else | |
125 { | |
126 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
127 } | |
128 } | |
129 | |
94 virtual void Start() | 130 virtual void Start() |
95 { | 131 { |
96 started_ = true; | 132 started_ = true; |
97 } | 133 } |
98 | 134 |
140 else | 176 else |
141 { | 177 { |
142 return instances_[position_]; | 178 return instances_[position_]; |
143 } | 179 } |
144 } | 180 } |
145 }; | 181 |
146 | 182 |
147 | 183 const std::vector<std::string>& GetInstances() const |
148 class StoreScuJob : public InstancesIteratorJob | 184 { |
149 { | 185 return instances_; |
150 private: | 186 } |
151 ServerContext& context_; | 187 |
152 std::string localAet_; | 188 |
153 RemoteModalityParameters remote_; | 189 const std::set<std::string>& GetFailedInstances() const |
154 bool permissive_; | 190 { |
155 std::string moveOriginatorAet_; | 191 return failedInstances_; |
156 uint16_t moveOriginatorId_; | 192 } |
157 std::auto_ptr<DicomUserConnection> connection_; | 193 |
158 std::set<std::string> failedInstances_; | 194 |
159 | |
160 void CreateConnection() | |
161 { | |
162 if (connection_.get() == NULL) | |
163 { | |
164 connection_.reset(new DicomUserConnection); | |
165 connection_->SetLocalApplicationEntityTitle(localAet_); | |
166 connection_->SetRemoteModality(remote_); | |
167 } | |
168 } | |
169 | |
170 public: | |
171 StoreScuJob(ServerContext& context) : | |
172 context_(context), | |
173 localAet_("ORTHANC"), | |
174 permissive_(false), | |
175 moveOriginatorId_(0) // By default, not a C-MOVE | |
176 { | |
177 } | |
178 | |
179 void AddResource(const std::string& publicId) | |
180 { | |
181 typedef std::list<std::string> Instances; | |
182 | |
183 Instances instances; | |
184 context_.GetIndex().GetChildInstances(instances, publicId); | |
185 | |
186 Reserve(GetInstancesCount() + instances.size()); | |
187 | |
188 for (Instances::const_iterator it = instances.begin(); | |
189 it != instances.end(); ++it) | |
190 { | |
191 AddInstance(*it); | |
192 } | |
193 } | |
194 | |
195 const std::string& GetLocalAet() const | |
196 { | |
197 return localAet_; | |
198 } | |
199 | |
200 void SetLocalAet(const std::string& aet) | |
201 { | |
202 if (IsStarted()) | |
203 { | |
204 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
205 } | |
206 else | |
207 { | |
208 localAet_ = aet; | |
209 } | |
210 } | |
211 | |
212 const RemoteModalityParameters& GetRemoteModality() const | |
213 { | |
214 return remote_; | |
215 } | |
216 | |
217 void SetRemoteModality(const RemoteModalityParameters& remote) | |
218 { | |
219 if (IsStarted()) | |
220 { | |
221 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
222 } | |
223 else | |
224 { | |
225 remote_ = remote; | |
226 } | |
227 } | |
228 | |
229 bool IsPermissive() const | |
230 { | |
231 return permissive_; | |
232 } | |
233 | |
234 void SetPermissive(bool permissive) | |
235 { | |
236 if (IsStarted()) | |
237 { | |
238 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
239 } | |
240 else | |
241 { | |
242 permissive_ = permissive; | |
243 } | |
244 } | |
245 | |
246 bool HasMoveOriginator() const | |
247 { | |
248 return moveOriginatorId_ != 0; | |
249 } | |
250 | |
251 const std::string& GetMoveOriginatorAet() const | |
252 { | |
253 if (HasMoveOriginator()) | |
254 { | |
255 return moveOriginatorAet_; | |
256 } | |
257 else | |
258 { | |
259 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
260 } | |
261 } | |
262 | |
263 uint16_t GetMoveOriginatorId() const | |
264 { | |
265 if (HasMoveOriginator()) | |
266 { | |
267 return moveOriginatorId_; | |
268 } | |
269 else | |
270 { | |
271 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
272 } | |
273 } | |
274 | |
275 void SetMoveOriginator(const std::string& aet, | |
276 int id) | |
277 { | |
278 if (IsStarted()) | |
279 { | |
280 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
281 } | |
282 else if (id < 0 || | |
283 id >= 65536) | |
284 { | |
285 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
286 } | |
287 else | |
288 { | |
289 moveOriginatorId_ = static_cast<uint16_t>(id); | |
290 moveOriginatorAet_ = aet; | |
291 } | |
292 } | |
293 | |
294 virtual JobStepResult* ExecuteStep() | 195 virtual JobStepResult* ExecuteStep() |
295 { | 196 { |
296 if (IsDone()) | 197 if (IsDone()) |
297 { | 198 { |
298 return new JobStepResult(JobStepCode_Success); | 199 return new JobStepResult(JobStepCode_Failure); |
299 } | 200 } |
300 | 201 |
301 CreateConnection(); | 202 bool ok; |
302 | |
303 bool ok = false; | |
304 | 203 |
305 try | 204 try |
306 { | 205 { |
307 std::string dicom; | 206 ok = HandleInstance(GetCurrentInstance()); |
308 context_.ReadDicom(dicom, GetCurrentInstance()); | |
309 | |
310 if (HasMoveOriginator()) | |
311 { | |
312 connection_->Store(dicom, moveOriginatorAet_, moveOriginatorId_); | |
313 } | |
314 else | |
315 { | |
316 connection_->Store(dicom); | |
317 } | |
318 | |
319 boost::this_thread::sleep(boost::posix_time::milliseconds(300)); | |
320 | |
321 ok = true; | |
322 } | 207 } |
323 catch (OrthancException& e) | 208 catch (OrthancException& e) |
324 { | 209 { |
210 ok = false; | |
325 } | 211 } |
326 | 212 |
327 if (!ok) | 213 if (!ok) |
328 { | 214 { |
329 if (permissive_) | 215 if (permissive_) |
346 { | 232 { |
347 return new JobStepResult(JobStepCode_Continue); | 233 return new JobStepResult(JobStepCode_Continue); |
348 } | 234 } |
349 } | 235 } |
350 | 236 |
237 virtual void GetInternalContent(Json::Value& value) | |
238 { | |
239 Json::Value v = Json::arrayValue; | |
240 | |
241 for (size_t i = 0; i < instances_.size(); i++) | |
242 { | |
243 v.append(instances_[i]); | |
244 } | |
245 | |
246 value["Instances"] = v; | |
247 | |
248 | |
249 v = Json::arrayValue; | |
250 | |
251 for (std::set<std::string>::const_iterator it = failedInstances_.begin(); | |
252 it != failedInstances_.end(); ++it) | |
253 { | |
254 v.append(*it); | |
255 } | |
256 | |
257 value["FailedInstances"] = v; | |
258 } | |
259 }; | |
260 | |
261 | |
262 class DicomStoreJob : public SetOfInstancesJob | |
263 { | |
264 private: | |
265 ServerContext& context_; | |
266 std::string localAet_; | |
267 RemoteModalityParameters remote_; | |
268 std::string moveOriginatorAet_; | |
269 uint16_t moveOriginatorId_; | |
270 std::auto_ptr<DicomUserConnection> connection_; | |
271 | |
272 void OpenConnection() | |
273 { | |
274 if (connection_.get() == NULL) | |
275 { | |
276 connection_.reset(new DicomUserConnection); | |
277 connection_->SetLocalApplicationEntityTitle(localAet_); | |
278 connection_->SetRemoteModality(remote_); | |
279 } | |
280 } | |
281 | |
282 protected: | |
283 virtual bool HandleInstance(const std::string& instance) | |
284 { | |
285 OpenConnection(); | |
286 | |
287 LOG(INFO) << "Sending instance " << instance << " to modality \"" | |
288 << remote_.GetApplicationEntityTitle() << "\""; | |
289 | |
290 std::string dicom; | |
291 context_.ReadDicom(dicom, GetCurrentInstance()); | |
292 | |
293 if (HasMoveOriginator()) | |
294 { | |
295 connection_->Store(dicom, moveOriginatorAet_, moveOriginatorId_); | |
296 } | |
297 else | |
298 { | |
299 connection_->Store(dicom); | |
300 } | |
301 | |
302 boost::this_thread::sleep(boost::posix_time::milliseconds(500)); | |
303 | |
304 return true; | |
305 } | |
306 | |
307 public: | |
308 DicomStoreJob(ServerContext& context) : | |
309 context_(context), | |
310 localAet_("ORTHANC"), | |
311 moveOriginatorId_(0) // By default, not a C-MOVE | |
312 { | |
313 } | |
314 | |
315 const std::string& GetLocalAet() const | |
316 { | |
317 return localAet_; | |
318 } | |
319 | |
320 void SetLocalAet(const std::string& aet) | |
321 { | |
322 if (IsStarted()) | |
323 { | |
324 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
325 } | |
326 else | |
327 { | |
328 localAet_ = aet; | |
329 } | |
330 } | |
331 | |
332 const RemoteModalityParameters& GetRemoteModality() const | |
333 { | |
334 return remote_; | |
335 } | |
336 | |
337 void SetRemoteModality(const RemoteModalityParameters& remote) | |
338 { | |
339 if (IsStarted()) | |
340 { | |
341 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
342 } | |
343 else | |
344 { | |
345 remote_ = remote; | |
346 } | |
347 } | |
348 | |
349 bool HasMoveOriginator() const | |
350 { | |
351 return moveOriginatorId_ != 0; | |
352 } | |
353 | |
354 const std::string& GetMoveOriginatorAet() const | |
355 { | |
356 if (HasMoveOriginator()) | |
357 { | |
358 return moveOriginatorAet_; | |
359 } | |
360 else | |
361 { | |
362 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
363 } | |
364 } | |
365 | |
366 uint16_t GetMoveOriginatorId() const | |
367 { | |
368 if (HasMoveOriginator()) | |
369 { | |
370 return moveOriginatorId_; | |
371 } | |
372 else | |
373 { | |
374 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
375 } | |
376 } | |
377 | |
378 void SetMoveOriginator(const std::string& aet, | |
379 int id) | |
380 { | |
381 if (IsStarted()) | |
382 { | |
383 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
384 } | |
385 else if (id < 0 || | |
386 id >= 65536) | |
387 { | |
388 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
389 } | |
390 else | |
391 { | |
392 moveOriginatorId_ = static_cast<uint16_t>(id); | |
393 moveOriginatorAet_ = aet; | |
394 } | |
395 } | |
396 | |
351 virtual void ReleaseResources() // For pausing jobs | 397 virtual void ReleaseResources() // For pausing jobs |
352 { | 398 { |
353 connection_.reset(NULL); | 399 connection_.reset(NULL); |
354 } | 400 } |
355 | 401 |
356 virtual void GetJobType(std::string& target) | 402 virtual void GetJobType(std::string& target) |
357 { | 403 { |
358 target = "C-Store"; | 404 target = "DicomStore"; |
359 } | 405 } |
360 | 406 |
361 virtual void GetPublicContent(Json::Value& value) | 407 virtual void GetPublicContent(Json::Value& value) |
362 { | 408 { |
363 value["LocalAet"] = localAet_; | 409 value["LocalAet"] = localAet_; |
367 { | 413 { |
368 value["MoveOriginatorAET"] = GetMoveOriginatorAet(); | 414 value["MoveOriginatorAET"] = GetMoveOriginatorAet(); |
369 value["MoveOriginatorID"] = GetMoveOriginatorId(); | 415 value["MoveOriginatorID"] = GetMoveOriginatorId(); |
370 } | 416 } |
371 | 417 |
372 Json::Value v = Json::arrayValue; | 418 value["InstancesCount"] = static_cast<uint32_t>(GetInstances().size()); |
373 for (std::set<std::string>::const_iterator it = failedInstances_.begin(); | 419 value["FailedInstancesCount"] = static_cast<uint32_t>(GetFailedInstances().size()); |
374 it != failedInstances_.end(); ++it) | 420 } |
375 { | 421 }; |
376 v.append(*it); | 422 |
377 } | 423 |
378 | 424 class OrthancPeerStoreJob : public SetOfInstancesJob |
379 value["FailedInstances"] = v; | 425 { |
380 } | 426 private: |
381 | 427 ServerContext& context_; |
382 virtual void GetInternalContent(Json::Value& value) | 428 WebServiceParameters peer_; |
383 { | 429 std::auto_ptr<HttpClient> client_; |
384 // TODO | 430 |
431 protected: | |
432 virtual bool HandleInstance(const std::string& instance) | |
433 { | |
434 if (client_.get() == NULL) | |
435 { | |
436 client_.reset(new HttpClient(peer_, "instances")); | |
437 client_->SetMethod(HttpMethod_Post); | |
438 } | |
439 | |
440 LOG(INFO) << "Sending instance " << instance << " to peer \"" | |
441 << peer_.GetUrl() << "\""; | |
442 | |
443 context_.ReadDicom(client_->GetBody(), GetCurrentInstance()); | |
444 | |
445 std::string answer; | |
446 return client_->Apply(answer); | |
447 } | |
448 | |
449 public: | |
450 OrthancPeerStoreJob(ServerContext& context) : | |
451 context_(context) | |
452 { | |
453 } | |
454 | |
455 const WebServiceParameters& GetPeer() const | |
456 { | |
457 return peer_; | |
458 } | |
459 | |
460 virtual void ReleaseResources() // For pausing jobs | |
461 { | |
462 client_.reset(NULL); | |
463 } | |
464 | |
465 virtual void GetJobType(std::string& target) | |
466 { | |
467 target = "OrthancPeerStore"; | |
468 } | |
469 | |
470 virtual void GetPublicContent(Json::Value& value) | |
471 { | |
472 Json::Value v; | |
473 peer_.ToJson(v); | |
474 value["Peer"] = v; | |
475 | |
476 value["InstancesCount"] = static_cast<uint32_t>(GetInstances().size()); | |
477 value["FailedInstancesCount"] = static_cast<uint32_t>(GetFailedInstances().size()); | |
385 } | 478 } |
386 }; | 479 }; |
387 } | 480 } |
388 | 481 |
389 | 482 |
1034 bool asynchronous = Toolbox::GetJsonBooleanField(request, "Asynchronous", false); | 1127 bool asynchronous = Toolbox::GetJsonBooleanField(request, "Asynchronous", false); |
1035 std::string moveOriginatorAET = Toolbox::GetJsonStringField(request, "MoveOriginatorAet", context.GetDefaultLocalApplicationEntityTitle()); | 1128 std::string moveOriginatorAET = Toolbox::GetJsonStringField(request, "MoveOriginatorAet", context.GetDefaultLocalApplicationEntityTitle()); |
1036 int moveOriginatorID = Toolbox::GetJsonIntegerField(request, "MoveOriginatorID", 0 /* By default, not a C-MOVE */); | 1129 int moveOriginatorID = Toolbox::GetJsonIntegerField(request, "MoveOriginatorID", 0 /* By default, not a C-MOVE */); |
1037 int priority = Toolbox::GetJsonIntegerField(request, "Priority", 0); | 1130 int priority = Toolbox::GetJsonIntegerField(request, "Priority", 0); |
1038 | 1131 |
1039 if (moveOriginatorID < 0 || | |
1040 moveOriginatorID >= 65536) | |
1041 { | |
1042 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
1043 } | |
1044 | |
1045 RemoteModalityParameters p = Configuration::GetModalityUsingSymbolicName(remote); | 1132 RemoteModalityParameters p = Configuration::GetModalityUsingSymbolicName(remote); |
1046 | 1133 |
1047 #if 1 | 1134 std::auto_ptr<DicomStoreJob> job(new DicomStoreJob(context)); |
1048 std::auto_ptr<StoreScuJob> job(new StoreScuJob(context)); | |
1049 job->SetLocalAet(localAet); | 1135 job->SetLocalAet(localAet); |
1050 job->SetRemoteModality(p); | 1136 job->SetRemoteModality(p); |
1051 job->SetPermissive(permissive); | 1137 job->SetPermissive(permissive); |
1052 | 1138 |
1053 if (moveOriginatorID != 0) | 1139 if (moveOriginatorID != 0) |
1054 { | 1140 { |
1055 job->SetMoveOriginator(moveOriginatorAET, static_cast<uint16_t>(moveOriginatorID)); | 1141 job->SetMoveOriginator(moveOriginatorAET, moveOriginatorID); |
1056 } | 1142 } |
1057 | 1143 |
1144 job->Reserve(instances.size()); | |
1145 | |
1058 for (std::list<std::string>::const_iterator | 1146 for (std::list<std::string>::const_iterator |
1059 it = instances.begin(); it != instances.end(); ++it) | 1147 it = instances.begin(); it != instances.end(); ++it) |
1060 { | 1148 { |
1061 job->AddInstance(*it); | 1149 job->AddInstance(*it); |
1062 } | 1150 } |
1078 } | 1166 } |
1079 else | 1167 else |
1080 { | 1168 { |
1081 call.GetOutput().SignalError(HttpStatus_500_InternalServerError); | 1169 call.GetOutput().SignalError(HttpStatus_500_InternalServerError); |
1082 } | 1170 } |
1083 | |
1084 #else | |
1085 ServerJob job; | |
1086 for (std::list<std::string>::const_iterator | |
1087 it = instances.begin(); it != instances.end(); ++it) | |
1088 { | |
1089 std::auto_ptr<StoreScuCommand> command(new StoreScuCommand(context, localAet, p, permissive)); | |
1090 | |
1091 if (moveOriginatorID != 0) | |
1092 { | |
1093 command->SetMoveOriginator(moveOriginatorAET, static_cast<uint16_t>(moveOriginatorID)); | |
1094 } | |
1095 | |
1096 job.AddCommand(command.release()).AddInput(*it); | |
1097 } | |
1098 | |
1099 job.SetDescription("HTTP request: Store-SCU to peer \"" + remote + "\""); | |
1100 | |
1101 if (asynchronous) | |
1102 { | |
1103 // Asynchronous mode: Submit the job, but don't wait for its completion | |
1104 context.GetScheduler().Submit(job); | |
1105 call.GetOutput().AnswerBuffer("{}", "application/json"); | |
1106 } | |
1107 else if (context.GetScheduler().SubmitAndWait(job)) | |
1108 { | |
1109 // Synchronous mode: We have submitted and waited for completion | |
1110 call.GetOutput().AnswerBuffer("{}", "application/json"); | |
1111 } | |
1112 else | |
1113 { | |
1114 call.GetOutput().SignalError(HttpStatus_500_InternalServerError); | |
1115 } | |
1116 #endif | |
1117 } | 1171 } |
1118 | 1172 |
1119 | 1173 |
1120 /*************************************************************************** | 1174 /*************************************************************************** |
1121 * DICOM C-Move SCU | 1175 * DICOM C-Move SCU |