Mercurial > hg > orthanc-stone
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 } |