diff Sources/StorageArea.cpp @ 79:068551520123

New Python function: "orthanc.RegisterStorageArea()"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 12 Aug 2021 17:52:17 +0200
parents
children ad31ac9a6fef
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sources/StorageArea.cpp	Thu Aug 12 17:52:17 2021 +0200
@@ -0,0 +1,243 @@
+/**
+ * Python plugin for Orthanc
+ * Copyright (C) 2020-2021 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "StorageArea.h"
+
+#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
+#include "PythonString.h"
+
+
+static PyObject*  createCallback_ = NULL;
+static PyObject*  readCallback_ = NULL;
+static PyObject*  removeCallback_ = NULL;
+
+
+static OrthancPluginErrorCode RunCallback(PythonLock& lock,
+                                          PyObject* callback,
+                                          PythonObject& args,
+                                          const std::string& name)
+{
+  PythonObject result(lock, PyObject_CallObject(callback, args.GetPyObject()));
+
+  std::string traceback;
+  if (lock.HasErrorOccurred(traceback))
+  {
+    OrthancPlugins::LogError("Error in the Python " + name + " callback, traceback:\n" + traceback);
+    return OrthancPluginErrorCode_Plugin;
+  }
+  else
+  {
+    return OrthancPluginErrorCode_Success;
+  }
+}
+
+
+static OrthancPluginErrorCode StorageCreate(const char *uuid,
+                                            const void *content,
+                                            int64_t size,
+                                            OrthancPluginContentType type)
+{
+  try
+  {
+    if (createCallback_ == NULL)
+    {
+      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+    }
+    
+    PythonLock lock;
+
+    PythonObject args(lock, PyTuple_New(3));
+
+    PythonString str(lock, uuid);
+    PyTuple_SetItem(args.GetPyObject(), 0, str.Release());
+    PyTuple_SetItem(args.GetPyObject(), 1, PyLong_FromLong(type));
+    PyTuple_SetItem(args.GetPyObject(), 2, PyBytes_FromStringAndSize(reinterpret_cast<const char*>(content), size));
+
+    return RunCallback(lock, createCallback_, args, "StorageCreate");
+  }
+  catch (OrthancPlugins::PluginException& e)
+  {
+    return e.GetErrorCode();
+  }
+}
+
+
+static OrthancPluginErrorCode StorageRead(void **content,
+                                          int64_t *size,
+                                          const char *uuid,
+                                          OrthancPluginContentType type)
+{
+  try
+  {
+    if (readCallback_ == NULL)
+    {
+      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+    }
+    
+    PythonLock lock;
+
+    PythonObject args(lock, PyTuple_New(2));
+
+    PythonString str(lock, uuid);
+    PyTuple_SetItem(args.GetPyObject(), 0, str.Release());
+    PyTuple_SetItem(args.GetPyObject(), 1, PyLong_FromLong(type));
+    
+    PythonObject result(lock, PyObject_CallObject(readCallback_, args.GetPyObject()));
+    
+    std::string traceback;
+    if (lock.HasErrorOccurred(traceback))
+    {
+      OrthancPlugins::LogError("Error in the Python StorageRead callback, traceback:\n" + traceback);
+      return OrthancPluginErrorCode_Plugin;
+    }
+    else if (!PyBytes_Check(result.GetPyObject()))
+    {
+      OrthancPlugins::LogError("The Python StorageRead callback has not returned a byte array as expected");
+      return OrthancPluginErrorCode_Plugin;
+    }
+    else
+    {
+      char* pythonBuffer = NULL;
+      Py_ssize_t pythonSize = 0;
+      if (PyBytes_AsStringAndSize(result.GetPyObject(), &pythonBuffer, &pythonSize) == 1)
+      {
+        OrthancPlugins::LogError("Cannot access the byte buffer returned by the Python StorageRead callback");
+        return OrthancPluginErrorCode_Plugin;
+      }
+      else
+      {
+        if (pythonSize == 0)
+        {
+          *content = NULL;
+          *size = 0;
+        }
+        else
+        {
+          *content = malloc(pythonSize);
+          *size = pythonSize;
+          if (*content == NULL)
+          {
+            return OrthancPluginErrorCode_NotEnoughMemory;
+          }
+
+          memcpy(*content, pythonBuffer, pythonSize);
+        }
+        
+        return OrthancPluginErrorCode_Success;
+      }
+    }
+  }
+  catch (OrthancPlugins::PluginException& e)
+  {
+    return e.GetErrorCode();
+  }
+}
+
+
+static OrthancPluginErrorCode StorageRemove(const char *uuid,
+                                            OrthancPluginContentType type)
+{
+  try
+  {
+    if (removeCallback_ == NULL)
+    {
+      throw OrthancPlugins::PluginException(OrthancPluginErrorCode_InternalError);
+    }
+    
+    PythonLock lock;
+
+    PythonObject args(lock, PyTuple_New(2));
+
+    PythonString str(lock, uuid);
+    PyTuple_SetItem(args.GetPyObject(), 0, str.Release());
+    PyTuple_SetItem(args.GetPyObject(), 1, PyLong_FromLong(type));
+
+    return RunCallback(lock, removeCallback_, args, "StorageRemove");
+  }
+  catch (OrthancPlugins::PluginException& e)
+  {
+    return e.GetErrorCode();
+  }
+}
+
+
+PyObject* RegisterStorageArea(PyObject* module, PyObject* args)
+{
+  // The GIL is locked at this point (no need to create "PythonLock")
+
+  PyObject* a = NULL;
+  PyObject* b = NULL;
+  PyObject* c = NULL;
+
+  if (!PyArg_ParseTuple(args, "OOO", &a, &b, &c) ||
+      a == NULL ||
+      b == NULL ||
+      c == NULL)
+  {
+    PyErr_SetString(PyExc_ValueError, "Expected three callback functions to register a custom storage area");
+    return NULL;
+  }
+  else if (createCallback_ != NULL ||
+           readCallback_ != NULL ||
+           removeCallback_ != NULL)
+  {
+    PyErr_SetString(PyExc_RuntimeError, "Cannot register twice a custom storage area");
+    return NULL;
+  }
+  else
+  {
+    OrthancPlugins::LogInfo("Registering a custom storage area in Python");
+
+    OrthancPluginRegisterStorageArea(OrthancPlugins::GetGlobalContext(),
+                                     StorageCreate, StorageRead, StorageRemove);
+    
+    createCallback_ = a;
+    Py_XINCREF(createCallback_);
+    
+    readCallback_ = b;
+    Py_XINCREF(readCallback_);
+    
+    removeCallback_ = c;
+    Py_XINCREF(removeCallback_);
+    
+    Py_INCREF(Py_None);
+    return Py_None;
+  }
+}
+
+
+void FinalizeStorageArea()
+{
+  PythonLock lock;
+  
+  if (createCallback_ != NULL)
+  {
+    Py_XDECREF(createCallback_);
+  }
+  
+  if (readCallback_ != NULL)
+  {
+    Py_XDECREF(readCallback_);
+  }
+  
+  if (removeCallback_ != NULL)
+  {
+    Py_XDECREF(removeCallback_);
+  }
+}