Mercurial > hg > orthanc
annotate OrthancServer/OrthancRestApi.cpp @ 216:e5d5d4a9a326
refactored upload of dicom through http
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 29 Nov 2012 11:57:35 +0100 |
parents | c07170f3f4f7 |
children | 1ac3aacd10a5 |
rev | line source |
---|---|
0 | 1 /** |
62 | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
0 | 3 * Copyright (C) 2012 Medical Physics Department, CHU of Liege, |
4 * Belgium | |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU General Public License as | |
8 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
136 | 10 * |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
0 | 22 * |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
62 | 33 #include "OrthancRestApi.h" |
0 | 34 |
62 | 35 #include "OrthancInitialization.h" |
0 | 36 #include "FromDcmtkBridge.h" |
213
4ce7fdcc8879
access to tags, simplified-tags and file of an instance
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
212
diff
changeset
|
37 #include "ServerToolbox.h" |
0 | 38 #include "../Core/Uuid.h" |
208 | 39 #include "../Core/HttpServer/FilesystemHttpSender.h" |
0 | 40 |
41 #include <dcmtk/dcmdata/dcistrmb.h> | |
42 #include <dcmtk/dcmdata/dcfilefo.h> | |
43 #include <boost/lexical_cast.hpp> | |
44 | |
62 | 45 namespace Orthanc |
0 | 46 { |
47 static void SendJson(HttpOutput& output, | |
48 const Json::Value& value) | |
49 { | |
50 Json::StyledWriter writer; | |
51 std::string s = writer.write(value); | |
52 output.AnswerBufferWithContentType(s, "application/json"); | |
53 } | |
54 | |
62 | 55 void OrthancRestApi::ConnectToModality(DicomUserConnection& c, |
50 | 56 const std::string& name) |
0 | 57 { |
58 std::string aet, address; | |
59 int port; | |
60 GetDicomModality(name, aet, address, port); | |
62 | 61 c.SetLocalApplicationEntityTitle(GetGlobalStringParameter("DicomAet", "ORTHANC")); |
0 | 62 c.SetDistantApplicationEntityTitle(aet); |
63 c.SetDistantHost(address); | |
64 c.SetDistantPort(port); | |
65 c.Open(); | |
66 } | |
67 | |
62 | 68 bool OrthancRestApi::MergeQueryAndTemplate(DicomMap& result, |
50 | 69 const std::string& postData) |
0 | 70 { |
71 Json::Value query; | |
72 Json::Reader reader; | |
73 | |
74 if (!reader.parse(postData, query) || | |
8 | 75 query.type() != Json::objectValue) |
0 | 76 { |
77 return false; | |
78 } | |
79 | |
80 Json::Value::Members members = query.getMemberNames(); | |
81 for (size_t i = 0; i < members.size(); i++) | |
82 { | |
83 DicomTag t = FromDcmtkBridge::FindTag(members[i]); | |
84 result.SetValue(t, query[members[i]].asString()); | |
85 } | |
86 | |
87 return true; | |
88 } | |
89 | |
62 | 90 bool OrthancRestApi::DicomFindPatient(Json::Value& result, |
50 | 91 DicomUserConnection& c, |
92 const std::string& postData) | |
0 | 93 { |
94 DicomMap m; | |
95 DicomMap::SetupFindPatientTemplate(m); | |
96 if (!MergeQueryAndTemplate(m, postData)) | |
97 { | |
98 return false; | |
99 } | |
100 | |
101 DicomFindAnswers answers; | |
102 c.FindPatient(answers, m); | |
103 answers.ToJson(result); | |
104 return true; | |
105 } | |
106 | |
62 | 107 bool OrthancRestApi::DicomFindStudy(Json::Value& result, |
50 | 108 DicomUserConnection& c, |
109 const std::string& postData) | |
0 | 110 { |
111 DicomMap m; | |
112 DicomMap::SetupFindStudyTemplate(m); | |
113 if (!MergeQueryAndTemplate(m, postData)) | |
114 { | |
115 return false; | |
116 } | |
117 | |
80 | 118 if (m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 && |
119 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) | |
0 | 120 { |
121 return false; | |
122 } | |
123 | |
124 DicomFindAnswers answers; | |
125 c.FindStudy(answers, m); | |
126 answers.ToJson(result); | |
127 return true; | |
128 } | |
129 | |
62 | 130 bool OrthancRestApi::DicomFindSeries(Json::Value& result, |
50 | 131 DicomUserConnection& c, |
132 const std::string& postData) | |
0 | 133 { |
134 DicomMap m; | |
135 DicomMap::SetupFindSeriesTemplate(m); | |
136 if (!MergeQueryAndTemplate(m, postData)) | |
137 { | |
138 return false; | |
139 } | |
140 | |
80 | 141 if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 && |
142 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) || | |
143 m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2) | |
0 | 144 { |
145 return false; | |
146 } | |
147 | |
148 DicomFindAnswers answers; | |
149 c.FindSeries(answers, m); | |
150 answers.ToJson(result); | |
151 return true; | |
152 } | |
153 | |
62 | 154 bool OrthancRestApi::DicomFind(Json::Value& result, |
50 | 155 DicomUserConnection& c, |
156 const std::string& postData) | |
0 | 157 { |
158 DicomMap m; | |
159 DicomMap::SetupFindPatientTemplate(m); | |
160 if (!MergeQueryAndTemplate(m, postData)) | |
161 { | |
162 return false; | |
163 } | |
164 | |
165 DicomFindAnswers patients; | |
166 c.FindPatient(patients, m); | |
167 | |
168 // Loop over the found patients | |
169 result = Json::arrayValue; | |
170 for (size_t i = 0; i < patients.GetSize(); i++) | |
171 { | |
172 Json::Value patient(Json::objectValue); | |
173 FromDcmtkBridge::ToJson(patient, patients.GetAnswer(i)); | |
174 | |
175 DicomMap::SetupFindStudyTemplate(m); | |
176 if (!MergeQueryAndTemplate(m, postData)) | |
177 { | |
178 return false; | |
179 } | |
80 | 180 m.CopyTagIfExists(patients.GetAnswer(i), DICOM_TAG_PATIENT_ID); |
0 | 181 |
182 DicomFindAnswers studies; | |
183 c.FindStudy(studies, m); | |
184 | |
185 patient["Studies"] = Json::arrayValue; | |
186 | |
187 // Loop over the found studies | |
188 for (size_t j = 0; j < studies.GetSize(); j++) | |
189 { | |
190 Json::Value study(Json::objectValue); | |
191 FromDcmtkBridge::ToJson(study, studies.GetAnswer(j)); | |
192 | |
193 DicomMap::SetupFindSeriesTemplate(m); | |
194 if (!MergeQueryAndTemplate(m, postData)) | |
195 { | |
196 return false; | |
197 } | |
80 | 198 m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_PATIENT_ID); |
199 m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_STUDY_INSTANCE_UID); | |
0 | 200 |
201 DicomFindAnswers series; | |
202 c.FindSeries(series, m); | |
203 | |
204 // Loop over the found series | |
205 study["Series"] = Json::arrayValue; | |
206 for (size_t k = 0; k < series.GetSize(); k++) | |
207 { | |
208 Json::Value series2(Json::objectValue); | |
209 FromDcmtkBridge::ToJson(series2, series.GetAnswer(k)); | |
210 study["Series"].append(series2); | |
211 } | |
212 | |
213 patient["Studies"].append(study); | |
214 } | |
215 | |
216 result.append(patient); | |
217 } | |
218 | |
219 return true; | |
220 } | |
221 | |
222 | |
223 | |
62 | 224 bool OrthancRestApi::DicomStore(Json::Value& result, |
50 | 225 DicomUserConnection& c, |
226 const std::string& postData) | |
0 | 227 { |
228 Json::Value found(Json::objectValue); | |
229 | |
230 if (!Toolbox::IsUuid(postData)) | |
231 { | |
232 // This is not a UUID, assume this is a DICOM instance | |
233 c.Store(postData); | |
234 } | |
212 | 235 else if (index_.LookupResource(found, postData, ResourceType_Series)) |
0 | 236 { |
237 // The UUID corresponds to a series | |
126 | 238 for (Json::Value::ArrayIndex i = 0; i < found["Instances"].size(); i++) |
0 | 239 { |
240 std::string uuid = found["Instances"][i].asString(); | |
241 Json::Value instance(Json::objectValue); | |
212 | 242 if (index_.LookupResource(instance, uuid, ResourceType_Instance)) |
0 | 243 { |
244 std::string content; | |
245 storage_.ReadFile(content, instance["FileUuid"].asString()); | |
246 c.Store(content); | |
247 } | |
248 else | |
249 { | |
250 return false; | |
251 } | |
252 } | |
253 } | |
212 | 254 else if (index_.LookupResource(found, postData, ResourceType_Instance)) |
0 | 255 { |
256 // The UUID corresponds to an instance | |
257 std::string content; | |
258 storage_.ReadFile(content, found["FileUuid"].asString()); | |
259 c.Store(content); | |
260 } | |
261 else | |
262 { | |
263 return false; | |
264 } | |
265 | |
266 return true; | |
267 } | |
268 | |
269 | |
62 | 270 OrthancRestApi::OrthancRestApi(ServerIndex& index, |
50 | 271 const std::string& path) : |
0 | 272 index_(index), |
273 storage_(path) | |
274 { | |
275 GetListOfDicomModalities(modalities_); | |
276 } | |
277 | |
278 | |
62 | 279 void OrthancRestApi::Handle( |
0 | 280 HttpOutput& output, |
281 const std::string& method, | |
282 const UriComponents& uri, | |
283 const Arguments& headers, | |
207 | 284 const Arguments& getArguments, |
0 | 285 const std::string& postData) |
286 { | |
287 bool existingResource = false; | |
288 Json::Value result(Json::objectValue); | |
289 | |
290 | |
291 // DICOM bridge ------------------------------------------------------------- | |
292 | |
293 if ((uri.size() == 2 || | |
294 uri.size() == 3) && | |
295 uri[0] == "modalities") | |
296 { | |
297 if (modalities_.find(uri[1]) == modalities_.end()) | |
298 { | |
299 // Unknown modality | |
300 } | |
301 else if (uri.size() == 2) | |
302 { | |
303 if (method != "GET") | |
304 { | |
305 output.SendMethodNotAllowedError("POST"); | |
306 return; | |
307 } | |
308 else | |
309 { | |
310 existingResource = true; | |
311 result = Json::arrayValue; | |
312 result.append("find-patient"); | |
313 result.append("find-study"); | |
314 result.append("find-series"); | |
315 result.append("find"); | |
316 result.append("store"); | |
317 } | |
318 } | |
319 else if (uri.size() == 3) | |
320 { | |
321 if (uri[2] != "find-patient" && | |
322 uri[2] != "find-study" && | |
323 uri[2] != "find-series" && | |
324 uri[2] != "find" && | |
325 uri[2] != "store") | |
326 { | |
327 // Unknown request | |
328 } | |
329 else if (method != "POST") | |
330 { | |
331 output.SendMethodNotAllowedError("POST"); | |
332 return; | |
333 } | |
334 else | |
335 { | |
336 DicomUserConnection connection; | |
337 ConnectToModality(connection, uri[1]); | |
338 existingResource = true; | |
339 | |
340 if ((uri[2] == "find-patient" && !DicomFindPatient(result, connection, postData)) || | |
341 (uri[2] == "find-study" && !DicomFindStudy(result, connection, postData)) || | |
342 (uri[2] == "find-series" && !DicomFindSeries(result, connection, postData)) || | |
343 (uri[2] == "find" && !DicomFind(result, connection, postData)) || | |
344 (uri[2] == "store" && !DicomStore(result, connection, postData))) | |
345 { | |
62 | 346 output.SendHeader(Orthanc_HttpStatus_400_BadRequest); |
0 | 347 return; |
348 } | |
349 } | |
350 } | |
351 } | |
352 | |
353 | |
354 if (existingResource) | |
355 { | |
356 SendJson(output, result); | |
357 } | |
358 else | |
359 { | |
62 | 360 output.SendHeader(Orthanc_HttpStatus_404_NotFound); |
0 | 361 } |
362 } | |
363 } |