Mercurial > hg > orthanc-python
changeset 119:cf6decdf9e15
wrapped new SDK callback: orthanc.RegisterStorageCommitmentScpCallback()
author | Alain Mazy <am@osimis.io> |
---|---|
date | Mon, 28 Aug 2023 18:30:42 +0200 |
parents | 7f8f26ef5006 |
children | a3f77cf16396 |
files | CMakeLists.txt NEWS Sources/ICallbackRegistration.cpp Sources/ICallbackRegistration.h Sources/Plugin.cpp Sources/StorageCommitmentScpCallback.cpp Sources/StorageCommitmentScpCallback.h |
diffstat | 7 files changed, 274 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Mon Aug 28 16:44:41 2023 +0200 +++ b/CMakeLists.txt Mon Aug 28 18:30:42 2023 +0200 @@ -186,6 +186,7 @@ Sources/ReceivedInstanceCallback.cpp Sources/RestCallbacks.cpp Sources/StorageArea.cpp + Sources/StorageCommitmentScpCallback.cpp # Third-party sources ${CMAKE_SOURCE_DIR}/Resources/Orthanc/Plugins/OrthancPluginCppWrapper.cpp
--- a/NEWS Mon Aug 28 16:44:41 2023 +0200 +++ b/NEWS Mon Aug 28 18:30:42 2023 +0200 @@ -1,6 +1,9 @@ Pending changes in the mainline =============================== +* New functions from the SDK wrapped in Python: + - orthanc.RegisterStorageCommitmentScpCallback() + Maintenance: * Fix memory leaks when a python script calls orthanc.RestApiPost() and sibling methods, in IncomingHttpRequestFilter and in the CMove callback.
--- a/Sources/ICallbackRegistration.cpp Mon Aug 28 16:44:41 2023 +0200 +++ b/Sources/ICallbackRegistration.cpp Mon Aug 28 18:30:42 2023 +0200 @@ -58,6 +58,46 @@ } +PyObject *ICallbackRegistration::Apply2(ICallbackRegistration& registration, + PyObject* args, + PyObject*& singletonCallback1, + PyObject*& singletonCallback2, + const std::string& details) +{ + // https://docs.python.org/3/extending/extending.html#calling-python-functions-from-c + PyObject* callback1 = NULL; + PyObject* callback2 = NULL; + + if (!PyArg_ParseTuple(args, "OO", &callback1, &callback2) || + callback1 == NULL || callback2 == NULL) + { + const std::string message = "Expected two callback functions to register " + details; + PyErr_SetString(PyExc_ValueError, message.c_str()); + return NULL; + } + else if (singletonCallback1 != NULL || singletonCallback2 != NULL) + { + const std::string message = "Can only register once for " + details; + PyErr_SetString(PyExc_RuntimeError, message.c_str()); + return NULL; + } + else + { + OrthancPlugins::LogInfo("Registering callbacks " + details); + registration.Register(); + + singletonCallback1 = callback1; + Py_XINCREF(singletonCallback1); + + singletonCallback2 = callback2; + Py_XINCREF(singletonCallback2); + + Py_INCREF(Py_None); + return Py_None; + } +} + + void ICallbackRegistration::Unregister(PyObject*& singletonCallback) { PythonLock lock;
--- a/Sources/ICallbackRegistration.h Mon Aug 28 16:44:41 2023 +0200 +++ b/Sources/ICallbackRegistration.h Mon Aug 28 18:30:42 2023 +0200 @@ -40,5 +40,12 @@ PyObject*& singletonCallback, const std::string& details); + // The GIL must be locked + static PyObject *Apply2(ICallbackRegistration& registration, + PyObject* args, + PyObject*& singletonCallback1, + PyObject*& singletonCallback2, + const std::string& details); + static void Unregister(PyObject*& singletonCallback); };
--- a/Sources/Plugin.cpp Mon Aug 28 16:44:41 2023 +0200 +++ b/Sources/Plugin.cpp Mon Aug 28 18:30:42 2023 +0200 @@ -32,6 +32,7 @@ #include "IncomingInstanceFilter.h" #include "ReceivedInstanceCallback.h" #include "StorageArea.h" +#include "StorageCommitmentScpCallback.h" #include "RestCallbacks.h" #include "PythonModule.h" @@ -391,6 +392,15 @@ } /** + * New in release 4.1 + **/ + + { + PyMethodDef f = { "RegisterStorageCommitmentScpCallback", RegisterStorageCommitmentScpCallback, METH_VARARGS, "" }; + functions.push_back(f); + } + + /** * Append all the global functions that were automatically generated **/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/StorageCommitmentScpCallback.cpp Mon Aug 28 18:30:42 2023 +0200 @@ -0,0 +1,186 @@ +/** + * Python plugin for Orthanc + * Copyright (C) 2020-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, 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 "StorageCommitmentScpCallback.h" + +#include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" +#include "ICallbackRegistration.h" +#include "PythonString.h" + + +static PyObject* storageCommitmentScpCallback_ = NULL; +static PyObject* storageCommitmentLookupCallback_ = NULL; + + +static OrthancPluginErrorCode StorageCommitmentSCPCallback( + void** handler /* out */, + const char* jobId, + const char* transactionUid, + const char* const* sopClassUids, + const char* const* sopInstanceUids, + uint32_t countInstances, + const char* remoteAet, + const char* calledAet) +{ + try + { + PythonLock lock; + + PythonObject args(lock, PyTuple_New(6)); + { + PythonString str(lock, jobId); + PyTuple_SetItem(args.GetPyObject(), 0, str.Release()); + } + { + PythonString str(lock, transactionUid); + PyTuple_SetItem(args.GetPyObject(), 1, str.Release()); + } + { + PythonObject sopClassUidList(lock, PyList_New(countInstances)); + for (uint32_t i = 0; i < countInstances; i++) + { + PythonString str(lock, sopClassUids[i]); + PyList_SetItem(sopClassUidList.GetPyObject(), i, str.Release()); + } + PyTuple_SetItem(args.GetPyObject(), 2, sopClassUidList.Release()); + PythonObject sopInstanceUidList(lock, PyList_New(countInstances)); + for (uint32_t i = 0; i < countInstances; i++) + { + PythonString str(lock, sopInstanceUids[i]); + PyList_SetItem(sopInstanceUidList.GetPyObject(), i, str.Release()); + } + PyTuple_SetItem(args.GetPyObject(), 3, sopInstanceUidList.Release()); + } + { + PythonString str(lock, remoteAet); + PyTuple_SetItem(args.GetPyObject(), 4, str.Release()); + } + { + PythonString str(lock, calledAet); + PyTuple_SetItem(args.GetPyObject(), 5, str.Release()); + } + + PythonObject result(lock, PyObject_CallObject(storageCommitmentScpCallback_, args.GetPyObject())); + *handler = result.Release(); + + std::string traceback; + if (lock.HasErrorOccurred(traceback)) + { + OrthancPlugins::LogError("Error in the Python storage commitment SCP callback, " + "traceback:\n" + traceback); + return OrthancPluginErrorCode_Plugin; + } + } + catch (OrthancPlugins::PluginException& e) + { + OrthancPlugins::LogError("Error in the Python storage commitment SCP callback: " + + std::string(e.What(OrthancPlugins::GetGlobalContext()))); + } + return OrthancPluginErrorCode_Success; +} + +static OrthancPluginErrorCode StorageCommitmentLookupCallback( + OrthancPluginStorageCommitmentFailureReason* target /* out */, + void* handler, + const char* sopClassUid, + const char* sopInstanceUid) +{ + try + { + PythonLock lock; + + PythonObject args(lock, PyTuple_New(3)); + { + PythonString str(lock, sopClassUid); + PyTuple_SetItem(args.GetPyObject(), 0, str.Release()); + } + { + PythonString str(lock, sopInstanceUid); + PyTuple_SetItem(args.GetPyObject(), 1, str.Release()); + } + { + PyObject* data = (PyObject*) handler; + Py_INCREF(data); // Keep a reference before it was stolen by PyTuple_SetItem. + PyTuple_SetItem(args.GetPyObject(), 2, data); + } + + PythonObject result(lock, PyObject_CallObject(storageCommitmentLookupCallback_, args.GetPyObject())); + + if (!PyLong_Check(result.GetPyObject())) + { + OrthancPlugins::LogError("The Python storage commitment Lookup callback has not returned an int as the return value"); + return OrthancPluginErrorCode_Plugin; + } + + *target = static_cast<OrthancPluginStorageCommitmentFailureReason>(PyLong_AsLong(result.GetPyObject())); + + std::string traceback; + if (lock.HasErrorOccurred(traceback)) + { + OrthancPlugins::LogError("Error in the Python storage commitment Lookup callback, " + "traceback:\n" + traceback); + return OrthancPluginErrorCode_Plugin; + } + } + catch (OrthancPlugins::PluginException& e) + { + OrthancPlugins::LogError("Error in the Python storage commitment Lookup callback: " + + std::string(e.What(OrthancPlugins::GetGlobalContext()))); + } + return OrthancPluginErrorCode_Success; +} + +static void StorageCommitmentDestructor(void *handler) +{ + PythonLock lock; + Py_DECREF((PyObject*)handler); // Release the reference +} + +PyObject* RegisterStorageCommitmentScpCallback(PyObject* module, PyObject* args) +{ + // The GIL is locked at this point (no need to create "PythonLock") + + class Registration : public ICallbackRegistration + { + public: + virtual void Register() ORTHANC_OVERRIDE + { + OrthancPluginRegisterStorageCommitmentScpCallback( + OrthancPlugins::GetGlobalContext(), + StorageCommitmentSCPCallback, + StorageCommitmentDestructor, + StorageCommitmentLookupCallback); + } + }; + + { + Registration registration; + return ICallbackRegistration::Apply2(registration, args, + storageCommitmentScpCallback_, + storageCommitmentLookupCallback_, + "Python storage commitment SCP & Lookup callback"); + } +} + +void FinalizeStorageCommitmentScpCallback() +{ + ICallbackRegistration::Unregister(storageCommitmentScpCallback_); + ICallbackRegistration::Unregister(storageCommitmentLookupCallback_); +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/StorageCommitmentScpCallback.h Mon Aug 28 18:30:42 2023 +0200 @@ -0,0 +1,27 @@ +/** + * Python plugin for Orthanc + * Copyright (C) 2020-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, 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/>. + **/ + + +#pragma once + +#include "PythonHeaderWrapper.h" + +PyObject* RegisterStorageCommitmentScpCallback(PyObject* module, PyObject* args); + +void FinalizeStorageCommitmentScpCallback(); \ No newline at end of file