Mercurial > hg > orthanc
comparison OrthancServer/OrthancRestApi/OrthancRestModalities.cpp @ 751:5197fd35333c
refactoring of OrthancRestApi
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 14 Apr 2014 11:28:35 +0200 |
parents | |
children | 3bd0589af992 |
comparison
equal
deleted
inserted
replaced
750:4afad8cb94fd | 751:5197fd35333c |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2014 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. | |
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. | |
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 | |
33 #include "OrthancRestApi.h" | |
34 | |
35 #include "../DicomProtocol/DicomUserConnection.h" | |
36 #include "../OrthancInitialization.h" | |
37 #include "../../Core/HttpClient.h" | |
38 | |
39 #include <glog/logging.h> | |
40 | |
41 namespace Orthanc | |
42 { | |
43 // DICOM SCU ---------------------------------------------------------------- | |
44 | |
45 static bool MergeQueryAndTemplate(DicomMap& result, | |
46 const std::string& postData) | |
47 { | |
48 Json::Value query; | |
49 Json::Reader reader; | |
50 | |
51 if (!reader.parse(postData, query) || | |
52 query.type() != Json::objectValue) | |
53 { | |
54 return false; | |
55 } | |
56 | |
57 Json::Value::Members members = query.getMemberNames(); | |
58 for (size_t i = 0; i < members.size(); i++) | |
59 { | |
60 DicomTag t = FromDcmtkBridge::ParseTag(members[i]); | |
61 result.SetValue(t, query[members[i]].asString()); | |
62 } | |
63 | |
64 return true; | |
65 } | |
66 | |
67 static void DicomFindPatient(RestApi::PostCall& call) | |
68 { | |
69 DicomMap m; | |
70 DicomMap::SetupFindPatientTemplate(m); | |
71 if (!MergeQueryAndTemplate(m, call.GetPostBody())) | |
72 { | |
73 return; | |
74 } | |
75 | |
76 DicomUserConnection connection; | |
77 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", "")); | |
78 | |
79 DicomFindAnswers answers; | |
80 connection.FindPatient(answers, m); | |
81 | |
82 Json::Value result; | |
83 answers.ToJson(result); | |
84 call.GetOutput().AnswerJson(result); | |
85 } | |
86 | |
87 static void DicomFindStudy(RestApi::PostCall& call) | |
88 { | |
89 DicomMap m; | |
90 DicomMap::SetupFindStudyTemplate(m); | |
91 if (!MergeQueryAndTemplate(m, call.GetPostBody())) | |
92 { | |
93 return; | |
94 } | |
95 | |
96 if (m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 && | |
97 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) | |
98 { | |
99 return; | |
100 } | |
101 | |
102 DicomUserConnection connection; | |
103 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", "")); | |
104 | |
105 DicomFindAnswers answers; | |
106 connection.FindStudy(answers, m); | |
107 | |
108 Json::Value result; | |
109 answers.ToJson(result); | |
110 call.GetOutput().AnswerJson(result); | |
111 } | |
112 | |
113 static void DicomFindSeries(RestApi::PostCall& call) | |
114 { | |
115 DicomMap m; | |
116 DicomMap::SetupFindSeriesTemplate(m); | |
117 if (!MergeQueryAndTemplate(m, call.GetPostBody())) | |
118 { | |
119 return; | |
120 } | |
121 | |
122 if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 && | |
123 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) || | |
124 m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2) | |
125 { | |
126 return; | |
127 } | |
128 | |
129 DicomUserConnection connection; | |
130 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", "")); | |
131 | |
132 DicomFindAnswers answers; | |
133 connection.FindSeries(answers, m); | |
134 | |
135 Json::Value result; | |
136 answers.ToJson(result); | |
137 call.GetOutput().AnswerJson(result); | |
138 } | |
139 | |
140 static void DicomFindInstance(RestApi::PostCall& call) | |
141 { | |
142 DicomMap m; | |
143 DicomMap::SetupFindInstanceTemplate(m); | |
144 if (!MergeQueryAndTemplate(m, call.GetPostBody())) | |
145 { | |
146 return; | |
147 } | |
148 | |
149 if ((m.GetValue(DICOM_TAG_ACCESSION_NUMBER).AsString().size() <= 2 && | |
150 m.GetValue(DICOM_TAG_PATIENT_ID).AsString().size() <= 2) || | |
151 m.GetValue(DICOM_TAG_STUDY_INSTANCE_UID).AsString().size() <= 2 || | |
152 m.GetValue(DICOM_TAG_SERIES_INSTANCE_UID).AsString().size() <= 2) | |
153 { | |
154 return; | |
155 } | |
156 | |
157 DicomUserConnection connection; | |
158 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", "")); | |
159 | |
160 DicomFindAnswers answers; | |
161 connection.FindInstance(answers, m); | |
162 | |
163 Json::Value result; | |
164 answers.ToJson(result); | |
165 call.GetOutput().AnswerJson(result); | |
166 } | |
167 | |
168 static void DicomFind(RestApi::PostCall& call) | |
169 { | |
170 DicomMap m; | |
171 DicomMap::SetupFindPatientTemplate(m); | |
172 if (!MergeQueryAndTemplate(m, call.GetPostBody())) | |
173 { | |
174 return; | |
175 } | |
176 | |
177 DicomUserConnection connection; | |
178 ConnectToModalityUsingSymbolicName(connection, call.GetUriComponent("id", "")); | |
179 | |
180 DicomFindAnswers patients; | |
181 connection.FindPatient(patients, m); | |
182 | |
183 // Loop over the found patients | |
184 Json::Value result = Json::arrayValue; | |
185 for (size_t i = 0; i < patients.GetSize(); i++) | |
186 { | |
187 Json::Value patient(Json::objectValue); | |
188 FromDcmtkBridge::ToJson(patient, patients.GetAnswer(i)); | |
189 | |
190 DicomMap::SetupFindStudyTemplate(m); | |
191 if (!MergeQueryAndTemplate(m, call.GetPostBody())) | |
192 { | |
193 return; | |
194 } | |
195 m.CopyTagIfExists(patients.GetAnswer(i), DICOM_TAG_PATIENT_ID); | |
196 | |
197 DicomFindAnswers studies; | |
198 connection.FindStudy(studies, m); | |
199 | |
200 patient["Studies"] = Json::arrayValue; | |
201 | |
202 // Loop over the found studies | |
203 for (size_t j = 0; j < studies.GetSize(); j++) | |
204 { | |
205 Json::Value study(Json::objectValue); | |
206 FromDcmtkBridge::ToJson(study, studies.GetAnswer(j)); | |
207 | |
208 DicomMap::SetupFindSeriesTemplate(m); | |
209 if (!MergeQueryAndTemplate(m, call.GetPostBody())) | |
210 { | |
211 return; | |
212 } | |
213 m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_PATIENT_ID); | |
214 m.CopyTagIfExists(studies.GetAnswer(j), DICOM_TAG_STUDY_INSTANCE_UID); | |
215 | |
216 DicomFindAnswers series; | |
217 connection.FindSeries(series, m); | |
218 | |
219 // Loop over the found series | |
220 study["Series"] = Json::arrayValue; | |
221 for (size_t k = 0; k < series.GetSize(); k++) | |
222 { | |
223 Json::Value series2(Json::objectValue); | |
224 FromDcmtkBridge::ToJson(series2, series.GetAnswer(k)); | |
225 study["Series"].append(series2); | |
226 } | |
227 | |
228 patient["Studies"].append(study); | |
229 } | |
230 | |
231 result.append(patient); | |
232 } | |
233 | |
234 call.GetOutput().AnswerJson(result); | |
235 } | |
236 | |
237 | |
238 static bool GetInstancesToExport(std::list<std::string>& instances, | |
239 const std::string& remote, | |
240 RestApi::PostCall& call) | |
241 { | |
242 ServerContext& context = OrthancRestApi::GetContext(call); | |
243 | |
244 std::string stripped = Toolbox::StripSpaces(call.GetPostBody()); | |
245 | |
246 Json::Value request; | |
247 if (Toolbox::IsSHA1(stripped)) | |
248 { | |
249 // This is for compatibility with Orthanc <= 0.5.1. | |
250 request = stripped; | |
251 } | |
252 else if (!call.ParseJsonRequest(request)) | |
253 { | |
254 // Bad JSON request | |
255 return false; | |
256 } | |
257 | |
258 if (request.isString()) | |
259 { | |
260 context.GetIndex().LogExportedResource(request.asString(), remote); | |
261 context.GetIndex().GetChildInstances(instances, request.asString()); | |
262 } | |
263 else if (request.isArray()) | |
264 { | |
265 for (Json::Value::ArrayIndex i = 0; i < request.size(); i++) | |
266 { | |
267 if (!request[i].isString()) | |
268 { | |
269 return false; | |
270 } | |
271 | |
272 std::string stripped = Toolbox::StripSpaces(request[i].asString()); | |
273 if (!Toolbox::IsSHA1(stripped)) | |
274 { | |
275 return false; | |
276 } | |
277 | |
278 context.GetIndex().LogExportedResource(stripped, remote); | |
279 | |
280 std::list<std::string> tmp; | |
281 context.GetIndex().GetChildInstances(tmp, stripped); | |
282 | |
283 for (std::list<std::string>::const_iterator | |
284 it = tmp.begin(); it != tmp.end(); ++it) | |
285 { | |
286 instances.push_back(*it); | |
287 } | |
288 } | |
289 } | |
290 else | |
291 { | |
292 // Neither a string, nor a list of strings. Bad request. | |
293 return false; | |
294 } | |
295 | |
296 return true; | |
297 } | |
298 | |
299 | |
300 static void DicomStore(RestApi::PostCall& call) | |
301 { | |
302 ServerContext& context = OrthancRestApi::GetContext(call); | |
303 | |
304 std::string remote = call.GetUriComponent("id", ""); | |
305 | |
306 std::list<std::string> instances; | |
307 if (!GetInstancesToExport(instances, remote, call)) | |
308 { | |
309 return; | |
310 } | |
311 | |
312 DicomUserConnection connection; | |
313 ConnectToModalityUsingSymbolicName(connection, remote); | |
314 | |
315 for (std::list<std::string>::const_iterator | |
316 it = instances.begin(); it != instances.end(); ++it) | |
317 { | |
318 LOG(INFO) << "Sending resource " << *it << " to modality \"" << remote << "\""; | |
319 | |
320 std::string dicom; | |
321 context.ReadFile(dicom, *it, FileContentType_Dicom); | |
322 connection.Store(dicom); | |
323 } | |
324 | |
325 call.GetOutput().AnswerBuffer("{}", "application/json"); | |
326 } | |
327 | |
328 | |
329 // Orthanc Peers ------------------------------------------------------------ | |
330 | |
331 static bool IsExistingPeer(const OrthancRestApi::SetOfStrings& peers, | |
332 const std::string& id) | |
333 { | |
334 return peers.find(id) != peers.end(); | |
335 } | |
336 | |
337 static void ListPeers(RestApi::GetCall& call) | |
338 { | |
339 OrthancRestApi::SetOfStrings peers; | |
340 GetListOfOrthancPeers(peers); | |
341 | |
342 Json::Value result = Json::arrayValue; | |
343 for (OrthancRestApi::SetOfStrings::const_iterator | |
344 it = peers.begin(); it != peers.end(); ++it) | |
345 { | |
346 result.append(*it); | |
347 } | |
348 | |
349 call.GetOutput().AnswerJson(result); | |
350 } | |
351 | |
352 static void ListPeerOperations(RestApi::GetCall& call) | |
353 { | |
354 OrthancRestApi::SetOfStrings peers; | |
355 GetListOfOrthancPeers(peers); | |
356 | |
357 std::string id = call.GetUriComponent("id", ""); | |
358 if (IsExistingPeer(peers, id)) | |
359 { | |
360 Json::Value result = Json::arrayValue; | |
361 result.append("store"); | |
362 call.GetOutput().AnswerJson(result); | |
363 } | |
364 } | |
365 | |
366 static void PeerStore(RestApi::PostCall& call) | |
367 { | |
368 ServerContext& context = OrthancRestApi::GetContext(call); | |
369 | |
370 std::string remote = call.GetUriComponent("id", ""); | |
371 | |
372 std::list<std::string> instances; | |
373 if (!GetInstancesToExport(instances, remote, call)) | |
374 { | |
375 return; | |
376 } | |
377 | |
378 std::string url, username, password; | |
379 GetOrthancPeer(remote, url, username, password); | |
380 | |
381 // Configure the HTTP client | |
382 HttpClient client; | |
383 if (username.size() != 0 && password.size() != 0) | |
384 { | |
385 client.SetCredentials(username.c_str(), password.c_str()); | |
386 } | |
387 | |
388 client.SetUrl(url + "instances"); | |
389 client.SetMethod(HttpMethod_Post); | |
390 | |
391 // Loop over the instances that are to be sent | |
392 for (std::list<std::string>::const_iterator | |
393 it = instances.begin(); it != instances.end(); ++it) | |
394 { | |
395 LOG(INFO) << "Sending resource " << *it << " to peer \"" << remote << "\""; | |
396 | |
397 context.ReadFile(client.AccessPostData(), *it, FileContentType_Dicom); | |
398 | |
399 std::string answer; | |
400 if (!client.Apply(answer)) | |
401 { | |
402 LOG(ERROR) << "Unable to send resource " << *it << " to peer \"" << remote << "\""; | |
403 return; | |
404 } | |
405 } | |
406 | |
407 call.GetOutput().AnswerBuffer("{}", "application/json"); | |
408 } | |
409 | |
410 | |
411 // DICOM bridge ------------------------------------------------------------- | |
412 | |
413 static bool IsExistingModality(const OrthancRestApi::SetOfStrings& modalities, | |
414 const std::string& id) | |
415 { | |
416 return modalities.find(id) != modalities.end(); | |
417 } | |
418 | |
419 static void ListModalities(RestApi::GetCall& call) | |
420 { | |
421 OrthancRestApi::SetOfStrings modalities; | |
422 GetListOfDicomModalities(modalities); | |
423 | |
424 Json::Value result = Json::arrayValue; | |
425 for (OrthancRestApi::SetOfStrings::const_iterator | |
426 it = modalities.begin(); it != modalities.end(); ++it) | |
427 { | |
428 result.append(*it); | |
429 } | |
430 | |
431 call.GetOutput().AnswerJson(result); | |
432 } | |
433 | |
434 | |
435 static void ListModalityOperations(RestApi::GetCall& call) | |
436 { | |
437 OrthancRestApi::SetOfStrings modalities; | |
438 GetListOfDicomModalities(modalities); | |
439 | |
440 std::string id = call.GetUriComponent("id", ""); | |
441 if (IsExistingModality(modalities, id)) | |
442 { | |
443 Json::Value result = Json::arrayValue; | |
444 result.append("find-patient"); | |
445 result.append("find-study"); | |
446 result.append("find-series"); | |
447 result.append("find-instance"); | |
448 result.append("find"); | |
449 result.append("store"); | |
450 call.GetOutput().AnswerJson(result); | |
451 } | |
452 } | |
453 | |
454 | |
455 void OrthancRestApi::RegisterModalities() | |
456 { | |
457 Register("/modalities", ListModalities); | |
458 Register("/modalities/{id}", ListModalityOperations); | |
459 Register("/modalities/{id}/find-patient", DicomFindPatient); | |
460 Register("/modalities/{id}/find-study", DicomFindStudy); | |
461 Register("/modalities/{id}/find-series", DicomFindSeries); | |
462 Register("/modalities/{id}/find-instance", DicomFindInstance); | |
463 Register("/modalities/{id}/find", DicomFind); | |
464 Register("/modalities/{id}/store", DicomStore); | |
465 | |
466 Register("/peers", ListPeers); | |
467 Register("/peers/{id}", ListPeerOperations); | |
468 Register("/peers/{id}/store", PeerStore); | |
469 } | |
470 } |