comparison Sources/DicomScpCallbacks.cpp @ 63:32de70a1e4c7

New functions from the SDK wrapped in Python: CreateDicom, RegisterFindCallback, RegisterMoveCallback
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 10 Jun 2021 18:19:27 +0200
parents
children 091fb1903bfc
comparison
equal deleted inserted replaced
62:d82eef8c98fc 63:32de70a1e4c7
1 /**
2 * Python plugin for Orthanc
3 * Copyright (C) 2020-2021 Osimis S.A., Belgium
4 *
5 * This program is free software: you can redistribute it and/or
6 * modify it under the terms of the GNU Affero General Public License
7 * as published by the Free Software Foundation, either version 3 of
8 * the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 **/
18
19
20 #include "DicomScpCallbacks.h"
21
22 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h"
23 #include "Autogenerated/sdk.h"
24 #include "ICallbackRegistration.h"
25 #include "PythonString.h"
26
27
28 static PyObject* findScpCallback_ = NULL;
29 static PyObject* moveScpCallback_ = NULL;
30
31
32 static PyObject *GetFindQueryTag(sdk_OrthancPluginFindQuery_Object* self,
33 PyObject *args,
34 bool isGroup)
35 {
36 unsigned long index;
37 uint16_t group, element;
38
39 if (self->object_ == NULL)
40 {
41 PyErr_SetString(PyExc_ValueError, "Invalid object");
42 return NULL;
43 }
44 else if (!PyArg_ParseTuple(args, "k", &index))
45 {
46 PyErr_SetString(PyExc_TypeError, "Index is missing");
47 return NULL;
48 }
49 else if (OrthancPluginGetFindQueryTag(OrthancPlugins::GetGlobalContext(), &group, &element, self->object_, index) !=
50 OrthancPluginErrorCode_Success)
51 {
52 PyErr_SetString(PyExc_ValueError, "Index is out of range");
53 return NULL;
54 }
55 else if (isGroup)
56 {
57 return PyLong_FromUnsignedLong(group);
58 }
59 else
60 {
61 return PyLong_FromUnsignedLong(element);
62 }
63 }
64
65
66
67 // Check out "CUSTOM_METHODS" in "../CodeAnalysis/ParseOrthancSDK.py"
68 PyObject *GetFindQueryTagGroup(sdk_OrthancPluginFindQuery_Object* self, PyObject *args)
69 {
70 return GetFindQueryTag(self, args, true);
71 }
72
73
74 PyObject *GetFindQueryTagElement(sdk_OrthancPluginFindQuery_Object* self, PyObject *args)
75 {
76 return GetFindQueryTag(self, args, false);
77 }
78
79
80 static OrthancPluginErrorCode FindCallback(OrthancPluginFindAnswers *answers,
81 const OrthancPluginFindQuery *query,
82 const char *issuerAet,
83 const char *calledAet)
84 {
85 try
86 {
87 PythonLock lock;
88
89 PyObject *pAnswers, *pQuery;
90
91 {
92 PythonObject args(lock, PyTuple_New(2));
93 PyTuple_SetItem(args.GetPyObject(), 0, PyLong_FromSsize_t((intptr_t) answers));
94 PyTuple_SetItem(args.GetPyObject(), 1, PyBool_FromLong(true /* borrowed, don't destruct */));
95 pAnswers = PyObject_CallObject((PyObject *) GetOrthancPluginFindAnswersType(), args.GetPyObject());
96 }
97
98 {
99 PythonObject args(lock, PyTuple_New(2));
100 PyTuple_SetItem(args.GetPyObject(), 0, PyLong_FromSsize_t((intptr_t) query));
101 PyTuple_SetItem(args.GetPyObject(), 1, PyBool_FromLong(true /* borrowed, don't destruct */));
102 pQuery = PyObject_CallObject((PyObject *) GetOrthancPluginFindQueryType(), args.GetPyObject());
103 }
104
105 PythonString pIssuerAet(lock, issuerAet);
106 PythonString pCalledAet(lock, calledAet);
107
108 {
109 PythonObject args(lock, PyTuple_New(4));
110 PyTuple_SetItem(args.GetPyObject(), 0, pAnswers);
111 PyTuple_SetItem(args.GetPyObject(), 1, pQuery);
112 PyTuple_SetItem(args.GetPyObject(), 2, pIssuerAet.Release());
113 PyTuple_SetItem(args.GetPyObject(), 3, pCalledAet.Release());
114
115 assert(findScpCallback_ != NULL);
116 PythonObject result(lock, PyObject_CallObject(findScpCallback_, args.GetPyObject()));
117 }
118
119 return lock.CheckCallbackSuccess("Python C-FIND SCP callback");
120 }
121 catch (OrthancPlugins::PluginException& e)
122 {
123 return e.GetErrorCode();
124 }
125 }
126
127
128
129 class IMoveDriver : public boost::noncopyable
130 {
131 public:
132 virtual ~IMoveDriver()
133 {
134 }
135
136 virtual void Apply() = 0;
137 };
138
139
140 static void* CreateMoveCallback(OrthancPluginResourceType resourceType,
141 const char *patientId,
142 const char *accessionNumber,
143 const char *studyInstanceUid,
144 const char *seriesInstanceUid,
145 const char *sopInstanceUid,
146 const char *originatorAet,
147 const char *sourceAet,
148 const char *targetAet,
149 uint16_t originatorId)
150 {
151 class Driver : public IMoveDriver
152 {
153 private:
154 OrthancPluginResourceType resourceType_;
155 std::string patientId_;
156 std::string accessionNumber_;
157 std::string studyInstanceUid_;
158 std::string seriesInstanceUid_;
159 std::string sopInstanceUid_;
160 std::string originatorAet_;
161 std::string sourceAet_;
162 std::string targetAet_;
163 uint16_t originatorId_;
164
165 public:
166 Driver(OrthancPluginResourceType resourceType,
167 const char *patientId,
168 const char *accessionNumber,
169 const char *studyInstanceUid,
170 const char *seriesInstanceUid,
171 const char *sopInstanceUid,
172 const char *originatorAet,
173 const char *sourceAet,
174 const char *targetAet,
175 uint16_t originatorId) :
176 resourceType_(resourceType),
177 originatorId_(originatorId)
178 {
179 if (patientId != NULL)
180 {
181 patientId_.assign(patientId);
182 }
183
184 if (accessionNumber != NULL)
185 {
186 accessionNumber_.assign(accessionNumber);
187 }
188
189 if (studyInstanceUid != NULL)
190 {
191 studyInstanceUid_.assign(studyInstanceUid);
192 }
193
194 if (seriesInstanceUid != NULL)
195 {
196 seriesInstanceUid_.assign(seriesInstanceUid);
197 }
198
199 if (sopInstanceUid != NULL)
200 {
201 sopInstanceUid_.assign(sopInstanceUid);
202 }
203
204 if (originatorAet != NULL)
205 {
206 originatorAet_.assign(originatorAet);
207 }
208
209 if (sourceAet != NULL)
210 {
211 sourceAet_.assign(sourceAet);
212 }
213
214 if (targetAet != NULL)
215 {
216 targetAet_.assign(targetAet);
217 }
218 }
219
220
221 virtual void Apply() ORTHANC_OVERRIDE
222 {
223 PythonLock lock;
224
225 PythonObject kw(lock, PyDict_New());
226
227 std::string level;
228 switch (resourceType_)
229 {
230 case OrthancPluginResourceType_Patient:
231 level = "PATIENT";
232 break;
233
234 case OrthancPluginResourceType_Study:
235 level = "STUDY";
236 break;
237
238 case OrthancPluginResourceType_Series:
239 level = "SERIES";
240 break;
241
242 case OrthancPluginResourceType_Instance:
243 level = "INSTANCE";
244 break;
245
246 default:
247 throw OrthancPlugins::PluginException(OrthancPluginErrorCode_ParameterOutOfRange);
248 }
249
250 {
251 PythonString tmp(lock, level);
252 PyDict_SetItemString(kw.GetPyObject(), "Level", tmp.Release());
253 }
254
255 {
256 PythonString tmp(lock, patientId_);
257 PyDict_SetItemString(kw.GetPyObject(), "PatientID", tmp.Release());
258 }
259
260 {
261 PythonString tmp(lock, accessionNumber_);
262 PyDict_SetItemString(kw.GetPyObject(), "AccessionNumber", tmp.Release());
263 }
264
265 {
266 PythonString tmp(lock, studyInstanceUid_);
267 PyDict_SetItemString(kw.GetPyObject(), "StudyInstanceUID", tmp.Release());
268 }
269
270 {
271 PythonString tmp(lock, seriesInstanceUid_);
272 PyDict_SetItemString(kw.GetPyObject(), "SeriesInstanceUID", tmp.Release());
273 }
274
275 {
276 PythonString tmp(lock, sopInstanceUid_);
277 PyDict_SetItemString(kw.GetPyObject(), "SOPInstanceUID", tmp.Release());
278 }
279
280 {
281 PythonString tmp(lock, originatorAet_);
282 PyDict_SetItemString(kw.GetPyObject(), "OriginatorAET", tmp.Release());
283 }
284
285 {
286 PythonString tmp(lock, sourceAet_);
287 PyDict_SetItemString(kw.GetPyObject(), "SourceAET", tmp.Release());
288 }
289
290 {
291 PythonString tmp(lock, targetAet_);
292 PyDict_SetItemString(kw.GetPyObject(), "TargetAET", tmp.Release());
293 }
294
295 PyDict_SetItemString(kw.GetPyObject(), "OriginatorID", PyLong_FromUnsignedLong(originatorId_));
296
297 PythonObject args(lock, PyTuple_New(0));
298
299 assert(moveScpCallback_ != NULL);
300 PythonObject result(lock, PyObject_Call(moveScpCallback_, args.GetPyObject(), kw.GetPyObject()));
301
302 OrthancPluginErrorCode code = lock.CheckCallbackSuccess("Python C-MOVE SCP callback");
303 if (code != OrthancPluginErrorCode_Success)
304 {
305 throw OrthancPlugins::PluginException(code);
306 }
307 }
308 };
309
310 try
311 {
312 return new Driver(resourceType, patientId, accessionNumber, studyInstanceUid,
313 seriesInstanceUid, sopInstanceUid, originatorAet, sourceAet,
314 targetAet, originatorId);
315 }
316 catch (OrthancPlugins::PluginException& e)
317 {
318 return NULL;
319 }
320 }
321
322
323 static uint32_t GetMoveSize(void *moveDriver)
324 {
325 assert(moveDriver != NULL);
326 return 1;
327 }
328
329
330 OrthancPluginErrorCode ApplyMove(void *moveDriver)
331 {
332 assert(moveDriver != NULL);
333
334 try
335 {
336 reinterpret_cast<IMoveDriver*>(moveDriver)->Apply();
337 return OrthancPluginErrorCode_Success;
338 }
339 catch (OrthancPlugins::PluginException& e)
340 {
341 return e.GetErrorCode();
342 }
343 }
344
345
346 void FreeMove(void *moveDriver)
347 {
348 assert(moveDriver != NULL);
349 delete reinterpret_cast<IMoveDriver*>(moveDriver);
350 }
351
352
353 PyObject* RegisterFindCallback(PyObject* module, PyObject* args)
354 {
355 // The GIL is locked at this point (no need to create "PythonLock")
356
357 class Registration : public ICallbackRegistration
358 {
359 public:
360 virtual void Register() ORTHANC_OVERRIDE
361 {
362 OrthancPluginRegisterFindCallback(OrthancPlugins::GetGlobalContext(), FindCallback);
363 }
364 };
365
366 Registration registration;
367 return ICallbackRegistration::Apply(
368 registration, args, findScpCallback_, "Python C-FIND SCP callback");
369 }
370
371
372 PyObject* RegisterMoveCallback(PyObject* module, PyObject* args)
373 {
374 // The GIL is locked at this point (no need to create "PythonLock")
375
376 class Registration : public ICallbackRegistration
377 {
378 public:
379 virtual void Register() ORTHANC_OVERRIDE
380 {
381 OrthancPluginRegisterMoveCallback(
382 OrthancPlugins::GetGlobalContext(), CreateMoveCallback, GetMoveSize, ApplyMove, FreeMove);
383 }
384 };
385
386 Registration registration;
387 return ICallbackRegistration::Apply(
388 registration, args, moveScpCallback_, "Python C-MOVE SCP callback");
389 }
390
391
392 void FinalizeDicomScpCallbacks()
393 {
394 ICallbackRegistration::Unregister(findScpCallback_);
395 ICallbackRegistration::Unregister(moveScpCallback_);
396 }