comparison OrthancStone/Sources/Loaders/DicomResourcesLoader.cpp @ 1512:244ad1e4e76a

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