Mercurial > hg > orthanc
comparison OrthancServer/Plugins/Samples/Housekeeper/Plugin.cpp @ 5375:984300e70069
updated Housekeeper plugin to handle DicomWeb cache
author | Alain Mazy <am@osimis.io> |
---|---|
date | Tue, 22 Aug 2023 12:35:32 +0200 |
parents | 49477780e25a |
children | 4ab905749aed |
comparison
equal
deleted
inserted
replaced
5374:b216b57bbe6a | 5375:984300e70069 |
---|---|
43 static bool workerThreadShouldStop_ = false; | 43 static bool workerThreadShouldStop_ = false; |
44 static bool triggerOnStorageCompressionChange_ = true; | 44 static bool triggerOnStorageCompressionChange_ = true; |
45 static bool triggerOnMainDicomTagsChange_ = true; | 45 static bool triggerOnMainDicomTagsChange_ = true; |
46 static bool triggerOnUnnecessaryDicomAsJsonFiles_ = true; | 46 static bool triggerOnUnnecessaryDicomAsJsonFiles_ = true; |
47 static bool triggerOnIngestTranscodingChange_ = true; | 47 static bool triggerOnIngestTranscodingChange_ = true; |
48 | 48 static bool triggerOnDicomWebCacheChange_ = true; |
49 | 49 |
50 struct RunningPeriod | 50 struct RunningPeriod |
51 { | 51 { |
52 int fromHour_; | 52 int fromHour_; |
53 int toHour_; | 53 int toHour_; |
164 std::string patientsMainDicomTagsSignature; | 164 std::string patientsMainDicomTagsSignature; |
165 std::string studiesMainDicomTagsSignature; | 165 std::string studiesMainDicomTagsSignature; |
166 std::string seriesMainDicomTagsSignature; | 166 std::string seriesMainDicomTagsSignature; |
167 std::string instancesMainDicomTagsSignature; | 167 std::string instancesMainDicomTagsSignature; |
168 std::string ingestTranscoding; | 168 std::string ingestTranscoding; |
169 std::string dicomWebVersion; | |
169 bool storageCompressionEnabled; | 170 bool storageCompressionEnabled; |
170 | 171 |
171 DbConfiguration() | 172 DbConfiguration() |
172 : storageCompressionEnabled(false) | 173 : storageCompressionEnabled(false) |
173 { | 174 { |
184 patientsMainDicomTagsSignature.clear(); | 185 patientsMainDicomTagsSignature.clear(); |
185 studiesMainDicomTagsSignature.clear(); | 186 studiesMainDicomTagsSignature.clear(); |
186 seriesMainDicomTagsSignature.clear(); | 187 seriesMainDicomTagsSignature.clear(); |
187 instancesMainDicomTagsSignature.clear(); | 188 instancesMainDicomTagsSignature.clear(); |
188 ingestTranscoding.clear(); | 189 ingestTranscoding.clear(); |
190 dicomWebVersion.clear(); | |
189 } | 191 } |
190 | 192 |
191 void ToJson(Json::Value& target) | 193 void ToJson(Json::Value& target) |
192 { | 194 { |
193 if (!IsDefined()) | 195 if (!IsDefined()) |
208 | 210 |
209 target["MainDicomTagsSignature"] = signatures; | 211 target["MainDicomTagsSignature"] = signatures; |
210 target["OrthancVersion"] = orthancVersion; | 212 target["OrthancVersion"] = orthancVersion; |
211 target["StorageCompressionEnabled"] = storageCompressionEnabled; | 213 target["StorageCompressionEnabled"] = storageCompressionEnabled; |
212 target["IngestTranscoding"] = ingestTranscoding; | 214 target["IngestTranscoding"] = ingestTranscoding; |
215 target["DicomWebVersion"] = dicomWebVersion; | |
213 } | 216 } |
214 } | 217 } |
215 | 218 |
216 void FromJson(Json::Value& source) | 219 void FromJson(Json::Value& source) |
217 { | 220 { |
218 if (!source.isNull()) | 221 if (!source.isNull()) |
219 { | 222 { |
220 orthancVersion = source["OrthancVersion"].asString(); | 223 orthancVersion = source["OrthancVersion"].asString(); |
224 if (source.isMember("DicomWebVersion")) | |
225 { | |
226 dicomWebVersion = source["DicomWebVersion"].asString(); | |
227 } | |
228 else | |
229 { | |
230 dicomWebVersion = "1.14"; // the first change that requires processing has been introduced between 1.14 & 1.15 | |
231 } | |
221 | 232 |
222 const Json::Value& signatures = source["MainDicomTagsSignature"]; | 233 const Json::Value& signatures = source["MainDicomTagsSignature"]; |
223 patientsMainDicomTagsSignature = signatures["Patient"].asString(); | 234 patientsMainDicomTagsSignature = signatures["Patient"].asString(); |
224 studiesMainDicomTagsSignature = signatures["Study"].asString(); | 235 studiesMainDicomTagsSignature = signatures["Study"].asString(); |
225 seriesMainDicomTagsSignature = signatures["Series"].asString(); | 236 seriesMainDicomTagsSignature = signatures["Series"].asString(); |
319 pluginStatus_.lastProcessedChange = -1; | 330 pluginStatus_.lastProcessedChange = -1; |
320 pluginStatus_.lastChangeToProcess = -1; | 331 pluginStatus_.lastChangeToProcess = -1; |
321 pluginStatus_.lastTimeStarted = boost::date_time::not_a_date_time; | 332 pluginStatus_.lastTimeStarted = boost::date_time::not_a_date_time; |
322 | 333 |
323 pluginStatus_.lastProcessedConfiguration.orthancVersion = "1.9.0"; // when we don't know, we assume some files were stored with Orthanc 1.9.0 (last version saving the dicom-as-json files) | 334 pluginStatus_.lastProcessedConfiguration.orthancVersion = "1.9.0"; // when we don't know, we assume some files were stored with Orthanc 1.9.0 (last version saving the dicom-as-json files) |
335 pluginStatus_.lastProcessedConfiguration.dicomWebVersion = "1.14"; // the first change that requires processing has been introduced between 1.14 & 1.15 | |
324 | 336 |
325 // default main dicom tags signature are the one from Orthanc 1.4.2 (last time the list was changed): | 337 // default main dicom tags signature are the one from Orthanc 1.4.2 (last time the list was changed): |
326 pluginStatus_.lastProcessedConfiguration.patientsMainDicomTagsSignature = "0010,0010;0010,0020;0010,0030;0010,0040;0010,1000"; | 338 pluginStatus_.lastProcessedConfiguration.patientsMainDicomTagsSignature = "0010,0010;0010,0020;0010,0030;0010,0040;0010,1000"; |
327 pluginStatus_.lastProcessedConfiguration.studiesMainDicomTagsSignature = "0008,0020;0008,0030;0008,0050;0008,0080;0008,0090;0008,1030;0020,000d;0020,0010;0032,1032;0032,1060"; | 339 pluginStatus_.lastProcessedConfiguration.studiesMainDicomTagsSignature = "0008,0020;0008,0030;0008,0050;0008,0080;0008,0090;0008,1030;0020,000d;0020,0010;0032,1032;0032,1060"; |
328 pluginStatus_.lastProcessedConfiguration.seriesMainDicomTagsSignature = "0008,0021;0008,0031;0008,0060;0008,0070;0008,1010;0008,103e;0008,1070;0018,0010;0018,0015;0018,0024;0018,1030;0018,1090;0018,1400;0020,000e;0020,0011;0020,0037;0020,0105;0020,1002;0040,0254;0054,0081;0054,0101;0054,1000"; | 340 pluginStatus_.lastProcessedConfiguration.seriesMainDicomTagsSignature = "0008,0021;0008,0031;0008,0060;0008,0070;0008,1010;0008,103e;0008,1070;0018,0010;0018,0015;0018,0024;0018,1030;0018,1090;0018,1400;0020,000e;0020,0011;0020,0037;0020,0105;0020,1002;0040,0254;0054,0081;0054,0101;0054,1000"; |
358 configuration.instancesMainDicomTagsSignature = systemInfo["MainDicomTags"]["Instance"].asString(); | 370 configuration.instancesMainDicomTagsSignature = systemInfo["MainDicomTags"]["Instance"].asString(); |
359 configuration.storageCompressionEnabled = systemInfo["StorageCompression"].asBool(); | 371 configuration.storageCompressionEnabled = systemInfo["StorageCompression"].asBool(); |
360 configuration.ingestTranscoding = systemInfo["IngestTranscoding"].asString(); | 372 configuration.ingestTranscoding = systemInfo["IngestTranscoding"].asString(); |
361 | 373 |
362 configuration.orthancVersion = OrthancPlugins::GetGlobalContext()->orthancVersion; | 374 configuration.orthancVersion = OrthancPlugins::GetGlobalContext()->orthancVersion; |
375 | |
376 Json::Value pluginInfo; | |
377 if (OrthancPlugins::RestApiGet(pluginInfo, "/plugins/dicom-web", false)) | |
378 { | |
379 configuration.dicomWebVersion = pluginInfo["Version"].asString(); | |
380 } | |
363 } | 381 } |
364 | 382 |
365 static void CheckNeedsProcessing(bool& needsReconstruct, bool& needsReingest, const DbConfiguration& current, const DbConfiguration& last) | 383 static void CheckNeedsProcessing(bool& needsReconstruct, bool& needsReingest, bool& needsDicomWebCaching, const DbConfiguration& current, const DbConfiguration& last) |
366 { | 384 { |
367 needsReconstruct = false; | 385 needsReconstruct = false; |
368 needsReingest = false; | 386 needsReingest = false; |
387 needsDicomWebCaching = false; | |
369 | 388 |
370 if (!last.IsDefined()) | 389 if (!last.IsDefined()) |
371 { | 390 { |
372 return; | 391 return; |
373 } | 392 } |
472 { | 491 { |
473 OrthancPlugins::LogWarning("Housekeeper: ingest transcoding has changed but the trigger is disabled"); | 492 OrthancPlugins::LogWarning("Housekeeper: ingest transcoding has changed but the trigger is disabled"); |
474 } | 493 } |
475 } | 494 } |
476 | 495 |
496 if (!current.dicomWebVersion.empty()) | |
497 { | |
498 if (last.dicomWebVersion.empty()) | |
499 { | |
500 if (triggerOnDicomWebCacheChange_) | |
501 { | |
502 OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin is enabled and the housekeeper has never run, you might miss series metadata cache -> will perform housekeeping"); | |
503 } | |
504 needsDicomWebCaching = triggerOnDicomWebCacheChange_; | |
505 } | |
506 else | |
507 { | |
508 const char* lastDicomWebVersion = last.dicomWebVersion.c_str(); | |
509 | |
510 if (!OrthancPlugins::CheckMinimalVersion(lastDicomWebVersion, 1, 15, 0)) | |
511 { | |
512 if (triggerOnDicomWebCacheChange_) | |
513 { | |
514 OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin might miss series metadata cache -> will perform housekeeping"); | |
515 needsDicomWebCaching = true; | |
516 } | |
517 else | |
518 { | |
519 OrthancPlugins::LogWarning("Housekeeper: DicomWEB plugin might miss series metadata cache but the trigger has been disabled"); | |
520 } | |
521 } | |
522 } | |
523 } | |
477 } | 524 } |
478 | 525 |
479 static bool ProcessChanges(bool needsReconstruct, bool needsReingest, const DbConfiguration& currentDbConfiguration) | 526 static bool ProcessChanges(bool needsReconstruct, bool needsReingest, bool needsDicomWebCaching, const DbConfiguration& currentDbConfiguration) |
480 { | 527 { |
481 Json::Value changes; | 528 Json::Value changes; |
482 | 529 |
483 { | 530 { |
484 boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); | 531 boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); |
496 int64_t seq = change["Seq"].asInt64(); | 543 int64_t seq = change["Seq"].asInt64(); |
497 | 544 |
498 if (change["ChangeType"] == "NewStudy") // some StableStudy might be missing if orthanc was shutdown during a StableAge -> consider only the NewStudy events that can not be missed | 545 if (change["ChangeType"] == "NewStudy") // some StableStudy might be missing if orthanc was shutdown during a StableAge -> consider only the NewStudy events that can not be missed |
499 { | 546 { |
500 Json::Value result; | 547 Json::Value result; |
501 Json::Value request; | 548 |
502 if (needsReingest) | 549 if (needsReconstruct) |
503 { | 550 { |
504 request["ReconstructFiles"] = true; | 551 Json::Value request; |
552 if (needsReingest) | |
553 { | |
554 request["ReconstructFiles"] = true; | |
555 } | |
556 OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/reconstruct", request, false); | |
505 } | 557 } |
506 OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/reconstruct", request, false); | 558 |
559 if (needsDicomWebCaching) | |
560 { | |
561 Json::Value request; | |
562 OrthancPlugins::RestApiPost(result, "/studies/" + change["ID"].asString() + "/update-dicomweb-cache", request, true); | |
563 } | |
507 } | 564 } |
508 | 565 |
509 { | 566 { |
510 boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); | 567 boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); |
511 | 568 |
552 | 609 |
553 bool needsReconstruct = false; | 610 bool needsReconstruct = false; |
554 bool needsReingest = false; | 611 bool needsReingest = false; |
555 bool needsFullProcessing = false; | 612 bool needsFullProcessing = false; |
556 bool needsProcessing = false; | 613 bool needsProcessing = false; |
614 bool needsDicomWebCaching = false; | |
557 | 615 |
558 { | 616 { |
559 boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); | 617 boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); |
560 | 618 |
561 // compare with last full processed configuration | 619 // compare with last full processed configuration |
562 CheckNeedsProcessing(needsReconstruct, needsReingest, currentDbConfiguration, pluginStatus_.lastProcessedConfiguration); | 620 CheckNeedsProcessing(needsReconstruct, needsReingest, needsDicomWebCaching, currentDbConfiguration, pluginStatus_.lastProcessedConfiguration); |
563 needsFullProcessing = needsReconstruct || needsReingest; | 621 needsFullProcessing = needsReconstruct || needsReingest || needsDicomWebCaching; |
564 needsProcessing = needsFullProcessing; | 622 needsProcessing = needsFullProcessing; |
565 | 623 |
566 // if a processing was in progress, check if the config has changed since | 624 // if a processing was in progress, check if the config has changed since |
567 if (pluginStatus_.currentlyProcessingConfiguration.IsDefined()) | 625 if (pluginStatus_.currentlyProcessingConfiguration.IsDefined()) |
568 { | 626 { |
569 needsProcessing = true; // since a processing was in progress, we need at least a partial processing | 627 needsProcessing = true; // since a processing was in progress, we need at least a partial processing |
570 | 628 |
571 bool needsReconstruct2 = false; | 629 bool needsReconstruct2 = false; |
572 bool needsReingest2 = false; | 630 bool needsReingest2 = false; |
573 | 631 bool needsDicomWebCaching2 = false; |
574 CheckNeedsProcessing(needsReconstruct2, needsReingest2, currentDbConfiguration, pluginStatus_.currentlyProcessingConfiguration); | 632 |
575 needsFullProcessing = needsReconstruct2 || needsReingest2; // if the configuration has changed compared to the config being processed, we need a full processing again | 633 CheckNeedsProcessing(needsReconstruct2, needsReingest2, needsDicomWebCaching2, currentDbConfiguration, pluginStatus_.currentlyProcessingConfiguration); |
634 needsFullProcessing = needsReconstruct2 || needsReingest2 || needsDicomWebCaching2; // if the configuration has changed compared to the config being processed, we need a full processing again | |
576 } | 635 } |
577 } | 636 } |
578 | 637 |
579 if (!needsProcessing) | 638 if (!needsProcessing) |
580 { | 639 { |
619 | 678 |
620 while (!workerThreadShouldStop_ && !completed) | 679 while (!workerThreadShouldStop_ && !completed) |
621 { | 680 { |
622 if (runningPeriods_.isInPeriod()) | 681 if (runningPeriods_.isInPeriod()) |
623 { | 682 { |
624 completed = ProcessChanges(needsReconstruct, needsReingest, currentDbConfiguration); | 683 completed = ProcessChanges(needsReconstruct, needsReingest, needsDicomWebCaching, currentDbConfiguration); |
625 SaveStatusInDb(); | 684 SaveStatusInDb(); |
626 | 685 |
627 if (!completed) | 686 if (!completed) |
628 { | 687 { |
629 boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); | 688 boost::recursive_mutex::scoped_lock lock(pluginStatusMutex_); |
792 triggerOnStorageCompressionChange_ = housekeeper.GetBooleanValue("StorageCompressionChange", true); | 851 triggerOnStorageCompressionChange_ = housekeeper.GetBooleanValue("StorageCompressionChange", true); |
793 | 852 |
794 triggerOnMainDicomTagsChange_ = housekeeper.GetBooleanValue("MainDicomTagsChange", true); | 853 triggerOnMainDicomTagsChange_ = housekeeper.GetBooleanValue("MainDicomTagsChange", true); |
795 triggerOnUnnecessaryDicomAsJsonFiles_ = housekeeper.GetBooleanValue("UnnecessaryDicomAsJsonFiles", true); | 854 triggerOnUnnecessaryDicomAsJsonFiles_ = housekeeper.GetBooleanValue("UnnecessaryDicomAsJsonFiles", true); |
796 triggerOnIngestTranscodingChange_ = housekeeper.GetBooleanValue("IngestTranscodingChange", true); | 855 triggerOnIngestTranscodingChange_ = housekeeper.GetBooleanValue("IngestTranscodingChange", true); |
856 triggerOnDicomWebCacheChange_ = housekeeper.GetBooleanValue("DicomWebCacheChange", true); | |
797 } | 857 } |
798 | 858 |
799 if (housekeeper.GetJson().isMember("Schedule")) | 859 if (housekeeper.GetJson().isMember("Schedule")) |
800 { | 860 { |
801 runningPeriods_.load(housekeeper.GetJson()["Schedule"]); | 861 runningPeriods_.load(housekeeper.GetJson()["Schedule"]); |
802 } | 862 } |
803 | 863 |
804 OrthancPluginRegisterOnChangeCallback(c, OnChangeCallback); | 864 OrthancPluginRegisterOnChangeCallback(c, OnChangeCallback); |
805 OrthancPluginRegisterRestCallback(c, "/housekeeper/status", GetPluginStatus); // for bacward compatiblity with version 1.11.0 | 865 OrthancPluginRegisterRestCallback(c, "/housekeeper/status", GetPluginStatus); // for backward compatiblity with version 1.11.0 |
806 OrthancPluginRegisterRestCallback(c, "/plugins/housekeeper/status", GetPluginStatus); | 866 OrthancPluginRegisterRestCallback(c, "/plugins/housekeeper/status", GetPluginStatus); |
807 } | 867 } |
808 else | 868 else |
809 { | 869 { |
810 OrthancPlugins::LogWarning("Housekeeper plugin is disabled by the configuration file"); | 870 OrthancPlugins::LogWarning("Housekeeper plugin is disabled by the configuration file"); |