Mercurial > hg > orthanc
comparison OrthancServer/OrthancInitialization.cpp @ 759:8cfc6119a5bd dicom-rt
integration mainline -> dicom-rt
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 16 Apr 2014 16:04:55 +0200 |
parents | 45715eadc2e0 |
children | 537837f50fbb |
comparison
equal
deleted
inserted
replaced
605:b82292ba2083 | 759:8cfc6119a5bd |
---|---|
1 /** | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
3 * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege, | 3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, |
4 * Belgium | 4 * Belgium |
5 * | 5 * |
6 * This program is free software: you can redistribute it and/or | 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 | 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 | 8 * published by the Free Software Foundation, either version 3 of the |
33 #include "OrthancInitialization.h" | 33 #include "OrthancInitialization.h" |
34 | 34 |
35 #include "../Core/HttpClient.h" | 35 #include "../Core/HttpClient.h" |
36 #include "../Core/OrthancException.h" | 36 #include "../Core/OrthancException.h" |
37 #include "../Core/Toolbox.h" | 37 #include "../Core/Toolbox.h" |
38 #include "DicomProtocol/DicomServer.h" | |
38 #include "ServerEnumerations.h" | 39 #include "ServerEnumerations.h" |
39 | 40 |
40 #include <boost/lexical_cast.hpp> | 41 #include <boost/lexical_cast.hpp> |
41 #include <boost/filesystem.hpp> | 42 #include <boost/filesystem.hpp> |
42 #include <curl/curl.h> | 43 #include <curl/curl.h> |
43 #include <boost/thread.hpp> | 44 #include <boost/thread.hpp> |
44 #include <glog/logging.h> | 45 #include <glog/logging.h> |
45 | 46 |
46 namespace Orthanc | 47 namespace Orthanc |
47 { | 48 { |
48 static const char* CONFIGURATION_FILE = "Configuration.json"; | |
49 | |
50 static boost::mutex globalMutex_; | 49 static boost::mutex globalMutex_; |
51 static std::auto_ptr<Json::Value> configuration_; | 50 static std::auto_ptr<Json::Value> configuration_; |
52 static boost::filesystem::path defaultDirectory_; | 51 static boost::filesystem::path defaultDirectory_; |
53 | 52 |
54 | |
55 static void ReadGlobalConfiguration(const char* configurationFile) | 53 static void ReadGlobalConfiguration(const char* configurationFile) |
56 { | 54 { |
57 configuration_.reset(new Json::Value); | 55 configuration_.reset(new Json::Value); |
58 | 56 |
59 std::string content; | 57 std::string content; |
60 | 58 |
61 if (configurationFile) | 59 if (configurationFile) |
62 { | 60 { |
63 Toolbox::ReadFile(content, configurationFile); | 61 Toolbox::ReadFile(content, configurationFile); |
64 defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path(); | 62 defaultDirectory_ = boost::filesystem::path(configurationFile).parent_path(); |
65 LOG(INFO) << "Using the configuration from: " << configurationFile; | 63 LOG(WARNING) << "Using the configuration from: " << configurationFile; |
66 } | 64 } |
67 else | 65 else |
68 { | 66 { |
69 #if 0 && ORTHANC_STANDALONE == 1 && defined(__linux) | 67 #if ORTHANC_STANDALONE == 1 |
70 // Unused anymore | |
71 // Under Linux, try and open "../../etc/orthanc/Configuration.json" | |
72 try | |
73 { | |
74 boost::filesystem::path p = Toolbox::GetDirectoryOfExecutable(); | |
75 p = p.parent_path().parent_path(); | |
76 p /= "etc"; | |
77 p /= "orthanc"; | |
78 p /= CONFIGURATION_FILE; | |
79 | |
80 Toolbox::ReadFile(content, p.string()); | |
81 LOG(INFO) << "Using the configuration from: " << p.string(); | |
82 } | |
83 catch (OrthancException&) | |
84 { | |
85 // No configuration file found, give up with empty configuration | |
86 LOG(INFO) << "Using the default Orthanc configuration"; | |
87 return; | |
88 } | |
89 | |
90 #elif ORTHANC_STANDALONE == 1 | |
91 // No default path for the standalone configuration | 68 // No default path for the standalone configuration |
92 LOG(INFO) << "Using the default Orthanc configuration"; | 69 LOG(WARNING) << "Using the default Orthanc configuration"; |
93 return; | 70 return; |
94 | 71 |
95 #else | 72 #else |
96 // In a non-standalone build, we use the | 73 // In a non-standalone build, we use the |
97 // "Resources/Configuration.json" from the Orthanc distribution | 74 // "Resources/Configuration.json" from the Orthanc source code |
75 | |
98 try | 76 try |
99 { | 77 { |
100 boost::filesystem::path p = ORTHANC_PATH; | 78 boost::filesystem::path p = ORTHANC_PATH; |
101 p /= "Resources"; | 79 p /= "Resources"; |
102 p /= CONFIGURATION_FILE; | 80 p /= "Configuration.json"; |
103 Toolbox::ReadFile(content, p.string()); | 81 Toolbox::ReadFile(content, p.string()); |
104 LOG(INFO) << "Using the configuration from: " << p.string(); | 82 LOG(WARNING) << "Using the configuration from: " << p.string(); |
105 } | 83 } |
106 catch (OrthancException&) | 84 catch (OrthancException&) |
107 { | 85 { |
108 // No configuration file found, give up with empty configuration | 86 // No configuration file found, give up with empty configuration |
109 LOG(INFO) << "Using the default Orthanc configuration"; | 87 LOG(WARNING) << "Using the default Orthanc configuration"; |
110 return; | 88 return; |
111 } | 89 } |
112 #endif | 90 #endif |
113 } | 91 } |
114 | 92 |
142 | 120 |
143 try | 121 try |
144 { | 122 { |
145 RegisterUserMetadata(metadata, members[i]); | 123 RegisterUserMetadata(metadata, members[i]); |
146 } | 124 } |
147 catch (OrthancException e) | 125 catch (OrthancException&) |
148 { | 126 { |
149 LOG(ERROR) << "Cannot register this user-defined metadata: " << info; | 127 LOG(ERROR) << "Cannot register this user-defined metadata: " << info; |
150 throw e; | 128 throw; |
129 } | |
130 } | |
131 } | |
132 } | |
133 | |
134 | |
135 static void RegisterUserContentType() | |
136 { | |
137 if (configuration_->isMember("UserContentType")) | |
138 { | |
139 const Json::Value& parameter = (*configuration_) ["UserContentType"]; | |
140 | |
141 Json::Value::Members members = parameter.getMemberNames(); | |
142 for (size_t i = 0; i < members.size(); i++) | |
143 { | |
144 std::string info = "\"" + members[i] + "\" = " + parameter[members[i]].toStyledString(); | |
145 LOG(INFO) << "Registering user-defined attachment type: " << info; | |
146 | |
147 if (!parameter[members[i]].asBool()) | |
148 { | |
149 LOG(ERROR) << "Not a number in this user-defined attachment type: " << info; | |
150 throw OrthancException(ErrorCode_BadParameterType); | |
151 } | |
152 | |
153 int contentType = parameter[members[i]].asInt(); | |
154 | |
155 try | |
156 { | |
157 RegisterUserContentType(contentType, members[i]); | |
158 } | |
159 catch (OrthancException&) | |
160 { | |
161 LOG(ERROR) << "Cannot register this user-defined attachment type: " << info; | |
162 throw; | |
151 } | 163 } |
152 } | 164 } |
153 } | 165 } |
154 } | 166 } |
155 | 167 |
163 ReadGlobalConfiguration(configurationFile); | 175 ReadGlobalConfiguration(configurationFile); |
164 | 176 |
165 HttpClient::GlobalInitialize(); | 177 HttpClient::GlobalInitialize(); |
166 | 178 |
167 RegisterUserMetadata(); | 179 RegisterUserMetadata(); |
180 RegisterUserContentType(); | |
181 | |
182 DicomServer::InitializeDictionary(); | |
168 } | 183 } |
169 | 184 |
170 | 185 |
171 | 186 |
172 void OrthancFinalize() | 187 void OrthancFinalize() |
225 } | 240 } |
226 | 241 |
227 | 242 |
228 | 243 |
229 | 244 |
230 void GetDicomModality(const std::string& name, | 245 void GetDicomModalityUsingSymbolicName(const std::string& name, |
231 std::string& aet, | 246 std::string& aet, |
232 std::string& address, | 247 std::string& address, |
233 int& port, | 248 int& port, |
234 ModalityManufacturer& manufacturer) | 249 ModalityManufacturer& manufacturer) |
235 { | 250 { |
236 boost::mutex::scoped_lock lock(globalMutex_); | 251 boost::mutex::scoped_lock lock(globalMutex_); |
237 | 252 |
238 if (!configuration_->isMember("DicomModalities")) | 253 if (!configuration_->isMember("DicomModalities")) |
239 { | 254 { |
240 throw OrthancException(""); | 255 throw OrthancException(ErrorCode_BadFileFormat); |
241 } | 256 } |
242 | 257 |
243 const Json::Value& modalities = (*configuration_) ["DicomModalities"]; | 258 const Json::Value& modalities = (*configuration_) ["DicomModalities"]; |
244 if (modalities.type() != Json::objectValue || | 259 if (modalities.type() != Json::objectValue || |
245 !modalities.isMember(name) || | 260 !modalities.isMember(name) || |
246 (modalities[name].size() != 3 && modalities[name].size() != 4)) | 261 (modalities[name].size() != 3 && modalities[name].size() != 4)) |
247 { | 262 { |
248 throw OrthancException(""); | 263 throw OrthancException(ErrorCode_BadFileFormat); |
249 } | 264 } |
250 | 265 |
251 try | 266 try |
252 { | 267 { |
253 aet = modalities[name].get(0u, "").asString(); | 268 aet = modalities[name].get(0u, "").asString(); |
254 address = modalities[name].get(1u, "").asString(); | 269 address = modalities[name].get(1u, "").asString(); |
255 port = modalities[name].get(2u, "").asInt(); | 270 |
271 const Json::Value& portValue = modalities[name].get(2u, ""); | |
272 try | |
273 { | |
274 port = portValue.asInt(); | |
275 } | |
276 catch (std::runtime_error /* error inside JsonCpp */) | |
277 { | |
278 try | |
279 { | |
280 port = boost::lexical_cast<int>(portValue.asString()); | |
281 } | |
282 catch (boost::bad_lexical_cast) | |
283 { | |
284 throw OrthancException(ErrorCode_BadFileFormat); | |
285 } | |
286 } | |
256 | 287 |
257 if (modalities[name].size() == 4) | 288 if (modalities[name].size() == 4) |
258 { | 289 { |
259 manufacturer = StringToModalityManufacturer(modalities[name].get(3u, "").asString()); | 290 manufacturer = StringToModalityManufacturer(modalities[name].get(3u, "").asString()); |
260 } | 291 } |
261 else | 292 else |
262 { | 293 { |
263 manufacturer = ModalityManufacturer_Generic; | 294 manufacturer = ModalityManufacturer_Generic; |
264 } | 295 } |
265 } | 296 } |
266 catch (...) | 297 catch (OrthancException& e) |
267 { | 298 { |
268 throw OrthancException("Badly formatted DICOM modality"); | 299 LOG(ERROR) << "Syntax error in the definition of modality \"" << name |
300 << "\". Please check your configuration file."; | |
301 throw e; | |
269 } | 302 } |
270 } | 303 } |
271 | 304 |
272 | 305 |
273 | 306 |
278 { | 311 { |
279 boost::mutex::scoped_lock lock(globalMutex_); | 312 boost::mutex::scoped_lock lock(globalMutex_); |
280 | 313 |
281 if (!configuration_->isMember("OrthancPeers")) | 314 if (!configuration_->isMember("OrthancPeers")) |
282 { | 315 { |
283 throw OrthancException(""); | 316 throw OrthancException(ErrorCode_BadFileFormat); |
284 } | |
285 | |
286 const Json::Value& modalities = (*configuration_) ["OrthancPeers"]; | |
287 if (modalities.type() != Json::objectValue || | |
288 !modalities.isMember(name)) | |
289 { | |
290 throw OrthancException(""); | |
291 } | 317 } |
292 | 318 |
293 try | 319 try |
294 { | 320 { |
295 url = modalities[name].get(0u, "").asString(); | 321 const Json::Value& modalities = (*configuration_) ["OrthancPeers"]; |
296 | 322 if (modalities.type() != Json::objectValue || |
297 if (modalities[name].size() == 1) | 323 !modalities.isMember(name)) |
298 { | |
299 username = ""; | |
300 password = ""; | |
301 } | |
302 else if (modalities[name].size() == 3) | |
303 { | |
304 username = modalities[name].get(1u, "").asString(); | |
305 password = modalities[name].get(2u, "").asString(); | |
306 } | |
307 else | |
308 { | 324 { |
309 throw OrthancException(ErrorCode_BadFileFormat); | 325 throw OrthancException(ErrorCode_BadFileFormat); |
310 } | 326 } |
311 } | 327 |
312 catch (...) | 328 try |
313 { | 329 { |
314 throw OrthancException(ErrorCode_BadFileFormat); | 330 url = modalities[name].get(0u, "").asString(); |
315 } | 331 |
316 | 332 if (modalities[name].size() == 1) |
317 if (url.size() != 0 && url[url.size() - 1] != '/') | 333 { |
318 { | 334 username = ""; |
319 url += '/'; | 335 password = ""; |
336 } | |
337 else if (modalities[name].size() == 3) | |
338 { | |
339 username = modalities[name].get(1u, "").asString(); | |
340 password = modalities[name].get(2u, "").asString(); | |
341 } | |
342 else | |
343 { | |
344 throw OrthancException(ErrorCode_BadFileFormat); | |
345 } | |
346 } | |
347 catch (...) | |
348 { | |
349 throw OrthancException(ErrorCode_BadFileFormat); | |
350 } | |
351 | |
352 if (url.size() != 0 && url[url.size() - 1] != '/') | |
353 { | |
354 url += '/'; | |
355 } | |
356 } | |
357 catch (OrthancException& e) | |
358 { | |
359 LOG(ERROR) << "Syntax error in the definition of peer \"" << name | |
360 << "\". Please check your configuration file."; | |
361 throw e; | |
320 } | 362 } |
321 } | 363 } |
322 | 364 |
323 | 365 |
324 static bool ReadKeys(std::set<std::string>& target, | 366 static bool ReadKeys(std::set<std::string>& target, |
419 return (base / relative).string(); | 461 return (base / relative).string(); |
420 | 462 |
421 However, for some unknown reason, some versions of Boost do not | 463 However, for some unknown reason, some versions of Boost do not |
422 make the proper path resolution when "baseDirectory" is an | 464 make the proper path resolution when "baseDirectory" is an |
423 absolute path. So, a hack is used below. | 465 absolute path. So, a hack is used below. |
424 **/ | 466 **/ |
425 | 467 |
426 if (relative.is_absolute()) | 468 if (relative.is_absolute()) |
427 { | 469 { |
428 return relative.string(); | 470 return relative.string(); |
429 } | 471 } |
462 for (Json::Value::ArrayIndex i = 0; i < lst.size(); i++) | 504 for (Json::Value::ArrayIndex i = 0; i < lst.size(); i++) |
463 { | 505 { |
464 target.push_back(lst[i].asString()); | 506 target.push_back(lst[i].asString()); |
465 } | 507 } |
466 } | 508 } |
509 | |
510 | |
511 void ConnectToModalityUsingSymbolicName(DicomUserConnection& connection, | |
512 const std::string& name) | |
513 { | |
514 std::string aet, address; | |
515 int port; | |
516 ModalityManufacturer manufacturer; | |
517 GetDicomModalityUsingSymbolicName(name, aet, address, port, manufacturer); | |
518 | |
519 LOG(WARNING) << "Connecting to remote DICOM modality: AET=" << aet << ", address=" << address << ", port=" << port; | |
520 | |
521 connection.SetLocalApplicationEntityTitle(GetGlobalStringParameter("DicomAet", "ORTHANC")); | |
522 connection.SetDistantApplicationEntityTitle(aet); | |
523 connection.SetDistantHost(address); | |
524 connection.SetDistantPort(port); | |
525 connection.SetDistantManufacturer(manufacturer); | |
526 connection.Open(); | |
527 } | |
528 | |
529 | |
530 bool IsSameAETitle(const std::string& aet1, | |
531 const std::string& aet2) | |
532 { | |
533 if (GetGlobalBoolParameter("StrictAetComparison", false)) | |
534 { | |
535 // Case-sensitive matching | |
536 return aet1 == aet2; | |
537 } | |
538 else | |
539 { | |
540 // Case-insensitive matching (default) | |
541 std::string tmp1, tmp2; | |
542 Toolbox::ToLowerCase(tmp1, aet1); | |
543 Toolbox::ToLowerCase(tmp2, aet2); | |
544 return tmp1 == tmp2; | |
545 } | |
546 } | |
547 | |
548 | |
549 bool LookupDicomModalityUsingAETitle(const std::string& aet, | |
550 std::string& symbolicName, | |
551 std::string& address, | |
552 int& port, | |
553 ModalityManufacturer& manufacturer) | |
554 { | |
555 std::set<std::string> modalities; | |
556 GetListOfDicomModalities(modalities); | |
557 | |
558 for (std::set<std::string>::const_iterator | |
559 it = modalities.begin(); it != modalities.end(); ++it) | |
560 { | |
561 try | |
562 { | |
563 std::string thisAet; | |
564 GetDicomModalityUsingSymbolicName(*it, thisAet, address, port, manufacturer); | |
565 | |
566 if (IsSameAETitle(aet, thisAet)) | |
567 { | |
568 return true; | |
569 } | |
570 } | |
571 catch (OrthancException&) | |
572 { | |
573 } | |
574 } | |
575 | |
576 return false; | |
577 } | |
578 | |
579 | |
580 bool IsKnownAETitle(const std::string& aet) | |
581 { | |
582 std::string symbolicName, address; | |
583 int port; | |
584 ModalityManufacturer manufacturer; | |
585 | |
586 return LookupDicomModalityUsingAETitle(aet, symbolicName, address, port, manufacturer); | |
587 } | |
588 | |
589 | |
590 void ConnectToModalityUsingAETitle(DicomUserConnection& connection, | |
591 const std::string& aet) | |
592 { | |
593 std::string symbolicName, address; | |
594 int port; | |
595 ModalityManufacturer manufacturer; | |
596 | |
597 if (!LookupDicomModalityUsingAETitle(aet, symbolicName, address, port, manufacturer)) | |
598 { | |
599 throw OrthancException("Unknown modality: " + aet); | |
600 } | |
601 | |
602 LOG(WARNING) << "Connecting to remote DICOM modality: AET=" << aet << ", address=" << address << ", port=" << port; | |
603 | |
604 connection.SetLocalApplicationEntityTitle(GetGlobalStringParameter("DicomAet", "ORTHANC")); | |
605 connection.SetDistantApplicationEntityTitle(aet); | |
606 connection.SetDistantHost(address); | |
607 connection.SetDistantPort(port); | |
608 connection.SetDistantManufacturer(manufacturer); | |
609 connection.Open(); | |
610 } | |
467 } | 611 } |