comparison OrthancStone/Sources/Loaders/SeriesMetadataLoader.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/SeriesMetadataLoader.cpp@4db187d29731
children 85e117739eca
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 "SeriesMetadataLoader.h"
23
24 #include <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 SuccessMessage 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 SuccessMessage 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::SuccessMessage::SuccessMessage(
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<SeriesMetadataLoader> SeriesMetadataLoader::Create(ILoadersContext::ILock& context)
191 {
192 boost::shared_ptr<DicomResourcesLoader> loader(DicomResourcesLoader::Create(context));
193
194 boost::shared_ptr<SeriesMetadataLoader> obj(new SeriesMetadataLoader(loader));
195 obj->Register<DicomResourcesLoader::SuccessMessage>(*loader, &SeriesMetadataLoader::Handle);
196 return obj;
197 }
198
199
200 SeriesMetadataLoader::Accessor::Accessor(SeriesMetadataLoader& that,
201 const std::string& seriesInstanceUid)
202 {
203 Series::const_iterator found = that.series_.find(seriesInstanceUid);
204 if (found != that.series_.end())
205 {
206 assert(found->second != NULL);
207 series_ = found->second;
208 }
209 }
210
211
212 size_t SeriesMetadataLoader::Accessor::GetInstancesCount() const
213 {
214 if (IsComplete())
215 {
216 return series_->GetSize();
217 }
218 else
219 {
220 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
221 }
222 }
223
224
225 const Orthanc::DicomMap& SeriesMetadataLoader::Accessor::GetInstance(size_t index) const
226 {
227 if (IsComplete())
228 {
229 return series_->GetResource(index);
230 }
231 else
232 {
233 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
234 }
235 }
236
237
238 void SeriesMetadataLoader::ScheduleLoadSeries(int priority,
239 const DicomSource& source,
240 const std::string& studyInstanceUid,
241 const std::string& seriesInstanceUid)
242 {
243 if (state_ != State_Setup &&
244 state_ != State_Default)
245 {
246 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
247 "The loader is working in DICOMDIR state");
248 }
249
250 state_ = State_Default;
251
252 // Only re-schedule the loading if the previous loading was with lower priority
253 if (!IsScheduledWithHigherPriority(seriesInstanceUid, priority))
254 {
255 if (source.IsDicomWeb())
256 {
257 boost::shared_ptr<LoadedDicomResources> target
258 (new LoadedDicomResources(Orthanc::DICOM_TAG_SOP_INSTANCE_UID));
259 loader_->ScheduleGetDicomWeb(
260 target, priority, source,
261 "/studies/" + studyInstanceUid + "/series/" + seriesInstanceUid + "/metadata",
262 NULL /* TODO PAYLOAD */);
263
264 scheduled_[seriesInstanceUid] = priority;
265 }
266 else if (source.IsOrthanc())
267 {
268 // This flavor of the method is only available with DICOMweb, as
269 // Orthanc requires the "PatientID" to be known
270 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
271 "The PatientID must be provided on Orthanc sources");
272 }
273 else
274 {
275 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
276 }
277 }
278 }
279
280
281 void SeriesMetadataLoader::ScheduleLoadSeries(int priority,
282 const DicomSource& source,
283 const std::string& patientId,
284 const std::string& studyInstanceUid,
285 const std::string& seriesInstanceUid)
286 {
287 if (state_ != State_Setup &&
288 state_ != State_Default)
289 {
290 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
291 "The loader is working in DICOMDIR state");
292 }
293
294 state_ = State_Default;
295
296 if (source.IsDicomWeb())
297 {
298 ScheduleLoadSeries(priority, source, studyInstanceUid, seriesInstanceUid);
299 }
300 else if (!IsScheduledWithHigherPriority(seriesInstanceUid, priority))
301 {
302 if (source.IsOrthanc())
303 {
304 // Dummy SOP Instance UID, as we are working at the "series" level
305 Orthanc::DicomInstanceHasher hasher(patientId, studyInstanceUid, seriesInstanceUid, "dummy");
306
307 boost::shared_ptr<LoadedDicomResources> target
308 (new LoadedDicomResources(Orthanc::DICOM_TAG_SOP_INSTANCE_UID));
309
310 loader_->ScheduleLoadOrthancResources(target, priority, source, Orthanc::ResourceType_Series,
311 hasher.HashSeries(), Orthanc::ResourceType_Instance,
312 NULL /* TODO PAYLOAD */);
313
314 scheduled_[seriesInstanceUid] = priority;
315 }
316 else
317 {
318 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
319 }
320 }
321 }
322
323
324 void SeriesMetadataLoader::ScheduleLoadDicomDir(int priority,
325 const DicomSource& source,
326 const std::string& path)
327 {
328 if (!source.IsDicomDir())
329 {
330 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
331 }
332
333 if (state_ != State_Setup)
334 {
335 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
336 "The loader cannot load two different DICOMDIR");
337 }
338
339 state_ = State_DicomDir;
340 dicomDirPath_ = path;
341 boost::shared_ptr<LoadedDicomResources> dicomDir
342 (new LoadedDicomResources(Orthanc::DICOM_TAG_REFERENCED_SOP_INSTANCE_UID_IN_FILE));
343 loader_->ScheduleLoadDicomDir(dicomDir, priority, source, path,
344 NULL /* TODO PAYLOAD */);
345 }
346 }