Mercurial > hg > orthanc
changeset 6963:a710e864790d streaming
integration default->streaming
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Fri, 26 Jun 2026 15:12:06 +0200 |
| parents | 7b52e79a4aca (current diff) 3948b93ed918 (diff) |
| children | 5eb525758b7f |
| files | NEWS OrthancFramework/Sources/Logging.cpp OrthancServer/Sources/ServerJobs/ArchiveJob.cpp |
| diffstat | 4 files changed, 123 insertions(+), 65 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Thu Jun 18 17:05:30 2026 +0200 +++ b/NEWS Fri Jun 26 15:12:06 2026 +0200 @@ -53,6 +53,9 @@ "/modalities/../get", "queries/../answers/../retrieve" now always overwrites the "DicomAet" and the "DicomModalities.LocalAet" configurations. * Optimized the "/patients|studies|series|instances/../statistics" routes. +* Updated the naming structure of the zip downloaded from "/../../archive" routes. Notably added + SeriesNumber at the series level and now using " - " instead of " " to separate DICOM tags used + in folder names. Plugin SDK ----------
--- a/OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake Thu Jun 18 17:05:30 2026 +0200 +++ b/OrthancFramework/Resources/CMake/DownloadOrthancFramework.cmake Fri Jun 26 15:12:06 2026 +0200 @@ -238,6 +238,11 @@ # for HttpClient that returns the answer body in case of HTTP error set(ORTHANC_FRAMEWORK_PRE_RELEASE ON) set(ORTHANC_FRAMEWORK_MD5 "665f8aa70d7c5091bc20da37cf664910") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "004b351797fe") + # PixelsMasker 0.1.2 (framework post-1.12.11) + # for ScopedThreadNameSetter + set(ORTHANC_FRAMEWORK_PRE_RELEASE ON) + set(ORTHANC_FRAMEWORK_MD5 "f078ca997217b831ab3f6741f08a8c07") endif() endif() endif()
--- a/OrthancFramework/Sources/Logging.cpp Thu Jun 18 17:05:30 2026 +0200 +++ b/OrthancFramework/Sources/Logging.cpp Fri Jun 26 15:12:06 2026 +0200 @@ -538,10 +538,10 @@ #include "SystemToolbox.h" #include "Toolbox.h" -#include <fstream> +#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/filesystem.hpp> #include <boost/thread.hpp> -#include <boost/date_time/posix_time/posix_time.hpp> +#include <fstream> namespace @@ -631,14 +631,14 @@ static std::string pluginName_; // this string can only be non-empty if running from a plugin static bool hasOrthancAdvancedLogging_ = false; // Whether the Orthanc runtime is >= 1.12.4 static bool hasClearThreadName_ = false; // Whether the Orthanc runtime is >= 1.12.12 -static boost::recursive_mutex threadNamesMutex_; +static boost::shared_mutex threadNamesMutex_; static ThreadNamesMap threadNames_; static bool enableThreadNames_ = true; static boost::recursive_mutex threadContextsMutex_; static ThreadContextsMap threadContexts_; static bool enableContexts_ = true; static std::list<Orthanc::Logging::ILoggingListener*> loggingListeners_; -static boost::mutex loggingListenersMutex_; +static boost::shared_mutex loggingListenersMutex_; namespace Orthanc @@ -723,45 +723,42 @@ } - void SetCurrentThreadNameInternal(const boost::thread::id& id, const std::string& name) + void SetCurrentThreadName(const std::string& name) { - boost::recursive_mutex::scoped_lock lock(threadNamesMutex_); - if (name.size() > 16) { throw OrthancException(ErrorCode_InternalError, std::string("Thread name can not exceed 16 characters: ") + name); } - // std::cout << "+++++ SetCurrentThreadNameInternal " << id << " " << name << std::endl; + if (pluginContext_ == NULL) + { + const boost::thread::id threadId = boost::this_thread::get_id(); + + { + boost::unique_lock<boost::shared_mutex> lock(threadNamesMutex_); + #if !defined(NDEBUG) - if (threadNames_.find(id) != threadNames_.end()) - { - throw OrthancException(ErrorCode_InternalError, std::string("This thread already has a name or another thread is re-using the same threadId and you have not called 'ClearCurrentThreadName()': ") + name); - } + if (threadNames_.find(threadId) != threadNames_.end()) + { + throw OrthancException(ErrorCode_InternalError, std::string("This thread already has a name or another thread is re-using the same threadId and you have not called 'ClearCurrentThreadName()': ") + name); + } - for (ThreadNamesMap::const_iterator it = threadNames_.begin(); it != threadNames_.end(); ++it) - { - if (it->second == name) - { - throw OrthancException(ErrorCode_InternalError, std::string("Another thread already uses this thread name: ") + name); - } - } + for (ThreadNamesMap::const_iterator it = threadNames_.begin(); it != threadNames_.end(); ++it) + { + if (it->second == name) + { + throw OrthancException(ErrorCode_InternalError, std::string("Another thread already uses this thread name: ") + name); + } + } #endif - threadNames_[id] = name; + threadNames_[threadId] = name; + } #if defined(__linux__) && !defined(NDEBUG) && !defined(__LSB_VERSION__) - // set the thread name at "system" level too -> required to have the thread names visible in GDB ! - pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); // thread names are limited to 15 in Linux + // set the thread name at "system" level too -> required to have the thread names visible in GDB ! + pthread_setname_np(pthread_self(), name.substr(0, 15).c_str()); // thread names are limited to 15 in Linux #endif - } - - void SetCurrentThreadName(const std::string& name) - { - if (pluginContext_ == NULL) - { - boost::recursive_mutex::scoped_lock lock(threadNamesMutex_); - SetCurrentThreadNameInternal(boost::this_thread::get_id(), name); } else { @@ -769,47 +766,55 @@ } } + bool HasCurrentThreadName() { - boost::thread::id threadId = boost::this_thread::get_id(); + const boost::thread::id threadId = boost::this_thread::get_id(); - boost::recursive_mutex::scoped_lock lock(threadNamesMutex_); - return threadNames_.find(threadId) != threadNames_.end(); + { + boost::shared_lock<boost::shared_mutex> lock(threadNamesMutex_); + return threadNames_.find(threadId) != threadNames_.end(); + } } + void ClearCurrentThreadName() { if (pluginContext_ == NULL) { - boost::thread::id threadId = boost::this_thread::get_id(); + const boost::thread::id threadId = boost::this_thread::get_id(); - boost::recursive_mutex::scoped_lock lock(threadNamesMutex_); - - // std::cout << "+++++ ClearCurrentThreadName " << threadId << std::endl; - threadNames_.erase(threadId); + { + boost::unique_lock<boost::shared_mutex> lock(threadNamesMutex_); + threadNames_.erase(threadId); + } } else if (hasClearThreadName_) // only recent runtimes support it (from 1.12.12) { pluginContext_->InvokeService(pluginContext_, _OrthancPluginService_ClearCurrentThreadName, NULL); } + } - } std::string GetCurrentThreadName() { - boost::thread::id threadId = boost::this_thread::get_id(); + const boost::thread::id threadId = boost::this_thread::get_id(); - boost::recursive_mutex::scoped_lock lock(threadNamesMutex_); + { + boost::shared_lock<boost::shared_mutex> lock(threadNamesMutex_); - if (threadNames_.find(threadId) == threadNames_.end()) - { - // set the threadId as the thread name - SetCurrentThreadNameInternal(threadId, boost::lexical_cast<std::string>(threadId)); + std::map<boost::thread::id, std::string>::const_iterator found = threadNames_.find(threadId); + if (found != threadNames_.end()) + { + return found->second; + } } - return threadNames_[threadId]; + // "SetCurrentThreadName()" has not been invoked for this thread + return boost::lexical_cast<std::string>(threadId); } + bool LookupCurrentContext(std::string& result) { boost::thread::id threadId = boost::this_thread::get_id(); @@ -825,11 +830,13 @@ return false; } + ScopedThreadNameSetter::ScopedThreadNameSetter(const std::string& threadName) { SetCurrentThreadName(threadName); } + ScopedThreadNameSetter::~ScopedThreadNameSetter() { ClearCurrentThreadName(); @@ -893,16 +900,18 @@ void AddLoggingListener(ILoggingListener* listener) { - boost::mutex::scoped_lock lock(loggingListenersMutex_); + boost::unique_lock<boost::shared_mutex> lock(loggingListenersMutex_); loggingListeners_.push_back(listener); } + void ClearLoggingListeners() { - boost::mutex::scoped_lock lock(loggingListenersMutex_); + boost::unique_lock<boost::shared_mutex> lock(loggingListenersMutex_); loggingListeners_.clear(); } + static void GetLinePrefix(std::string& prefix, LogLevel level, const char* pluginName, // when logging in the core but coming from a plugin, pluginName_ is NULL but this argument is != NULL @@ -1041,12 +1050,14 @@ } } + void Finalize() { boost::mutex::scoped_lock lock(loggingStreamsMutex_); loggingStreamsContext_.reset(NULL); } + void Reset() { { @@ -1249,10 +1260,20 @@ *stream_ << messageStream_.str() << "\n"; stream_->flush(); - boost::mutex::scoped_lock lock(loggingListenersMutex_); - for (std::list<Orthanc::Logging::ILoggingListener*>::iterator it = loggingListeners_.begin(); it != loggingListeners_.end(); ++it) { - (*it)->HandleLog(level_, category_, pluginName_, file_, line_, messageStream_.str()); + boost::shared_lock<boost::shared_mutex> lock(loggingListenersMutex_); + + for (std::list<Orthanc::Logging::ILoggingListener*>::iterator it = loggingListeners_.begin(); it != loggingListeners_.end(); ++it) + { + try + { + (*it)->HandleLog(level_, category_, pluginName_, file_, line_, messageStream_.str()); + } + catch (...) + { + // Don't throw in destructors + } + } } } @@ -1283,4 +1304,4 @@ } #endif // ORTHANC_ENABLE_LOGGING -// NOLINTEND(bugprone-reserved-identifier) \ No newline at end of file +// NOLINTEND(bugprone-reserved-identifier)
--- a/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp Thu Jun 18 17:05:30 2026 +0200 +++ b/OrthancServer/Sources/ServerJobs/ArchiveJob.cpp Fri Jun 26 15:12:06 2026 +0200 @@ -712,6 +712,7 @@ const std::string& publicId) ORTHANC_OVERRIDE { std::string path; + std::vector<std::string> pathParts; DicomMap tags; ResourceType resourceIdLevel = GetResourceIdType(level); @@ -723,18 +724,48 @@ { case ArchiveResourceType_Patient: case ArchiveResourceType_PatientInfoFromStudy: - path = GetTag(tags, DICOM_TAG_PATIENT_ID) + " " + GetTag(tags, DICOM_TAG_PATIENT_NAME); - break; - + { + pathParts.push_back(GetTag(tags, DICOM_TAG_PATIENT_ID)); + std::string patientName = GetTag(tags, DICOM_TAG_PATIENT_NAME); + if (!patientName.empty()) + { + pathParts.push_back(patientName); + } + }; break; case ArchiveResourceType_Study: - path = GetTag(tags, DICOM_TAG_ACCESSION_NUMBER) + " " + GetTag(tags, DICOM_TAG_STUDY_DESCRIPTION); - break; - + { + std::string accessionNumber = GetTag(tags, DICOM_TAG_ACCESSION_NUMBER); + std::string studyDescription = GetTag(tags, DICOM_TAG_STUDY_DESCRIPTION); + if (!accessionNumber.empty()) + { + pathParts.push_back(accessionNumber); + } + if (!studyDescription.empty()) + { + pathParts.push_back(studyDescription); + } + }; break; case ArchiveResourceType_Series: { + std::string seriesNumber = GetTag(tags, DICOM_TAG_SERIES_NUMBER); std::string modality = GetTag(tags, DICOM_TAG_MODALITY); - path = modality + " " + GetTag(tags, DICOM_TAG_SERIES_DESCRIPTION); - + std::string seriesDescription = GetTag(tags, DICOM_TAG_SERIES_DESCRIPTION); + + if (!seriesNumber.empty()) + { + pathParts.push_back(seriesNumber); + } + + if (!modality.empty()) + { + pathParts.push_back(modality); + } + + if (!seriesDescription.empty()) + { + pathParts.push_back(seriesDescription); + } + if (modality.size() == 0) { snprintf(instanceFormat_, sizeof(instanceFormat_) - 1, "%%08d.dcm"); @@ -751,16 +782,14 @@ } counter_ = 0; - - break; - } - + }; break; default: THROW_WITH_FILE_AND_LINE_INFO(ErrorCode_InternalError); } } - path = Toolbox::StripSpaces(path); + Toolbox::JoinStrings(path, pathParts, " - "); + path = Toolbox::StripSpaces(path); if (path.empty() || (static_cast<size_t>(boost::count(path, '^')) == path.size())) // this happens with non ASCII patient names: only the '^' remains and this is not a valid zip folder name
