Mercurial > hg > orthanc-stone
changeset 2295:bc68bb942f9e refactoring tip
added class StoneApplication::Configuration
line wrap: on
line diff
--- a/Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -144,14 +144,6 @@ ProcessOptions(argc, argv); /** - Create the shared loaders context - */ - loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); - - // we are in SDL --> downcast to concrete type - boost::shared_ptr<GenericLoadersContext> loadersContext = boost::dynamic_pointer_cast<GenericLoadersContext>(loadersContext_); - - /** Url of the Orthanc instance Typically, in a native application (Qt, SDL), it will be an absolute URL like "http://localhost:8042". In wasm on the browser, it could be an absolute URL, provided you do not have cross-origin problems, or a relative @@ -161,6 +153,8 @@ */ std::string orthancUrl = arguments_["orthanc"]; + StoneApplication::Configuration configuration; + { Orthanc::WebServiceParameters p; if (HasArgument("orthanc")) @@ -176,9 +170,19 @@ { ORTHANC_ASSERT(!HasArgument("password")); } - loadersContext->SetOrthancParameters(p); + configuration.SetRemoteOrthancParameters(p); } + OrthancStone::StoneApplication::Initialize(configuration); + + /** + Create the shared loaders context + */ + loadersContext_.reset(new GenericLoadersContext(configuration, 1, 4, 1)); + + // we are in SDL --> downcast to concrete type + boost::shared_ptr<GenericLoadersContext> loadersContext = boost::dynamic_pointer_cast<GenericLoadersContext>(loadersContext_); + loadersContext->StartOracle(); CreateLoaders(); @@ -462,7 +466,6 @@ int main(int argc, char* argv[]) { OrthancStone::StoneInitialize(); - OrthancStone::StoneApplication::Initialize(); OrthancStone::SdlWindow::GlobalInitialize(); int status = 0;
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -181,7 +181,6 @@ int status = 0; OrthancStone::StoneInitialize(); - OrthancStone::StoneApplication::Initialize(); OrthancStone::SdlWindow::GlobalInitialize(); try @@ -204,11 +203,14 @@ boost::shared_ptr<OrthancStone::UndoStack> undoStack(new OrthancStone::UndoStack); #endif - OrthancStone::GenericLoadersContext context(1, 4, 1); - Orthanc::WebServiceParameters orthancWebService; orthancWebService.SetUrl(orthancUrl); - context.SetOrthancParameters(orthancWebService); + + OrthancStone::StoneApplication::Configuration configuration; + configuration.SetRemoteOrthancParameters(orthancWebService); + + OrthancStone::GenericLoadersContext context(configuration, 1, 4, 1); + OrthancStone::StoneApplication::Initialize(configuration); context.StartOracle();
--- a/Applications/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/Applications/Samples/WebAssembly/RtViewer/RtViewerWasm.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -26,6 +26,7 @@ // Stone of Orthanc includes #include "../../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.h" #include "../../../../OrthancStone/Sources/Platforms/WebAssembly/WebGLViewport.h" +#include "../../../../OrthancStone/Sources/StoneApplication.h" #include "../../../../OrthancStone/Sources/StoneException.h" #include "../../../../OrthancStone/Sources/StoneInitialization.h" @@ -141,19 +142,26 @@ void RtViewerApp::RunWasm() { - loadersContext_.reset(new WebAssemblyLoadersContext(1, 4, 1)); + StoneApplication::Configuration configuration; + + if (HasArgument("orthanc")) + { + configuration.SetLocalOrthancRoot(GetArgument("orthanc")); + } + else + { + configuration.SetLocalOrthancRoot(".."); + } + + configuration.SetDicomCacheSize(128 * 1024 * 1024); // 128MB + + OrthancStone::StoneApplication::Initialize(configuration); + loadersContext_.reset(new WebAssemblyLoadersContext(configuration, 1, 4, 1)); // we are in WASM --> downcast to concrete type boost::shared_ptr<WebAssemblyLoadersContext> loadersContext = boost::dynamic_pointer_cast<WebAssemblyLoadersContext>(loadersContext_); - if (HasArgument("orthanc")) - loadersContext->SetLocalOrthanc(GetArgument("orthanc")); - else - loadersContext->SetLocalOrthanc(".."); - - loadersContext->SetDicomCacheSize(128 * 1024 * 1024); // 128MB - CreateLoaders(); CreateView("RtViewer_Axial", VolumeProjection_Axial); @@ -178,7 +186,6 @@ try { OrthancStone::StoneInitialize(); - OrthancStone::StoneApplication::Initialize(); //Orthanc::Logging::EnableTraceLevel(true); Orthanc::Logging::EnableInfoLevel(true);
--- a/Applications/Samples/WebAssembly/SingleFrameViewer/SingleFrameViewer.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/Applications/Samples/WebAssembly/SingleFrameViewer/SingleFrameViewer.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -25,6 +25,7 @@ #include "../../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.h" #include "../../../../OrthancStone/Sources/Platforms/WebAssembly/WebGLViewport.h" #include "../../../../OrthancStone/Sources/Platforms/WebAssembly/WebGLViewportsRegistry.h" +#include "../../../../OrthancStone/Sources/StoneApplication.h" #include "../../../../OrthancStone/Sources/StoneException.h" #include "../../../../OrthancStone/Sources/StoneInitialization.h" @@ -80,7 +81,6 @@ try { OrthancStone::StoneInitialize(); - OrthancStone::StoneApplication::Initialize(); Orthanc::Logging::EnableInfoLevel(true); //Orthanc::Logging::EnableTraceLevel(true); @@ -91,9 +91,13 @@ << "." << __EMSCRIPTEN_tiny__; LOG(INFO) << "Endianness: " << Orthanc::EnumerationToString(Orthanc::Toolbox::DetectEndianness()); - context_.reset(new OrthancStone::WebAssemblyLoadersContext(1, 4, 1)); - context_->SetLocalOrthanc(".."); - context_->SetDicomCacheSize(128 * 1024 * 1024); // 128MB + + OrthancStone::StoneApplication::Configuration configuration; + configuration.SetLocalOrthancRoot(".."); + configuration.SetDicomCacheSize(128 * 1024 * 1024); // 128MB + + OrthancStone::StoneApplication::Initialize(configuration); + context_.reset(new OrthancStone::WebAssemblyLoadersContext(configuration, 1, 4, 1)); DISPATCH_JAVASCRIPT_EVENT("WasmModuleInitialized"); }
--- a/Applications/StoneWebViewer/WebApplication/app.js Fri Sep 25 14:02:54 2026 +0200 +++ b/Applications/StoneWebViewer/WebApplication/app.js Fri Sep 25 18:24:15 2026 +0200 @@ -1595,21 +1595,11 @@ **/ stone.Setup(Module); - stone.SetDicomWebRoot(app.globalConfiguration.DicomWebRoot, - true /* assume "/rendered" is available in DICOMweb (could be a configuration option) */); + stone.Configure(JSON.stringify(app.globalConfiguration)); + stone.SetSoftwareRendering(app.settingSoftwareRendering); stone.SetLinearInterpolation(app.settingLinearInterpolation); - if ('DicomCacheSize' in app.globalConfiguration) { - stone.SetDicomCacheSize(app.globalConfiguration.DicomCacheSize); - } - - // Calls to "stone.AddHttpHeader()" must be after "stone.SetDicomWebRoot()", - // and before "stone.SetSkipSeriesFromModalities()" - for (var header in app.globalConfiguration.DicomWebHttpHeaders) { - stone.AddHttpHeader(header, app.globalConfiguration.DicomWebHttpHeaders[header]); - } - // Bearer token is new in Stone Web viewer 2.0 var token = getParameterFromUrl('token'); if (token !== undefined) { @@ -1632,10 +1622,6 @@ * Calls to "stone.XXX()" can be reordered after this point. **/ - if ('SkipSeriesFromModalities' in app.globalConfiguration) { - stone.SetSkipSeriesFromModalities(JSON.stringify(app.globalConfiguration.SkipSeriesFromModalities)); - } - if (app.globalConfiguration.ShowInfoPanelAtStartup == 'Always') { app.modalNotDiagnostic = true; } else if (app.globalConfiguration.ShowInfoPanelAtStartup == 'Never') { @@ -1646,16 +1632,6 @@ alert('Bad value for option "ShowInfoPanelAtStartup": ' + app.globalConfiguration.ShowInfoPanelAtStartup); } - var color = app.globalConfiguration['AnnotationsColor']; - if (color !== undefined) { - stone.SetAnnotationsColor(color[0], color[1], color[2]); - } - - color = app.globalConfiguration['HighlightedAnnotationsColor']; - if (color !== undefined) { - stone.SetHighlightedAnnotationsColor(color[0], color[1], color[2]); - } - console.warn('Stone properly initialized'); app.stoneWebViewerVersion = stone.GetStoneWebViewerVersion();
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -26,15 +26,6 @@ #include <emscripten.h> -// Orthanc framework includes -#include <Cache/MemoryObjectCache.h> -#include <DicomFormat/DicomArray.h> -#include <DicomParsing/ParsedDicomFile.h> -#include <Images/Image.h> -#include <Images/ImageProcessing.h> -#include <Images/JpegReader.h> -#include <Logging.h> - // Stone includes #include "../../../OrthancStone/Sources/Loaders/DicomResourcesLoader.h" #include "../../../OrthancStone/Sources/Loaders/SeriesMetadataLoader.h" @@ -66,6 +57,16 @@ #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.h" #include "../../../OrthancStone/Sources/Platforms/WebAssembly/WebGLViewport.h" +// Orthanc framework includes +#include <Cache/MemoryObjectCache.h> +#include <DicomFormat/DicomArray.h> +#include <DicomParsing/ParsedDicomFile.h> +#include <Images/Image.h> +#include <Images/ImageProcessing.h> +#include <Images/JpegReader.h> +#include <Logging.h> +#include <SerializationToolbox.h> + #include <algorithm> #include <boost/make_shared.hpp> @@ -4898,6 +4899,44 @@ // END TODO Refactoring +static bool ParseColor(uint8_t& red, + uint8_t& green, + uint8_t& blue, + const Json::Value& configuration, + const std::string& key) +{ + if (configuration.isMember(key)) + { + const Json::Value& value = configuration[key]; + + if (value.isArray() && + value.size() == 3 && + value[0].isIntegral() && + value[1].isIntegral() && + value[2].isIntegral() && + value[0].asInt() >= 0 && + value[0].asInt() <= 255 && + value[1].asInt() >= 0 && + value[1].asInt() <= 255 && + value[2].asInt() >= 0 && + value[2].asInt() <= 255) + { + red = static_cast<uint8_t>(value[0].asInt()); + green = static_cast<uint8_t>(value[1].asInt()); + blue = static_cast<uint8_t>(value[2].asInt()); + return true; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + else + { + return 0; + } +} + extern "C" { @@ -4906,16 +4945,11 @@ printf("Initializing Stone\n"); OrthancStone::StoneInitialize(); - OrthancStone::StoneApplication::Initialize(); - Orthanc::Logging::EnableInfoLevel(true); //Orthanc::Logging::EnableTraceLevel(true); LOG(INFO) << "Using DCMTK version: " << DCMTK_VERSION_NUMBER; - context_.reset(new OrthancStone::WebAssemblyLoadersContext(1, 4, 1)); - context_->SetDicomCacheSize(128 * 1024 * 1024); // 128MB - framesCache_.reset(new FramesCache); instancesCache_.reset(new InstancesCache); overlayLayerSource_.reset(new OverlayLayerSource); @@ -4943,28 +4977,63 @@ EMSCRIPTEN_KEEPALIVE - void SetDicomWebRoot(const char* uri, - int useRendered) + void Configure(const char* globalConfiguration) { try { - source_.SetDicomWebSource(uri); - source_.SetDicomWebRendered(useRendered != 0); - } - EXTERN_CATCH_EXCEPTIONS; - } - - - EMSCRIPTEN_KEEPALIVE - void SetDicomWebThroughOrthanc(const char* orthancRoot, - const char* serverName, - int hasRendered) - { - try - { - context_->SetLocalOrthanc(orthancRoot); - source_.SetDicomWebThroughOrthancSource(serverName); - source_.SetDicomWebRendered(hasRendered != 0); + Json::Value parsed; + if (Orthanc::Toolbox::ReadJson(parsed, globalConfiguration)) + { + OrthancStone::StoneApplication::Configuration configuration; + + source_.SetDicomWebSource(Orthanc::SerializationToolbox::ReadString(parsed, "DicomWebRoot")); + source_.SetDicomWebRendered(true); // assume "/rendered" is available in DICOMweb (could be a configuration option) + + // Another possibility: + // configuration.SetLocalOrthancRoot(orthancRoot); source_.SetDicomWebThroughOrthancSource(serverName); + + unsigned int size = Orthanc::SerializationToolbox::ReadUnsignedInteger(parsed, "DicomCacheSize", 0); + configuration.SetDicomCacheSize(size * 1024 * 1024); // The size is expressed in MB in the configuration file + + static const char* const KEY_DICOM_WEB_HEADERS = "DicomWebHttpHeaders"; + if (parsed.isMember(KEY_DICOM_WEB_HEADERS)) + { + std::map<std::string, std::string> headers; + Orthanc::SerializationToolbox::ReadMapOfStrings(headers, parsed, KEY_DICOM_WEB_HEADERS); + + for (std::map<std::string, std::string>::const_iterator it = headers.begin(); it != headers.end(); ++it) + { + source_.AddHttpHeader(it->first, it->second); + } + } + + uint8_t r, g, b; + if (ParseColor(r, g, b, parsed, "AnnotationsColor")) + { + SetAnnotationsColor(OrthancStone::Color(r, g, b)); + } + + if (ParseColor(r, g, b, parsed, "HighlightedAnnotationsColor")) + { + SetHighlightedColor(OrthancStone::Color(r, g, b)); + } + + context_.reset(new OrthancStone::WebAssemblyLoadersContext(configuration, 1, 4, 1)); + + static const char* const KEY_SKIP_SERIES = "SkipSeriesFromModalities"; + if (parsed.isMember(KEY_SKIP_SERIES)) + { + std::vector<std::string> modalities; + Orthanc::SerializationToolbox::ReadArrayOfStrings(modalities, parsed, KEY_SKIP_SERIES); + GetResourcesLoader().SetSkipSeriesFromModalities(modalities); + } + + OrthancStone::StoneApplication::Initialize(configuration); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } } EXTERN_CATCH_EXCEPTIONS; } @@ -4983,50 +5052,6 @@ EMSCRIPTEN_KEEPALIVE - void SetDicomCacheSize(int sizeMB) - { - try - { - if (sizeMB == 0) - { - LOG(WARNING) << "The DICOM cache is disabled"; - } - else - { - LOG(INFO) << "The DICOM cache size is set to " << sizeMB << "MB"; - } - - if (sizeMB >= 0) - { - context_->SetDicomCacheSize(sizeMB * 1024 * 1024); - } - } - EXTERN_CATCH_EXCEPTIONS; - } - - - EMSCRIPTEN_KEEPALIVE - void SetSkipSeriesFromModalities(const char* value) - { - try - { - LOG(WARNING) << "SetSkipSeriesFromModalities " << value; - - Json::Value modalities; - Orthanc::Toolbox::ReadJson(modalities, value); - std::vector<std::string> skipSeriesFromModalities; - - for (Json::Value::ArrayIndex i = 0; i < modalities.size(); i++) - { - skipSeriesFromModalities.push_back(modalities[i].asString()); - } - GetResourcesLoader().SetSkipSeriesFromModalities(skipSeriesFromModalities); - } - EXTERN_CATCH_EXCEPTIONS; - } - - - EMSCRIPTEN_KEEPALIVE void FetchAllStudies() { try @@ -5712,34 +5737,6 @@ EMSCRIPTEN_KEEPALIVE - void SetAnnotationsColor(int red, - int green, - int blue) - { - try - { - OrthancStone::Color color(red, green, blue); - SetAnnotationsColor(color); - } - EXTERN_CATCH_EXCEPTIONS; - } - - - EMSCRIPTEN_KEEPALIVE - void SetHighlightedAnnotationsColor(int red, - int green, - int blue) - { - try - { - OrthancStone::Color color(red, green, blue); - SetHighlightedColor(color); - } - EXTERN_CATCH_EXCEPTIONS; - } - - - EMSCRIPTEN_KEEPALIVE void *Allocate(size_t size) { return malloc(size);
--- a/OrthancStone/Sources/Loaders/GenericLoadersContext.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Loaders/GenericLoadersContext.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -93,11 +93,12 @@ } - GenericLoadersContext::GenericLoadersContext(unsigned int maxHighPriority, + GenericLoadersContext::GenericLoadersContext(const StoneApplication::Configuration& configuration, + unsigned int maxHighPriority, unsigned int maxStandardPriority, unsigned int maxLowPriority) { - oracle_.reset(new ThreadedOracle(*this)); + oracle_.reset(new ThreadedOracle(configuration, *this)); scheduler_ = OracleScheduler::Create(*oracle_, oracleObservable_, *this, maxHighPriority, maxStandardPriority, maxLowPriority); @@ -117,27 +118,6 @@ } - void GenericLoadersContext::SetOrthancParameters(const Orthanc::WebServiceParameters& parameters) - { - boost::recursive_mutex::scoped_lock lock(mutex_); - oracle_->SetOrthancParameters(parameters); - } - - - void GenericLoadersContext::SetRootDirectory(const std::string& root) - { - boost::recursive_mutex::scoped_lock lock(mutex_); - oracle_->SetRootDirectory(root); - } - - - void GenericLoadersContext::SetDicomCacheSize(size_t size) - { - boost::recursive_mutex::scoped_lock lock(mutex_); - oracle_->SetDicomCacheSize(size); - } - - void GenericLoadersContext::StartOracle() { boost::recursive_mutex::scoped_lock lock(mutex_);
--- a/OrthancStone/Sources/Loaders/GenericLoadersContext.h Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Loaders/GenericLoadersContext.h Fri Sep 25 18:24:15 2026 +0200 @@ -32,6 +32,7 @@ namespace OrthancStone { + // TODO Refactoring - Remove this class class GenericLoadersContext : public ILoadersContext, private IMessageEmitter @@ -57,7 +58,8 @@ const IMessage& message) ORTHANC_OVERRIDE; public: - GenericLoadersContext(unsigned int maxHighPriority, + GenericLoadersContext(const StoneApplication::Configuration& configuration, + unsigned int maxHighPriority, unsigned int maxStandardPriority, unsigned int maxLowPriority); @@ -65,12 +67,6 @@ virtual ILock* Lock() ORTHANC_OVERRIDE; - void SetOrthancParameters(const Orthanc::WebServiceParameters& parameters); - - void SetRootDirectory(const std::string& root); - - void SetDicomCacheSize(size_t size); - void StartOracle(); void StopOracle();
--- a/OrthancStone/Sources/Oracle/GenericOracleRunner.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Oracle/GenericOracleRunner.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -442,22 +442,22 @@ break; case IOracleCommand::Type_OrthancRestApi: - RunInternal(callback, orthanc_, + RunInternal(callback, configuration_.GetRemoteOrthancParameters(), dynamic_cast<const OrthancRestApiCommand&>(callback.GetCommand())); break; case IOracleCommand::Type_GetOrthancImage: - RunInternal(callback, orthanc_, + RunInternal(callback, configuration_.GetRemoteOrthancParameters(), dynamic_cast<const GetOrthancImageCommand&>(callback.GetCommand())); break; case IOracleCommand::Type_GetOrthancWebViewerJpeg: - RunInternal(callback, orthanc_, + RunInternal(callback, configuration_.GetRemoteOrthancParameters(), dynamic_cast<const GetOrthancWebViewerJpegCommand&>(callback.GetCommand())); break; case IOracleCommand::Type_ReadFile: - RunInternal(callback, rootDirectory_, + RunInternal(callback, configuration_.GetRootDirectory(), dynamic_cast<const ReadFileCommand&>(callback.GetCommand())); break; @@ -467,12 +467,12 @@ switch (callback.GetCommand().GetType()) { case IOracleCommand::Type_ParseDicomFromFile: - RunInternal(callback, dicomCache_, rootDirectory_, + RunInternal(callback, dicomCache_, configuration_.GetRootDirectory(), dynamic_cast<const ParseDicomFromFileCommand&>(callback.GetCommand())); break; case IOracleCommand::Type_ParseDicomFromWado: - RunInternal(callback, dicomCache_, orthanc_, + RunInternal(callback, dicomCache_, configuration_.GetRemoteOrthancParameters(), dynamic_cast<const ParseDicomFromWadoCommand&>(callback.GetCommand())); break;
--- a/OrthancStone/Sources/Oracle/GenericOracleRunner.h Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Oracle/GenericOracleRunner.h Fri Sep 25 18:24:15 2026 +0200 @@ -33,6 +33,7 @@ # include "../Toolbox/ParsedDicomCache.h" #endif +#include "../StoneApplication.h" #include "IOracleCommand.h" #include "OracleCallback.h" @@ -44,37 +45,16 @@ class GenericOracleRunner : public boost::noncopyable { private: - Orthanc::WebServiceParameters orthanc_; - std::string rootDirectory_; + StoneApplication::Configuration configuration_; #if ORTHANC_ENABLE_DCMTK == 1 boost::shared_ptr<ParsedDicomCache> dicomCache_; #endif public: - GenericOracleRunner() : - rootDirectory_(".") - { - } - - void SetOrthanc(const Orthanc::WebServiceParameters& orthanc) - { - orthanc_ = orthanc; - } - - const Orthanc::WebServiceParameters& GetOrthanc() const + GenericOracleRunner(const StoneApplication::Configuration& configuration) : + configuration_(configuration) { - return orthanc_; - } - - void SetRootDirectory(const std::string& rootDirectory) - { - rootDirectory_ = rootDirectory; - } - - const std::string& GetRootDirectory() const - { - return rootDirectory_; } #if ORTHANC_ENABLE_DCMTK == 1
--- a/OrthancStone/Sources/Oracle/ThreadedOracle.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Oracle/ThreadedOracle.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -135,20 +135,14 @@ } else { - GenericOracleRunner runner; - - { - boost::mutex::scoped_lock lock(mutex_); - runner.SetOrthanc(orthanc_); - runner.SetRootDirectory(rootDirectory_); + GenericOracleRunner runner(configuration_); #if ORTHANC_ENABLE_DCMTK == 1 - if (dicomCache_) - { - runner.SetDicomCache(dicomCache_); - } + if (dicomCache_) + { + runner.SetDicomCache(dicomCache_); + } #endif - } runner.Run(*item); } @@ -191,7 +185,7 @@ that->sleepingCommands_->AwakeExpired(that->emitter_); - boost::this_thread::sleep(boost::posix_time::milliseconds(that->sleepingTimeResolution_)); + boost::this_thread::sleep(boost::posix_time::milliseconds(that->configuration_.GetWorkersTimeResolution())); } } @@ -232,14 +226,23 @@ } - ThreadedOracle::ThreadedOracle(IMessageEmitter& emitter) : + ThreadedOracle::ThreadedOracle(const StoneApplication::Configuration& configuration, + IMessageEmitter& emitter) : + configuration_(configuration), emitter_(emitter), - rootDirectory_("."), state_(State_Setup), workers_(4), - sleepingCommands_(new SleepingCommands), - sleepingTimeResolution_(50) // By default, time resolution of 50ms + sleepingCommands_(new SleepingCommands) { + if (configuration.GetDicomCacheSize() == 0) + { + LOG(WARNING) << "The DICOM cache is disabled"; + } + else + { + LOG(INFO) << "The DICOM cache size is set to " << configuration.GetDicomCacheSize() << " bytes"; + dicomCache_.reset(new ParsedDicomCache(configuration.GetDicomCacheSize())); + } } @@ -266,85 +269,6 @@ } - void ThreadedOracle::SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc) - { - boost::mutex::scoped_lock lock(mutex_); - orthanc_ = orthanc; - } - - - void ThreadedOracle::SetRootDirectory(const std::string& rootDirectory) - { - boost::mutex::scoped_lock lock(mutex_); - rootDirectory_ = rootDirectory; - } - - - void ThreadedOracle::SetThreadsCount(unsigned int count) - { - boost::mutex::scoped_lock lock(mutex_); - - if (count == 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else if (state_ != State_Setup) - { - LOG(ERROR) << "ThreadedOracle::SetThreadsCount(): (state_ != State_Setup)"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - workers_.resize(count); - } - } - - - void ThreadedOracle::SetSleepingTimeResolution(unsigned int milliseconds) - { - boost::mutex::scoped_lock lock(mutex_); - - if (milliseconds == 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else if (state_ != State_Setup) - { - LOG(ERROR) << "ThreadedOracle::SetSleepingTimeResolution(): (state_ != State_Setup)"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - sleepingTimeResolution_ = milliseconds; - } - } - - - void ThreadedOracle::SetDicomCacheSize(size_t size) - { -#if ORTHANC_ENABLE_DCMTK == 1 - boost::mutex::scoped_lock lock(mutex_); - - if (state_ != State_Setup) - { - LOG(ERROR) << "ThreadedOracle::SetDicomCacheSize(): (state_ != State_Setup)"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - if (size == 0) - { - dicomCache_.reset(); - } - else - { - dicomCache_.reset(new ParsedDicomCache(size)); - } - } -#endif - } - - void ThreadedOracle::Start() { boost::mutex::scoped_lock lock(mutex_); @@ -483,11 +407,12 @@ }; - ThreadedOracle::ThreadedOracle(unsigned int countWorkers) : - sleepingThread_(new SleepRunnable, 50 /* milliseconds */) + ThreadedOracle::ThreadedOracle(const StoneApplication::Configuration& configuration) : + configuration_(configuration), + sleepingThread_(new SleepRunnable, configuration.GetWorkersTimeResolution()) { - threadPool_.SetThreadsCount(countWorkers); - threadPool_.SetDequeueTimeout(50); + threadPool_.SetThreadsCount(configuration.GetOracleThreadsCount()); + threadPool_.SetDequeueTimeout(configuration.GetWorkersTimeResolution()); } @@ -517,31 +442,22 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } - try + if (command->GetType() == IOracleCommand::Type_Sleep) { - switch (command->GetType()) - { - case IOracleCommand::Type_Sleep: - { - SleepRunnable& runnable = dynamic_cast<SleepRunnable&>(sleepingThread_.GetRunnable()); - runnable.Add(environment, client, dynamic_cast<SleepOracleCommand*>(protection.release())); - break; - } + SleepRunnable& runnable = dynamic_cast<SleepRunnable&>(sleepingThread_.GetRunnable()); + runnable.Add(environment, client, dynamic_cast<SleepOracleCommand*>(protection.release())); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, - "Command type not implemented by the Threaded Oracle: " + - boost::lexical_cast<std::string>(command->GetType())); - } - } - catch (Orthanc::OrthancException& e) - { - environment.NotifyOracleError(client, protection.release(), e); - } - catch (...) - { - environment.NotifyOracleError(client, protection.release(), Orthanc::OrthancException(Orthanc::ErrorCode_InternalError)); - } + + bool ThreadedOracle::Schedule(boost::shared_ptr<IObserver> receiver, + IOracleCommand* command) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } } }
--- a/OrthancStone/Sources/Oracle/ThreadedOracle.h Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Oracle/ThreadedOracle.h Fri Sep 25 18:24:15 2026 +0200 @@ -44,8 +44,8 @@ #include "../Messages/IMessageEmitter.h" #include "../Platforms/Native/RunnableThread.h" +#include "../StoneApplication.h" #include "GenericOracleRunner.h" -#include "IEnvironment.h" #include "IOracle.h" #include <MultiThreading/SharedMessageQueue.h> @@ -66,16 +66,14 @@ class SleepingCommands; + StoneApplication::Configuration configuration_; IMessageEmitter& emitter_; - Orthanc::WebServiceParameters orthanc_; - std::string rootDirectory_; Orthanc::SharedMessageQueue queue_; State state_; boost::mutex mutex_; std::vector<boost::thread*> workers_; boost::shared_ptr<SleepingCommands> sleepingCommands_; boost::thread sleepingWorker_; - unsigned int sleepingTimeResolution_; #if ORTHANC_ENABLE_DCMTK == 1 boost::shared_ptr<ParsedDicomCache> dicomCache_; @@ -90,19 +88,10 @@ void StopInternal(); public: - explicit ThreadedOracle(IMessageEmitter& emitter); - - virtual ~ThreadedOracle(); - - void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc); + ThreadedOracle(const StoneApplication::Configuration& configuration, + IMessageEmitter& emitter); - void SetRootDirectory(const std::string& rootDirectory); - - void SetThreadsCount(unsigned int count); - - void SetSleepingTimeResolution(unsigned int milliseconds); - - void SetDicomCacheSize(size_t size); + virtual ~ThreadedOracle() ORTHANC_OVERRIDE; void Start(); @@ -118,16 +107,19 @@ namespace New { - class ThreadedOracle : public IOracle + class ThreadedOracle : + public ::OrthancStone::IOracle, // TODO Refactoring - Remove this + public ::OrthancStone::New::IOracle { private: class SleepRunnable; - RunnableThread sleepingThread_; - Orthanc::ThreadPool threadPool_; + StoneApplication::Configuration configuration_; + RunnableThread sleepingThread_; + Orthanc::ThreadPool threadPool_; public: - ThreadedOracle(unsigned int countWorkers); + ThreadedOracle(const StoneApplication::Configuration& configuration); void Start(); @@ -136,6 +128,9 @@ virtual void Submit(IEnvironment& environment, const boost::shared_ptr<IOracleClient>& client, IOracleCommand* command /* takes ownership */) ORTHANC_OVERRIDE; + + virtual bool Schedule(boost::shared_ptr<IObserver> receiver, + IOracleCommand* command) ORTHANC_OVERRIDE; }; } }
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -77,11 +77,12 @@ }; - WebAssemblyLoadersContext::WebAssemblyLoadersContext(unsigned int maxHighPriority, + WebAssemblyLoadersContext::WebAssemblyLoadersContext(const StoneApplication::Configuration& configuration, + unsigned int maxHighPriority, unsigned int maxStandardPriority, - unsigned int maxLowPriority) + unsigned int maxLowPriority) : + oracle_(configuration) { - oracle_.GetOracleObservable(); scheduler_ = OracleScheduler::Create(oracle_, oracle_.GetOracleObservable(), oracle_, maxHighPriority, maxStandardPriority, maxLowPriority);
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.h Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyLoadersContext.h Fri Sep 25 18:24:15 2026 +0200 @@ -31,6 +31,7 @@ namespace OrthancStone { + // TODO Refactoring - Remove this class class WebAssemblyLoadersContext : public ILoadersContext { private: @@ -41,25 +42,11 @@ std::list< boost::shared_ptr<IObserver> > loaders_; public: - WebAssemblyLoadersContext(unsigned int maxHighPriority, + WebAssemblyLoadersContext(const StoneApplication::Configuration& configuration, + unsigned int maxHighPriority, unsigned int maxStandardPriority, unsigned int maxLowPriority); - void SetLocalOrthanc(const std::string& root) - { - oracle_.SetLocalOrthanc(root); - } - - void SetRemoteOrthanc(const Orthanc::WebServiceParameters& orthanc) - { - oracle_.SetRemoteOrthanc(orthanc); - } - - void SetDicomCacheSize(size_t size) - { - oracle_.SetDicomCacheSize(size); - } - WebAssemblyOracle::CachedInstanceAccessor* AccessCachedInstance(const std::string& sopInstanceUid) { return new WebAssemblyOracle::CachedInstanceAccessor(oracle_, sopInstanceUid);
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -384,24 +384,24 @@ switch (method_) { - case Orthanc::HttpMethod_Get: - method = "GET"; - break; + case Orthanc::HttpMethod_Get: + method = "GET"; + break; - case Orthanc::HttpMethod_Post: - method = "POST"; - break; + case Orthanc::HttpMethod_Post: + method = "POST"; + break; - case Orthanc::HttpMethod_Delete: - method = "DELETE"; - break; + case Orthanc::HttpMethod_Delete: + method = "DELETE"; + break; - case Orthanc::HttpMethod_Put: - method = "PUT"; - break; + case Orthanc::HttpMethod_Put: + method = "PUT"; + break; - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } strcpy(attr.requestMethod, method); @@ -464,59 +464,59 @@ { switch (callback.GetCommand().GetType()) { - case IOracleCommand::Type_Http: - { - callback.NotifySuccess(new HttpCommand::SuccessMessage( - dynamic_cast<const HttpCommand&>(callback.GetCommand()), headers, answer)); - break; - } + case IOracleCommand::Type_Http: + { + callback.NotifySuccess(new HttpCommand::SuccessMessage( + dynamic_cast<const HttpCommand&>(callback.GetCommand()), headers, answer)); + break; + } - case IOracleCommand::Type_OrthancRestApi: - { - LOG(TRACE) << "WebAssemblyOracle::FetchContext::SuccessCallback. About to call EmitMessage(message);"; - callback.NotifySuccess(new OrthancRestApiCommand::SuccessMessage( - dynamic_cast<const OrthancRestApiCommand&>(callback.GetCommand()), headers, answer)); - break; - } + case IOracleCommand::Type_OrthancRestApi: + { + LOG(TRACE) << "WebAssemblyOracle::FetchContext::SuccessCallback. About to call EmitMessage(message);"; + callback.NotifySuccess(new OrthancRestApiCommand::SuccessMessage( + dynamic_cast<const OrthancRestApiCommand&>(callback.GetCommand()), headers, answer)); + break; + } - case IOracleCommand::Type_GetOrthancImage: - { - dynamic_cast<const GetOrthancImageCommand&>(callback.GetCommand()).ProcessHttpAnswer(callback, answer, headers); - break; - } + case IOracleCommand::Type_GetOrthancImage: + { + dynamic_cast<const GetOrthancImageCommand&>(callback.GetCommand()).ProcessHttpAnswer(callback, answer, headers); + break; + } - case IOracleCommand::Type_GetOrthancWebViewerJpeg: - { - dynamic_cast<const GetOrthancWebViewerJpegCommand&>(callback.GetCommand()).ProcessHttpAnswer(callback, answer); - break; - } + case IOracleCommand::Type_GetOrthancWebViewerJpeg: + { + dynamic_cast<const GetOrthancWebViewerJpegCommand&>(callback.GetCommand()).ProcessHttpAnswer(callback, answer); + break; + } - case IOracleCommand::Type_ParseDicomFromWado: - { + case IOracleCommand::Type_ParseDicomFromWado: + { #if ORTHANC_ENABLE_DCMTK == 1 - const ParseDicomFromWadoCommand& c = dynamic_cast<const ParseDicomFromWadoCommand&>(callback.GetCommand()); + const ParseDicomFromWadoCommand& c = dynamic_cast<const ParseDicomFromWadoCommand&>(callback.GetCommand()); - size_t fileSize; - std::unique_ptr<Orthanc::ParsedDicomFile> dicom - (ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, headers)); + size_t fileSize; + std::unique_ptr<Orthanc::ParsedDicomFile> dicom + (ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, headers)); - callback.NotifySuccess(new ParseDicomSuccessMessage(c, c.GetSource(), *dicom, fileSize, true)); + callback.NotifySuccess(new ParseDicomSuccessMessage(c, c.GetSource(), *dicom, fileSize, true)); - if (dicomCache_.get()) - { - // Store it into the cache for future use - dicomCache_->Acquire(BUCKET_SOP, c.GetSopInstanceUid(), dicom.release(), fileSize, true); - } + if (dicomCache_.get()) + { + // Store it into the cache for future use + dicomCache_->Acquire(BUCKET_SOP, c.GetSopInstanceUid(), dicom.release(), fileSize, true); + } #else - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); #endif - break; - } + break; + } - default: - LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle (in SuccessCallback): " - << callback.GetCommand().GetType(); - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + default: + LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle (in SuccessCallback): " + << callback.GetCommand().GetType(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } } @@ -524,18 +524,20 @@ void WebAssemblyOracle::SetOrthancUrl(FetchCommand& command, const std::string& uri) const { - if (isLocalOrthanc_) + if (configuration_.IsLocalOrthanc()) { - command.SetUrl(StoneToolbox::JoinUrl(localOrthancRoot_, uri)); + command.SetUrl(StoneToolbox::JoinUrl(configuration_.GetLocalOrthancRoot(), uri)); } else { - command.SetUrl(StoneToolbox::JoinUrl(remoteOrthanc_.GetUrl(), uri)); - command.AddHttpHeaders(remoteOrthanc_.GetHttpHeaders()); + const Orthanc::WebServiceParameters& orthanc = configuration_.GetRemoteOrthancParameters(); - if (!remoteOrthanc_.GetUsername().empty()) + command.SetUrl(StoneToolbox::JoinUrl(orthanc.GetUrl(), uri)); + command.AddHttpHeaders(orthanc.GetHttpHeaders()); + + if (!orthanc.GetUsername().empty()) { - command.SetCredentials(remoteOrthanc_.GetUsername(), remoteOrthanc_.GetPassword()); + command.SetCredentials(orthanc.GetUsername(), orthanc.GetPassword()); } } } @@ -658,54 +660,54 @@ switch (command.GetRestCommand().GetType()) { - case IOracleCommand::Type_Http: + case IOracleCommand::Type_Http: + { + const HttpCommand& rest = + dynamic_cast<const HttpCommand&>(command.GetRestCommand()); + + FetchCommand fetch(*this, protection.release()); + + fetch.SetMethod(rest.GetMethod()); + fetch.SetUrl(rest.GetUrl()); + fetch.AddHttpHeaders(rest.GetHttpHeaders()); + fetch.SetTimeout(rest.GetTimeout()); + + if (rest.GetMethod() == Orthanc::HttpMethod_Post || + rest.GetMethod() == Orthanc::HttpMethod_Put) { - const HttpCommand& rest = - dynamic_cast<const HttpCommand&>(command.GetRestCommand()); - - FetchCommand fetch(*this, protection.release()); + std::string body = rest.GetBody(); + fetch.SetBody(body); + } - fetch.SetMethod(rest.GetMethod()); - fetch.SetUrl(rest.GetUrl()); - fetch.AddHttpHeaders(rest.GetHttpHeaders()); - fetch.SetTimeout(rest.GetTimeout()); - - if (rest.GetMethod() == Orthanc::HttpMethod_Post || - rest.GetMethod() == Orthanc::HttpMethod_Put) - { - std::string body = rest.GetBody(); - fetch.SetBody(body); - } - - fetch.Execute(); - break; + fetch.Execute(); + break; + } + + case IOracleCommand::Type_OrthancRestApi: + { + const OrthancRestApiCommand& rest = + dynamic_cast<const OrthancRestApiCommand&>(command.GetRestCommand()); + + FetchCommand fetch(*this, protection.release()); + + fetch.SetMethod(rest.GetMethod()); + SetOrthancUrl(fetch, rest.GetUri()); + fetch.AddHttpHeaders(rest.GetHttpHeaders()); + fetch.SetTimeout(rest.GetTimeout()); + + if (rest.GetMethod() == Orthanc::HttpMethod_Post || + rest.GetMethod() == Orthanc::HttpMethod_Put) + { + std::string body = rest.GetBody(); + fetch.SetBody(body); } - case IOracleCommand::Type_OrthancRestApi: - { - const OrthancRestApiCommand& rest = - dynamic_cast<const OrthancRestApiCommand&>(command.GetRestCommand()); - - FetchCommand fetch(*this, protection.release()); - - fetch.SetMethod(rest.GetMethod()); - SetOrthancUrl(fetch, rest.GetUri()); - fetch.AddHttpHeaders(rest.GetHttpHeaders()); - fetch.SetTimeout(rest.GetTimeout()); + fetch.Execute(); + break; + } - if (rest.GetMethod() == Orthanc::HttpMethod_Post || - rest.GetMethod() == Orthanc::HttpMethod_Put) - { - std::string body = rest.GetBody(); - fetch.SetBody(body); - } - - fetch.Execute(); - break; - } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } } @@ -721,55 +723,74 @@ switch (protection->GetCommand().GetType()) { - case IOracleCommand::Type_Http: - { - FetchCommand fetch(*this, protection.release()); - ExecuteHttpCommand(fetch); - break; - } + case IOracleCommand::Type_Http: + { + FetchCommand fetch(*this, protection.release()); + ExecuteHttpCommand(fetch); + break; + } - case IOracleCommand::Type_OrthancRestApi: - { - FetchCommand fetch(*this, protection.release()); - ExecuteOrthancRestApiCommand(fetch); - break; - } + case IOracleCommand::Type_OrthancRestApi: + { + FetchCommand fetch(*this, protection.release()); + ExecuteOrthancRestApiCommand(fetch); + break; + } - case IOracleCommand::Type_GetOrthancImage: - { - FetchCommand fetch(*this, protection.release()); - ExecuteGetOrthancImageCommand(fetch); - break; - } + case IOracleCommand::Type_GetOrthancImage: + { + FetchCommand fetch(*this, protection.release()); + ExecuteGetOrthancImageCommand(fetch); + break; + } - case IOracleCommand::Type_GetOrthancWebViewerJpeg: - { - FetchCommand fetch(*this, protection.release()); - ExecuteGetOrthancWebViewerJpegCommand(fetch); - break; - } + case IOracleCommand::Type_GetOrthancWebViewerJpeg: + { + FetchCommand fetch(*this, protection.release()); + ExecuteGetOrthancWebViewerJpegCommand(fetch); + break; + } - case IOracleCommand::Type_Sleep: - { - unsigned int timeoutMS = dynamic_cast<const SleepOracleCommand&>(protection->GetCommand()).GetDelay(); - emscripten_set_timeout(TimeoutCallback, timeoutMS, protection.release()); - break; - } + case IOracleCommand::Type_Sleep: + { + unsigned int timeoutMS = dynamic_cast<const SleepOracleCommand&>(protection->GetCommand()).GetDelay(); + emscripten_set_timeout(TimeoutCallback, timeoutMS, protection.release()); + break; + } + + case IOracleCommand::Type_ParseDicomFromWado: +#if ORTHANC_ENABLE_DCMTK == 1 + ExecuteParseDicomFromWadoCommand(protection.release()); +#else + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, + "DCMTK must be enabled to parse DICOM files"); +#endif + break; - case IOracleCommand::Type_ParseDicomFromWado: + default: + LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle (in Schedule): " + << protection->GetCommand().GetType(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + + WebAssemblyOracle::WebAssemblyOracle(const StoneApplication::Configuration& configuration) : + configuration_(configuration) + { #if ORTHANC_ENABLE_DCMTK == 1 - ExecuteParseDicomFromWadoCommand(protection.release()); + if (configuration.GetDicomCacheSize() == 0) + { + LOG(WARNING) << "The DICOM cache is disabled"; + } + else + { + LOG(INFO) << "The DICOM cache size is set to " << configuration.GetDicomCacheSize() << " bytes"; + dicomCache_.reset(new ParsedDicomCache(configuration.GetDicomCacheSize())); + } #else - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, - "DCMTK must be enabled to parse DICOM files"); + LOG(INFO) << "DCMTK support is disabled, the DICOM cache is disabled"; #endif - break; - - default: - LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle (in Schedule): " - << protection->GetCommand().GetType(); - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } } @@ -793,23 +814,6 @@ } - void WebAssemblyOracle::SetDicomCacheSize(size_t size) - { -#if ORTHANC_ENABLE_DCMTK == 1 - if (size == 0) - { - dicomCache_.reset(); - } - else - { - dicomCache_.reset(new ParsedDicomCache(size)); - } -#else - LOG(INFO) << "DCMTK support is disabled, the DICOM cache is disabled"; -#endif - } - - WebAssemblyOracle::CachedInstanceAccessor::CachedInstanceAccessor(WebAssemblyOracle& oracle, const std::string& sopInstanceUid) { @@ -821,6 +825,7 @@ #endif } + bool WebAssemblyOracle::CachedInstanceAccessor::IsValid() const { #if ORTHANC_ENABLE_DCMTK == 1 @@ -831,6 +836,7 @@ #endif } + #if ORTHANC_ENABLE_DCMTK == 1 const Orthanc::ParsedDicomFile& WebAssemblyOracle::CachedInstanceAccessor::GetDicom() const { @@ -846,6 +852,7 @@ } #endif + size_t WebAssemblyOracle::CachedInstanceAccessor::GetFileSize() const { #if ORTHANC_ENABLE_DCMTK == 1 @@ -861,6 +868,7 @@ } } + bool WebAssemblyOracle::CachedInstanceAccessor::HasPixelData() const { #if ORTHANC_ENABLE_DCMTK == 1
--- a/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.h Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/Platforms/WebAssembly/WebAssemblyOracle.h Fri Sep 25 18:24:15 2026 +0200 @@ -35,10 +35,11 @@ #include "../../Oracle/OracleCallback.h" // TODO Refactoring +#include "../../Messages/IMessageEmitter.h" #include "../../Messages/IObservable.h" -#include "../../Messages/IMessageEmitter.h" #include "../../Oracle/IEnvironment.h" #include "../../Oracle/IOracle.h" +#include "../../StoneApplication.h" #if ORTHANC_ENABLE_DCMTK == 1 # include "../../Toolbox/ParsedDicomCache.h" @@ -75,10 +76,8 @@ void ExecuteParseDicomFromWadoCommand(IOracleCallback* callback); - IObservable oracleObservable_; - bool isLocalOrthanc_; - std::string localOrthancRoot_; - Orthanc::WebServiceParameters remoteOrthanc_; + StoneApplication::Configuration configuration_; + IObservable oracleObservable_; #if ORTHANC_ENABLE_DCMTK == 1 std::unique_ptr<ParsedDicomCache> dicomCache_; @@ -91,10 +90,7 @@ void Submit(IOracleCallback* callback); public: - WebAssemblyOracle() : - isLocalOrthanc_(false) - { - } + WebAssemblyOracle(const StoneApplication::Configuration& configuration); virtual void EmitMessage(boost::weak_ptr<IObserver> observer, const IMessage& message) ORTHANC_OVERRIDE @@ -114,20 +110,6 @@ return oracleObservable_; } - void SetLocalOrthanc(const std::string& root) - { - isLocalOrthanc_ = true; - localOrthancRoot_ = root; - } - - void SetRemoteOrthanc(const Orthanc::WebServiceParameters& orthanc) - { - isLocalOrthanc_ = false; - remoteOrthanc_ = orthanc; - } - - void SetDicomCacheSize(size_t size); - class CachedInstanceAccessor : public boost::noncopyable { private:
--- a/OrthancStone/Sources/StoneApplication.cpp Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/StoneApplication.cpp Fri Sep 25 18:24:15 2026 +0200 @@ -40,6 +40,82 @@ namespace OrthancStone { + StoneApplication::Configuration::Configuration() : + isLocalOrthanc_(false), + rootDirectory_("."), + oracleThreadsCount_(4), + workersTimeResolution_(50), // By default, time resolution of 50ms + dicomCacheSize_(0) // By default, no DICOM cache + { + } + + + void StoneApplication::Configuration::SetRemoteOrthancParameters(const Orthanc::WebServiceParameters& orthanc) + { + isLocalOrthanc_ = false; + remoteOrthanc_ = orthanc; + } + + + const Orthanc::WebServiceParameters& StoneApplication::Configuration::GetRemoteOrthancParameters() const + { + if (isLocalOrthanc_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return remoteOrthanc_; + } + } + + + void StoneApplication::Configuration::SetLocalOrthancRoot(const std::string& root) + { + isLocalOrthanc_ = true; + localOrthancRoot_ = root; + } + + + const std::string& StoneApplication::Configuration::GetLocalOrthancRoot() const + { + if (isLocalOrthanc_) + { + return localOrthancRoot_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + void StoneApplication::Configuration::SetOracleThreadsCount(unsigned int count) + { + if (count == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + oracleThreadsCount_ = count; + } + } + + + void StoneApplication::Configuration::SetWorkersTimeResolution(unsigned int milliseconds) + { + if (milliseconds == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + workersTimeResolution_ = milliseconds; + } + } + + #if ORTHANC_STONE_TARGET_PLATFORM_WASM == 1 class StoneApplication::PImpl { @@ -48,6 +124,11 @@ WebAssemblyOracle oracle_; public: + PImpl(const Configuration& configuration) : + oracle_(configuration) + { + } + IEnvironment& GetEnvironment() { return environment_; @@ -77,8 +158,8 @@ New::ThreadedOracle oracle_; public: - PImpl(unsigned int oracleThreads) : - oracle_(oracleThreads) + PImpl(const Configuration& configuration) : + oracle_(configuration) { } @@ -109,14 +190,13 @@ static Orthanc::Mutex applicationMutex_; static std::unique_ptr<StoneApplication> application_; - static unsigned int threadsCount_ = 4; - StoneApplication::StoneApplication() + StoneApplication::StoneApplication(const Configuration& configuration) { #if ORTHANC_STONE_TARGET_PLATFORM_WASM == 1 - pimpl_ = new PImpl; + pimpl_ = new PImpl(configuration); #elif ORTHANC_STONE_TARGET_PLATFORM_NATIVE == 1 - pimpl_ = new PImpl(threadsCount_); + pimpl_ = new PImpl(configuration); #else # error Support your platform here #endif @@ -143,13 +223,13 @@ } - void StoneApplication::Initialize() + void StoneApplication::Initialize(const Configuration& configuration) { Orthanc::Mutex::ScopedLock lock(applicationMutex_); if (application_.get() == NULL) { - application_.reset(new StoneApplication); + application_.reset(new StoneApplication(configuration)); application_->pimpl_->Start(); } else @@ -184,18 +264,4 @@ assert(pimpl_ != NULL); pimpl_->GetOracle().Submit(pimpl_->GetEnvironment(), client, command); } - - - void StoneApplication::SetThreadsCount(unsigned int count) - { - if (count == 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - Orthanc::Mutex::ScopedLock lock(applicationMutex_); - threadsCount_ = count; - } - } }
--- a/OrthancStone/Sources/StoneApplication.h Fri Sep 25 14:02:54 2026 +0200 +++ b/OrthancStone/Sources/StoneApplication.h Fri Sep 25 18:24:15 2026 +0200 @@ -25,23 +25,89 @@ #include "Oracle/IEnvironment.h" -#include <string> +#include <WebServiceParameters.h> namespace OrthancStone { class StoneApplication : public boost::noncopyable { + public: + class Configuration + { + private: + bool isLocalOrthanc_; + Orthanc::WebServiceParameters remoteOrthanc_; + std::string localOrthancRoot_; + std::string rootDirectory_; + unsigned int oracleThreadsCount_; + unsigned int workersTimeResolution_; + size_t dicomCacheSize_; + + public: + Configuration(); + + bool IsLocalOrthanc() const + { + return isLocalOrthanc_; + } + + void SetRemoteOrthancParameters(const Orthanc::WebServiceParameters& orthanc); + + const Orthanc::WebServiceParameters& GetRemoteOrthancParameters() const; + + // Using a local Orthanc only makes sense for WebAssembly, if it + // is Orthanc that serves the Web application + void SetLocalOrthancRoot(const std::string& root); + + const std::string& GetLocalOrthancRoot() const; + + void SetRootDirectory(const std::string& root) + { + rootDirectory_ = root; + } + + const std::string& GetRootDirectory() const + { + return rootDirectory_; + } + + void SetOracleThreadsCount(unsigned int count); + + unsigned int GetOracleThreadsCount() const + { + return oracleThreadsCount_; + } + + void SetWorkersTimeResolution(unsigned int milliseconds); + + unsigned int GetWorkersTimeResolution() const + { + return workersTimeResolution_; + } + + // Setting the cache size to zero disables it + void SetDicomCacheSize(size_t size) + { + dicomCacheSize_ = size; + } + + size_t GetDicomCacheSize() const + { + return dicomCacheSize_; + } + }; + private: class PImpl; PImpl* pimpl_; - StoneApplication(); + StoneApplication(const Configuration& configuration); public: - static StoneApplication& GetInstance(); + static void Initialize(const Configuration& configuration); - static void Initialize(); + static StoneApplication& GetInstance(); static void Finalize(); @@ -51,7 +117,5 @@ void Submit(const boost::shared_ptr<IOracleClient>& client, IOracleCommand* command /* takes ownership */); - - static void SetThreadsCount(unsigned int count); }; }
