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