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 }