Mercurial > hg > orthanc
comparison OrthancServer/ServerIndex.cpp @ 1364:111e23bb4904 query-retrieve
integration mainline->query-retrieve
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 21 May 2015 16:58:30 +0200 |
parents | c2c28dd17e87 216db29c5aa9 |
children | b22ba8c5edbe |
comparison
equal
deleted
inserted
replaced
953:f894be6e7cc1 | 1364:111e23bb4904 |
---|---|
1 /** | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, | 3 * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics |
4 * Belgium | 4 * Department, University Hospital of Liege, 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 |
9 * License, or (at your option) any later version. | 9 * License, or (at your option) any later version. |
35 | 35 |
36 #ifndef NOMINMAX | 36 #ifndef NOMINMAX |
37 #define NOMINMAX | 37 #define NOMINMAX |
38 #endif | 38 #endif |
39 | 39 |
40 #include "ServerIndexChange.h" | |
40 #include "EmbeddedResources.h" | 41 #include "EmbeddedResources.h" |
41 #include "OrthancInitialization.h" | 42 #include "OrthancInitialization.h" |
42 #include "../Core/Toolbox.h" | 43 #include "../Core/Toolbox.h" |
43 #include "../Core/Uuid.h" | 44 #include "../Core/Uuid.h" |
44 #include "../Core/DicomFormat/DicomArray.h" | 45 #include "../Core/DicomFormat/DicomArray.h" |
45 #include "../Core/SQLite/Transaction.h" | |
46 #include "FromDcmtkBridge.h" | 46 #include "FromDcmtkBridge.h" |
47 #include "ServerContext.h" | 47 #include "ServerContext.h" |
48 | 48 |
49 #include <boost/lexical_cast.hpp> | 49 #include <boost/lexical_cast.hpp> |
50 #include <stdio.h> | 50 #include <stdio.h> |
57 namespace Internals | 57 namespace Internals |
58 { | 58 { |
59 class ServerIndexListener : public IServerIndexListener | 59 class ServerIndexListener : public IServerIndexListener |
60 { | 60 { |
61 private: | 61 private: |
62 struct FileToRemove | |
63 { | |
64 private: | |
65 std::string uuid_; | |
66 FileContentType type_; | |
67 | |
68 public: | |
69 FileToRemove(const FileInfo& info) : uuid_(info.GetUuid()), | |
70 type_(info.GetContentType()) | |
71 { | |
72 } | |
73 | |
74 const std::string& GetUuid() const | |
75 { | |
76 return uuid_; | |
77 } | |
78 | |
79 FileContentType GetContentType() const | |
80 { | |
81 return type_; | |
82 } | |
83 }; | |
84 | |
62 ServerContext& context_; | 85 ServerContext& context_; |
63 bool hasRemainingLevel_; | 86 bool hasRemainingLevel_; |
64 ResourceType remainingType_; | 87 ResourceType remainingType_; |
65 std::string remainingPublicId_; | 88 std::string remainingPublicId_; |
66 std::list<std::string> pendingFilesToRemove_; | 89 std::list<FileToRemove> pendingFilesToRemove_; |
90 std::list<ServerIndexChange> pendingChanges_; | |
67 uint64_t sizeOfFilesToRemove_; | 91 uint64_t sizeOfFilesToRemove_; |
92 bool insideTransaction_; | |
93 | |
94 void Reset() | |
95 { | |
96 sizeOfFilesToRemove_ = 0; | |
97 hasRemainingLevel_ = false; | |
98 pendingFilesToRemove_.clear(); | |
99 pendingChanges_.clear(); | |
100 } | |
68 | 101 |
69 public: | 102 public: |
70 ServerIndexListener(ServerContext& context) : | 103 ServerIndexListener(ServerContext& context) : context_(context), |
71 context_(context) | 104 insideTransaction_(false) |
72 { | 105 { |
73 Reset(); | 106 Reset(); |
74 assert(ResourceType_Patient < ResourceType_Study && | 107 assert(ResourceType_Patient < ResourceType_Study && |
75 ResourceType_Study < ResourceType_Series && | 108 ResourceType_Study < ResourceType_Series && |
76 ResourceType_Series < ResourceType_Instance); | 109 ResourceType_Series < ResourceType_Instance); |
77 } | 110 } |
78 | 111 |
79 void Reset() | 112 void StartTransaction() |
80 { | 113 { |
81 sizeOfFilesToRemove_ = 0; | 114 Reset(); |
82 hasRemainingLevel_ = false; | 115 insideTransaction_ = true; |
83 pendingFilesToRemove_.clear(); | 116 } |
117 | |
118 void EndTransaction() | |
119 { | |
120 insideTransaction_ = false; | |
84 } | 121 } |
85 | 122 |
86 uint64_t GetSizeOfFilesToRemove() | 123 uint64_t GetSizeOfFilesToRemove() |
87 { | 124 { |
88 return sizeOfFilesToRemove_; | 125 return sizeOfFilesToRemove_; |
89 } | 126 } |
90 | 127 |
91 void CommitFilesToRemove() | 128 void CommitFilesToRemove() |
92 { | 129 { |
93 for (std::list<std::string>::iterator | 130 for (std::list<FileToRemove>::const_iterator |
94 it = pendingFilesToRemove_.begin(); | 131 it = pendingFilesToRemove_.begin(); |
95 it != pendingFilesToRemove_.end(); ++it) | 132 it != pendingFilesToRemove_.end(); ++it) |
96 { | 133 { |
97 context_.RemoveFile(*it); | 134 context_.RemoveFile(it->GetUuid(), it->GetContentType()); |
135 } | |
136 } | |
137 | |
138 void CommitChanges() | |
139 { | |
140 for (std::list<ServerIndexChange>::const_iterator | |
141 it = pendingChanges_.begin(); | |
142 it != pendingChanges_.end(); ++it) | |
143 { | |
144 context_.SignalChange(*it); | |
98 } | 145 } |
99 } | 146 } |
100 | 147 |
101 virtual void SignalRemainingAncestor(ResourceType parentType, | 148 virtual void SignalRemainingAncestor(ResourceType parentType, |
102 const std::string& publicId) | 149 const std::string& publicId) |
103 { | 150 { |
104 LOG(INFO) << "Remaining ancestor \"" << publicId << "\" (" << parentType << ")"; | 151 VLOG(1) << "Remaining ancestor \"" << publicId << "\" (" << parentType << ")"; |
105 | 152 |
106 if (hasRemainingLevel_) | 153 if (hasRemainingLevel_) |
107 { | 154 { |
108 if (parentType < remainingType_) | 155 if (parentType < remainingType_) |
109 { | 156 { |
120 } | 167 } |
121 | 168 |
122 virtual void SignalFileDeleted(const FileInfo& info) | 169 virtual void SignalFileDeleted(const FileInfo& info) |
123 { | 170 { |
124 assert(Toolbox::IsUuid(info.GetUuid())); | 171 assert(Toolbox::IsUuid(info.GetUuid())); |
125 pendingFilesToRemove_.push_back(info.GetUuid()); | 172 pendingFilesToRemove_.push_back(FileToRemove(info)); |
126 sizeOfFilesToRemove_ += info.GetCompressedSize(); | 173 sizeOfFilesToRemove_ += info.GetCompressedSize(); |
174 } | |
175 | |
176 virtual void SignalChange(const ServerIndexChange& change) | |
177 { | |
178 VLOG(1) << "Change related to resource " << change.GetPublicId() << " of type " | |
179 << EnumerationToString(change.GetResourceType()) << ": " | |
180 << EnumerationToString(change.GetChangeType()); | |
181 | |
182 if (insideTransaction_) | |
183 { | |
184 pendingChanges_.push_back(change); | |
185 } | |
186 else | |
187 { | |
188 context_.SignalChange(change); | |
189 } | |
127 } | 190 } |
128 | 191 |
129 bool HasRemainingLevel() const | 192 bool HasRemainingLevel() const |
130 { | 193 { |
131 return hasRemainingLevel_; | 194 return hasRemainingLevel_; |
148 | 211 |
149 class ServerIndex::Transaction | 212 class ServerIndex::Transaction |
150 { | 213 { |
151 private: | 214 private: |
152 ServerIndex& index_; | 215 ServerIndex& index_; |
153 std::auto_ptr<SQLite::Transaction> transaction_; | 216 std::auto_ptr<SQLite::ITransaction> transaction_; |
154 bool isCommitted_; | 217 bool isCommitted_; |
155 | 218 |
156 public: | 219 public: |
157 Transaction(ServerIndex& index) : | 220 Transaction(ServerIndex& index) : |
158 index_(index), | 221 index_(index), |
159 isCommitted_(false) | 222 isCommitted_(false) |
160 { | 223 { |
161 assert(index_.currentStorageSize_ == index_.db_->GetTotalCompressedSize()); | 224 transaction_.reset(index_.db_.StartTransaction()); |
162 | |
163 index_.listener_->Reset(); | |
164 transaction_.reset(index_.db_->StartTransaction()); | |
165 transaction_->Begin(); | 225 transaction_->Begin(); |
226 | |
227 assert(index_.currentStorageSize_ == index_.db_.GetTotalCompressedSize()); | |
228 | |
229 index_.listener_->StartTransaction(); | |
230 } | |
231 | |
232 ~Transaction() | |
233 { | |
234 index_.listener_->EndTransaction(); | |
235 | |
236 if (!isCommitted_) | |
237 { | |
238 transaction_->Rollback(); | |
239 } | |
166 } | 240 } |
167 | 241 |
168 void Commit(uint64_t sizeOfAddedFiles) | 242 void Commit(uint64_t sizeOfAddedFiles) |
169 { | 243 { |
170 if (!isCommitted_) | 244 if (!isCommitted_) |
179 index_.currentStorageSize_ += sizeOfAddedFiles; | 253 index_.currentStorageSize_ += sizeOfAddedFiles; |
180 | 254 |
181 assert(index_.currentStorageSize_ >= index_.listener_->GetSizeOfFilesToRemove()); | 255 assert(index_.currentStorageSize_ >= index_.listener_->GetSizeOfFilesToRemove()); |
182 index_.currentStorageSize_ -= index_.listener_->GetSizeOfFilesToRemove(); | 256 index_.currentStorageSize_ -= index_.listener_->GetSizeOfFilesToRemove(); |
183 | 257 |
184 assert(index_.currentStorageSize_ == index_.db_->GetTotalCompressedSize()); | 258 // Send all the pending changes to the Orthanc plugins |
259 index_.listener_->CommitChanges(); | |
185 | 260 |
186 isCommitted_ = true; | 261 isCommitted_ = true; |
187 } | 262 } |
188 } | 263 } |
189 }; | 264 }; |
190 | 265 |
191 | 266 |
192 struct ServerIndex::UnstableResourcePayload | 267 class ServerIndex::UnstableResourcePayload |
193 { | 268 { |
194 Orthanc::ResourceType type_; | 269 private: |
270 ResourceType type_; | |
271 std::string publicId_; | |
195 boost::posix_time::ptime time_; | 272 boost::posix_time::ptime time_; |
196 | 273 |
197 UnstableResourcePayload() : type_(Orthanc::ResourceType_Instance) | 274 public: |
198 { | 275 UnstableResourcePayload() : type_(ResourceType_Instance) |
199 } | 276 { |
200 | 277 } |
201 UnstableResourcePayload(Orthanc::ResourceType type) : type_(type) | 278 |
279 UnstableResourcePayload(Orthanc::ResourceType type, | |
280 const std::string& publicId) : | |
281 type_(type), | |
282 publicId_(publicId) | |
202 { | 283 { |
203 time_ = boost::posix_time::second_clock::local_time(); | 284 time_ = boost::posix_time::second_clock::local_time(); |
204 } | 285 } |
205 | 286 |
206 unsigned int GetAge() const | 287 unsigned int GetAge() const |
207 { | 288 { |
208 return (boost::posix_time::second_clock::local_time() - time_).total_seconds(); | 289 return (boost::posix_time::second_clock::local_time() - time_).total_seconds(); |
290 } | |
291 | |
292 ResourceType GetResourceType() const | |
293 { | |
294 return type_; | |
295 } | |
296 | |
297 const std::string& GetPublicId() const | |
298 { | |
299 return publicId_; | |
209 } | 300 } |
210 }; | 301 }; |
211 | 302 |
212 | 303 |
213 bool ServerIndex::DeleteResource(Json::Value& target, | 304 bool ServerIndex::DeleteResource(Json::Value& target, |
214 const std::string& uuid, | 305 const std::string& uuid, |
215 ResourceType expectedType) | 306 ResourceType expectedType) |
216 { | 307 { |
217 boost::mutex::scoped_lock lock(mutex_); | 308 boost::mutex::scoped_lock lock(mutex_); |
218 listener_->Reset(); | |
219 | 309 |
220 Transaction t(*this); | 310 Transaction t(*this); |
221 | 311 |
222 int64_t id; | 312 int64_t id; |
223 ResourceType type; | 313 ResourceType type; |
224 if (!db_->LookupResource(uuid, id, type) || | 314 if (!db_.LookupResource(id, type, uuid) || |
225 expectedType != type) | 315 expectedType != type) |
226 { | 316 { |
227 return false; | 317 return false; |
228 } | 318 } |
229 | 319 |
230 db_->DeleteResource(id); | 320 db_.DeleteResource(id); |
231 | 321 |
232 if (listener_->HasRemainingLevel()) | 322 if (listener_->HasRemainingLevel()) |
233 { | 323 { |
234 ResourceType type = listener_->GetRemainingType(); | 324 ResourceType type = listener_->GetRemainingType(); |
235 const std::string& uuid = listener_->GetRemainingPublicId(); | 325 const std::string& uuid = listener_->GetRemainingPublicId(); |
250 } | 340 } |
251 | 341 |
252 | 342 |
253 void ServerIndex::FlushThread(ServerIndex* that) | 343 void ServerIndex::FlushThread(ServerIndex* that) |
254 { | 344 { |
255 unsigned int sleep; | 345 // By default, wait for 10 seconds before flushing |
346 unsigned int sleep = 10; | |
256 | 347 |
257 try | 348 try |
258 { | 349 { |
259 boost::mutex::scoped_lock lock(that->mutex_); | 350 boost::mutex::scoped_lock lock(that->mutex_); |
260 std::string sleepString = that->db_->GetGlobalProperty(GlobalProperty_FlushSleep); | 351 std::string sleepString; |
261 sleep = boost::lexical_cast<unsigned int>(sleepString); | 352 |
353 if (that->db_.LookupGlobalProperty(sleepString, GlobalProperty_FlushSleep) && | |
354 Toolbox::IsInteger(sleepString)) | |
355 { | |
356 sleep = boost::lexical_cast<unsigned int>(sleepString); | |
357 } | |
262 } | 358 } |
263 catch (boost::bad_lexical_cast&) | 359 catch (boost::bad_lexical_cast&) |
264 { | 360 { |
265 // By default, wait for 10 seconds before flushing | |
266 sleep = 10; | |
267 } | 361 } |
268 | 362 |
269 LOG(INFO) << "Starting the database flushing thread (sleep = " << sleep << ")"; | 363 LOG(INFO) << "Starting the database flushing thread (sleep = " << sleep << ")"; |
270 | 364 |
271 unsigned int count = 0; | 365 unsigned int count = 0; |
278 { | 372 { |
279 continue; | 373 continue; |
280 } | 374 } |
281 | 375 |
282 boost::mutex::scoped_lock lock(that->mutex_); | 376 boost::mutex::scoped_lock lock(that->mutex_); |
283 that->db_->FlushToDisk(); | 377 that->db_.FlushToDisk(); |
284 count = 0; | 378 count = 0; |
285 } | 379 } |
286 | 380 |
287 LOG(INFO) << "Stopping the database flushing thread"; | 381 LOG(INFO) << "Stopping the database flushing thread"; |
288 } | 382 } |
289 | 383 |
290 | 384 |
291 static void ComputeExpectedNumberOfInstances(DatabaseWrapper& db, | 385 static void ComputeExpectedNumberOfInstances(IDatabaseWrapper& db, |
292 int64_t series, | 386 int64_t series, |
293 const DicomMap& dicomSummary) | 387 const DicomMap& dicomSummary) |
294 { | 388 { |
295 try | 389 try |
296 { | 390 { |
326 { | 420 { |
327 } | 421 } |
328 } | 422 } |
329 | 423 |
330 | 424 |
425 | |
426 | |
427 bool ServerIndex::GetMetadataAsInteger(int64_t& result, | |
428 int64_t id, | |
429 MetadataType type) | |
430 { | |
431 std::string s; | |
432 if (!db_.LookupMetadata(s, id, type)) | |
433 { | |
434 return false; | |
435 } | |
436 | |
437 try | |
438 { | |
439 result = boost::lexical_cast<int64_t>(s); | |
440 return true; | |
441 } | |
442 catch (boost::bad_lexical_cast&) | |
443 { | |
444 return false; | |
445 } | |
446 } | |
447 | |
448 | |
449 void ServerIndex::LogChange(int64_t internalId, | |
450 ChangeType changeType, | |
451 ResourceType resourceType, | |
452 const std::string& publicId) | |
453 { | |
454 ServerIndexChange change(changeType, resourceType, publicId); | |
455 | |
456 if (changeType <= ChangeType_INTERNAL_LastLogged) | |
457 { | |
458 db_.LogChange(internalId, change); | |
459 } | |
460 | |
461 assert(listener_.get() != NULL); | |
462 listener_->SignalChange(change); | |
463 } | |
464 | |
465 | |
466 uint64_t ServerIndex::IncrementGlobalSequenceInternal(GlobalProperty property) | |
467 { | |
468 std::string oldValue; | |
469 | |
470 if (db_.LookupGlobalProperty(oldValue, property)) | |
471 { | |
472 uint64_t oldNumber; | |
473 | |
474 try | |
475 { | |
476 oldNumber = boost::lexical_cast<uint64_t>(oldValue); | |
477 db_.SetGlobalProperty(property, boost::lexical_cast<std::string>(oldNumber + 1)); | |
478 return oldNumber + 1; | |
479 } | |
480 catch (boost::bad_lexical_cast&) | |
481 { | |
482 throw OrthancException(ErrorCode_InternalError); | |
483 } | |
484 } | |
485 else | |
486 { | |
487 // Initialize the sequence at "1" | |
488 db_.SetGlobalProperty(property, "1"); | |
489 return 1; | |
490 } | |
491 } | |
492 | |
493 | |
494 | |
495 void ServerIndex::SetMainDicomTags(int64_t resource, | |
496 const DicomMap& tags) | |
497 { | |
498 DicomArray flattened(tags); | |
499 for (size_t i = 0; i < flattened.GetSize(); i++) | |
500 { | |
501 const DicomElement& element = flattened.GetElement(i); | |
502 db_.SetMainDicomTag(resource, element.GetTag(), element.GetValue().AsString()); | |
503 } | |
504 } | |
505 | |
506 | |
507 int64_t ServerIndex::CreateResource(const std::string& publicId, | |
508 ResourceType type) | |
509 { | |
510 int64_t id = db_.CreateResource(publicId, type); | |
511 | |
512 ChangeType changeType; | |
513 switch (type) | |
514 { | |
515 case ResourceType_Patient: | |
516 changeType = ChangeType_NewPatient; | |
517 break; | |
518 | |
519 case ResourceType_Study: | |
520 changeType = ChangeType_NewStudy; | |
521 break; | |
522 | |
523 case ResourceType_Series: | |
524 changeType = ChangeType_NewSeries; | |
525 break; | |
526 | |
527 case ResourceType_Instance: | |
528 changeType = ChangeType_NewInstance; | |
529 break; | |
530 | |
531 default: | |
532 throw OrthancException(ErrorCode_InternalError); | |
533 } | |
534 | |
535 ServerIndexChange change(changeType, type, publicId); | |
536 db_.LogChange(id, change); | |
537 | |
538 assert(listener_.get() != NULL); | |
539 listener_->SignalChange(change); | |
540 | |
541 return id; | |
542 } | |
543 | |
544 | |
331 ServerIndex::ServerIndex(ServerContext& context, | 545 ServerIndex::ServerIndex(ServerContext& context, |
332 const std::string& dbPath) : | 546 IDatabaseWrapper& db) : |
333 done_(false), | 547 done_(false), |
548 db_(db), | |
334 maximumStorageSize_(0), | 549 maximumStorageSize_(0), |
335 maximumPatients_(0) | 550 maximumPatients_(0) |
336 { | 551 { |
337 listener_.reset(new Internals::ServerIndexListener(context)); | 552 listener_.reset(new Internals::ServerIndexListener(context)); |
338 | 553 db_.SetListener(*listener_); |
339 if (dbPath == ":memory:") | 554 |
340 { | 555 currentStorageSize_ = db_.GetTotalCompressedSize(); |
341 db_.reset(new DatabaseWrapper(*listener_)); | |
342 } | |
343 else | |
344 { | |
345 boost::filesystem::path p = dbPath; | |
346 | |
347 try | |
348 { | |
349 boost::filesystem::create_directories(p); | |
350 } | |
351 catch (boost::filesystem::filesystem_error) | |
352 { | |
353 } | |
354 | |
355 db_.reset(new DatabaseWrapper(p.string() + "/index", *listener_)); | |
356 } | |
357 | |
358 currentStorageSize_ = db_->GetTotalCompressedSize(); | |
359 | 556 |
360 // Initial recycling if the parameters have changed since the last | 557 // Initial recycling if the parameters have changed since the last |
361 // execution of Orthanc | 558 // execution of Orthanc |
362 StandaloneRecycling(); | 559 StandaloneRecycling(); |
363 | 560 |
364 flushThread_ = boost::thread(FlushThread, this); | 561 if (db.HasFlushToDisk()) |
562 { | |
563 flushThread_ = boost::thread(FlushThread, this); | |
564 } | |
565 | |
365 unstableResourcesMonitorThread_ = boost::thread(UnstableResourcesMonitorThread, this); | 566 unstableResourcesMonitorThread_ = boost::thread(UnstableResourcesMonitorThread, this); |
366 } | 567 } |
367 | 568 |
368 | 569 |
369 ServerIndex::~ServerIndex() | 570 ServerIndex::~ServerIndex() |
370 { | 571 { |
371 done_ = true; | 572 done_ = true; |
372 | 573 |
373 if (flushThread_.joinable()) | 574 if (db_.HasFlushToDisk() && |
575 flushThread_.joinable()) | |
374 { | 576 { |
375 flushThread_.join(); | 577 flushThread_.join(); |
376 } | 578 } |
377 | 579 |
378 if (unstableResourcesMonitorThread_.joinable()) | 580 if (unstableResourcesMonitorThread_.joinable()) |
380 unstableResourcesMonitorThread_.join(); | 582 unstableResourcesMonitorThread_.join(); |
381 } | 583 } |
382 } | 584 } |
383 | 585 |
384 | 586 |
385 StoreStatus ServerIndex::Store(const DicomMap& dicomSummary, | 587 StoreStatus ServerIndex::Store(std::map<MetadataType, std::string>& instanceMetadata, |
588 const DicomMap& dicomSummary, | |
386 const Attachments& attachments, | 589 const Attachments& attachments, |
387 const std::string& remoteAet) | 590 const std::string& remoteAet, |
388 { | 591 const MetadataMap& metadata) |
389 boost::mutex::scoped_lock lock(mutex_); | 592 { |
390 listener_->Reset(); | 593 boost::mutex::scoped_lock lock(mutex_); |
594 | |
595 instanceMetadata.clear(); | |
391 | 596 |
392 DicomInstanceHasher hasher(dicomSummary); | 597 DicomInstanceHasher hasher(dicomSummary); |
393 | 598 |
394 try | 599 try |
395 { | 600 { |
397 | 602 |
398 // Do nothing if the instance already exists | 603 // Do nothing if the instance already exists |
399 { | 604 { |
400 ResourceType type; | 605 ResourceType type; |
401 int64_t tmp; | 606 int64_t tmp; |
402 if (db_->LookupResource(hasher.HashInstance(), tmp, type)) | 607 if (db_.LookupResource(tmp, type, hasher.HashInstance())) |
403 { | 608 { |
404 assert(type == ResourceType_Instance); | 609 assert(type == ResourceType_Instance); |
610 db_.GetAllMetadata(instanceMetadata, tmp); | |
405 return StoreStatus_AlreadyStored; | 611 return StoreStatus_AlreadyStored; |
406 } | 612 } |
407 } | 613 } |
408 | 614 |
409 // Ensure there is enough room in the storage for the new instance | 615 // Ensure there is enough room in the storage for the new instance |
415 } | 621 } |
416 | 622 |
417 Recycle(instanceSize, hasher.HashPatient()); | 623 Recycle(instanceSize, hasher.HashPatient()); |
418 | 624 |
419 // Create the instance | 625 // Create the instance |
420 int64_t instance = db_->CreateResource(hasher.HashInstance(), ResourceType_Instance); | 626 int64_t instance = CreateResource(hasher.HashInstance(), ResourceType_Instance); |
421 | 627 |
422 DicomMap dicom; | 628 DicomMap dicom; |
423 dicomSummary.ExtractInstanceInformation(dicom); | 629 dicomSummary.ExtractInstanceInformation(dicom); |
424 db_->SetMainDicomTags(instance, dicom); | 630 SetMainDicomTags(instance, dicom); |
425 | 631 |
426 // Detect up to which level the patient/study/series/instance | 632 // Detect up to which level the patient/study/series/instance |
427 // hierarchy must be created | 633 // hierarchy must be created |
428 int64_t patient = -1, study = -1, series = -1; | 634 int64_t patient = -1, study = -1, series = -1; |
429 bool isNewPatient = false; | 635 bool isNewPatient = false; |
431 bool isNewSeries = false; | 637 bool isNewSeries = false; |
432 | 638 |
433 { | 639 { |
434 ResourceType dummy; | 640 ResourceType dummy; |
435 | 641 |
436 if (db_->LookupResource(hasher.HashSeries(), series, dummy)) | 642 if (db_.LookupResource(series, dummy, hasher.HashSeries())) |
437 { | 643 { |
438 assert(dummy == ResourceType_Series); | 644 assert(dummy == ResourceType_Series); |
439 // The patient, the study and the series already exist | 645 // The patient, the study and the series already exist |
440 | 646 |
441 bool ok = (db_->LookupResource(hasher.HashPatient(), patient, dummy) && | 647 bool ok = (db_.LookupResource(patient, dummy, hasher.HashPatient()) && |
442 db_->LookupResource(hasher.HashStudy(), study, dummy)); | 648 db_.LookupResource(study, dummy, hasher.HashStudy())); |
443 assert(ok); | 649 assert(ok); |
444 } | 650 } |
445 else if (db_->LookupResource(hasher.HashStudy(), study, dummy)) | 651 else if (db_.LookupResource(study, dummy, hasher.HashStudy())) |
446 { | 652 { |
447 assert(dummy == ResourceType_Study); | 653 assert(dummy == ResourceType_Study); |
448 | 654 |
449 // New series: The patient and the study already exist | 655 // New series: The patient and the study already exist |
450 isNewSeries = true; | 656 isNewSeries = true; |
451 | 657 |
452 bool ok = db_->LookupResource(hasher.HashPatient(), patient, dummy); | 658 bool ok = db_.LookupResource(patient, dummy, hasher.HashPatient()); |
453 assert(ok); | 659 assert(ok); |
454 } | 660 } |
455 else if (db_->LookupResource(hasher.HashPatient(), patient, dummy)) | 661 else if (db_.LookupResource(patient, dummy, hasher.HashPatient())) |
456 { | 662 { |
457 assert(dummy == ResourceType_Patient); | 663 assert(dummy == ResourceType_Patient); |
458 | 664 |
459 // New study and series: The patient already exist | 665 // New study and series: The patient already exist |
460 isNewStudy = true; | 666 isNewStudy = true; |
470 } | 676 } |
471 | 677 |
472 // Create the series if needed | 678 // Create the series if needed |
473 if (isNewSeries) | 679 if (isNewSeries) |
474 { | 680 { |
475 series = db_->CreateResource(hasher.HashSeries(), ResourceType_Series); | 681 series = CreateResource(hasher.HashSeries(), ResourceType_Series); |
476 dicomSummary.ExtractSeriesInformation(dicom); | 682 dicomSummary.ExtractSeriesInformation(dicom); |
477 db_->SetMainDicomTags(series, dicom); | 683 SetMainDicomTags(series, dicom); |
478 } | 684 } |
479 | 685 |
480 // Create the study if needed | 686 // Create the study if needed |
481 if (isNewStudy) | 687 if (isNewStudy) |
482 { | 688 { |
483 study = db_->CreateResource(hasher.HashStudy(), ResourceType_Study); | 689 study = CreateResource(hasher.HashStudy(), ResourceType_Study); |
484 dicomSummary.ExtractStudyInformation(dicom); | 690 dicomSummary.ExtractStudyInformation(dicom); |
485 db_->SetMainDicomTags(study, dicom); | 691 SetMainDicomTags(study, dicom); |
486 } | 692 } |
487 | 693 |
488 // Create the patient if needed | 694 // Create the patient if needed |
489 if (isNewPatient) | 695 if (isNewPatient) |
490 { | 696 { |
491 patient = db_->CreateResource(hasher.HashPatient(), ResourceType_Patient); | 697 patient = CreateResource(hasher.HashPatient(), ResourceType_Patient); |
492 dicomSummary.ExtractPatientInformation(dicom); | 698 dicomSummary.ExtractPatientInformation(dicom); |
493 db_->SetMainDicomTags(patient, dicom); | 699 SetMainDicomTags(patient, dicom); |
494 } | 700 } |
495 | 701 |
496 // Create the parent-to-child links | 702 // Create the parent-to-child links |
497 db_->AttachChild(series, instance); | 703 db_.AttachChild(series, instance); |
498 | 704 |
499 if (isNewSeries) | 705 if (isNewSeries) |
500 { | 706 { |
501 db_->AttachChild(study, series); | 707 db_.AttachChild(study, series); |
502 } | 708 } |
503 | 709 |
504 if (isNewStudy) | 710 if (isNewStudy) |
505 { | 711 { |
506 db_->AttachChild(patient, study); | 712 db_.AttachChild(patient, study); |
507 } | 713 } |
508 | 714 |
509 // Sanity checks | 715 // Sanity checks |
510 assert(patient != -1); | 716 assert(patient != -1); |
511 assert(study != -1); | 717 assert(study != -1); |
514 | 720 |
515 // Attach the files to the newly created instance | 721 // Attach the files to the newly created instance |
516 for (Attachments::const_iterator it = attachments.begin(); | 722 for (Attachments::const_iterator it = attachments.begin(); |
517 it != attachments.end(); ++it) | 723 it != attachments.end(); ++it) |
518 { | 724 { |
519 db_->AddAttachment(instance, *it); | 725 db_.AddAttachment(instance, *it); |
520 } | 726 } |
521 | 727 |
522 // Attach the metadata | 728 // Attach the user-specified metadata |
729 for (MetadataMap::const_iterator | |
730 it = metadata.begin(); it != metadata.end(); ++it) | |
731 { | |
732 switch (it->first.first) | |
733 { | |
734 case ResourceType_Patient: | |
735 db_.SetMetadata(patient, it->first.second, it->second); | |
736 break; | |
737 | |
738 case ResourceType_Study: | |
739 db_.SetMetadata(study, it->first.second, it->second); | |
740 break; | |
741 | |
742 case ResourceType_Series: | |
743 db_.SetMetadata(series, it->first.second, it->second); | |
744 break; | |
745 | |
746 case ResourceType_Instance: | |
747 db_.SetMetadata(instance, it->first.second, it->second); | |
748 instanceMetadata[it->first.second] = it->second; | |
749 break; | |
750 | |
751 default: | |
752 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
753 } | |
754 } | |
755 | |
756 // Attach the auto-computed metadata for the patient/study/series levels | |
523 std::string now = Toolbox::GetNowIsoString(); | 757 std::string now = Toolbox::GetNowIsoString(); |
524 db_->SetMetadata(instance, MetadataType_Instance_ReceptionDate, now); | 758 db_.SetMetadata(series, MetadataType_LastUpdate, now); |
525 db_->SetMetadata(series, MetadataType_LastUpdate, now); | 759 db_.SetMetadata(study, MetadataType_LastUpdate, now); |
526 db_->SetMetadata(study, MetadataType_LastUpdate, now); | 760 db_.SetMetadata(patient, MetadataType_LastUpdate, now); |
527 db_->SetMetadata(patient, MetadataType_LastUpdate, now); | 761 |
528 db_->SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet); | 762 // Attach the auto-computed metadata for the instance level, |
763 // reflecting these additions into the input metadata map | |
764 db_.SetMetadata(instance, MetadataType_Instance_ReceptionDate, now); | |
765 instanceMetadata[MetadataType_Instance_ReceptionDate] = now; | |
766 | |
767 db_.SetMetadata(instance, MetadataType_Instance_RemoteAet, remoteAet); | |
768 instanceMetadata[MetadataType_Instance_RemoteAet] = remoteAet; | |
529 | 769 |
530 const DicomValue* value; | 770 const DicomValue* value; |
531 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || | 771 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_INSTANCE_NUMBER)) != NULL || |
532 (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) | 772 (value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGE_INDEX)) != NULL) |
533 { | 773 { |
534 db_->SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString()); | 774 db_.SetMetadata(instance, MetadataType_Instance_IndexInSeries, value->AsString()); |
535 } | 775 instanceMetadata[MetadataType_Instance_IndexInSeries] = value->AsString(); |
536 | 776 } |
777 | |
778 // Check whether the series of this new instance is now completed | |
537 if (isNewSeries) | 779 if (isNewSeries) |
538 { | 780 { |
539 ComputeExpectedNumberOfInstances(*db_, series, dicomSummary); | 781 ComputeExpectedNumberOfInstances(db_, series, dicomSummary); |
540 } | 782 } |
541 | 783 |
542 // Check whether the series of this new instance is now completed | |
543 SeriesStatus seriesStatus = GetSeriesStatus(series); | 784 SeriesStatus seriesStatus = GetSeriesStatus(series); |
544 if (seriesStatus == SeriesStatus_Complete) | 785 if (seriesStatus == SeriesStatus_Complete) |
545 { | 786 { |
546 db_->LogChange(ChangeType_CompletedSeries, series, ResourceType_Series); | 787 LogChange(series, ChangeType_CompletedSeries, ResourceType_Series, hasher.HashSeries()); |
547 } | 788 } |
548 | 789 |
549 // Mark the parent resources of this instance as unstable | 790 // Mark the parent resources of this instance as unstable |
550 MarkAsUnstable(patient, ResourceType_Patient); | 791 MarkAsUnstable(series, ResourceType_Series, hasher.HashSeries()); |
551 MarkAsUnstable(study, ResourceType_Study); | 792 MarkAsUnstable(study, ResourceType_Study, hasher.HashStudy()); |
552 MarkAsUnstable(series, ResourceType_Series); | 793 MarkAsUnstable(patient, ResourceType_Patient, hasher.HashPatient()); |
553 | 794 |
554 t.Commit(instanceSize); | 795 t.Commit(instanceSize); |
555 | 796 |
556 return StoreStatus_Success; | 797 return StoreStatus_Success; |
557 } | 798 } |
558 catch (OrthancException& e) | 799 catch (OrthancException& e) |
559 { | 800 { |
560 LOG(ERROR) << "EXCEPTION [" << e.What() << "]" | 801 LOG(ERROR) << "EXCEPTION [" << e.What() << "]"; |
561 << " (SQLite status: " << db_->GetErrorMessage() << ")"; | |
562 } | 802 } |
563 | 803 |
564 return StoreStatus_Failure; | 804 return StoreStatus_Failure; |
565 } | 805 } |
566 | 806 |
569 { | 809 { |
570 boost::mutex::scoped_lock lock(mutex_); | 810 boost::mutex::scoped_lock lock(mutex_); |
571 target = Json::objectValue; | 811 target = Json::objectValue; |
572 | 812 |
573 uint64_t cs = currentStorageSize_; | 813 uint64_t cs = currentStorageSize_; |
574 assert(cs == db_->GetTotalCompressedSize()); | 814 assert(cs == db_.GetTotalCompressedSize()); |
575 uint64_t us = db_->GetTotalUncompressedSize(); | 815 uint64_t us = db_.GetTotalUncompressedSize(); |
576 target["TotalDiskSize"] = boost::lexical_cast<std::string>(cs); | 816 target["TotalDiskSize"] = boost::lexical_cast<std::string>(cs); |
577 target["TotalUncompressedSize"] = boost::lexical_cast<std::string>(us); | 817 target["TotalUncompressedSize"] = boost::lexical_cast<std::string>(us); |
578 target["TotalDiskSizeMB"] = boost::lexical_cast<unsigned int>(cs / MEGA_BYTES); | 818 target["TotalDiskSizeMB"] = static_cast<unsigned int>(cs / MEGA_BYTES); |
579 target["TotalUncompressedSizeMB"] = boost::lexical_cast<unsigned int>(us / MEGA_BYTES); | 819 target["TotalUncompressedSizeMB"] = static_cast<unsigned int>(us / MEGA_BYTES); |
580 | 820 |
581 target["CountPatients"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Patient)); | 821 target["CountPatients"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Patient)); |
582 target["CountStudies"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Study)); | 822 target["CountStudies"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Study)); |
583 target["CountSeries"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Series)); | 823 target["CountSeries"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Series)); |
584 target["CountInstances"] = static_cast<unsigned int>(db_->GetResourceCount(ResourceType_Instance)); | 824 target["CountInstances"] = static_cast<unsigned int>(db_.GetResourceCount(ResourceType_Instance)); |
585 } | 825 } |
586 | 826 |
587 | 827 |
588 | 828 |
589 SeriesStatus ServerIndex::GetSeriesStatus(int64_t id) | 829 SeriesStatus ServerIndex::GetSeriesStatus(int64_t id) |
590 { | 830 { |
591 // Get the expected number of instances in this series (from the metadata) | 831 // Get the expected number of instances in this series (from the metadata) |
592 std::string s = db_->GetMetadata(id, MetadataType_Series_ExpectedNumberOfInstances); | 832 int64_t expected; |
593 | 833 if (!GetMetadataAsInteger(expected, id, MetadataType_Series_ExpectedNumberOfInstances)) |
594 size_t expected; | |
595 try | |
596 { | |
597 expected = boost::lexical_cast<size_t>(s); | |
598 } | |
599 catch (boost::bad_lexical_cast&) | |
600 { | 834 { |
601 return SeriesStatus_Unknown; | 835 return SeriesStatus_Unknown; |
602 } | 836 } |
603 | 837 |
604 // Loop over the instances of this series | 838 // Loop over the instances of this series |
605 std::list<int64_t> children; | 839 std::list<int64_t> children; |
606 db_->GetChildrenInternalId(children, id); | 840 db_.GetChildrenInternalId(children, id); |
607 | 841 |
608 std::set<size_t> instances; | 842 std::set<int64_t> instances; |
609 for (std::list<int64_t>::const_iterator | 843 for (std::list<int64_t>::const_iterator |
610 it = children.begin(); it != children.end(); ++it) | 844 it = children.begin(); it != children.end(); ++it) |
611 { | 845 { |
612 // Get the index of this instance in the series | 846 // Get the index of this instance in the series |
613 s = db_->GetMetadata(*it, MetadataType_Instance_IndexInSeries); | 847 int64_t index; |
614 size_t index; | 848 if (!GetMetadataAsInteger(index, *it, MetadataType_Instance_IndexInSeries)) |
615 try | |
616 { | |
617 index = boost::lexical_cast<size_t>(s); | |
618 } | |
619 catch (boost::bad_lexical_cast&) | |
620 { | 849 { |
621 return SeriesStatus_Unknown; | 850 return SeriesStatus_Unknown; |
622 } | 851 } |
623 | 852 |
624 if (!(index > 0 && index <= expected)) | 853 if (!(index > 0 && index <= expected)) |
634 } | 863 } |
635 | 864 |
636 instances.insert(index); | 865 instances.insert(index); |
637 } | 866 } |
638 | 867 |
639 if (instances.size() == expected) | 868 if (static_cast<int64_t>(instances.size()) == expected) |
640 { | 869 { |
641 return SeriesStatus_Complete; | 870 return SeriesStatus_Complete; |
642 } | 871 } |
643 else | 872 else |
644 { | 873 { |
650 | 879 |
651 void ServerIndex::MainDicomTagsToJson(Json::Value& target, | 880 void ServerIndex::MainDicomTagsToJson(Json::Value& target, |
652 int64_t resourceId) | 881 int64_t resourceId) |
653 { | 882 { |
654 DicomMap tags; | 883 DicomMap tags; |
655 db_->GetMainDicomTags(tags, resourceId); | 884 db_.GetMainDicomTags(tags, resourceId); |
656 target["MainDicomTags"] = Json::objectValue; | 885 target["MainDicomTags"] = Json::objectValue; |
657 FromDcmtkBridge::ToJson(target["MainDicomTags"], tags); | 886 FromDcmtkBridge::ToJson(target["MainDicomTags"], tags); |
658 } | 887 } |
659 | 888 |
660 bool ServerIndex::LookupResource(Json::Value& result, | 889 bool ServerIndex::LookupResource(Json::Value& result, |
666 boost::mutex::scoped_lock lock(mutex_); | 895 boost::mutex::scoped_lock lock(mutex_); |
667 | 896 |
668 // Lookup for the requested resource | 897 // Lookup for the requested resource |
669 int64_t id; | 898 int64_t id; |
670 ResourceType type; | 899 ResourceType type; |
671 if (!db_->LookupResource(publicId, id, type) || | 900 if (!db_.LookupResource(id, type, publicId) || |
672 type != expectedType) | 901 type != expectedType) |
673 { | 902 { |
674 return false; | 903 return false; |
675 } | 904 } |
676 | 905 |
677 // Find the parent resource (if it exists) | 906 // Find the parent resource (if it exists) |
678 if (type != ResourceType_Patient) | 907 if (type != ResourceType_Patient) |
679 { | 908 { |
680 int64_t parentId; | 909 int64_t parentId; |
681 if (!db_->LookupParent(parentId, id)) | 910 if (!db_.LookupParent(parentId, id)) |
682 { | 911 { |
683 throw OrthancException(ErrorCode_InternalError); | 912 throw OrthancException(ErrorCode_InternalError); |
684 } | 913 } |
685 | 914 |
686 std::string parent = db_->GetPublicId(parentId); | 915 std::string parent = db_.GetPublicId(parentId); |
687 | 916 |
688 switch (type) | 917 switch (type) |
689 { | 918 { |
690 case ResourceType_Study: | 919 case ResourceType_Study: |
691 result["ParentPatient"] = parent; | 920 result["ParentPatient"] = parent; |
704 } | 933 } |
705 } | 934 } |
706 | 935 |
707 // List the children resources | 936 // List the children resources |
708 std::list<std::string> children; | 937 std::list<std::string> children; |
709 db_->GetChildrenPublicId(children, id); | 938 db_.GetChildrenPublicId(children, id); |
710 | 939 |
711 if (type != ResourceType_Instance) | 940 if (type != ResourceType_Instance) |
712 { | 941 { |
713 Json::Value c = Json::arrayValue; | 942 Json::Value c = Json::arrayValue; |
714 | 943 |
751 case ResourceType_Series: | 980 case ResourceType_Series: |
752 { | 981 { |
753 result["Type"] = "Series"; | 982 result["Type"] = "Series"; |
754 result["Status"] = EnumerationToString(GetSeriesStatus(id)); | 983 result["Status"] = EnumerationToString(GetSeriesStatus(id)); |
755 | 984 |
756 int i; | 985 int64_t i; |
757 if (db_->GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) | 986 if (GetMetadataAsInteger(i, id, MetadataType_Series_ExpectedNumberOfInstances)) |
758 result["ExpectedNumberOfInstances"] = i; | 987 result["ExpectedNumberOfInstances"] = static_cast<int>(i); |
759 else | 988 else |
760 result["ExpectedNumberOfInstances"] = Json::nullValue; | 989 result["ExpectedNumberOfInstances"] = Json::nullValue; |
761 | 990 |
762 break; | 991 break; |
763 } | 992 } |
765 case ResourceType_Instance: | 994 case ResourceType_Instance: |
766 { | 995 { |
767 result["Type"] = "Instance"; | 996 result["Type"] = "Instance"; |
768 | 997 |
769 FileInfo attachment; | 998 FileInfo attachment; |
770 if (!db_->LookupAttachment(attachment, id, FileContentType_Dicom)) | 999 if (!db_.LookupAttachment(attachment, id, FileContentType_Dicom)) |
771 { | 1000 { |
772 throw OrthancException(ErrorCode_InternalError); | 1001 throw OrthancException(ErrorCode_InternalError); |
773 } | 1002 } |
774 | 1003 |
775 result["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize()); | 1004 result["FileSize"] = static_cast<unsigned int>(attachment.GetUncompressedSize()); |
776 result["FileUuid"] = attachment.GetUuid(); | 1005 result["FileUuid"] = attachment.GetUuid(); |
777 | 1006 |
778 int i; | 1007 int64_t i; |
779 if (db_->GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) | 1008 if (GetMetadataAsInteger(i, id, MetadataType_Instance_IndexInSeries)) |
780 result["IndexInSeries"] = i; | 1009 result["IndexInSeries"] = static_cast<int>(i); |
781 else | 1010 else |
782 result["IndexInSeries"] = Json::nullValue; | 1011 result["IndexInSeries"] = Json::nullValue; |
783 | 1012 |
784 break; | 1013 break; |
785 } | 1014 } |
792 result["ID"] = publicId; | 1021 result["ID"] = publicId; |
793 MainDicomTagsToJson(result, id); | 1022 MainDicomTagsToJson(result, id); |
794 | 1023 |
795 std::string tmp; | 1024 std::string tmp; |
796 | 1025 |
797 tmp = db_->GetMetadata(id, MetadataType_AnonymizedFrom); | 1026 if (db_.LookupMetadata(tmp, id, MetadataType_AnonymizedFrom)) |
798 if (tmp.size() != 0) | 1027 { |
799 result["AnonymizedFrom"] = tmp; | 1028 result["AnonymizedFrom"] = tmp; |
800 | 1029 } |
801 tmp = db_->GetMetadata(id, MetadataType_ModifiedFrom); | 1030 |
802 if (tmp.size() != 0) | 1031 if (db_.LookupMetadata(tmp, id, MetadataType_ModifiedFrom)) |
1032 { | |
803 result["ModifiedFrom"] = tmp; | 1033 result["ModifiedFrom"] = tmp; |
1034 } | |
804 | 1035 |
805 if (type == ResourceType_Patient || | 1036 if (type == ResourceType_Patient || |
806 type == ResourceType_Study || | 1037 type == ResourceType_Study || |
807 type == ResourceType_Series) | 1038 type == ResourceType_Series) |
808 { | 1039 { |
809 result["IsStable"] = !unstableResources_.Contains(id); | 1040 result["IsStable"] = !unstableResources_.Contains(id); |
1041 | |
1042 if (db_.LookupMetadata(tmp, id, MetadataType_LastUpdate)) | |
1043 { | |
1044 result["LastUpdate"] = tmp; | |
1045 } | |
810 } | 1046 } |
811 | 1047 |
812 return true; | 1048 return true; |
813 } | 1049 } |
814 | 1050 |
819 { | 1055 { |
820 boost::mutex::scoped_lock lock(mutex_); | 1056 boost::mutex::scoped_lock lock(mutex_); |
821 | 1057 |
822 int64_t id; | 1058 int64_t id; |
823 ResourceType type; | 1059 ResourceType type; |
824 if (!db_->LookupResource(instanceUuid, id, type)) | 1060 if (!db_.LookupResource(id, type, instanceUuid)) |
825 { | 1061 { |
826 throw OrthancException(ErrorCode_InternalError); | 1062 throw OrthancException(ErrorCode_UnknownResource); |
827 } | 1063 } |
828 | 1064 |
829 if (db_->LookupAttachment(attachment, id, contentType)) | 1065 if (db_.LookupAttachment(attachment, id, contentType)) |
830 { | 1066 { |
831 assert(attachment.GetContentType() == contentType); | 1067 assert(attachment.GetContentType() == contentType); |
832 return true; | 1068 return true; |
833 } | 1069 } |
834 else | 1070 else |
837 } | 1073 } |
838 } | 1074 } |
839 | 1075 |
840 | 1076 |
841 | 1077 |
842 void ServerIndex::GetAllUuids(Json::Value& target, | 1078 void ServerIndex::GetAllUuids(std::list<std::string>& target, |
843 ResourceType resourceType) | 1079 ResourceType resourceType) |
844 { | 1080 { |
845 boost::mutex::scoped_lock lock(mutex_); | 1081 boost::mutex::scoped_lock lock(mutex_); |
846 db_->GetAllPublicIds(target, resourceType); | 1082 db_.GetAllPublicIds(target, resourceType); |
847 } | 1083 } |
848 | 1084 |
849 | 1085 |
850 bool ServerIndex::GetChanges(Json::Value& target, | 1086 template <typename T> |
1087 static void FormatLog(Json::Value& target, | |
1088 const std::list<T>& log, | |
1089 const std::string& name, | |
1090 bool done, | |
1091 int64_t since) | |
1092 { | |
1093 Json::Value items = Json::arrayValue; | |
1094 for (typename std::list<T>::const_iterator | |
1095 it = log.begin(); it != log.end(); ++it) | |
1096 { | |
1097 Json::Value item; | |
1098 it->Format(item); | |
1099 items.append(item); | |
1100 } | |
1101 | |
1102 target = Json::objectValue; | |
1103 target[name] = items; | |
1104 target["Done"] = done; | |
1105 | |
1106 int64_t last = (log.empty() ? since : log.back().GetSeq()); | |
1107 target["Last"] = static_cast<int>(last); | |
1108 } | |
1109 | |
1110 | |
1111 void ServerIndex::GetChanges(Json::Value& target, | |
851 int64_t since, | 1112 int64_t since, |
852 unsigned int maxResults) | 1113 unsigned int maxResults) |
853 { | 1114 { |
854 boost::mutex::scoped_lock lock(mutex_); | 1115 std::list<ServerIndexChange> changes; |
855 db_->GetChanges(target, since, maxResults); | 1116 bool done; |
856 return true; | 1117 |
857 } | 1118 { |
858 | 1119 boost::mutex::scoped_lock lock(mutex_); |
859 bool ServerIndex::GetLastChange(Json::Value& target) | 1120 db_.GetChanges(changes, done, since, maxResults); |
860 { | 1121 } |
861 boost::mutex::scoped_lock lock(mutex_); | 1122 |
862 db_->GetLastChange(target); | 1123 FormatLog(target, changes, "Changes", done, since); |
863 return true; | 1124 } |
864 } | 1125 |
1126 | |
1127 void ServerIndex::GetLastChange(Json::Value& target) | |
1128 { | |
1129 std::list<ServerIndexChange> changes; | |
1130 | |
1131 { | |
1132 boost::mutex::scoped_lock lock(mutex_); | |
1133 db_.GetLastChange(changes); | |
1134 } | |
1135 | |
1136 FormatLog(target, changes, "Changes", true, 0); | |
1137 } | |
1138 | |
865 | 1139 |
866 void ServerIndex::LogExportedResource(const std::string& publicId, | 1140 void ServerIndex::LogExportedResource(const std::string& publicId, |
867 const std::string& remoteModality) | 1141 const std::string& remoteModality) |
868 { | 1142 { |
869 boost::mutex::scoped_lock lock(mutex_); | 1143 boost::mutex::scoped_lock lock(mutex_); |
1144 Transaction transaction(*this); | |
870 | 1145 |
871 int64_t id; | 1146 int64_t id; |
872 ResourceType type; | 1147 ResourceType type; |
873 if (!db_->LookupResource(publicId, id, type)) | 1148 if (!db_.LookupResource(id, type, publicId)) |
874 { | 1149 { |
875 throw OrthancException(ErrorCode_InternalError); | 1150 throw OrthancException(ErrorCode_InternalError); |
876 } | 1151 } |
877 | 1152 |
878 std::string patientId; | 1153 std::string patientId; |
886 // Iteratively go up inside the patient/study/series/instance hierarchy | 1161 // Iteratively go up inside the patient/study/series/instance hierarchy |
887 bool done = false; | 1162 bool done = false; |
888 while (!done) | 1163 while (!done) |
889 { | 1164 { |
890 DicomMap map; | 1165 DicomMap map; |
891 db_->GetMainDicomTags(map, currentId); | 1166 db_.GetMainDicomTags(map, currentId); |
892 | 1167 |
893 switch (currentType) | 1168 switch (currentType) |
894 { | 1169 { |
895 case ResourceType_Patient: | 1170 case ResourceType_Patient: |
896 patientId = map.GetValue(DICOM_TAG_PATIENT_ID).AsString(); | 1171 patientId = map.GetValue(DICOM_TAG_PATIENT_ID).AsString(); |
918 | 1193 |
919 // If we have not reached the Patient level, find the parent of | 1194 // If we have not reached the Patient level, find the parent of |
920 // the current resource | 1195 // the current resource |
921 if (!done) | 1196 if (!done) |
922 { | 1197 { |
923 bool ok = db_->LookupParent(currentId, currentId); | 1198 bool ok = db_.LookupParent(currentId, currentId); |
924 assert(ok); | 1199 assert(ok); |
925 } | 1200 } |
926 } | 1201 } |
927 | 1202 |
928 // No need for a SQLite::Transaction here, as we only insert 1 record | 1203 ExportedResource resource(-1, |
929 db_->LogExportedResource(type, | 1204 type, |
930 publicId, | 1205 publicId, |
931 remoteModality, | 1206 remoteModality, |
932 patientId, | 1207 Toolbox::GetNowIsoString(), |
933 studyInstanceUid, | 1208 patientId, |
934 seriesInstanceUid, | 1209 studyInstanceUid, |
935 sopInstanceUid); | 1210 seriesInstanceUid, |
936 } | 1211 sopInstanceUid); |
937 | 1212 |
938 | 1213 db_.LogExportedResource(resource); |
939 bool ServerIndex::GetExportedResources(Json::Value& target, | 1214 transaction.Commit(0); |
1215 } | |
1216 | |
1217 | |
1218 void ServerIndex::GetExportedResources(Json::Value& target, | |
940 int64_t since, | 1219 int64_t since, |
941 unsigned int maxResults) | 1220 unsigned int maxResults) |
942 { | 1221 { |
943 boost::mutex::scoped_lock lock(mutex_); | 1222 std::list<ExportedResource> exported; |
944 db_->GetExportedResources(target, since, maxResults); | 1223 bool done; |
945 return true; | 1224 |
946 } | 1225 { |
947 | 1226 boost::mutex::scoped_lock lock(mutex_); |
948 bool ServerIndex::GetLastExportedResource(Json::Value& target) | 1227 db_.GetExportedResources(exported, done, since, maxResults); |
949 { | 1228 } |
950 boost::mutex::scoped_lock lock(mutex_); | 1229 |
951 db_->GetLastExportedResource(target); | 1230 FormatLog(target, exported, "Exports", done, since); |
952 return true; | 1231 } |
1232 | |
1233 | |
1234 void ServerIndex::GetLastExportedResource(Json::Value& target) | |
1235 { | |
1236 std::list<ExportedResource> exported; | |
1237 | |
1238 { | |
1239 boost::mutex::scoped_lock lock(mutex_); | |
1240 db_.GetLastExportedResource(exported); | |
1241 } | |
1242 | |
1243 FormatLog(target, exported, "Exports", true, 0); | |
953 } | 1244 } |
954 | 1245 |
955 | 1246 |
956 bool ServerIndex::IsRecyclingNeeded(uint64_t instanceSize) | 1247 bool ServerIndex::IsRecyclingNeeded(uint64_t instanceSize) |
957 { | 1248 { |
958 if (maximumStorageSize_ != 0) | 1249 if (maximumStorageSize_ != 0) |
959 { | 1250 { |
960 uint64_t currentSize = currentStorageSize_ - listener_->GetSizeOfFilesToRemove(); | 1251 uint64_t currentSize = currentStorageSize_ - listener_->GetSizeOfFilesToRemove(); |
961 assert(db_->GetTotalCompressedSize() == currentSize); | 1252 assert(db_.GetTotalCompressedSize() == currentSize); |
962 | 1253 |
963 if (currentSize + instanceSize > maximumStorageSize_) | 1254 if (currentSize + instanceSize > maximumStorageSize_) |
964 { | 1255 { |
965 return true; | 1256 return true; |
966 } | 1257 } |
967 } | 1258 } |
968 | 1259 |
969 if (maximumPatients_ != 0) | 1260 if (maximumPatients_ != 0) |
970 { | 1261 { |
971 uint64_t patientCount = db_->GetResourceCount(ResourceType_Patient); | 1262 uint64_t patientCount = db_.GetResourceCount(ResourceType_Patient); |
972 if (patientCount > maximumPatients_) | 1263 if (patientCount > maximumPatients_) |
973 { | 1264 { |
974 return true; | 1265 return true; |
975 } | 1266 } |
976 } | 1267 } |
989 | 1280 |
990 // Check whether other DICOM instances from this patient are | 1281 // Check whether other DICOM instances from this patient are |
991 // already stored | 1282 // already stored |
992 int64_t patientToAvoid; | 1283 int64_t patientToAvoid; |
993 ResourceType type; | 1284 ResourceType type; |
994 bool hasPatientToAvoid = db_->LookupResource(newPatientId, patientToAvoid, type); | 1285 bool hasPatientToAvoid = db_.LookupResource(patientToAvoid, type, newPatientId); |
995 | 1286 |
996 if (hasPatientToAvoid && type != ResourceType_Patient) | 1287 if (hasPatientToAvoid && type != ResourceType_Patient) |
997 { | 1288 { |
998 throw OrthancException(ErrorCode_InternalError); | 1289 throw OrthancException(ErrorCode_InternalError); |
999 } | 1290 } |
1004 while (true) | 1295 while (true) |
1005 { | 1296 { |
1006 // If other instances of this patient are already in the store, | 1297 // If other instances of this patient are already in the store, |
1007 // we must avoid to recycle them | 1298 // we must avoid to recycle them |
1008 bool ok = hasPatientToAvoid ? | 1299 bool ok = hasPatientToAvoid ? |
1009 db_->SelectPatientToRecycle(patientToRecycle, patientToAvoid) : | 1300 db_.SelectPatientToRecycle(patientToRecycle, patientToAvoid) : |
1010 db_->SelectPatientToRecycle(patientToRecycle); | 1301 db_.SelectPatientToRecycle(patientToRecycle); |
1011 | 1302 |
1012 if (!ok) | 1303 if (!ok) |
1013 { | 1304 { |
1014 throw OrthancException(ErrorCode_FullStorage); | 1305 throw OrthancException(ErrorCode_FullStorage); |
1015 } | 1306 } |
1016 | 1307 |
1017 LOG(INFO) << "Recycling one patient"; | 1308 VLOG(1) << "Recycling one patient"; |
1018 db_->DeleteResource(patientToRecycle); | 1309 db_.DeleteResource(patientToRecycle); |
1019 | 1310 |
1020 if (!IsRecyclingNeeded(instanceSize)) | 1311 if (!IsRecyclingNeeded(instanceSize)) |
1021 { | 1312 { |
1022 // OK, we're done | 1313 // OK, we're done |
1023 break; | 1314 break; |
1073 boost::mutex::scoped_lock lock(mutex_); | 1364 boost::mutex::scoped_lock lock(mutex_); |
1074 | 1365 |
1075 // Lookup for the requested resource | 1366 // Lookup for the requested resource |
1076 int64_t id; | 1367 int64_t id; |
1077 ResourceType type; | 1368 ResourceType type; |
1078 if (!db_->LookupResource(publicId, id, type) || | 1369 if (!db_.LookupResource(id, type, publicId) || |
1079 type != ResourceType_Patient) | 1370 type != ResourceType_Patient) |
1080 { | 1371 { |
1081 throw OrthancException(ErrorCode_ParameterOutOfRange); | 1372 throw OrthancException(ErrorCode_ParameterOutOfRange); |
1082 } | 1373 } |
1083 | 1374 |
1084 return db_->IsProtectedPatient(id); | 1375 return db_.IsProtectedPatient(id); |
1085 } | 1376 } |
1086 | 1377 |
1087 | 1378 |
1088 void ServerIndex::SetProtectedPatient(const std::string& publicId, | 1379 void ServerIndex::SetProtectedPatient(const std::string& publicId, |
1089 bool isProtected) | 1380 bool isProtected) |
1090 { | 1381 { |
1091 boost::mutex::scoped_lock lock(mutex_); | 1382 boost::mutex::scoped_lock lock(mutex_); |
1383 Transaction transaction(*this); | |
1092 | 1384 |
1093 // Lookup for the requested resource | 1385 // Lookup for the requested resource |
1094 int64_t id; | 1386 int64_t id; |
1095 ResourceType type; | 1387 ResourceType type; |
1096 if (!db_->LookupResource(publicId, id, type) || | 1388 if (!db_.LookupResource(id, type, publicId) || |
1097 type != ResourceType_Patient) | 1389 type != ResourceType_Patient) |
1098 { | 1390 { |
1099 throw OrthancException(ErrorCode_ParameterOutOfRange); | 1391 throw OrthancException(ErrorCode_ParameterOutOfRange); |
1100 } | 1392 } |
1101 | 1393 |
1102 // No need for a SQLite::Transaction here, as we only make 1 write to the DB | 1394 db_.SetProtectedPatient(id, isProtected); |
1103 db_->SetProtectedPatient(id, isProtected); | 1395 transaction.Commit(0); |
1104 | 1396 |
1105 if (isProtected) | 1397 if (isProtected) |
1106 LOG(INFO) << "Patient " << publicId << " has been protected"; | 1398 LOG(INFO) << "Patient " << publicId << " has been protected"; |
1107 else | 1399 else |
1108 LOG(INFO) << "Patient " << publicId << " has been unprotected"; | 1400 LOG(INFO) << "Patient " << publicId << " has been unprotected"; |
1116 | 1408 |
1117 boost::mutex::scoped_lock lock(mutex_); | 1409 boost::mutex::scoped_lock lock(mutex_); |
1118 | 1410 |
1119 ResourceType type; | 1411 ResourceType type; |
1120 int64_t resource; | 1412 int64_t resource; |
1121 if (!db_->LookupResource(publicId, resource, type)) | 1413 if (!db_.LookupResource(resource, type, publicId)) |
1122 { | 1414 { |
1123 throw OrthancException(ErrorCode_UnknownResource); | 1415 throw OrthancException(ErrorCode_UnknownResource); |
1124 } | 1416 } |
1125 | 1417 |
1126 if (type == ResourceType_Instance) | 1418 if (type == ResourceType_Instance) |
1128 // An instance cannot have a child | 1420 // An instance cannot have a child |
1129 throw OrthancException(ErrorCode_BadParameterType); | 1421 throw OrthancException(ErrorCode_BadParameterType); |
1130 } | 1422 } |
1131 | 1423 |
1132 std::list<int64_t> tmp; | 1424 std::list<int64_t> tmp; |
1133 db_->GetChildrenInternalId(tmp, resource); | 1425 db_.GetChildrenInternalId(tmp, resource); |
1134 | 1426 |
1135 for (std::list<int64_t>::const_iterator | 1427 for (std::list<int64_t>::const_iterator |
1136 it = tmp.begin(); it != tmp.end(); ++it) | 1428 it = tmp.begin(); it != tmp.end(); ++it) |
1137 { | 1429 { |
1138 result.push_back(db_->GetPublicId(*it)); | 1430 result.push_back(db_.GetPublicId(*it)); |
1139 } | 1431 } |
1140 } | 1432 } |
1141 | 1433 |
1142 | 1434 |
1143 void ServerIndex::GetChildInstances(std::list<std::string>& result, | 1435 void ServerIndex::GetChildInstances(std::list<std::string>& result, |
1147 | 1439 |
1148 boost::mutex::scoped_lock lock(mutex_); | 1440 boost::mutex::scoped_lock lock(mutex_); |
1149 | 1441 |
1150 ResourceType type; | 1442 ResourceType type; |
1151 int64_t top; | 1443 int64_t top; |
1152 if (!db_->LookupResource(publicId, top, type)) | 1444 if (!db_.LookupResource(top, type, publicId)) |
1153 { | 1445 { |
1154 throw OrthancException(ErrorCode_UnknownResource); | 1446 throw OrthancException(ErrorCode_UnknownResource); |
1155 } | 1447 } |
1156 | 1448 |
1157 if (type == ResourceType_Instance) | 1449 if (type == ResourceType_Instance) |
1170 { | 1462 { |
1171 // Get the internal ID of the current resource | 1463 // Get the internal ID of the current resource |
1172 int64_t resource = toExplore.top(); | 1464 int64_t resource = toExplore.top(); |
1173 toExplore.pop(); | 1465 toExplore.pop(); |
1174 | 1466 |
1175 if (db_->GetResourceType(resource) == ResourceType_Instance) | 1467 if (db_.GetResourceType(resource) == ResourceType_Instance) |
1176 { | 1468 { |
1177 result.push_back(db_->GetPublicId(resource)); | 1469 result.push_back(db_.GetPublicId(resource)); |
1178 } | 1470 } |
1179 else | 1471 else |
1180 { | 1472 { |
1181 // Tag all the children of this resource as to be explored | 1473 // Tag all the children of this resource as to be explored |
1182 db_->GetChildrenInternalId(tmp, resource); | 1474 db_.GetChildrenInternalId(tmp, resource); |
1183 for (std::list<int64_t>::const_iterator | 1475 for (std::list<int64_t>::const_iterator |
1184 it = tmp.begin(); it != tmp.end(); ++it) | 1476 it = tmp.begin(); it != tmp.end(); ++it) |
1185 { | 1477 { |
1186 toExplore.push(*it); | 1478 toExplore.push(*it); |
1187 } | 1479 } |
1196 { | 1488 { |
1197 boost::mutex::scoped_lock lock(mutex_); | 1489 boost::mutex::scoped_lock lock(mutex_); |
1198 | 1490 |
1199 ResourceType rtype; | 1491 ResourceType rtype; |
1200 int64_t id; | 1492 int64_t id; |
1201 if (!db_->LookupResource(publicId, id, rtype)) | 1493 if (!db_.LookupResource(id, rtype, publicId)) |
1202 { | 1494 { |
1203 throw OrthancException(ErrorCode_UnknownResource); | 1495 throw OrthancException(ErrorCode_UnknownResource); |
1204 } | 1496 } |
1205 | 1497 |
1206 db_->SetMetadata(id, type, value); | 1498 db_.SetMetadata(id, type, value); |
1207 } | 1499 } |
1208 | 1500 |
1209 | 1501 |
1210 void ServerIndex::DeleteMetadata(const std::string& publicId, | 1502 void ServerIndex::DeleteMetadata(const std::string& publicId, |
1211 MetadataType type) | 1503 MetadataType type) |
1212 { | 1504 { |
1213 boost::mutex::scoped_lock lock(mutex_); | 1505 boost::mutex::scoped_lock lock(mutex_); |
1214 | 1506 |
1215 ResourceType rtype; | 1507 ResourceType rtype; |
1216 int64_t id; | 1508 int64_t id; |
1217 if (!db_->LookupResource(publicId, id, rtype)) | 1509 if (!db_.LookupResource(id, rtype, publicId)) |
1218 { | 1510 { |
1219 throw OrthancException(ErrorCode_UnknownResource); | 1511 throw OrthancException(ErrorCode_UnknownResource); |
1220 } | 1512 } |
1221 | 1513 |
1222 db_->DeleteMetadata(id, type); | 1514 db_.DeleteMetadata(id, type); |
1223 } | 1515 } |
1224 | 1516 |
1225 | 1517 |
1226 bool ServerIndex::LookupMetadata(std::string& target, | 1518 bool ServerIndex::LookupMetadata(std::string& target, |
1227 const std::string& publicId, | 1519 const std::string& publicId, |
1229 { | 1521 { |
1230 boost::mutex::scoped_lock lock(mutex_); | 1522 boost::mutex::scoped_lock lock(mutex_); |
1231 | 1523 |
1232 ResourceType rtype; | 1524 ResourceType rtype; |
1233 int64_t id; | 1525 int64_t id; |
1234 if (!db_->LookupResource(publicId, id, rtype)) | 1526 if (!db_.LookupResource(id, rtype, publicId)) |
1235 { | 1527 { |
1236 throw OrthancException(ErrorCode_UnknownResource); | 1528 throw OrthancException(ErrorCode_UnknownResource); |
1237 } | 1529 } |
1238 | 1530 |
1239 return db_->LookupMetadata(target, id, type); | 1531 return db_.LookupMetadata(target, id, type); |
1240 } | 1532 } |
1241 | 1533 |
1242 | 1534 |
1243 void ServerIndex::ListAvailableMetadata(std::list<MetadataType>& target, | 1535 void ServerIndex::ListAvailableMetadata(std::list<MetadataType>& target, |
1244 const std::string& publicId) | 1536 const std::string& publicId) |
1245 { | 1537 { |
1246 boost::mutex::scoped_lock lock(mutex_); | 1538 boost::mutex::scoped_lock lock(mutex_); |
1247 | 1539 |
1248 ResourceType rtype; | 1540 ResourceType rtype; |
1249 int64_t id; | 1541 int64_t id; |
1250 if (!db_->LookupResource(publicId, id, rtype)) | 1542 if (!db_.LookupResource(id, rtype, publicId)) |
1251 { | 1543 { |
1252 throw OrthancException(ErrorCode_UnknownResource); | 1544 throw OrthancException(ErrorCode_UnknownResource); |
1253 } | 1545 } |
1254 | 1546 |
1255 db_->ListAvailableMetadata(target, id); | 1547 db_.ListAvailableMetadata(target, id); |
1256 } | 1548 } |
1257 | 1549 |
1258 | 1550 |
1259 void ServerIndex::ListAvailableAttachments(std::list<FileContentType>& target, | 1551 void ServerIndex::ListAvailableAttachments(std::list<FileContentType>& target, |
1260 const std::string& publicId, | 1552 const std::string& publicId, |
1262 { | 1554 { |
1263 boost::mutex::scoped_lock lock(mutex_); | 1555 boost::mutex::scoped_lock lock(mutex_); |
1264 | 1556 |
1265 ResourceType type; | 1557 ResourceType type; |
1266 int64_t id; | 1558 int64_t id; |
1267 if (!db_->LookupResource(publicId, id, type) || | 1559 if (!db_.LookupResource(id, type, publicId) || |
1268 expectedType != type) | 1560 expectedType != type) |
1269 { | 1561 { |
1270 throw OrthancException(ErrorCode_UnknownResource); | 1562 throw OrthancException(ErrorCode_UnknownResource); |
1271 } | 1563 } |
1272 | 1564 |
1273 db_->ListAvailableAttachments(target, id); | 1565 db_.ListAvailableAttachments(target, id); |
1274 } | 1566 } |
1275 | 1567 |
1276 | 1568 |
1277 bool ServerIndex::LookupParent(std::string& target, | 1569 bool ServerIndex::LookupParent(std::string& target, |
1278 const std::string& publicId) | 1570 const std::string& publicId) |
1279 { | 1571 { |
1280 boost::mutex::scoped_lock lock(mutex_); | 1572 boost::mutex::scoped_lock lock(mutex_); |
1281 | 1573 |
1282 ResourceType type; | 1574 ResourceType type; |
1283 int64_t id; | 1575 int64_t id; |
1284 if (!db_->LookupResource(publicId, id, type)) | 1576 if (!db_.LookupResource(id, type, publicId)) |
1285 { | 1577 { |
1286 throw OrthancException(ErrorCode_UnknownResource); | 1578 throw OrthancException(ErrorCode_UnknownResource); |
1287 } | 1579 } |
1288 | 1580 |
1289 int64_t parentId; | 1581 int64_t parentId; |
1290 if (db_->LookupParent(parentId, id)) | 1582 if (db_.LookupParent(parentId, id)) |
1291 { | 1583 { |
1292 target = db_->GetPublicId(parentId); | 1584 target = db_.GetPublicId(parentId); |
1293 return true; | 1585 return true; |
1294 } | 1586 } |
1295 else | 1587 else |
1296 { | 1588 { |
1297 return false; | 1589 return false; |
1300 | 1592 |
1301 | 1593 |
1302 uint64_t ServerIndex::IncrementGlobalSequence(GlobalProperty sequence) | 1594 uint64_t ServerIndex::IncrementGlobalSequence(GlobalProperty sequence) |
1303 { | 1595 { |
1304 boost::mutex::scoped_lock lock(mutex_); | 1596 boost::mutex::scoped_lock lock(mutex_); |
1305 | 1597 Transaction transaction(*this); |
1306 std::auto_ptr<SQLite::Transaction> transaction(db_->StartTransaction()); | 1598 |
1307 | 1599 uint64_t seq = IncrementGlobalSequenceInternal(sequence); |
1308 transaction->Begin(); | 1600 transaction.Commit(0); |
1309 uint64_t seq = db_->IncrementGlobalSequence(sequence); | |
1310 transaction->Commit(); | |
1311 | 1601 |
1312 return seq; | 1602 return seq; |
1313 } | 1603 } |
1314 | 1604 |
1315 | 1605 |
1316 | 1606 |
1317 void ServerIndex::LogChange(ChangeType changeType, | 1607 void ServerIndex::LogChange(ChangeType changeType, |
1318 const std::string& publicId) | 1608 const std::string& publicId) |
1319 { | 1609 { |
1320 boost::mutex::scoped_lock lock(mutex_); | 1610 boost::mutex::scoped_lock lock(mutex_); |
1321 std::auto_ptr<SQLite::Transaction> transaction(db_->StartTransaction()); | 1611 Transaction transaction(*this); |
1322 transaction->Begin(); | |
1323 | 1612 |
1324 int64_t id; | 1613 int64_t id; |
1325 ResourceType type; | 1614 ResourceType type; |
1326 if (!db_->LookupResource(publicId, id, type)) | 1615 if (!db_.LookupResource(id, type, publicId)) |
1327 { | 1616 { |
1328 throw OrthancException(ErrorCode_UnknownResource); | 1617 throw OrthancException(ErrorCode_UnknownResource); |
1329 } | 1618 } |
1330 | 1619 |
1331 db_->LogChange(changeType, id, type); | 1620 LogChange(id, changeType, type, publicId); |
1332 | 1621 transaction.Commit(0); |
1333 transaction->Commit(); | |
1334 } | 1622 } |
1335 | 1623 |
1336 | 1624 |
1337 void ServerIndex::DeleteChanges() | 1625 void ServerIndex::DeleteChanges() |
1338 { | 1626 { |
1339 boost::mutex::scoped_lock lock(mutex_); | 1627 boost::mutex::scoped_lock lock(mutex_); |
1340 db_->ClearTable("Changes"); | 1628 db_.ClearChanges(); |
1341 } | 1629 } |
1342 | 1630 |
1343 void ServerIndex::DeleteExportedResources() | 1631 void ServerIndex::DeleteExportedResources() |
1344 { | 1632 { |
1345 boost::mutex::scoped_lock lock(mutex_); | 1633 boost::mutex::scoped_lock lock(mutex_); |
1346 db_->ClearTable("ExportedResources"); | 1634 db_.ClearExportedResources(); |
1347 } | 1635 } |
1348 | 1636 |
1349 | 1637 |
1350 void ServerIndex::GetStatisticsInternal(/* out */ uint64_t& compressedSize, | 1638 void ServerIndex::GetStatisticsInternal(/* out */ uint64_t& compressedSize, |
1351 /* out */ uint64_t& uncompressedSize, | 1639 /* out */ uint64_t& uncompressedSize, |
1368 { | 1656 { |
1369 // Get the internal ID of the current resource | 1657 // Get the internal ID of the current resource |
1370 int64_t resource = toExplore.top(); | 1658 int64_t resource = toExplore.top(); |
1371 toExplore.pop(); | 1659 toExplore.pop(); |
1372 | 1660 |
1373 ResourceType thisType = db_->GetResourceType(resource); | 1661 ResourceType thisType = db_.GetResourceType(resource); |
1374 | 1662 |
1375 std::list<FileContentType> f; | 1663 std::list<FileContentType> f; |
1376 db_->ListAvailableAttachments(f, resource); | 1664 db_.ListAvailableAttachments(f, resource); |
1377 | 1665 |
1378 for (std::list<FileContentType>::const_iterator | 1666 for (std::list<FileContentType>::const_iterator |
1379 it = f.begin(); it != f.end(); ++it) | 1667 it = f.begin(); it != f.end(); ++it) |
1380 { | 1668 { |
1381 FileInfo attachment; | 1669 FileInfo attachment; |
1382 if (db_->LookupAttachment(attachment, resource, *it)) | 1670 if (db_.LookupAttachment(attachment, resource, *it)) |
1383 { | 1671 { |
1384 compressedSize += attachment.GetCompressedSize(); | 1672 compressedSize += attachment.GetCompressedSize(); |
1385 uncompressedSize += attachment.GetUncompressedSize(); | 1673 uncompressedSize += attachment.GetUncompressedSize(); |
1386 } | 1674 } |
1387 } | 1675 } |
1406 break; | 1694 break; |
1407 } | 1695 } |
1408 | 1696 |
1409 // Tag all the children of this resource as to be explored | 1697 // Tag all the children of this resource as to be explored |
1410 std::list<int64_t> tmp; | 1698 std::list<int64_t> tmp; |
1411 db_->GetChildrenInternalId(tmp, resource); | 1699 db_.GetChildrenInternalId(tmp, resource); |
1412 for (std::list<int64_t>::const_iterator | 1700 for (std::list<int64_t>::const_iterator |
1413 it = tmp.begin(); it != tmp.end(); ++it) | 1701 it = tmp.begin(); it != tmp.end(); ++it) |
1414 { | 1702 { |
1415 toExplore.push(*it); | 1703 toExplore.push(*it); |
1416 } | 1704 } |
1435 { | 1723 { |
1436 boost::mutex::scoped_lock lock(mutex_); | 1724 boost::mutex::scoped_lock lock(mutex_); |
1437 | 1725 |
1438 ResourceType type; | 1726 ResourceType type; |
1439 int64_t top; | 1727 int64_t top; |
1440 if (!db_->LookupResource(publicId, top, type)) | 1728 if (!db_.LookupResource(top, type, publicId)) |
1441 { | 1729 { |
1442 throw OrthancException(ErrorCode_UnknownResource); | 1730 throw OrthancException(ErrorCode_UnknownResource); |
1443 } | 1731 } |
1444 | 1732 |
1445 uint64_t uncompressedSize; | 1733 uint64_t uncompressedSize; |
1450 GetStatisticsInternal(compressedSize, uncompressedSize, countStudies, | 1738 GetStatisticsInternal(compressedSize, uncompressedSize, countStudies, |
1451 countSeries, countInstances, top, type); | 1739 countSeries, countInstances, top, type); |
1452 | 1740 |
1453 target = Json::objectValue; | 1741 target = Json::objectValue; |
1454 target["DiskSize"] = boost::lexical_cast<std::string>(compressedSize); | 1742 target["DiskSize"] = boost::lexical_cast<std::string>(compressedSize); |
1455 target["DiskSizeMB"] = boost::lexical_cast<unsigned int>(compressedSize / MEGA_BYTES); | 1743 target["DiskSizeMB"] = static_cast<unsigned int>(compressedSize / MEGA_BYTES); |
1456 target["UncompressedSize"] = boost::lexical_cast<std::string>(uncompressedSize); | 1744 target["UncompressedSize"] = boost::lexical_cast<std::string>(uncompressedSize); |
1457 target["UncompressedSizeMB"] = boost::lexical_cast<unsigned int>(uncompressedSize / MEGA_BYTES); | 1745 target["UncompressedSizeMB"] = static_cast<unsigned int>(uncompressedSize / MEGA_BYTES); |
1458 | 1746 |
1459 switch (type) | 1747 switch (type) |
1460 { | 1748 { |
1461 // Do NOT add "break" below this point! | 1749 // Do NOT add "break" below this point! |
1462 case ResourceType_Patient: | 1750 case ResourceType_Patient: |
1484 { | 1772 { |
1485 boost::mutex::scoped_lock lock(mutex_); | 1773 boost::mutex::scoped_lock lock(mutex_); |
1486 | 1774 |
1487 ResourceType type; | 1775 ResourceType type; |
1488 int64_t top; | 1776 int64_t top; |
1489 if (!db_->LookupResource(publicId, top, type)) | 1777 if (!db_.LookupResource(top, type, publicId)) |
1490 { | 1778 { |
1491 throw OrthancException(ErrorCode_UnknownResource); | 1779 throw OrthancException(ErrorCode_UnknownResource); |
1492 } | 1780 } |
1493 | 1781 |
1494 GetStatisticsInternal(compressedSize, uncompressedSize, countStudies, | 1782 GetStatisticsInternal(compressedSize, uncompressedSize, countStudies, |
1521 | 1809 |
1522 UnstableResourcePayload payload; | 1810 UnstableResourcePayload payload; |
1523 int64_t id = that->unstableResources_.RemoveOldest(payload); | 1811 int64_t id = that->unstableResources_.RemoveOldest(payload); |
1524 | 1812 |
1525 // Ensure that the resource is still existing before logging the change | 1813 // Ensure that the resource is still existing before logging the change |
1526 if (that->db_->IsExistingResource(id)) | 1814 if (that->db_.IsExistingResource(id)) |
1527 { | 1815 { |
1528 switch (payload.type_) | 1816 switch (payload.GetResourceType()) |
1529 { | 1817 { |
1530 case Orthanc::ResourceType_Patient: | 1818 case ResourceType_Patient: |
1531 that->db_->LogChange(ChangeType_StablePatient, id, ResourceType_Patient); | 1819 that->LogChange(id, ChangeType_StablePatient, ResourceType_Patient, payload.GetPublicId()); |
1532 break; | 1820 break; |
1533 | 1821 |
1534 case Orthanc::ResourceType_Study: | 1822 case ResourceType_Study: |
1535 that->db_->LogChange(ChangeType_StableStudy, id, ResourceType_Study); | 1823 that->LogChange(id, ChangeType_StableStudy, ResourceType_Study, payload.GetPublicId()); |
1536 break; | 1824 break; |
1537 | 1825 |
1538 case Orthanc::ResourceType_Series: | 1826 case ResourceType_Series: |
1539 that->db_->LogChange(ChangeType_StableSeries, id, ResourceType_Series); | 1827 that->LogChange(id, ChangeType_StableSeries, ResourceType_Series, payload.GetPublicId()); |
1540 break; | 1828 break; |
1541 | 1829 |
1542 default: | 1830 default: |
1543 throw OrthancException(ErrorCode_InternalError); | 1831 throw OrthancException(ErrorCode_InternalError); |
1544 } | 1832 } |
1551 LOG(INFO) << "Closing the monitor thread for stable resources"; | 1839 LOG(INFO) << "Closing the monitor thread for stable resources"; |
1552 } | 1840 } |
1553 | 1841 |
1554 | 1842 |
1555 void ServerIndex::MarkAsUnstable(int64_t id, | 1843 void ServerIndex::MarkAsUnstable(int64_t id, |
1556 Orthanc::ResourceType type) | 1844 Orthanc::ResourceType type, |
1845 const std::string& publicId) | |
1557 { | 1846 { |
1558 // WARNING: Before calling this method, "mutex_" must be locked. | 1847 // WARNING: Before calling this method, "mutex_" must be locked. |
1559 | 1848 |
1560 assert(type == Orthanc::ResourceType_Patient || | 1849 assert(type == Orthanc::ResourceType_Patient || |
1561 type == Orthanc::ResourceType_Study || | 1850 type == Orthanc::ResourceType_Study || |
1562 type == Orthanc::ResourceType_Series); | 1851 type == Orthanc::ResourceType_Series); |
1563 | 1852 |
1564 unstableResources_.AddOrMakeMostRecent(id, type); | 1853 UnstableResourcePayload payload(type, publicId); |
1854 unstableResources_.AddOrMakeMostRecent(id, payload); | |
1565 //LOG(INFO) << "Unstable resource: " << EnumerationToString(type) << " " << id; | 1855 //LOG(INFO) << "Unstable resource: " << EnumerationToString(type) << " " << id; |
1566 } | 1856 |
1567 | 1857 LogChange(id, ChangeType_NewChildInstance, type, publicId); |
1568 | 1858 } |
1569 | 1859 |
1570 void ServerIndex::LookupTagValue(std::list<std::string>& result, | 1860 |
1571 DicomTag tag, | 1861 |
1572 const std::string& value, | 1862 void ServerIndex::LookupIdentifier(std::list<std::string>& result, |
1573 ResourceType type) | 1863 const DicomTag& tag, |
1864 const std::string& value, | |
1865 ResourceType type) | |
1574 { | 1866 { |
1575 result.clear(); | 1867 result.clear(); |
1576 | 1868 |
1577 boost::mutex::scoped_lock lock(mutex_); | 1869 boost::mutex::scoped_lock lock(mutex_); |
1578 | 1870 |
1579 std::list<int64_t> id; | 1871 std::list<int64_t> id; |
1580 db_->LookupTagValue(id, tag, value); | 1872 db_.LookupIdentifier(id, tag, value); |
1581 | 1873 |
1582 for (std::list<int64_t>::const_iterator | 1874 for (std::list<int64_t>::const_iterator |
1583 it = id.begin(); it != id.end(); ++it) | 1875 it = id.begin(); it != id.end(); ++it) |
1584 { | 1876 { |
1585 if (db_->GetResourceType(*it) == type) | 1877 if (db_.GetResourceType(*it) == type) |
1586 { | 1878 { |
1587 result.push_back(db_->GetPublicId(*it)); | 1879 result.push_back(db_.GetPublicId(*it)); |
1588 } | 1880 } |
1589 } | 1881 } |
1590 } | 1882 } |
1591 | 1883 |
1592 | 1884 |
1593 void ServerIndex::LookupTagValue(std::list<std::string>& result, | 1885 void ServerIndex::LookupIdentifier(std::list<std::string>& result, |
1594 DicomTag tag, | 1886 const DicomTag& tag, |
1595 const std::string& value) | 1887 const std::string& value) |
1596 { | 1888 { |
1597 result.clear(); | 1889 result.clear(); |
1598 | 1890 |
1599 boost::mutex::scoped_lock lock(mutex_); | 1891 boost::mutex::scoped_lock lock(mutex_); |
1600 | 1892 |
1601 std::list<int64_t> id; | 1893 std::list<int64_t> id; |
1602 db_->LookupTagValue(id, tag, value); | 1894 db_.LookupIdentifier(id, tag, value); |
1603 | 1895 |
1604 for (std::list<int64_t>::const_iterator | 1896 for (std::list<int64_t>::const_iterator |
1605 it = id.begin(); it != id.end(); ++it) | 1897 it = id.begin(); it != id.end(); ++it) |
1606 { | 1898 { |
1607 result.push_back(db_->GetPublicId(*it)); | 1899 result.push_back(db_.GetPublicId(*it)); |
1608 } | 1900 } |
1609 } | 1901 } |
1610 | 1902 |
1611 | 1903 |
1612 void ServerIndex::LookupTagValue(std::list<std::string>& result, | 1904 void ServerIndex::LookupIdentifier(std::list< std::pair<ResourceType, std::string> >& result, |
1613 const std::string& value) | 1905 const std::string& value) |
1614 { | 1906 { |
1615 result.clear(); | 1907 result.clear(); |
1616 | 1908 |
1617 boost::mutex::scoped_lock lock(mutex_); | 1909 boost::mutex::scoped_lock lock(mutex_); |
1618 | 1910 |
1619 std::list<int64_t> id; | 1911 std::list<int64_t> id; |
1620 db_->LookupTagValue(id, value); | 1912 db_.LookupIdentifier(id, value); |
1621 | 1913 |
1622 for (std::list<int64_t>::const_iterator | 1914 for (std::list<int64_t>::const_iterator |
1623 it = id.begin(); it != id.end(); ++it) | 1915 it = id.begin(); it != id.end(); ++it) |
1624 { | 1916 { |
1625 result.push_back(db_->GetPublicId(*it)); | 1917 result.push_back(std::make_pair(db_.GetResourceType(*it), |
1918 db_.GetPublicId(*it))); | |
1626 } | 1919 } |
1627 } | 1920 } |
1628 | 1921 |
1629 | 1922 |
1630 StoreStatus ServerIndex::AddAttachment(const FileInfo& attachment, | 1923 StoreStatus ServerIndex::AddAttachment(const FileInfo& attachment, |
1634 | 1927 |
1635 Transaction t(*this); | 1928 Transaction t(*this); |
1636 | 1929 |
1637 ResourceType resourceType; | 1930 ResourceType resourceType; |
1638 int64_t resourceId; | 1931 int64_t resourceId; |
1639 if (!db_->LookupResource(publicId, resourceId, resourceType)) | 1932 if (!db_.LookupResource(resourceId, resourceType, publicId)) |
1640 { | 1933 { |
1641 return StoreStatus_Failure; // Inexistent resource | 1934 return StoreStatus_Failure; // Inexistent resource |
1642 } | 1935 } |
1643 | 1936 |
1644 // Remove possible previous attachment | 1937 // Remove possible previous attachment |
1645 db_->DeleteAttachment(resourceId, attachment.GetContentType()); | 1938 db_.DeleteAttachment(resourceId, attachment.GetContentType()); |
1646 | 1939 |
1647 // Locate the patient of the target resource | 1940 // Locate the patient of the target resource |
1648 int64_t patientId = resourceId; | 1941 int64_t patientId = resourceId; |
1649 for (;;) | 1942 for (;;) |
1650 { | 1943 { |
1651 int64_t parent; | 1944 int64_t parent; |
1652 if (db_->LookupParent(parent, patientId)) | 1945 if (db_.LookupParent(parent, patientId)) |
1653 { | 1946 { |
1654 // We have not reached the patient level yet | 1947 // We have not reached the patient level yet |
1655 patientId = parent; | 1948 patientId = parent; |
1656 } | 1949 } |
1657 else | 1950 else |
1660 break; | 1953 break; |
1661 } | 1954 } |
1662 } | 1955 } |
1663 | 1956 |
1664 // Possibly apply the recycling mechanism while preserving this patient | 1957 // Possibly apply the recycling mechanism while preserving this patient |
1665 assert(db_->GetResourceType(patientId) == ResourceType_Patient); | 1958 assert(db_.GetResourceType(patientId) == ResourceType_Patient); |
1666 Recycle(attachment.GetCompressedSize(), db_->GetPublicId(patientId)); | 1959 Recycle(attachment.GetCompressedSize(), db_.GetPublicId(patientId)); |
1667 | 1960 |
1668 db_->AddAttachment(resourceId, attachment); | 1961 db_.AddAttachment(resourceId, attachment); |
1669 | 1962 |
1670 t.Commit(attachment.GetCompressedSize()); | 1963 t.Commit(attachment.GetCompressedSize()); |
1671 | 1964 |
1672 return StoreStatus_Success; | 1965 return StoreStatus_Success; |
1673 } | 1966 } |
1675 | 1968 |
1676 void ServerIndex::DeleteAttachment(const std::string& publicId, | 1969 void ServerIndex::DeleteAttachment(const std::string& publicId, |
1677 FileContentType type) | 1970 FileContentType type) |
1678 { | 1971 { |
1679 boost::mutex::scoped_lock lock(mutex_); | 1972 boost::mutex::scoped_lock lock(mutex_); |
1680 listener_->Reset(); | |
1681 | |
1682 Transaction t(*this); | 1973 Transaction t(*this); |
1683 | 1974 |
1684 ResourceType rtype; | 1975 ResourceType rtype; |
1685 int64_t id; | 1976 int64_t id; |
1686 if (!db_->LookupResource(publicId, id, rtype)) | 1977 if (!db_.LookupResource(id, rtype, publicId)) |
1687 { | 1978 { |
1688 throw OrthancException(ErrorCode_UnknownResource); | 1979 throw OrthancException(ErrorCode_UnknownResource); |
1689 } | 1980 } |
1690 | 1981 |
1691 db_->DeleteAttachment(id, type); | 1982 db_.DeleteAttachment(id, type); |
1692 | 1983 |
1693 t.Commit(0); | 1984 t.Commit(0); |
1694 } | 1985 } |
1695 | 1986 |
1696 | 1987 |
1988 bool ServerIndex::GetMetadata(Json::Value& target, | |
1989 const std::string& publicId) | |
1990 { | |
1991 boost::mutex::scoped_lock lock(mutex_); | |
1992 | |
1993 target = Json::objectValue; | |
1994 | |
1995 ResourceType type; | |
1996 int64_t id; | |
1997 if (!db_.LookupResource(id, type, publicId)) | |
1998 { | |
1999 return false; | |
2000 } | |
2001 | |
2002 std::list<MetadataType> metadata; | |
2003 db_.ListAvailableMetadata(metadata, id); | |
2004 | |
2005 for (std::list<MetadataType>::const_iterator | |
2006 it = metadata.begin(); it != metadata.end(); ++it) | |
2007 { | |
2008 std::string key = EnumerationToString(*it); | |
2009 | |
2010 std::string value; | |
2011 if (!db_.LookupMetadata(value, id, *it)) | |
2012 { | |
2013 value.clear(); | |
2014 } | |
2015 | |
2016 target[key] = value; | |
2017 } | |
2018 | |
2019 return true; | |
2020 } | |
2021 | |
2022 | |
2023 void ServerIndex::SetGlobalProperty(GlobalProperty property, | |
2024 const std::string& value) | |
2025 { | |
2026 boost::mutex::scoped_lock lock(mutex_); | |
2027 db_.SetGlobalProperty(property, value); | |
2028 } | |
2029 | |
2030 | |
2031 std::string ServerIndex::GetGlobalProperty(GlobalProperty property, | |
2032 const std::string& defaultValue) | |
2033 { | |
2034 boost::mutex::scoped_lock lock(mutex_); | |
2035 | |
2036 std::string value; | |
2037 if (db_.LookupGlobalProperty(value, property)) | |
2038 { | |
2039 return value; | |
2040 } | |
2041 else | |
2042 { | |
2043 return defaultValue; | |
2044 } | |
2045 } | |
2046 | |
2047 | |
2048 bool ServerIndex::GetMainDicomTags(DicomMap& result, | |
2049 const std::string& publicId, | |
2050 ResourceType expectedType) | |
2051 { | |
2052 result.Clear(); | |
2053 | |
2054 boost::mutex::scoped_lock lock(mutex_); | |
2055 | |
2056 // Lookup for the requested resource | |
2057 int64_t id; | |
2058 ResourceType type; | |
2059 if (!db_.LookupResource(id, type, publicId) || | |
2060 type != expectedType) | |
2061 { | |
2062 return false; | |
2063 } | |
2064 else | |
2065 { | |
2066 db_.GetMainDicomTags(result, id); | |
2067 return true; | |
2068 } | |
2069 } | |
1697 } | 2070 } |