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 }