Mercurial > hg > orthanc-python
changeset 302:7ffc9e968b3e
SCP callbacks: worklists + storage commitment
| author | Alain Mazy <am@orthanc.team> |
|---|---|
| date | Wed, 26 Nov 2025 15:54:16 +0100 |
| parents | 4210556d96ab |
| children | a3af4f9f6b99 fbb298dd7cda |
| files | CodeAnalysis/CustomFunctions.json NEWS Sources/DicomScpCallbacks.cpp Sources/StorageCommitmentScpCallback.cpp |
| diffstat | 4 files changed, 231 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/CodeAnalysis/CustomFunctions.json Wed Nov 26 08:39:29 2025 +0100 +++ b/CodeAnalysis/CustomFunctions.json Wed Nov 26 15:54:16 2025 +0100 @@ -207,7 +207,7 @@ ], "return_sdk_type" : "void", "since_sdk" : [ 1, 12, 10 ], - "sdk_functions" : [ "OrthancPluginRegisterFindCallback" ] + "sdk_functions" : [ "OrthancPluginRegisterFindCallback2" ] }, { @@ -277,7 +277,8 @@ } ], "return_sdk_type" : "void", - "since_sdk" : [ 1, 12, 10 ] + "since_sdk" : [ 1, 1, 0 ], + "sdk_functions" : [ "OrthancPluginRegisterMoveCallback" ] }, { @@ -324,7 +325,8 @@ } ], "return_sdk_type" : "void", - "since_sdk" : [ 1, 12, 10 ] + "since_sdk" : [ 1, 12, 10 ], + "sdk_functions" : [ "OrthancPluginRegisterMoveCallback2" ] }, { @@ -351,6 +353,30 @@ }, { + "comment" : "New in release 7.0", + "short_name" : "RegisterWorklistCallback2", + "implementation" : "RegisterWorklistCallback2", + "documentation" : { + "description" : [ "Register a callback to handle modality worklists requests (v2)." ], + "args" : { + "callback" : "The callback function." + } + }, + "args" : [ + { + "sdk_name" : "callback", + "sdk_type" : "Callable", + "callable_type" : "WorklistCallback2", + "callable_protocol_args" : "answers: WorklistAnswers, query: WorklistQuery, connection: DicomConnection", + "callable_protocol_return" : "None" + } + ], + "return_sdk_type" : "void", + "sdk_functions" : [ "OrthancPluginRegisterWorklistCallback2" ], + "since_sdk" : [ 1, 12, 10 ] + }, + + { "comment" : "New in release 3.3", "short_name" : "RegisterStorageArea", "implementation" : "RegisterStorageArea", @@ -467,6 +493,38 @@ }, { + "comment" : "New in release 7.0", + "short_name" : "RegisterStorageCommitmentScpCallback2", + "implementation" : "RegisterStorageCommitmentScpCallback2", + "documentation" : { + "description" : [ "Register a callback to handle incoming requests to the storage commitment SCP (v2)." ], + "args" : { + "callback" : "Main callback that creates the a driver to handle an incoming storage commitment request.", + "lookup" : "Callback function to get the status of one DICOM instance." + } + }, + "args" : [ + { + "sdk_name" : "callback", + "sdk_type" : "Callable", + "callable_type" : "StorageCommitmentScpCallback2", + "callable_protocol_args" : "job_id: str, transaction_uid: str, sop_class_uids: list[str], sop_instance_uids: list[str], connection: DicomConnection", + "callable_protocol_return" : "object", "comment" : "This is the newly created storage commitment driver." + }, + { + "sdk_name" : "lookup", + "sdk_type" : "Callable", + "callable_type" : "StorageCommitmentLookup", + "callable_protocol_args" : "sop_class_uid: str, sop_instance_uid: str, driver: object", + "callable_protocol_return" : "StorageCommitmentFailureReason" + } + ], + "return_sdk_type" : "void", + "sdk_functions" : [ "OrthancPluginRegisterStorageCommitmentScpCallback2" ], + "since_sdk" : [ 1, 12, 10 ] + }, + + { "comment" : "New in release 6.0", "short_name" : "GetKeyValue", "implementation" : "GetKeyValue",
--- a/NEWS Wed Nov 26 08:39:29 2025 +0100 +++ b/NEWS Wed Nov 26 15:54:16 2025 +0100 @@ -7,8 +7,9 @@ * Added new SCP callbacks: - RegisterFindCallback2 - RegisterMoveCallback3 - - TODO: worklist + storage commitment - + - RegisterWorklistCallback2 + - RegisterStorageCommitmentScpCallback2 + Version 6.0 (2025-08-12) ========================
--- a/Sources/DicomScpCallbacks.cpp Wed Nov 26 08:39:29 2025 +0100 +++ b/Sources/DicomScpCallbacks.cpp Wed Nov 26 15:54:16 2025 +0100 @@ -846,7 +846,58 @@ } } - + +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 10) +OrthancPluginErrorCode WorklistCallback2(OrthancPluginWorklistAnswers* answers, + const OrthancPluginWorklistQuery* query, + const OrthancPluginDicomConnection* connection) +{ + try + { + PythonLock lock; + + PyObject *pAnswers, *pQuery, *pConnection; + + { + PythonObject args(lock, PyTuple_New(2)); + PyTuple_SetItem(args.GetPyObject(), 0, PyLong_FromSsize_t((intptr_t) answers)); + PyTuple_SetItem(args.GetPyObject(), 1, PyBool_FromLong(true /* borrowed, don't destruct */)); + pAnswers = PyObject_CallObject((PyObject *) GetOrthancPluginWorklistAnswersType(), args.GetPyObject()); + } + + { + PythonObject args(lock, PyTuple_New(2)); + PyTuple_SetItem(args.GetPyObject(), 0, PyLong_FromSsize_t((intptr_t) query)); + PyTuple_SetItem(args.GetPyObject(), 1, PyBool_FromLong(true /* borrowed, don't destruct */)); + pQuery = PyObject_CallObject((PyObject *) GetOrthancPluginWorklistQueryType(), args.GetPyObject()); + } + + { + PythonObject argsConnection(lock, PyTuple_New(2)); + PyTuple_SetItem(argsConnection.GetPyObject(), 0, PyLong_FromSsize_t((intptr_t) connection)); + PyTuple_SetItem(argsConnection.GetPyObject(), 1, PyBool_FromLong(true /* borrowed, don't destruct */)); + pConnection = PyObject_CallObject((PyObject*) GetOrthancPluginDicomConnectionType(), argsConnection.GetPyObject()); + } + + { + PythonObject args(lock, PyTuple_New(3)); + PyTuple_SetItem(args.GetPyObject(), 0, pAnswers); + PyTuple_SetItem(args.GetPyObject(), 1, pQuery); + PyTuple_SetItem(args.GetPyObject(), 2, pConnection); + + assert(worklistScpCallback_ != NULL); + PythonObject result(lock, PyObject_CallObject(worklistScpCallback_, args.GetPyObject())); + } + + return lock.CheckCallbackSuccess("Python C-FIND SCP for worklist callback (v2)"); + } + catch (OrthancPlugins::PluginException& e) + { + return e.GetErrorCode(); + } +} +#endif + PyObject* RegisterFindCallback(PyObject* module, PyObject* args) { // The GIL is locked at this point (no need to create "PythonLock") @@ -967,6 +1018,25 @@ registration, args, worklistScpCallback_, "Python C-FIND SCP for worklist callback"); } +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 10) +PyObject* RegisterWorklistCallback2(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 + { + OrthancPluginRegisterWorklistCallback2(OrthancPlugins::GetGlobalContext(), WorklistCallback2); + } + }; + + Registration registration; + return ICallbackRegistration::Apply( + registration, args, worklistScpCallback_, "Python C-FIND SCP for worklist callback (v2)"); +} +#endif void FinalizeDicomScpCallbacks() {
--- a/Sources/StorageCommitmentScpCallback.cpp Wed Nov 26 08:39:29 2025 +0100 +++ b/Sources/StorageCommitmentScpCallback.cpp Wed Nov 26 15:54:16 2025 +0100 @@ -32,6 +32,7 @@ #include "ICallbackRegistration.h" #include "PythonString.h" +#include <sdk.h> static PyObject* storageCommitmentScpCallback_ = NULL; static PyObject* storageCommitmentLookupCallback_ = NULL; @@ -103,6 +104,73 @@ return OrthancPluginErrorCode_Success; } +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 10) +static OrthancPluginErrorCode StorageCommitmentSCPCallback2( + void** handler /* out */, + const char* jobId, + const char* transactionUid, + const char* const* sopClassUids, + const char* const* sopInstanceUids, + uint32_t countInstances, + const OrthancPluginDicomConnection* connection) +{ + try + { + PythonLock lock; + + PythonObject args(lock, PyTuple_New(5)); + { + 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()); + } + { + PythonObject argsConnection(lock, PyTuple_New(2)); + PyTuple_SetItem(argsConnection.GetPyObject(), 0, PyLong_FromSsize_t((intptr_t) connection)); + PyTuple_SetItem(argsConnection.GetPyObject(), 1, PyBool_FromLong(true /* borrowed, don't destruct */)); + PyObject *pConnection = PyObject_CallObject((PyObject*) GetOrthancPluginDicomConnectionType(), argsConnection.GetPyObject()); + + PyTuple_SetItem(args.GetPyObject(), 4, pConnection); + } + + PythonObject result(lock, PyObject_CallObject(storageCommitmentScpCallback_, args.GetPyObject())); + *handler = result.Release(); + + std::string traceback; + if (lock.HasErrorOccurred(traceback)) + { + ORTHANC_PLUGINS_LOG_ERROR("Error in the Python storage commitment SCP callback (v2), traceback:\n" + traceback); + return OrthancPluginErrorCode_Plugin; + } + } + catch (OrthancPlugins::PluginException& e) + { + ORTHANC_PLUGINS_LOG_ERROR("Error in the Python storage commitment SCP callback (v2): " + + std::string(e.What(OrthancPlugins::GetGlobalContext()))); + } + return OrthancPluginErrorCode_Success; +} +#endif + static OrthancPluginErrorCode StorageCommitmentLookupCallback( OrthancPluginStorageCommitmentFailureReason* target /* out */, void* handler, @@ -185,6 +253,34 @@ } } +#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 12, 10) +PyObject* RegisterStorageCommitmentScpCallback2(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 + { + OrthancPluginRegisterStorageCommitmentScpCallback2( + OrthancPlugins::GetGlobalContext(), + StorageCommitmentSCPCallback2, + StorageCommitmentDestructor, + StorageCommitmentLookupCallback); + } + }; + + { + Registration registration; + return ICallbackRegistration::Apply2(registration, args, + storageCommitmentScpCallback_, + storageCommitmentLookupCallback_, + "Python storage commitment SCP & Lookup callback (v2)"); + } +} +#endif + void FinalizeStorageCommitmentScpCallback() { ICallbackRegistration::Unregister(storageCommitmentScpCallback_);
