diff Sources/Plugin.cpp @ 45:967f947014ac nexus

adding experimental support for nexus models
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 09 Apr 2024 22:13:01 +0200
parents 8a1daa321afe
children 9b2a2fcc9878
line wrap: on
line diff
--- a/Sources/Plugin.cpp	Sat Apr 06 17:19:16 2024 +0200
+++ b/Sources/Plugin.cpp	Tue Apr 09 22:13:01 2024 +0200
@@ -30,6 +30,7 @@
 
 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
 
+#include <Cache/MemoryStringCache.h>
 #include <DicomParsing/FromDcmtkBridge.h>
 #include <Images/ImageProcessing.h>
 #include <Logging.h>
@@ -123,7 +124,7 @@
     return;
   }
 
-  std::string file = request->groups[0];
+  const std::string file = request->groups[0];
 
   if (boost::starts_with(file, "libs/"))
   {
@@ -701,6 +702,173 @@
 }
 
 
+void ServeNexusAssets(OrthancPluginRestOutput* output,
+                      const char* url,
+                      const OrthancPluginHttpRequest* request)
+{
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET");
+    return;
+  }
+
+  const std::string file = request->groups[0];
+
+  Orthanc::EmbeddedResources::FileResourceId resourceId;
+  Orthanc::MimeType mimeType;
+
+  if (file == "threejs.html")
+  {
+    resourceId = Orthanc::EmbeddedResources::NEXUS_HTML;
+    mimeType = Orthanc::MimeType_Html;
+  }
+  else if (file == "js/meco.js")
+  {
+    resourceId = Orthanc::EmbeddedResources::NEXUS_MECO_JS;
+    mimeType = Orthanc::MimeType_JavaScript;
+  }
+  else if (file == "js/nexus.js")
+  {
+    resourceId = Orthanc::EmbeddedResources::NEXUS_JS;
+    mimeType = Orthanc::MimeType_JavaScript;
+  }
+  else if (file == "js/nexus_three.js")
+  {
+    resourceId = Orthanc::EmbeddedResources::NEXUS_THREE_JS;
+    mimeType = Orthanc::MimeType_JavaScript;
+  }
+  else if (file == "js/TrackballControls.js")
+  {
+    resourceId = Orthanc::EmbeddedResources::NEXUS_TRACKBALL_JS;
+    mimeType = Orthanc::MimeType_JavaScript;
+  }
+  else
+  {
+    OrthancPluginSendHttpStatusCode(OrthancPlugins::GetGlobalContext(), output, 404);
+    return;
+  }
+
+  std::string s;
+  Orthanc::EmbeddedResources::GetFileResource(s, resourceId);
+  OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, s.c_str(), s.size(), Orthanc::EnumerationToString(mimeType));
+}
+
+
+static Orthanc::MemoryStringCache nexusCache_;
+
+void ExtractNexusModel(OrthancPluginRestOutput* output,
+                       const char* url,
+                       const OrthancPluginHttpRequest* request)
+{
+  OrthancPluginContext* context = OrthancPlugins::GetGlobalContext();
+
+  if (request->method != OrthancPluginHttpMethod_Get)
+  {
+    OrthancPluginSendMethodNotAllowed(context, output, "GET");
+    return;
+  }
+
+  const std::string instanceId = request->groups[0];
+
+  std::string range;
+
+  for (uint32_t i = 0; i < request->headersCount; i++)
+  {
+    if (std::string(request->headersKeys[i]) == "range")
+    {
+      range = request->headersValues[i];
+    }
+  }
+
+  static const std::string BYTES = "bytes=";
+
+  if (!boost::starts_with(range, BYTES))
+  {
+    OrthancPluginSendHttpStatusCode(context, output, 416);  // Range not satisfiable
+    return;
+  }
+
+  std::vector<std::string> tokens;
+  Orthanc::Toolbox::TokenizeString(tokens, range.substr(BYTES.length()), '-');
+
+  uint64_t start, end;
+
+  if (tokens.size() != 2 ||
+      !Orthanc::SerializationToolbox::ParseUnsignedInteger64(start, tokens[0]) ||
+      !Orthanc::SerializationToolbox::ParseUnsignedInteger64(end, tokens[1]) ||
+      start < 0 ||
+      start > end)
+  {
+    OrthancPluginSendHttpStatusCode(context, output, 416);  // Range not satisfiable
+    return;
+  }
+
+  uint64_t modelSize;
+  std::string part;
+
+#if 0
+  {
+    // Use no cache
+    std::string model;
+    if (!OrthancPlugins::RestApiGetString(model, "/instances/" + instanceId + "/content/4205-1001", false))
+    {
+      OrthancPluginSendHttpStatusCode(context, output, 404);
+      return;
+    }
+
+    modelSize = model.size();
+
+    if (end >= modelSize)
+    {
+      OrthancPluginSendHttpStatusCode(context, output, 416);  // Range not satisfiable
+      return;
+    }
+
+    part = model.substr(start, end - start + 1);
+  }
+#else
+  {
+    Orthanc::MemoryStringCache::Accessor accessor(nexusCache_);
+
+    std::string model;
+    if (!accessor.Fetch(model, instanceId))
+    {
+      if (OrthancPlugins::RestApiGetString(model, "/instances/" + instanceId + "/content/4205-1001", false))
+      {
+        accessor.Add(instanceId, model);
+      }
+      else
+      {
+        OrthancPluginSendHttpStatusCode(context, output, 404);
+        return;
+      }
+    }
+
+    modelSize = model.size();
+
+    if (end >= modelSize)
+    {
+      OrthancPluginSendHttpStatusCode(context, output, 416);  // Range not satisfiable
+      return;
+    }
+
+    part = model.substr(start, end - start + 1);
+  }
+#endif
+
+  std::string s = ("bytes " + boost::lexical_cast<std::string>(start) + "-" +
+                   boost::lexical_cast<std::string>(end) + "/" +
+                   boost::lexical_cast<std::string>(modelSize));
+  OrthancPluginSetHttpHeader(context, output, "Content-Range", s.c_str());
+
+  s = boost::lexical_cast<std::string>(part.size());
+  OrthancPluginSetHttpHeader(context, output, "Content-Length", s.c_str());
+  OrthancPluginSetHttpHeader(context, output, "Content-Type", "application/octet-stream");
+
+  OrthancPluginSendHttpStatus(context, output, 206 /* partial content */, part.c_str(), part.size());
+}
+
+
 extern "C"
 {
   ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
@@ -748,6 +916,10 @@
       OrthancPlugins::RegisterRestCallback<EncodeNifti>("/stl/encode-nifti", true);
     }
 
+    nexusCache_.SetMaximumSize(512 * 1024 * 1024);  // Cache of 512MB for Nexus
+    OrthancPlugins::RegisterRestCallback<ExtractNexusModel>("/instances/([0-9a-f-]+)/nexus", true);
+    OrthancPlugins::RegisterRestCallback<ServeNexusAssets>("/stl/nexus/(.*)", true);
+
     OrthancPlugins::OrthancConfiguration globalConfiguration;
     OrthancPlugins::OrthancConfiguration configuration;
     globalConfiguration.GetSection(configuration, "STL");