Mercurial > hg > orthanc-stone
comparison Framework/Loaders/DicomResourcesLoader.cpp @ 1228:c471a0aa137b broker
adding the next generation of loaders
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 09 Dec 2019 13:58:37 +0100 |
parents | |
children | a8248b08115c |
comparison
equal
deleted
inserted
replaced
1227:a1c0c9c9f9af | 1228:c471a0aa137b |
---|---|
1 /** | |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #include "DicomResourcesLoader.h" | |
23 | |
24 #if !defined(ORTHANC_ENABLE_DCMTK) | |
25 # error The macro ORTHANC_ENABLE_DCMTK must be defined | |
26 #endif | |
27 | |
28 #if ORTHANC_ENABLE_DCMTK == 1 | |
29 # include "../Oracle/ParseDicomFromFileCommand.h" | |
30 # include <Core/DicomParsing/ParsedDicomFile.h> | |
31 #endif | |
32 | |
33 #include <boost/filesystem/path.hpp> | |
34 | |
35 namespace OrthancStone | |
36 { | |
37 static std::string GetUri(Orthanc::ResourceType level) | |
38 { | |
39 switch (level) | |
40 { | |
41 case Orthanc::ResourceType_Patient: | |
42 return "patients"; | |
43 | |
44 case Orthanc::ResourceType_Study: | |
45 return "studies"; | |
46 | |
47 case Orthanc::ResourceType_Series: | |
48 return "series"; | |
49 | |
50 case Orthanc::ResourceType_Instance: | |
51 return "instances"; | |
52 | |
53 default: | |
54 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
55 } | |
56 } | |
57 | |
58 | |
59 class DicomResourcesLoader::Handler : public Orthanc::IDynamicObject | |
60 { | |
61 private: | |
62 boost::shared_ptr<DicomResourcesLoader> loader_; | |
63 boost::shared_ptr<LoadedDicomResources> target_; | |
64 int priority_; | |
65 DicomSource source_; | |
66 boost::shared_ptr<Orthanc::IDynamicObject> userPayload_; | |
67 | |
68 public: | |
69 Handler(boost::shared_ptr<DicomResourcesLoader> loader, | |
70 boost::shared_ptr<LoadedDicomResources> target, | |
71 int priority, | |
72 const DicomSource& source, | |
73 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) : | |
74 loader_(loader), | |
75 target_(target), | |
76 priority_(priority), | |
77 source_(source), | |
78 userPayload_(userPayload) | |
79 { | |
80 if (!loader || | |
81 !target) | |
82 { | |
83 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
84 } | |
85 } | |
86 | |
87 virtual ~Handler() | |
88 { | |
89 } | |
90 | |
91 void BroadcastSuccess() | |
92 { | |
93 SuccessMessage message(*loader_, target_, priority_, source_, userPayload_.get()); | |
94 loader_->BroadcastMessage(message); | |
95 } | |
96 | |
97 boost::shared_ptr<DicomResourcesLoader> GetLoader() | |
98 { | |
99 assert(loader_); | |
100 return loader_; | |
101 } | |
102 | |
103 boost::shared_ptr<LoadedDicomResources> GetTarget() | |
104 { | |
105 assert(target_); | |
106 return target_; | |
107 } | |
108 | |
109 int GetPriority() const | |
110 { | |
111 return priority_; | |
112 } | |
113 | |
114 const DicomSource& GetSource() const | |
115 { | |
116 return source_; | |
117 } | |
118 | |
119 const boost::shared_ptr<Orthanc::IDynamicObject> GetUserPayload() const | |
120 { | |
121 return userPayload_; | |
122 } | |
123 }; | |
124 | |
125 | |
126 class DicomResourcesLoader::StringHandler : public DicomResourcesLoader::Handler | |
127 { | |
128 public: | |
129 StringHandler(boost::shared_ptr<DicomResourcesLoader> loader, | |
130 boost::shared_ptr<LoadedDicomResources> target, | |
131 int priority, | |
132 const DicomSource& source, | |
133 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) : | |
134 Handler(loader, target, priority, source, userPayload) | |
135 { | |
136 } | |
137 | |
138 virtual void HandleJson(const Json::Value& body) = 0; | |
139 | |
140 virtual void HandleString(const std::string& body) | |
141 { | |
142 Json::Reader reader; | |
143 Json::Value value; | |
144 if (reader.parse(body, value)) | |
145 { | |
146 HandleJson(value); | |
147 } | |
148 else | |
149 { | |
150 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
151 } | |
152 } | |
153 }; | |
154 | |
155 | |
156 class DicomResourcesLoader::DicomWebHandler : public StringHandler | |
157 { | |
158 public: | |
159 DicomWebHandler(boost::shared_ptr<DicomResourcesLoader> loader, | |
160 boost::shared_ptr<LoadedDicomResources> target, | |
161 int priority, | |
162 const DicomSource& source, | |
163 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) : | |
164 StringHandler(loader, target, priority, source, userPayload) | |
165 { | |
166 } | |
167 | |
168 virtual void HandleJson(const Json::Value& body) | |
169 { | |
170 GetTarget()->AddFromDicomWeb(body); | |
171 BroadcastSuccess(); | |
172 } | |
173 }; | |
174 | |
175 | |
176 class DicomResourcesLoader::OrthancHandler : public StringHandler | |
177 { | |
178 private: | |
179 boost::shared_ptr<unsigned int> remainingCommands_; | |
180 | |
181 protected: | |
182 void CloseCommand() | |
183 { | |
184 assert(remainingCommands_); | |
185 | |
186 if (*remainingCommands_ == 0) | |
187 { | |
188 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
189 } | |
190 | |
191 (*remainingCommands_) --; | |
192 | |
193 if (*remainingCommands_ == 0) | |
194 { | |
195 BroadcastSuccess(); | |
196 } | |
197 } | |
198 | |
199 public: | |
200 OrthancHandler(boost::shared_ptr<DicomResourcesLoader> loader, | |
201 boost::shared_ptr<LoadedDicomResources> target, | |
202 int priority, | |
203 const DicomSource& source, | |
204 boost::shared_ptr<unsigned int> remainingCommands, | |
205 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) : | |
206 StringHandler(loader, target, priority, source, userPayload), | |
207 remainingCommands_(remainingCommands) | |
208 { | |
209 if (!remainingCommands) | |
210 { | |
211 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
212 } | |
213 | |
214 (*remainingCommands) ++; | |
215 } | |
216 | |
217 boost::shared_ptr<unsigned int> GetRemainingCommands() | |
218 { | |
219 assert(remainingCommands_); | |
220 return remainingCommands_; | |
221 } | |
222 }; | |
223 | |
224 | |
225 class DicomResourcesLoader::OrthancInstanceTagsHandler : public OrthancHandler | |
226 { | |
227 public: | |
228 OrthancInstanceTagsHandler(boost::shared_ptr<DicomResourcesLoader> loader, | |
229 boost::shared_ptr<LoadedDicomResources> target, | |
230 int priority, | |
231 const DicomSource& source, | |
232 boost::shared_ptr<unsigned int> remainingCommands, | |
233 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) : | |
234 OrthancHandler(loader, target, priority, source, remainingCommands, userPayload) | |
235 { | |
236 } | |
237 | |
238 virtual void HandleJson(const Json::Value& body) | |
239 { | |
240 GetTarget()->AddFromOrthanc(body); | |
241 CloseCommand(); | |
242 } | |
243 }; | |
244 | |
245 | |
246 class DicomResourcesLoader::OrthancOneChildInstanceHandler : public OrthancHandler | |
247 { | |
248 public: | |
249 OrthancOneChildInstanceHandler(boost::shared_ptr<DicomResourcesLoader> loader, | |
250 boost::shared_ptr<LoadedDicomResources> target, | |
251 int priority, | |
252 const DicomSource& source, | |
253 boost::shared_ptr<unsigned int> remainingCommands, | |
254 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) : | |
255 OrthancHandler(loader, target, priority, source, remainingCommands, userPayload) | |
256 { | |
257 } | |
258 | |
259 virtual void HandleJson(const Json::Value& body) | |
260 { | |
261 static const char* const ID = "ID"; | |
262 | |
263 if (body.type() == Json::arrayValue) | |
264 { | |
265 if (body.size() > 0) | |
266 { | |
267 if (body[0].type() == Json::objectValue && | |
268 body[0].isMember(ID) && | |
269 body[0][ID].type() == Json::stringValue) | |
270 { | |
271 GetLoader()->ScheduleLoadOrthancInstanceTags | |
272 (GetTarget(), GetPriority(), GetSource(), body[0][ID].asString(), GetRemainingCommands(), GetUserPayload()); | |
273 CloseCommand(); | |
274 } | |
275 else | |
276 { | |
277 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
278 } | |
279 } | |
280 } | |
281 else | |
282 { | |
283 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
284 } | |
285 } | |
286 }; | |
287 | |
288 | |
289 class DicomResourcesLoader::OrthancAllChildrenInstancesHandler : public OrthancHandler | |
290 { | |
291 private: | |
292 Orthanc::ResourceType bottomLevel_; | |
293 | |
294 public: | |
295 OrthancAllChildrenInstancesHandler(boost::shared_ptr<DicomResourcesLoader> loader, | |
296 boost::shared_ptr<LoadedDicomResources> target, | |
297 int priority, | |
298 const DicomSource& source, | |
299 boost::shared_ptr<unsigned int> remainingCommands, | |
300 Orthanc::ResourceType bottomLevel, | |
301 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) : | |
302 OrthancHandler(loader, target, priority, source, remainingCommands, userPayload), | |
303 bottomLevel_(bottomLevel) | |
304 { | |
305 } | |
306 | |
307 virtual void HandleJson(const Json::Value& body) | |
308 { | |
309 static const char* const ID = "ID"; | |
310 static const char* const INSTANCES = "Instances"; | |
311 | |
312 if (body.type() == Json::arrayValue) | |
313 { | |
314 for (Json::Value::ArrayIndex i = 0; i < body.size(); i++) | |
315 { | |
316 switch (bottomLevel_) | |
317 { | |
318 case Orthanc::ResourceType_Patient: | |
319 case Orthanc::ResourceType_Study: | |
320 if (body[i].type() == Json::objectValue && | |
321 body[i].isMember(ID) && | |
322 body[i][ID].type() == Json::stringValue) | |
323 { | |
324 GetLoader()->ScheduleLoadOrthancOneChildInstance | |
325 (GetTarget(), GetPriority(), GetSource(), bottomLevel_, | |
326 body[i][ID].asString(), GetRemainingCommands(), GetUserPayload()); | |
327 } | |
328 else | |
329 { | |
330 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
331 } | |
332 | |
333 break; | |
334 | |
335 case Orthanc::ResourceType_Series: | |
336 // At the series level, avoid a call to | |
337 // "/series/.../instances", as we already have this | |
338 // information in the JSON | |
339 if (body[i].type() == Json::objectValue && | |
340 body[i].isMember(INSTANCES) && | |
341 body[i][INSTANCES].type() == Json::arrayValue) | |
342 { | |
343 if (body[i][INSTANCES].size() > 0) | |
344 { | |
345 if (body[i][INSTANCES][0].type() == Json::stringValue) | |
346 { | |
347 GetLoader()->ScheduleLoadOrthancInstanceTags | |
348 (GetTarget(), GetPriority(), GetSource(), | |
349 body[i][INSTANCES][0].asString(), GetRemainingCommands(), GetUserPayload()); | |
350 } | |
351 else | |
352 { | |
353 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
354 } | |
355 } | |
356 } | |
357 | |
358 break; | |
359 | |
360 case Orthanc::ResourceType_Instance: | |
361 if (body[i].type() == Json::objectValue && | |
362 body[i].isMember(ID) && | |
363 body[i][ID].type() == Json::stringValue) | |
364 { | |
365 GetLoader()->ScheduleLoadOrthancInstanceTags | |
366 (GetTarget(), GetPriority(), GetSource(), | |
367 body[i][ID].asString(), GetRemainingCommands(), GetUserPayload()); | |
368 } | |
369 else | |
370 { | |
371 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
372 } | |
373 | |
374 break; | |
375 | |
376 default: | |
377 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
378 } | |
379 } | |
380 } | |
381 | |
382 CloseCommand(); | |
383 } | |
384 }; | |
385 | |
386 | |
387 #if ORTHANC_ENABLE_DCMTK == 1 | |
388 static void ExploreDicomDir(OrthancStone::LoadedDicomResources& instances, | |
389 const Orthanc::ParsedDicomDir& dicomDir, | |
390 Orthanc::ResourceType level, | |
391 size_t index, | |
392 const Orthanc::DicomMap& parent) | |
393 { | |
394 std::string expectedType; | |
395 | |
396 switch (level) | |
397 { | |
398 case Orthanc::ResourceType_Patient: | |
399 expectedType = "PATIENT"; | |
400 break; | |
401 | |
402 case Orthanc::ResourceType_Study: | |
403 expectedType = "STUDY"; | |
404 break; | |
405 | |
406 case Orthanc::ResourceType_Series: | |
407 expectedType = "SERIES"; | |
408 break; | |
409 | |
410 case Orthanc::ResourceType_Instance: | |
411 expectedType = "IMAGE"; | |
412 break; | |
413 | |
414 default: | |
415 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
416 } | |
417 | |
418 for (;;) | |
419 { | |
420 std::auto_ptr<Orthanc::DicomMap> current(dicomDir.GetItem(index).Clone()); | |
421 current->RemoveBinaryTags(); | |
422 current->Merge(parent); | |
423 | |
424 std::string type; | |
425 if (!current->LookupStringValue(type, Orthanc::DICOM_TAG_DIRECTORY_RECORD_TYPE, false)) | |
426 { | |
427 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
428 } | |
429 | |
430 if (type == expectedType) | |
431 { | |
432 if (level == Orthanc::ResourceType_Instance) | |
433 { | |
434 instances.AddResource(*current); | |
435 } | |
436 else | |
437 { | |
438 size_t lower; | |
439 if (dicomDir.LookupLower(lower, index)) | |
440 { | |
441 ExploreDicomDir(instances, dicomDir, Orthanc::GetChildResourceType(level), lower, *current); | |
442 } | |
443 } | |
444 } | |
445 | |
446 size_t next; | |
447 if (dicomDir.LookupNext(next, index)) | |
448 { | |
449 index = next; | |
450 } | |
451 else | |
452 { | |
453 return; | |
454 } | |
455 } | |
456 } | |
457 #endif | |
458 | |
459 | |
460 #if ORTHANC_ENABLE_DCMTK == 1 | |
461 void DicomResourcesLoader::GetDicomDirInstances(LoadedDicomResources& target, | |
462 const Orthanc::ParsedDicomDir& dicomDir) | |
463 { | |
464 Orthanc::DicomMap parent; | |
465 ExploreDicomDir(target, dicomDir, Orthanc::ResourceType_Patient, 0, parent); | |
466 } | |
467 #endif | |
468 | |
469 | |
470 #if ORTHANC_ENABLE_DCMTK == 1 | |
471 class DicomResourcesLoader::DicomDirHandler : public StringHandler | |
472 { | |
473 public: | |
474 DicomDirHandler(boost::shared_ptr<DicomResourcesLoader> loader, | |
475 boost::shared_ptr<LoadedDicomResources> target, | |
476 int priority, | |
477 const DicomSource& source, | |
478 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) : | |
479 StringHandler(loader, target, priority, source, userPayload) | |
480 { | |
481 } | |
482 | |
483 virtual void HandleJson(const Json::Value& body) | |
484 { | |
485 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
486 } | |
487 | |
488 virtual void HandleString(const std::string& body) | |
489 { | |
490 Orthanc::ParsedDicomDir dicomDir(body); | |
491 GetDicomDirInstances(*GetTarget(), dicomDir); | |
492 BroadcastSuccess(); | |
493 } | |
494 }; | |
495 #endif | |
496 | |
497 | |
498 void DicomResourcesLoader::Handle(const HttpCommand::SuccessMessage& message) | |
499 { | |
500 if (message.GetOrigin().HasPayload()) | |
501 { | |
502 dynamic_cast<StringHandler&>(message.GetOrigin().GetPayload()).HandleString(message.GetAnswer()); | |
503 } | |
504 } | |
505 | |
506 | |
507 void DicomResourcesLoader::Handle(const OrthancRestApiCommand::SuccessMessage& message) | |
508 { | |
509 if (message.GetOrigin().HasPayload()) | |
510 { | |
511 dynamic_cast<StringHandler&>(message.GetOrigin().GetPayload()).HandleString(message.GetAnswer()); | |
512 } | |
513 } | |
514 | |
515 | |
516 void DicomResourcesLoader::Handle(const ReadFileCommand::SuccessMessage& message) | |
517 { | |
518 if (message.GetOrigin().HasPayload()) | |
519 { | |
520 dynamic_cast<StringHandler&>(message.GetOrigin().GetPayload()).HandleString(message.GetContent()); | |
521 } | |
522 } | |
523 | |
524 | |
525 #if ORTHANC_ENABLE_DCMTK == 1 | |
526 void DicomResourcesLoader::Handle(const ParseDicomSuccessMessage& message) | |
527 { | |
528 if (message.GetOrigin().HasPayload()) | |
529 { | |
530 Handler& handler = dynamic_cast<Handler&>(message.GetOrigin().GetPayload()); | |
531 | |
532 std::set<Orthanc::DicomTag> ignoreTagLength; | |
533 ignoreTagLength.insert(Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR); // Needed for RT-DOSE | |
534 | |
535 Orthanc::DicomMap summary; | |
536 message.GetDicom().ExtractDicomSummary(summary, ignoreTagLength); | |
537 handler.GetTarget()->AddResource(summary); | |
538 | |
539 handler.BroadcastSuccess(); | |
540 } | |
541 } | |
542 #endif | |
543 | |
544 | |
545 void DicomResourcesLoader::Handle(const OracleCommandExceptionMessage& message) | |
546 { | |
547 // TODO | |
548 LOG(ERROR) << "Exception: " << message.GetException().What(); | |
549 } | |
550 | |
551 | |
552 void DicomResourcesLoader::ScheduleLoadOrthancInstanceTags(boost::shared_ptr<LoadedDicomResources> target, | |
553 int priority, | |
554 const DicomSource& source, | |
555 const std::string& instanceId, | |
556 boost::shared_ptr<unsigned int> remainingCommands, | |
557 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) | |
558 { | |
559 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
560 command->SetUri("/instances/" + instanceId + "/tags"); | |
561 command->AcquirePayload(new OrthancInstanceTagsHandler(shared_from_this(), target, priority, | |
562 source, remainingCommands, userPayload)); | |
563 | |
564 { | |
565 std::auto_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
566 lock->Schedule(GetSharedObserver(), priority, command.release()); | |
567 } | |
568 } | |
569 | |
570 | |
571 void DicomResourcesLoader::ScheduleLoadOrthancOneChildInstance(boost::shared_ptr<LoadedDicomResources> target, | |
572 int priority, | |
573 const DicomSource& source, | |
574 Orthanc::ResourceType level, | |
575 const std::string& id, | |
576 boost::shared_ptr<unsigned int> remainingCommands, | |
577 boost::shared_ptr<Orthanc::IDynamicObject> userPayload) | |
578 { | |
579 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
580 command->SetUri("/" + GetUri(level) + "/" + id + "/instances"); | |
581 command->AcquirePayload(new OrthancOneChildInstanceHandler(shared_from_this(), target, priority, | |
582 source, remainingCommands, userPayload)); | |
583 | |
584 { | |
585 std::auto_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
586 lock->Schedule(GetSharedObserver(), priority, command.release()); | |
587 } | |
588 } | |
589 | |
590 | |
591 | |
592 const Orthanc::IDynamicObject& DicomResourcesLoader::SuccessMessage::GetUserPayload() const | |
593 { | |
594 if (userPayload_ == NULL) | |
595 { | |
596 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
597 } | |
598 else | |
599 { | |
600 return *userPayload_; | |
601 } | |
602 } | |
603 | |
604 | |
605 boost::shared_ptr<IObserver> DicomResourcesLoader::Factory::Create(ILoadersContext::ILock& stone) | |
606 { | |
607 boost::shared_ptr<DicomResourcesLoader> result(new DicomResourcesLoader(stone.GetContext())); | |
608 result->Register<HttpCommand::SuccessMessage>(stone.GetOracleObservable(), &DicomResourcesLoader::Handle); | |
609 result->Register<OracleCommandExceptionMessage>(stone.GetOracleObservable(), &DicomResourcesLoader::Handle); | |
610 result->Register<OrthancRestApiCommand::SuccessMessage>(stone.GetOracleObservable(), &DicomResourcesLoader::Handle); | |
611 result->Register<ReadFileCommand::SuccessMessage>(stone.GetOracleObservable(), &DicomResourcesLoader::Handle); | |
612 | |
613 #if ORTHANC_ENABLE_DCMTK == 1 | |
614 result->Register<ParseDicomSuccessMessage>(stone.GetOracleObservable(), &DicomResourcesLoader::Handle); | |
615 #endif | |
616 | |
617 return boost::shared_ptr<IObserver>(result); | |
618 } | |
619 | |
620 | |
621 static void SetIncludeTags(std::map<std::string, std::string>& arguments, | |
622 const std::set<Orthanc::DicomTag>& includeTags) | |
623 { | |
624 if (!includeTags.empty()) | |
625 { | |
626 std::string s; | |
627 bool first = true; | |
628 | |
629 for (std::set<Orthanc::DicomTag>::const_iterator | |
630 it = includeTags.begin(); it != includeTags.end(); ++it) | |
631 { | |
632 if (first) | |
633 { | |
634 first = false; | |
635 } | |
636 else | |
637 { | |
638 s += ","; | |
639 } | |
640 | |
641 char buf[16]; | |
642 sprintf(buf, "%04X%04X", it->GetGroup(), it->GetElement()); | |
643 s += std::string(buf); | |
644 } | |
645 | |
646 arguments["includefield"] = s; | |
647 } | |
648 } | |
649 | |
650 | |
651 void DicomResourcesLoader::ScheduleWado(boost::shared_ptr<LoadedDicomResources> target, | |
652 int priority, | |
653 const DicomSource& source, | |
654 const std::string& uri, | |
655 const std::set<Orthanc::DicomTag>& includeTags, | |
656 Orthanc::IDynamicObject* userPayload) | |
657 { | |
658 boost::shared_ptr<Orthanc::IDynamicObject> protection(userPayload); | |
659 | |
660 if (!source.IsDicomWeb()) | |
661 { | |
662 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "Not a DICOMweb source"); | |
663 } | |
664 | |
665 std::map<std::string, std::string> arguments, headers; | |
666 SetIncludeTags(arguments, includeTags); | |
667 | |
668 std::auto_ptr<IOracleCommand> command( | |
669 source.CreateDicomWebCommand(uri, arguments, headers, | |
670 new DicomWebHandler(shared_from_this(), target, priority, source, protection))); | |
671 | |
672 { | |
673 std::auto_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
674 lock->Schedule(GetSharedObserver(), priority, command.release()); | |
675 } | |
676 } | |
677 | |
678 | |
679 void DicomResourcesLoader::ScheduleQido(boost::shared_ptr<LoadedDicomResources> target, | |
680 int priority, | |
681 const DicomSource& source, | |
682 Orthanc::ResourceType level, | |
683 const Orthanc::DicomMap& filter, | |
684 const std::set<Orthanc::DicomTag>& includeTags, | |
685 Orthanc::IDynamicObject* userPayload) | |
686 { | |
687 boost::shared_ptr<Orthanc::IDynamicObject> protection(userPayload); | |
688 | |
689 if (!source.IsDicomWeb()) | |
690 { | |
691 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "Not a DICOMweb source"); | |
692 } | |
693 | |
694 std::string uri; | |
695 switch (level) | |
696 { | |
697 case Orthanc::ResourceType_Study: | |
698 uri = "/studies"; | |
699 break; | |
700 | |
701 case Orthanc::ResourceType_Series: | |
702 uri = "/series"; | |
703 break; | |
704 | |
705 case Orthanc::ResourceType_Instance: | |
706 uri = "/instances"; | |
707 break; | |
708 | |
709 default: | |
710 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
711 } | |
712 | |
713 std::set<Orthanc::DicomTag> tags; | |
714 filter.GetTags(tags); | |
715 | |
716 std::map<std::string, std::string> arguments, headers; | |
717 | |
718 for (std::set<Orthanc::DicomTag>::const_iterator it = tags.begin(); it != tags.end(); ++it) | |
719 { | |
720 std::string s; | |
721 if (filter.LookupStringValue(s, *it, false /* no binary */)) | |
722 { | |
723 char buf[16]; | |
724 sprintf(buf, "%04X%04X", it->GetGroup(), it->GetElement()); | |
725 arguments[buf] = s; | |
726 } | |
727 } | |
728 | |
729 SetIncludeTags(arguments, includeTags); | |
730 | |
731 std::auto_ptr<IOracleCommand> command( | |
732 source.CreateDicomWebCommand(uri, arguments, headers, | |
733 new DicomWebHandler(shared_from_this(), target, priority, source, protection))); | |
734 | |
735 | |
736 { | |
737 std::auto_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
738 lock->Schedule(GetSharedObserver(), priority, command.release()); | |
739 } | |
740 } | |
741 | |
742 | |
743 void DicomResourcesLoader::ScheduleLoadOrthancResources(boost::shared_ptr<LoadedDicomResources> target, | |
744 int priority, | |
745 const DicomSource& source, | |
746 Orthanc::ResourceType topLevel, | |
747 const std::string& topId, | |
748 Orthanc::ResourceType bottomLevel, | |
749 Orthanc::IDynamicObject* userPayload) | |
750 { | |
751 boost::shared_ptr<Orthanc::IDynamicObject> protection(userPayload); | |
752 | |
753 if (!source.IsOrthanc()) | |
754 { | |
755 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "Not an Orthanc source"); | |
756 } | |
757 | |
758 bool ok = false; | |
759 | |
760 switch (topLevel) | |
761 { | |
762 case Orthanc::ResourceType_Patient: | |
763 ok = (bottomLevel == Orthanc::ResourceType_Patient || | |
764 bottomLevel == Orthanc::ResourceType_Study || | |
765 bottomLevel == Orthanc::ResourceType_Series || | |
766 bottomLevel == Orthanc::ResourceType_Instance); | |
767 break; | |
768 | |
769 case Orthanc::ResourceType_Study: | |
770 ok = (bottomLevel == Orthanc::ResourceType_Study || | |
771 bottomLevel == Orthanc::ResourceType_Series || | |
772 bottomLevel == Orthanc::ResourceType_Instance); | |
773 break; | |
774 | |
775 case Orthanc::ResourceType_Series: | |
776 ok = (bottomLevel == Orthanc::ResourceType_Series || | |
777 bottomLevel == Orthanc::ResourceType_Instance); | |
778 break; | |
779 | |
780 case Orthanc::ResourceType_Instance: | |
781 ok = (bottomLevel == Orthanc::ResourceType_Instance); | |
782 break; | |
783 | |
784 default: | |
785 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
786 } | |
787 | |
788 if (!ok) | |
789 { | |
790 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
791 } | |
792 | |
793 boost::shared_ptr<unsigned int> remainingCommands(new unsigned int(0)); | |
794 | |
795 if (topLevel == Orthanc::ResourceType_Instance) | |
796 { | |
797 ScheduleLoadOrthancInstanceTags(target, priority, source, topId, remainingCommands, protection); | |
798 } | |
799 else if (topLevel == bottomLevel) | |
800 { | |
801 ScheduleLoadOrthancOneChildInstance(target, priority, source, topLevel, topId, remainingCommands, protection); | |
802 } | |
803 else | |
804 { | |
805 std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand); | |
806 command->SetUri("/" + GetUri(topLevel) + "/" + topId + "/" + GetUri(bottomLevel)); | |
807 command->AcquirePayload(new OrthancAllChildrenInstancesHandler | |
808 (shared_from_this(), target, priority, source, | |
809 remainingCommands, bottomLevel, protection)); | |
810 | |
811 { | |
812 std::auto_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
813 lock->Schedule(GetSharedObserver(), priority, command.release()); | |
814 } | |
815 } | |
816 } | |
817 | |
818 | |
819 void DicomResourcesLoader::ScheduleLoadDicomDir(boost::shared_ptr<LoadedDicomResources> target, | |
820 int priority, | |
821 const DicomSource& source, | |
822 const std::string& path, | |
823 Orthanc::IDynamicObject* userPayload) | |
824 { | |
825 boost::shared_ptr<Orthanc::IDynamicObject> protection(userPayload); | |
826 | |
827 if (!source.IsDicomDir()) | |
828 { | |
829 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "Not a DICOMDIR source"); | |
830 } | |
831 | |
832 if (target->GetIndexedTag() == Orthanc::DICOM_TAG_SOP_INSTANCE_UID) | |
833 { | |
834 LOG(WARNING) << "If loading DICOMDIR, it is advised to index tag " | |
835 << "ReferencedSopInstanceUidInFile (0004,1511)"; | |
836 } | |
837 | |
838 #if ORTHANC_ENABLE_DCMTK == 1 | |
839 std::auto_ptr<ReadFileCommand> command(new ReadFileCommand(path)); | |
840 command->AcquirePayload(new DicomDirHandler(shared_from_this(), target, priority, source, protection)); | |
841 | |
842 { | |
843 std::auto_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
844 lock->Schedule(GetSharedObserver(), priority, command.release()); | |
845 } | |
846 #else | |
847 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, | |
848 "DCMTK is disabled, cannot load DICOMDIR"); | |
849 #endif | |
850 } | |
851 | |
852 | |
853 void DicomResourcesLoader::ScheduleLoadDicomFile(boost::shared_ptr<LoadedDicomResources> target, | |
854 int priority, | |
855 const DicomSource& source, | |
856 const std::string& path, | |
857 bool includePixelData, | |
858 Orthanc::IDynamicObject* userPayload) | |
859 { | |
860 boost::shared_ptr<Orthanc::IDynamicObject> protection(userPayload); | |
861 | |
862 #if ORTHANC_ENABLE_DCMTK == 1 | |
863 std::auto_ptr<ParseDicomFromFileCommand> command(new ParseDicomFromFileCommand(path)); | |
864 command->SetPixelDataIncluded(includePixelData); | |
865 command->AcquirePayload(new Handler(shared_from_this(), target, priority, source, protection)); | |
866 | |
867 { | |
868 std::auto_ptr<ILoadersContext::ILock> lock(context_.Lock()); | |
869 lock->Schedule(GetSharedObserver(), priority, command.release()); | |
870 } | |
871 #else | |
872 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, | |
873 "DCMTK is disabled, cannot load DICOM files"); | |
874 #endif | |
875 } | |
876 | |
877 | |
878 bool DicomResourcesLoader::ScheduleLoadDicomFile(boost::shared_ptr<LoadedDicomResources> target, | |
879 int priority, | |
880 const DicomSource& source, | |
881 const std::string& dicomDirPath, | |
882 const Orthanc::DicomMap& dicomDirEntry, | |
883 bool includePixelData, | |
884 Orthanc::IDynamicObject* userPayload) | |
885 { | |
886 std::auto_ptr<Orthanc::IDynamicObject> protection(userPayload); | |
887 | |
888 #if ORTHANC_ENABLE_DCMTK == 1 | |
889 std::string file; | |
890 if (dicomDirEntry.LookupStringValue(file, Orthanc::DICOM_TAG_REFERENCED_FILE_ID, false)) | |
891 { | |
892 ScheduleLoadDicomFile(target, priority, source, ParseDicomFromFileCommand::GetDicomDirPath(dicomDirPath, file), | |
893 includePixelData, protection.release()); | |
894 return true; | |
895 } | |
896 else | |
897 { | |
898 return false; | |
899 } | |
900 #else | |
901 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, | |
902 "DCMTK is disabled, cannot load DICOM files"); | |
903 #endif | |
904 } | |
905 } |