comparison OrthancServer/ServerIndex.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 ec69658b031b
children 67e6400fca03 401a9633e492
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
89 89
90 void CommitFilesToRemove() 90 void CommitFilesToRemove()
91 { 91 {
92 for (std::list<std::string>::iterator 92 for (std::list<std::string>::iterator
93 it = pendingFilesToRemove_.begin(); 93 it = pendingFilesToRemove_.begin();
94 it != pendingFilesToRemove_.end(); it++) 94 it != pendingFilesToRemove_.end(); ++it)
95 { 95 {
96 context_.RemoveFile(*it); 96 context_.RemoveFile(*it);
97 } 97 }
98 } 98 }
99 99
253 { 253 {
254 unsigned int sleep; 254 unsigned int sleep;
255 255
256 try 256 try
257 { 257 {
258 boost::mutex::scoped_lock lock(that->mutex_);
258 std::string sleepString = that->db_->GetGlobalProperty(GlobalProperty_FlushSleep); 259 std::string sleepString = that->db_->GetGlobalProperty(GlobalProperty_FlushSleep);
259 sleep = boost::lexical_cast<unsigned int>(sleepString); 260 sleep = boost::lexical_cast<unsigned int>(sleepString);
260 } 261 }
261 catch (boost::bad_lexical_cast&) 262 catch (boost::bad_lexical_cast&)
262 { 263 {
288 289
289 static void ComputeExpectedNumberOfInstances(DatabaseWrapper& db, 290 static void ComputeExpectedNumberOfInstances(DatabaseWrapper& db,
290 int64_t series, 291 int64_t series,
291 const DicomMap& dicomSummary) 292 const DicomMap& dicomSummary)
292 { 293 {
293 const DicomValue* value; 294 try
294 const DicomValue* value2; 295 {
296 const DicomValue* value;
297 const DicomValue* value2;
295 298
296 try
297 {
298 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL && 299 if ((value = dicomSummary.TestAndGetValue(DICOM_TAG_IMAGES_IN_ACQUISITION)) != NULL &&
299 (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS)) != NULL) 300 (value2 = dicomSummary.TestAndGetValue(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS)) != NULL)
300 { 301 {
301 // Patch for series with temporal positions thanks to Will Ryder 302 // Patch for series with temporal positions thanks to Will Ryder
302 int64_t imagesInAcquisition = boost::lexical_cast<int64_t>(value->AsString()); 303 int64_t imagesInAcquisition = boost::lexical_cast<int64_t>(value->AsString());
405 } 406 }
406 407
407 // Ensure there is enough room in the storage for the new instance 408 // Ensure there is enough room in the storage for the new instance
408 uint64_t instanceSize = 0; 409 uint64_t instanceSize = 0;
409 for (Attachments::const_iterator it = attachments.begin(); 410 for (Attachments::const_iterator it = attachments.begin();
410 it != attachments.end(); it++) 411 it != attachments.end(); ++it)
411 { 412 {
412 instanceSize += it->GetCompressedSize(); 413 instanceSize += it->GetCompressedSize();
413 } 414 }
414 415
415 Recycle(instanceSize, hasher.HashPatient()); 416 Recycle(instanceSize, hasher.HashPatient());
510 assert(series != -1); 511 assert(series != -1);
511 assert(instance != -1); 512 assert(instance != -1);
512 513
513 // Attach the files to the newly created instance 514 // Attach the files to the newly created instance
514 for (Attachments::const_iterator it = attachments.begin(); 515 for (Attachments::const_iterator it = attachments.begin();
515 it != attachments.end(); it++) 516 it != attachments.end(); ++it)
516 { 517 {
517 db_->AddAttachment(instance, *it); 518 db_->AddAttachment(instance, *it);
518 } 519 }
519 520
520 // Attach the metadata 521 // Attach the metadata
591 592
592 size_t expected; 593 size_t expected;
593 try 594 try
594 { 595 {
595 expected = boost::lexical_cast<size_t>(s); 596 expected = boost::lexical_cast<size_t>(s);
596 if (expected < 0)
597 {
598 return SeriesStatus_Unknown;
599 }
600 } 597 }
601 catch (boost::bad_lexical_cast&) 598 catch (boost::bad_lexical_cast&)
602 { 599 {
603 return SeriesStatus_Unknown; 600 return SeriesStatus_Unknown;
604 } 601 }
607 std::list<int64_t> children; 604 std::list<int64_t> children;
608 db_->GetChildrenInternalId(children, id); 605 db_->GetChildrenInternalId(children, id);
609 606
610 std::set<size_t> instances; 607 std::set<size_t> instances;
611 for (std::list<int64_t>::const_iterator 608 for (std::list<int64_t>::const_iterator
612 it = children.begin(); it != children.end(); it++) 609 it = children.begin(); it != children.end(); ++it)
613 { 610 {
614 // Get the index of this instance in the series 611 // Get the index of this instance in the series
615 s = db_->GetMetadata(*it, MetadataType_Instance_IndexInSeries); 612 s = db_->GetMetadata(*it, MetadataType_Instance_IndexInSeries);
616 size_t index; 613 size_t index;
617 try 614 try
621 catch (boost::bad_lexical_cast&) 618 catch (boost::bad_lexical_cast&)
622 { 619 {
623 return SeriesStatus_Unknown; 620 return SeriesStatus_Unknown;
624 } 621 }
625 622
626 if (index <= 0 || index > expected) 623 if (!(index > 0 && index <= expected))
627 { 624 {
628 // Out-of-range instance index 625 // Out-of-range instance index
629 return SeriesStatus_Inconsistent; 626 return SeriesStatus_Inconsistent;
630 } 627 }
631 628
713 if (type != ResourceType_Instance) 710 if (type != ResourceType_Instance)
714 { 711 {
715 Json::Value c = Json::arrayValue; 712 Json::Value c = Json::arrayValue;
716 713
717 for (std::list<std::string>::const_iterator 714 for (std::list<std::string>::const_iterator
718 it = children.begin(); it != children.end(); it++) 715 it = children.begin(); it != children.end(); ++it)
719 { 716 {
720 c.append(*it); 717 c.append(*it);
721 } 718 }
722 719
723 switch (type) 720 switch (type)
821 { 818 {
822 boost::mutex::scoped_lock lock(mutex_); 819 boost::mutex::scoped_lock lock(mutex_);
823 820
824 int64_t id; 821 int64_t id;
825 ResourceType type; 822 ResourceType type;
826 if (!db_->LookupResource(instanceUuid, id, type) || 823 if (!db_->LookupResource(instanceUuid, id, type))
827 type != ResourceType_Instance)
828 { 824 {
829 throw OrthancException(ErrorCode_InternalError); 825 throw OrthancException(ErrorCode_InternalError);
830 } 826 }
831 827
832 if (db_->LookupAttachment(attachment, id, contentType)) 828 if (db_->LookupAttachment(attachment, id, contentType))
1110 else 1106 else
1111 LOG(INFO) << "Patient " << publicId << " has been unprotected"; 1107 LOG(INFO) << "Patient " << publicId << " has been unprotected";
1112 } 1108 }
1113 1109
1114 1110
1111 void ServerIndex::GetChildren(std::list<std::string>& result,
1112 const std::string& publicId)
1113 {
1114 result.clear();
1115
1116 boost::mutex::scoped_lock lock(mutex_);
1117
1118 ResourceType type;
1119 int64_t resource;
1120 if (!db_->LookupResource(publicId, resource, type))
1121 {
1122 throw OrthancException(ErrorCode_UnknownResource);
1123 }
1124
1125 if (type == ResourceType_Instance)
1126 {
1127 // An instance cannot have a child
1128 throw OrthancException(ErrorCode_BadParameterType);
1129 }
1130
1131 std::list<int64_t> tmp;
1132 db_->GetChildrenInternalId(tmp, resource);
1133
1134 for (std::list<int64_t>::const_iterator
1135 it = tmp.begin(); it != tmp.end(); ++it)
1136 {
1137 result.push_back(db_->GetPublicId(*it));
1138 }
1139 }
1140
1141
1115 void ServerIndex::GetChildInstances(std::list<std::string>& result, 1142 void ServerIndex::GetChildInstances(std::list<std::string>& result,
1116 const std::string& publicId) 1143 const std::string& publicId)
1117 { 1144 {
1118 result.clear(); 1145 result.clear();
1119 1146
1151 else 1178 else
1152 { 1179 {
1153 // Tag all the children of this resource as to be explored 1180 // Tag all the children of this resource as to be explored
1154 db_->GetChildrenInternalId(tmp, resource); 1181 db_->GetChildrenInternalId(tmp, resource);
1155 for (std::list<int64_t>::const_iterator 1182 for (std::list<int64_t>::const_iterator
1156 it = tmp.begin(); it != tmp.end(); it++) 1183 it = tmp.begin(); it != tmp.end(); ++it)
1157 { 1184 {
1158 toExplore.push(*it); 1185 toExplore.push(*it);
1159 } 1186 }
1160 } 1187 }
1161 } 1188 }
1210 1237
1211 return db_->LookupMetadata(target, id, type); 1238 return db_->LookupMetadata(target, id, type);
1212 } 1239 }
1213 1240
1214 1241
1215 bool ServerIndex::ListAvailableMetadata(std::list<MetadataType>& target, 1242 void ServerIndex::ListAvailableMetadata(std::list<MetadataType>& target,
1216 const std::string& publicId) 1243 const std::string& publicId)
1217 { 1244 {
1218 boost::mutex::scoped_lock lock(mutex_); 1245 boost::mutex::scoped_lock lock(mutex_);
1219 1246
1220 ResourceType rtype; 1247 ResourceType rtype;
1222 if (!db_->LookupResource(publicId, id, rtype)) 1249 if (!db_->LookupResource(publicId, id, rtype))
1223 { 1250 {
1224 throw OrthancException(ErrorCode_UnknownResource); 1251 throw OrthancException(ErrorCode_UnknownResource);
1225 } 1252 }
1226 1253
1227 return db_->ListAvailableMetadata(target, id); 1254 db_->ListAvailableMetadata(target, id);
1255 }
1256
1257
1258 void ServerIndex::ListAvailableAttachments(std::list<FileContentType>& target,
1259 const std::string& publicId,
1260 ResourceType expectedType)
1261 {
1262 boost::mutex::scoped_lock lock(mutex_);
1263
1264 ResourceType type;
1265 int64_t id;
1266 if (!db_->LookupResource(publicId, id, type) ||
1267 expectedType != type)
1268 {
1269 throw OrthancException(ErrorCode_UnknownResource);
1270 }
1271
1272 db_->ListAvailableAttachments(target, id);
1228 } 1273 }
1229 1274
1230 1275
1231 bool ServerIndex::LookupParent(std::string& target, 1276 bool ServerIndex::LookupParent(std::string& target,
1232 const std::string& publicId) 1277 const std::string& publicId)
1299 boost::mutex::scoped_lock lock(mutex_); 1344 boost::mutex::scoped_lock lock(mutex_);
1300 db_->ClearTable("ExportedResources"); 1345 db_->ClearTable("ExportedResources");
1301 } 1346 }
1302 1347
1303 1348
1304 void ServerIndex::GetStatistics(Json::Value& target, 1349 void ServerIndex::GetStatisticsInternal(/* out */ uint64_t& compressedSize,
1305 const std::string& publicId) 1350 /* out */ uint64_t& uncompressedSize,
1306 { 1351 /* out */ unsigned int& countStudies,
1307 boost::mutex::scoped_lock lock(mutex_); 1352 /* out */ unsigned int& countSeries,
1308 1353 /* out */ unsigned int& countInstances,
1309 ResourceType type; 1354 /* in */ int64_t id,
1310 int64_t top; 1355 /* in */ ResourceType type)
1311 if (!db_->LookupResource(publicId, top, type)) 1356 {
1312 {
1313 throw OrthancException(ErrorCode_UnknownResource);
1314 }
1315
1316 std::stack<int64_t> toExplore; 1357 std::stack<int64_t> toExplore;
1317 toExplore.push(top); 1358 toExplore.push(id);
1318 1359
1319 int countInstances = 0; 1360 countInstances = 0;
1320 int countSeries = 0; 1361 countSeries = 0;
1321 int countStudies = 0; 1362 countStudies = 0;
1322 uint64_t compressedSize = 0; 1363 compressedSize = 0;
1323 uint64_t uncompressedSize = 0; 1364 uncompressedSize = 0;
1324 1365
1325 while (!toExplore.empty()) 1366 while (!toExplore.empty())
1326 { 1367 {
1327 // Get the internal ID of the current resource 1368 // Get the internal ID of the current resource
1328 int64_t resource = toExplore.top(); 1369 int64_t resource = toExplore.top();
1329 toExplore.pop(); 1370 toExplore.pop();
1330 1371
1331 ResourceType thisType = db_->GetResourceType(resource); 1372 ResourceType thisType = db_->GetResourceType(resource);
1332 1373
1374 std::list<FileContentType> f;
1375 db_->ListAvailableAttachments(f, resource);
1376
1377 for (std::list<FileContentType>::const_iterator
1378 it = f.begin(); it != f.end(); ++it)
1379 {
1380 FileInfo attachment;
1381 if (db_->LookupAttachment(attachment, resource, *it))
1382 {
1383 compressedSize += attachment.GetCompressedSize();
1384 uncompressedSize += attachment.GetUncompressedSize();
1385 }
1386 }
1387
1333 if (thisType == ResourceType_Instance) 1388 if (thisType == ResourceType_Instance)
1334 { 1389 {
1335 std::list<FileContentType> f;
1336 db_->ListAvailableAttachments(f, resource);
1337
1338 for (std::list<FileContentType>::const_iterator
1339 it = f.begin(); it != f.end(); it++)
1340 {
1341 FileInfo attachment;
1342 if (db_->LookupAttachment(attachment, resource, *it))
1343 {
1344 compressedSize += attachment.GetCompressedSize();
1345 uncompressedSize += attachment.GetUncompressedSize();
1346 }
1347 }
1348
1349 countInstances++; 1390 countInstances++;
1350 } 1391 }
1351 else 1392 else
1352 { 1393 {
1353 switch (thisType) 1394 switch (thisType)
1366 1407
1367 // Tag all the children of this resource as to be explored 1408 // Tag all the children of this resource as to be explored
1368 std::list<int64_t> tmp; 1409 std::list<int64_t> tmp;
1369 db_->GetChildrenInternalId(tmp, resource); 1410 db_->GetChildrenInternalId(tmp, resource);
1370 for (std::list<int64_t>::const_iterator 1411 for (std::list<int64_t>::const_iterator
1371 it = tmp.begin(); it != tmp.end(); it++) 1412 it = tmp.begin(); it != tmp.end(); ++it)
1372 { 1413 {
1373 toExplore.push(*it); 1414 toExplore.push(*it);
1374 } 1415 }
1375 } 1416 }
1376 } 1417 }
1418
1419 if (countStudies == 0)
1420 {
1421 countStudies = 1;
1422 }
1423
1424 if (countSeries == 0)
1425 {
1426 countSeries = 1;
1427 }
1428 }
1429
1430
1431
1432 void ServerIndex::GetStatistics(Json::Value& target,
1433 const std::string& publicId)
1434 {
1435 boost::mutex::scoped_lock lock(mutex_);
1436
1437 ResourceType type;
1438 int64_t top;
1439 if (!db_->LookupResource(publicId, top, type))
1440 {
1441 throw OrthancException(ErrorCode_UnknownResource);
1442 }
1443
1444 uint64_t uncompressedSize;
1445 uint64_t compressedSize;
1446 unsigned int countStudies;
1447 unsigned int countSeries;
1448 unsigned int countInstances;
1449 GetStatisticsInternal(compressedSize, uncompressedSize, countStudies,
1450 countSeries, countInstances, top, type);
1377 1451
1378 target = Json::objectValue; 1452 target = Json::objectValue;
1379 target["DiskSize"] = boost::lexical_cast<std::string>(compressedSize); 1453 target["DiskSize"] = boost::lexical_cast<std::string>(compressedSize);
1380 target["DiskSizeMB"] = boost::lexical_cast<unsigned int>(compressedSize / MEGA_BYTES); 1454 target["DiskSizeMB"] = boost::lexical_cast<unsigned int>(compressedSize / MEGA_BYTES);
1381 target["UncompressedSize"] = boost::lexical_cast<std::string>(uncompressedSize); 1455 target["UncompressedSize"] = boost::lexical_cast<std::string>(uncompressedSize);
1395 1469
1396 case ResourceType_Instance: 1470 case ResourceType_Instance:
1397 default: 1471 default:
1398 break; 1472 break;
1399 } 1473 }
1474 }
1475
1476
1477 void ServerIndex::GetStatistics(/* out */ uint64_t& compressedSize,
1478 /* out */ uint64_t& uncompressedSize,
1479 /* out */ unsigned int& countStudies,
1480 /* out */ unsigned int& countSeries,
1481 /* out */ unsigned int& countInstances,
1482 const std::string& publicId)
1483 {
1484 boost::mutex::scoped_lock lock(mutex_);
1485
1486 ResourceType type;
1487 int64_t top;
1488 if (!db_->LookupResource(publicId, top, type))
1489 {
1490 throw OrthancException(ErrorCode_UnknownResource);
1491 }
1492
1493 GetStatisticsInternal(compressedSize, uncompressedSize, countStudies,
1494 countSeries, countInstances, top, type);
1400 } 1495 }
1401 1496
1402 1497
1403 void ServerIndex::UnstableResourcesMonitorThread(ServerIndex* that) 1498 void ServerIndex::UnstableResourcesMonitorThread(ServerIndex* that)
1404 { 1499 {
1471 1566
1472 1567
1473 1568
1474 void ServerIndex::LookupTagValue(std::list<std::string>& result, 1569 void ServerIndex::LookupTagValue(std::list<std::string>& result,
1475 DicomTag tag, 1570 DicomTag tag,
1476 const std::string& value) 1571 const std::string& value,
1572 ResourceType type)
1477 { 1573 {
1478 result.clear(); 1574 result.clear();
1479 1575
1480 boost::mutex::scoped_lock lock(mutex_); 1576 boost::mutex::scoped_lock lock(mutex_);
1481 1577
1482 std::list<int64_t> id; 1578 std::list<int64_t> id;
1483 db_->LookupTagValue(id, tag, value); 1579 db_->LookupTagValue(id, tag, value);
1484 1580
1485 for (std::list<int64_t>::const_iterator 1581 for (std::list<int64_t>::const_iterator
1486 it = id.begin(); it != id.end(); it++) 1582 it = id.begin(); it != id.end(); ++it)
1583 {
1584 if (db_->GetResourceType(*it) == type)
1585 {
1586 result.push_back(db_->GetPublicId(*it));
1587 }
1588 }
1589 }
1590
1591
1592 void ServerIndex::LookupTagValue(std::list<std::string>& result,
1593 DicomTag tag,
1594 const std::string& value)
1595 {
1596 result.clear();
1597
1598 boost::mutex::scoped_lock lock(mutex_);
1599
1600 std::list<int64_t> id;
1601 db_->LookupTagValue(id, tag, value);
1602
1603 for (std::list<int64_t>::const_iterator
1604 it = id.begin(); it != id.end(); ++it)
1487 { 1605 {
1488 result.push_back(db_->GetPublicId(*it)); 1606 result.push_back(db_->GetPublicId(*it));
1489 } 1607 }
1490 } 1608 }
1491 1609
1499 1617
1500 std::list<int64_t> id; 1618 std::list<int64_t> id;
1501 db_->LookupTagValue(id, value); 1619 db_->LookupTagValue(id, value);
1502 1620
1503 for (std::list<int64_t>::const_iterator 1621 for (std::list<int64_t>::const_iterator
1504 it = id.begin(); it != id.end(); it++) 1622 it = id.begin(); it != id.end(); ++it)
1505 { 1623 {
1506 result.push_back(db_->GetPublicId(*it)); 1624 result.push_back(db_->GetPublicId(*it));
1507 } 1625 }
1508 } 1626 }
1627
1628
1629 StoreStatus ServerIndex::AddAttachment(const FileInfo& attachment,
1630 const std::string& publicId)
1631 {
1632 boost::mutex::scoped_lock lock(mutex_);
1633
1634 Transaction t(*this);
1635
1636 ResourceType resourceType;
1637 int64_t resourceId;
1638 if (!db_->LookupResource(publicId, resourceId, resourceType))
1639 {
1640 return StoreStatus_Failure; // Inexistent resource
1641 }
1642
1643 // Remove possible previous attachment
1644 db_->DeleteAttachment(resourceId, attachment.GetContentType());
1645
1646 // Locate the patient of the target resource
1647 int64_t patientId = resourceId;
1648 for (;;)
1649 {
1650 int64_t parent;
1651 if (db_->LookupParent(parent, patientId))
1652 {
1653 // We have not reached the patient level yet
1654 patientId = parent;
1655 }
1656 else
1657 {
1658 // We have reached the patient level
1659 break;
1660 }
1661 }
1662
1663 // Possibly apply the recycling mechanism while preserving this patient
1664 assert(db_->GetResourceType(patientId) == ResourceType_Patient);
1665 Recycle(attachment.GetCompressedSize(), db_->GetPublicId(patientId));
1666
1667 db_->AddAttachment(resourceId, attachment);
1668
1669 t.Commit(attachment.GetCompressedSize());
1670
1671 return StoreStatus_Success;
1672 }
1673
1674
1675 void ServerIndex::DeleteAttachment(const std::string& publicId,
1676 FileContentType type)
1677 {
1678 boost::mutex::scoped_lock lock(mutex_);
1679 listener_->Reset();
1680
1681 Transaction t(*this);
1682
1683 ResourceType rtype;
1684 int64_t id;
1685 if (!db_->LookupResource(publicId, id, rtype))
1686 {
1687 throw OrthancException(ErrorCode_UnknownResource);
1688 }
1689
1690 db_->DeleteAttachment(id, type);
1691
1692 t.Commit(0);
1693 }
1694
1695
1509 } 1696 }