comparison Framework/Loaders/SeriesMetadataLoader.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 "SeriesMetadataLoader.h"
23
24 #include <Core/DicomFormat/DicomInstanceHasher.h>
25
26 namespace OrthancStone
27 {
28 SeriesMetadataLoader::SeriesMetadataLoader(boost::shared_ptr<DicomResourcesLoader>& loader) :
29 loader_(loader),
30 state_(State_Setup)
31 {
32 }
33
34
35 bool SeriesMetadataLoader::IsScheduledWithHigherPriority(const std::string& seriesInstanceUid,
36 int priority) const
37 {
38 if (series_.find(seriesInstanceUid) != series_.end())
39 {
40 // This series is readily available
41 return true;
42 }
43 else
44 {
45 std::map<std::string, int>::const_iterator found = scheduled_.find(seriesInstanceUid);
46
47 return (found != scheduled_.end() &&
48 found->second < priority);
49 }
50 }
51
52
53 void SeriesMetadataLoader::Handle(const DicomResourcesLoader::SuccessMessage& message)
54 {
55 assert(message.GetResources());
56
57 switch (state_)
58 {
59 case State_Setup:
60 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
61
62 case State_Default:
63 {
64 std::string studyInstanceUid;
65 std::string seriesInstanceUid;
66
67 if (message.GetResources()->LookupTagValueConsensus(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID) &&
68 message.GetResources()->LookupTagValueConsensus(seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID))
69 {
70 series_[seriesInstanceUid] = message.GetResources();
71
72 SeriesLoadedMessage loadedMessage(*this, message.GetDicomSource(), studyInstanceUid,
73 seriesInstanceUid, *message.GetResources());
74 BroadcastMessage(loadedMessage);
75 }
76
77 break;
78 }
79
80 case State_DicomDir:
81 {
82 assert(!dicomDir_);
83 assert(seriesSize_.empty());
84
85 dicomDir_ = message.GetResources();
86
87 for (size_t i = 0; i < message.GetResources()->GetSize(); i++)
88 {
89 std::string seriesInstanceUid;
90 if (message.GetResources()->GetResource(i).LookupStringValue
91 (seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false))
92 {
93 boost::shared_ptr<OrthancStone::LoadedDicomResources> target
94 (new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_SOP_INSTANCE_UID));
95
96 if (loader_->ScheduleLoadDicomFile(target, message.GetPriority(), message.GetDicomSource(), dicomDirPath_,
97 message.GetResources()->GetResource(i), false /* no need for pixel data */,
98 NULL /* TODO PAYLOAD */))
99 {
100 std::map<std::string, unsigned int>::iterator found = seriesSize_.find(seriesInstanceUid);
101 if (found == seriesSize_.end())
102 {
103 series_[seriesInstanceUid].reset
104 (new OrthancStone::LoadedDicomResources(Orthanc::DICOM_TAG_SOP_INSTANCE_UID));
105 seriesSize_[seriesInstanceUid] = 1;
106 }
107 else
108 {
109 found->second ++;
110 }
111 }
112 }
113 }
114
115 LOG(INFO) << "Read a DICOMDIR containing " << seriesSize_.size() << " series";
116
117 state_ = State_DicomFile;
118 break;
119 }
120
121 case State_DicomFile:
122 {
123 assert(dicomDir_);
124 assert(message.GetResources()->GetSize() <= 1); // Could be zero if corrupted DICOM instance
125
126 if (message.GetResources()->GetSize() == 1)
127 {
128 const Orthanc::DicomMap& instance = message.GetResources()->GetResource(0);
129
130 std::string studyInstanceUid;
131 std::string seriesInstanceUid;
132 if (instance.LookupStringValue(studyInstanceUid, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) &&
133 instance.LookupStringValue(seriesInstanceUid, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false))
134 {
135 Series::const_iterator series = series_.find(seriesInstanceUid);
136 std::map<std::string, unsigned int>::const_iterator size = seriesSize_.find(seriesInstanceUid);
137
138 if (series == series_.end() ||
139 size == seriesSize_.end())
140 {
141 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
142 }
143 else
144 {
145 series->second->AddResource(instance);
146
147 if (series->second->GetSize() > size->second)
148 {
149 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
150 }
151 else if (series->second->GetSize() == size->second)
152 {
153 // The series is complete
154 SeriesLoadedMessage loadedMessage(
155 *this, message.GetDicomSource(),
156 studyInstanceUid, seriesInstanceUid, *series->second);
157 loadedMessage.SetDicomDir(dicomDirPath_, dicomDir_);
158 BroadcastMessage(loadedMessage);
159 }
160 }
161 }
162 }
163
164 break;
165 }
166
167 default:
168 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
169 }
170 }
171
172
173 SeriesMetadataLoader::SeriesLoadedMessage::SeriesLoadedMessage(
174 const SeriesMetadataLoader& loader,
175 const DicomSource& source,
176 const std::string& studyInstanceUid,
177 const std::string& seriesInstanceUid,
178 LoadedDicomResources& instances) :
179 OriginMessage(loader),
180 source_(source),
181 studyInstanceUid_(studyInstanceUid),
182 seriesInstanceUid_(seriesInstanceUid),
183 instances_(instances)
184 {
185 LOG(INFO) << "Loaded series " << seriesInstanceUid
186 << ", number of instances: " << instances_.GetSize();
187 }
188
189
190 boost::shared_ptr<IObserver> SeriesMetadataLoader::Factory::Create(ILoadersContext::ILock& context)
191 {
192 DicomResourcesLoader::Factory factory;
193 boost::shared_ptr<DicomResourcesLoader> loader
194 (boost::dynamic_pointer_cast<DicomResourcesLoader>(factory.Create(context)));
195
196 boost::shared_ptr<SeriesMetadataLoader> obj(new SeriesMetadataLoader(loader));
197 obj->Register<DicomResourcesLoader::SuccessMessage>(*loader, &SeriesMetadataLoader::Handle);
198 return obj;
199 }
200
201
202 SeriesMetadataLoader::Accessor::Accessor(SeriesMetadataLoader& that,
203 const std::string& seriesInstanceUid)
204 {
205 Series::const_iterator found = that.series_.find(seriesInstanceUid);
206 if (found != that.series_.end())
207 {
208 assert(found->second != NULL);
209 series_ = found->second;
210 }
211 }
212
213
214 size_t SeriesMetadataLoader::Accessor::GetInstancesCount() const
215 {
216 if (IsComplete())
217 {
218 return series_->GetSize();
219 }
220 else
221 {
222 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
223 }
224 }
225
226
227 const Orthanc::DicomMap& SeriesMetadataLoader::Accessor::GetInstance(size_t index) const
228 {
229 if (IsComplete())
230 {
231 return series_->GetResource(index);
232 }
233 else
234 {
235 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
236 }
237 }
238
239
240 void SeriesMetadataLoader::ScheduleLoadSeries(int priority,
241 const DicomSource& source,
242 const std::string& studyInstanceUid,
243 const std::string& seriesInstanceUid)
244 {
245 if (state_ != State_Setup &&
246 state_ != State_Default)
247 {
248 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
249 "The loader is working in DICOMDIR state");
250 }
251
252 state_ = State_Default;
253
254 // Only re-schedule the loading if the previous loading was with lower priority
255 if (!IsScheduledWithHigherPriority(seriesInstanceUid, priority))
256 {
257 if (source.IsDicomWeb())
258 {
259 boost::shared_ptr<LoadedDicomResources> target
260 (new LoadedDicomResources(Orthanc::DICOM_TAG_SOP_INSTANCE_UID));
261 loader_->ScheduleWado(target, priority, source,
262 "/studies/" + studyInstanceUid + "/series/" + seriesInstanceUid + "/metadata",
263 NULL /* TODO PAYLOAD */);
264
265 scheduled_[seriesInstanceUid] = priority;
266 }
267 else if (source.IsOrthanc())
268 {
269 // This flavor of the method is only available with DICOMweb, as
270 // Orthanc requires the "PatientID" to be known
271 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
272 "The PatientID must be provided on Orthanc sources");
273 }
274 else
275 {
276 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
277 }
278 }
279 }
280
281
282 void SeriesMetadataLoader::ScheduleLoadSeries(int priority,
283 const DicomSource& source,
284 const std::string& patientId,
285 const std::string& studyInstanceUid,
286 const std::string& seriesInstanceUid)
287 {
288 if (state_ != State_Setup &&
289 state_ != State_Default)
290 {
291 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
292 "The loader is working in DICOMDIR state");
293 }
294
295 state_ = State_Default;
296
297 if (source.IsDicomWeb())
298 {
299 ScheduleLoadSeries(priority, source, studyInstanceUid, seriesInstanceUid);
300 }
301 else if (!IsScheduledWithHigherPriority(seriesInstanceUid, priority))
302 {
303 if (source.IsOrthanc())
304 {
305 // Dummy SOP Instance UID, as we are working at the "series" level
306 Orthanc::DicomInstanceHasher hasher(patientId, studyInstanceUid, seriesInstanceUid, "dummy");
307
308 boost::shared_ptr<LoadedDicomResources> target
309 (new LoadedDicomResources(Orthanc::DICOM_TAG_SOP_INSTANCE_UID));
310
311 loader_->ScheduleLoadOrthancResources(target, priority, source, Orthanc::ResourceType_Series,
312 hasher.HashSeries(), Orthanc::ResourceType_Instance,
313 NULL /* TODO PAYLOAD */);
314
315 scheduled_[seriesInstanceUid] = priority;
316 }
317 else
318 {
319 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
320 }
321 }
322 }
323
324
325 void SeriesMetadataLoader::ScheduleLoadDicomDir(int priority,
326 const DicomSource& source,
327 const std::string& path)
328 {
329 if (!source.IsDicomDir())
330 {
331 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
332 }
333
334 if (state_ != State_Setup)
335 {
336 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
337 "The loader cannot load two different DICOMDIR");
338 }
339
340 state_ = State_DicomDir;
341 dicomDirPath_ = path;
342 boost::shared_ptr<LoadedDicomResources> dicomDir
343 (new LoadedDicomResources(Orthanc::DICOM_TAG_REFERENCED_SOP_INSTANCE_UID_IN_FILE));
344 loader_->ScheduleLoadDicomDir(dicomDir, priority, source, path,
345 NULL /* TODO PAYLOAD */);
346 }
347 }