Mercurial > hg > orthanc-book
annotate Sphinx/source/users/lua.rst @ 232:15823481cf8f
added Jobs Lua callback
author | amazy |
---|---|
date | Tue, 26 Mar 2019 11:08:26 +0100 |
parents | 6e0a0b57697b |
children | f58c0784453d |
rev | line source |
---|---|
0 | 1 .. _lua: |
2 | |
3 Server-side scripting with Lua | |
4 ============================== | |
5 | |
6 .. contents:: | |
7 | |
8 Since release 0.5.2, Orthanc supports server-side scripting through | |
25 | 9 the `Lua <https://en.wikipedia.org/wiki/Lua_(programming_language)>`__ |
0 | 10 scripting language. Thanks to this major feature, Orthanc can be tuned |
11 to specific medical workflows without being driven by an external | |
12 script. This page summarizes the possibilities of Orthanc server-side | |
13 scripting. | |
14 | |
15 Many other examples are `available in the source distribution | |
16 <https://bitbucket.org/sjodogne/orthanc/src/default/Resources/Samples/Lua/>`__. | |
17 | |
18 | |
75 | 19 Installing a Lua script |
0 | 20 ----------------------- |
21 | |
22 .. highlight:: bash | |
23 | |
24 A custom Lua script can be installed either by the :ref:`configuration | |
25 file <configuration>`, or by uploading it | |
26 through the :ref:`REST API <rest-samples>`. | |
27 | |
28 To install it by the **configuration file** method, you just have to | |
29 specify the path to the file containing the Lua script in the | |
30 ``LuaScripts`` variable. | |
31 | |
32 To upload a script stored in the file "``script.lua``" through the | |
33 **REST API**, use the following command:: | |
34 | |
35 $ curl -X POST http://localhost:8042/tools/execute-script --data-binary @script.lua | |
36 | |
37 Pay attention to the fact that, contrarily to the scripts installed | |
38 from the configuration file, the scripts installed through the REST | |
39 API are non-persistent: They are discarded after a restart of Orthanc, | |
40 which makes them useful for script prototyping. You can also interpret | |
41 a single Lua command through the REST API:: | |
42 | |
43 $ curl -X POST http://localhost:8042/tools/execute-script --data-binary "print(42)" | |
44 | |
45 *Note:* The ``--data-binary`` cURL option is used instead of | |
46 ``--data`` to prevent the interpretation of newlines by cURL, which is | |
47 `mandatory for the proper evaluation | |
25 | 48 <http://stackoverflow.com/questions/3872427/how-to-send-line-break-with-curl>`__ of the possible |
0 | 49 comments inside the Lua script. |
50 | |
51 | |
52 Lua API | |
53 ------- | |
54 | |
55 | |
56 .. _lua-callbacks: | |
57 | |
58 Callbacks to react to events | |
59 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
60 | |
61 The Lua engine of Orthanc comes invokes the following callbacks that | |
62 are triggered on various events. Here are the **generic events**: | |
63 | |
64 * ``function Initialize()``: Invoked as soon as the Orthanc server is started. | |
65 * ``function Finalize()``: Invoked just before the Orthanc server is stopped. | |
66 | |
67 Some **permission-related events** allow to filter incoming requests: | |
68 | |
69 * ``function ReceivedInstanceFilter(dicom, origin)``: | |
70 Invoked to known whether an incoming DICOM instance should be | |
71 accepted. :ref:`See this section <lua-filter-dicom>`. The ``origin`` | |
72 parameter is :ref:`documented separately <lua-origin>`. | |
73 * ``function IncomingHttpRequestFilter(method, uri, ip, username, | |
74 httpHeaders)``: Invoked to known whether a REST request should be | |
75 accepted. :ref:`See this section <lua-filter-rest>`. | |
76 | |
232 | 77 Some **job-related events** allow to react to :ref:`job <jobs>` completion/failure: |
78 | |
79 * ``function OnJobSubmitted(jobId)``: | |
80 Invoked when a new job has been submitted. Note that this does not | |
81 mean the the job execution has started. | |
82 * ``function OnJobFailure(jobId)``: | |
83 Invoked when a job has failed. | |
84 * ``function OnJobSuccess(jobId)``: | |
85 Invoked when a job has completed successfully. | |
86 | |
0 | 87 Some **DICOM-related events** allow to react to the reception of |
88 new medical images: | |
89 | |
90 * ``function OnStoredInstance(instanceId, tags, metadata, origin)``: | |
91 Invoked whenever a new instance has been stored into Orthanc. | |
92 This is especially useful for :ref:`lua-auto-routing`. The ``origin`` | |
93 parameter is :ref:`documented separately <lua-origin>`. | |
94 * ``function OnStablePatient(patientId, tags, metadata)``: Invoked | |
95 whenever a patient has not received any new instance for a certain | |
96 amount of time (cf. the option ``StableAge`` in the | |
97 :ref:`configuration file <configuration>`). The :ref:`identifier | |
98 <orthanc-ids>` of the patient is provided, together with her DICOM | |
78 | 99 tags and her :ref:`metadata <metadata>`. |
0 | 100 * ``function OnStableSeries(seriesId, tags, metadata)``: Invoked |
101 whenever a series has not received any new instance for a certain | |
102 amount of time. | |
103 * ``function OnStableStudy(studyId, tags, metadata)``: Invoked | |
104 whenever a study has not received any new instance for a certain | |
105 amount of time. | |
106 * ``function IncomingFindRequestFilter(source, origin)``: Invoked | |
107 whenever Orthanc receives an incoming C-Find query through the DICOM | |
108 protocol. This allows to inspect the content of the C-Find query, | |
109 and possibly modify it if a patch is needed for some manufacturer. A | |
110 `sample script is available | |
111 <https://bitbucket.org/sjodogne/orthanc/src/default/Resources/Samples/Lua/IncomingFindRequestFilter.lua>`__. | |
112 | |
64
a3df3c2b68cf
import the documentation of the Worklists sample plugin
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
25
diff
changeset
|
113 Furthermore, whenever a DICOM association is negotiated for C-Store |
0 | 114 SCP, several callbacks are successively invoked to specify which |
115 **transfer syntaxes** are accepted for the association. These | |
116 callbacks are listed in `this sample script | |
117 <https://bitbucket.org/sjodogne/orthanc/src/default/Resources/Samples/Lua/TransferSyntaxEnable.lua>`__. | |
118 | |
119 *Note:* All of these callbacks are guaranteed to be **invoked in | |
120 mutual exclusion**. This implies that Lua scripting in Orthanc does | |
121 not support any kind of concurrency. | |
122 | |
123 | |
124 .. _lua-rest: | |
125 | |
126 Calling the REST API of Orthanc | |
127 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
128 | |
129 Lua scripts have :ref:`full access to the REST API <rest>` of Orthanc | |
130 through the following functions: | |
131 | |
132 * ``RestApiGet(uri, builtin)`` | |
133 * ``RestApiPost(uri, body, builtin)`` | |
134 * ``RestApiPut(uri, body, builtin)`` | |
135 * ``RestApiDelete(uri, builtin)`` | |
136 | |
137 The ``uri`` arguments specifies the URI against which to make the | |
138 request, and ``body`` is a string containing the body of POST/PUT | |
139 request. The ``builtin`` parameter is an optional Boolean that | |
140 specifies whether the request targets only the built-in REST API of | |
141 Orthanc (if set to ``true``), or the full the REST API after being | |
142 tainted by the plugins (if set to ``false``). | |
143 | |
144 | |
145 General-purpose functions | |
146 ^^^^^^^^^^^^^^^^^^^^^^^^^ | |
147 | |
148 The Lua engine of Orthanc contain several general-purpose ancillary | |
149 functions: | |
150 | |
151 * ``PrintRecursive(v)`` recursively prints the content of a `Lua table | |
152 <http://www.lua.org/pil/2.5.html>`__ to the log file of Orthanc. | |
153 * ``ParseJson(s)`` converts a string encoded in the `JSON format | |
154 <https://en.wikipedia.org/wiki/JSON>`__ to a Lua table. | |
155 * ``DumpJson(v, keepStrings)`` encodes a Lua table as a JSON string. | |
156 Setting the optional argument ``keepStrings`` (available from | |
157 release 0.9.5) to ``true`` prevents the automatic conversion of | |
158 strings to integers. | |
159 * ``GetOrthancConfiguration()`` returns a Lua table containing the | |
160 content of the :ref:`configuration files <configuration>` of | |
161 Orthanc. | |
162 | |
163 | |
164 Similarly to the functions to :ref:`call the REST API of Orthanc | |
165 <lua-rest>`, several functions are available to make generic HTTP | |
166 requests to Web services: | |
167 | |
90 | 168 * ``HttpGet(url, headers)`` |
169 * ``HttpPost(url, body, headers)`` | |
170 * ``HttpPut(url, body, headers)`` | |
171 * ``HttpDelete(url, headers)`` | |
0 | 172 * ``SetHttpCredentials(username, password)`` can be used to setup the |
173 HTTP credentials. | |
174 | |
91 | 175 The ``headers`` argument is optional and has been added in release |
176 1.2.1. It allows to set the HTTP headers for the HTTP client request. | |
0 | 177 |
178 .. _lua-origin: | |
179 | |
180 Origin of the instances | |
181 ^^^^^^^^^^^^^^^^^^^^^^^ | |
182 | |
183 Whenever Orthanc decides whether it should should store a new instance | |
184 (cf. the ``ReceivedInstanceFilter()`` callback), or whenever it has | |
185 actually stored a new instance (cf. the ``OnStoredInstance`` | |
186 callback), an ``origin`` parameter is provided. This parameter is a | |
187 `Lua table <http://www.lua.org/pil/2.5.html>`__ that describes from | |
188 which Orthanc subsystem the new instance comes from. | |
189 | |
190 There are 4 possible subsystems, that can be distinguished according | |
191 to the value of ``origin["RequestOrigin"]``: | |
192 | |
193 * ``RestApi``: The instance originates from some HTTP request to the REST | |
194 API. In this case, the ``RemoteIp`` and ``Username`` fields are | |
195 available in ``origin``. They respectively describe the IP address | |
196 of the HTTP client, and the username that was used for HTTP | |
197 authentication (as defined in the ``RegisteredUsers`` | |
198 :ref:`configuration variable <configuration>`). | |
199 * ``DicomProtocol``: The instance originates from a DICOM C-Store. | |
200 The fields ``RemoteIp``, ``RemoteAet`` and ``CalledAet`` | |
201 respectively provide the IP address of the DICOM SCU (client), the | |
202 application entity title of the DICOM SCU client, and the | |
203 application entity title of the Orthanc SCP server. The | |
204 ``CalledAet`` can be used for :ref:`advanced auto-routing scenarios | |
205 <lua-auto-routing>`, when a single instance of Orthanc acts as a | |
206 proxy for several DICOM SCU clients. | |
207 * ``Lua``: The instance originates from a Lua script. | |
208 * ``Plugins``: The instance originates from a plugin. | |
209 | |
210 | |
211 .. _lua-filter-dicom: | |
212 | |
75 | 213 Filtering incoming DICOM instances |
0 | 214 ---------------------------------- |
215 | |
216 .. highlight:: lua | |
217 | |
218 Each time a DICOM instance is received by Orthanc (either through the | |
219 DICOM protocol or through the REST API), the | |
220 ``ReceivedInstanceFilter()`` Lua function is invoked. If this callback | |
221 returns ``true``, the instance is accepted for storage. If it returns | |
222 ``false``, the instance is discarded. This mechanism can be used to | |
223 filter the incoming DICOM instances. Here is an example of a Lua | |
224 filter that only allows incoming instances of MR modality:: | |
225 | |
226 function ReceivedInstanceFilter(dicom, origin) | |
227 -- Only allow incoming MR images | |
228 if dicom.Modality == 'MR' then | |
229 return true | |
230 else | |
231 return false | |
232 end | |
233 end | |
234 | |
235 The argument dicom corresponds to a `Lua table | |
236 <http://www.lua.org/pil/2.5.html>`__ (i.e. an associative array) that | |
237 contains the DICOM tags of the incoming instance. For debugging | |
238 purpose, you can print this structure as follows:: | |
239 | |
240 function ReceivedInstanceFilter(dicom, origin) | |
241 PrintRecursive(dicom) | |
242 -- Accept all incoming instances (default behavior) | |
243 return true | |
244 end | |
245 | |
246 The argument ``origin`` is :ref:`documented separately <lua-origin>`. | |
247 | |
248 | |
249 .. _lua-filter-rest: | |
250 | |
75 | 251 Filtering incoming REST requests |
0 | 252 -------------------------------- |
253 | |
254 .. highlight:: lua | |
255 | |
256 Lua scripting can be used to control the access to the various URI of | |
257 the REST API. Each time an incoming HTTP request is received, the | |
258 ``IncomingHttpRequestFilter()`` Lua function is called. The access to | |
259 the resource is granted if and only if this callback script returns | |
260 ``true``. | |
261 | |
262 This mechanism can be used to implement fine-grained `access control | |
25 | 263 lists <https://en.wikipedia.org/wiki/Access_control_list>`__. Here is |
0 | 264 an example of a Lua script that limits POST, PUT and DELETE requests |
265 to an user that is called "admin":: | |
266 | |
267 function IncomingHttpRequestFilter(method, uri, ip, username, httpHeaders) | |
268 -- Only allow GET requests for non-admin users | |
269 | |
270 if method == 'GET' then | |
271 return true | |
272 elseif username == 'admin' then | |
273 return true | |
274 else | |
275 return false | |
276 end | |
277 end | |
278 | |
279 Here is a description of the arguments of this Lua callback: | |
280 | |
281 * ``method``: The HTTP method (GET, POST, PUT or DELETE). | |
282 * ``uri``: The path to the resource (e.g. ``/tools/generate-uid``). | |
283 * ``ip``: The IP address of the host that has issued the HTTP request (e.g. ``127.0.0.1``). | |
284 * ``username``: If HTTP Basic Authentication is enabled in the | |
285 :ref:`configuration file <configuration>`, the name of the user that | |
286 has issued the HTTP request (as defined in the ``RegisteredUsers`` | |
287 configuration variable). If the authentication is disabled, this | |
288 argument is set to the empty string. | |
289 * ``httpHeaders``: The HTTP headers of the incoming request. This | |
290 argument is available since Orthanc 1.0.1. It is useful if the | |
291 authentication should be achieved through tokens, for instance | |
292 against a `LDAP | |
293 <https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol>`__ | |
294 or `OAuth2 <https://en.wikipedia.org/wiki/OAuth>`__ server. | |
295 | |
296 | |
297 .. _lua-auto-routing: | |
298 | |
75 | 299 Auto-routing of DICOM images |
0 | 300 ---------------------------- |
301 | |
302 .. highlight:: lua | |
303 | |
304 Since release 0.8.0, the routing of DICOM flows can be very easily | |
305 automated with Orthanc. All you have to do is to declare your | |
306 destination modality in the :ref:`configuration file <configuration>` | |
307 (section ``DicomModalities``), then to create and install a Lua | |
308 script. For instance, here is a sample script:: | |
309 | |
310 function OnStoredInstance(instanceId, tags, metadata) | |
311 Delete(SendToModality(instanceId, 'sample')) | |
312 end | |
313 | |
314 If this script is loaded into Orthanc, whenever a new DICOM instance | |
315 is received by Orthanc, it will be routed to the modality whose | |
316 symbolic name is ``sample`` (through a Store-SCU command), then it | |
317 will be removed from Orthanc. In other words, this is a **one-liner | |
318 script to implement DICOM auto-routing**. | |
319 | |
320 Very importantly, thanks to this feature, you do not have to use the | |
321 REST API or to create external scripts in order to automate simple | |
322 imaging flows. The scripting engine is entirely contained inside the | |
323 Orthanc core system. | |
324 | |
325 Thanks to Lua expressiveness, you can also implement conditional | |
326 auto-routing. For instance, if you wish to route only patients whose | |
327 name contains "David", you would simply write:: | |
328 | |
329 function OnStoredInstance(instanceId, tags, metadata) | |
330 -- Extract the value of the "PatientName" DICOM tag | |
331 local patientName = string.lower(tags['PatientName']) | |
332 | |
333 if string.find(patientName, 'david') ~= nil then | |
334 -- Only route patients whose name contains "David" | |
335 Delete(SendToModality(instanceId, 'sample')) | |
336 | |
337 else | |
338 -- Delete the patients that are not called "David" | |
339 Delete(instanceId) | |
340 end | |
341 end | |
342 | |
343 Besides ``SendToModality()``, a mostly identical function with the | |
344 same arguments called ``SendToPeer()`` can be used to route instances | |
345 to :ref:`Orthanc peers <peers>`. It is also possible to modify the | |
346 received instances before routing them. For instance, here is how you | |
347 would replace the ``StationName`` DICOM tag:: | |
348 | |
349 function OnStoredInstance(instanceId, tags, metadata) | |
350 -- Ignore the instances that result from a modification to avoid | |
351 -- infinite loops | |
352 if (metadata['ModifiedFrom'] == nil and | |
353 metadata['AnonymizedFrom'] == nil) then | |
354 | |
355 -- The tags to be replaced | |
356 local replace = {} | |
357 replace['StationName'] = 'My Medical Device' | |
358 | |
359 -- The tags to be removed | |
360 local remove = { 'MilitaryRank' } | |
361 | |
362 -- Modify the instance, send it, then delete the modified instance | |
363 Delete(SendToModality(ModifyInstance(instanceId, replace, remove, true), 'sample')) | |
364 | |
365 -- Delete the original instance | |
366 Delete(instanceId) | |
367 end | |
368 end | |
369 | |
370 | |
75 | 371 Important remarks about auto-routing |
372 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
19
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
373 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
374 The ``SendToModality()``, ``SendToPeer()``, ``ModifyInstance()`` and |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
375 ``Delete()`` functions are for the most basic cases of auto-routing |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
376 (implying a single DICOM instance, and possibly a basic modification |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
377 of this instance). The ``ModifyInstance()`` function `could also lead |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
378 to problems |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
379 <https://groups.google.com/d/msg/orthanc-users/hmv2y-LgKm8/oMAuGJWMBgAJ>`__ |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
380 if it deals with tags wrongly interpreted as numbers by Lua. |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
381 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
382 For more evolved auto-routing scenarios, remember that Lua scripts |
191 | 383 :ref:`have full access to the REST API of Orthanc <lua-rest>`. This is |
19
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
384 illustrated by the ``AutoroutingModification.lua`` sample available in |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
385 the source distribution of Orthanc:: |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
386 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
387 function OnStoredInstance(instanceId, tags, metadata, origin) |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
388 -- Ignore the instances that result from the present Lua script to |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
389 -- avoid infinite loops |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
390 if origin['RequestOrigin'] ~= 'Lua' then |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
391 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
392 -- The tags to be replaced |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
393 local replace = {} |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
394 replace['StationName'] = 'My Medical Device' |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
395 replace['0031-1020'] = 'Some private tag' |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
396 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
397 -- The tags to be removed |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
398 local remove = { 'MilitaryRank' } |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
399 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
400 -- Modify the instance |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
401 local command = {} |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
402 command['Replace'] = replace |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
403 command['Remove'] = remove |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
404 local modifiedFile = RestApiPost('/instances/' .. instanceId .. '/modify', DumpJson(command, true)) |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
405 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
406 -- Upload the modified instance to the Orthanc database so that |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
407 -- it can be sent by Orthanc to other modalities |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
408 local modifiedId = ParseJson(RestApiPost('/instances/', modifiedFile)) ['ID'] |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
409 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
410 -- Send the modified instance to another modality |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
411 RestApiPost('/modalities/sample/store', modifiedId) |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
412 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
413 -- Delete the original and the modified instances |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
414 RestApiDelete('/instances/' .. instanceId) |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
415 RestApiDelete('/instances/' .. modifiedId) |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
416 end |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
417 end |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
418 |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
419 Also note that :ref:`other callbacks are available <lua-callbacks>` |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
420 (``OnStablePatient()``, ``OnStableStudy()`` and ``OnStableSeries()``) |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
421 to react to other events than the reception of a single instance |
c98317fedf87
note about autorouting
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
422 with ``OnStoredInstance()``. |
75 | 423 |
99
e83da2f99e45
added 'troubleshooting C-Find queries' in the worklist section
amazy
parents:
91
diff
changeset
|
424 .. _lua-fix-cfind: |
75 | 425 |
77
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
426 Fixing C-Find requests |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
427 ---------------------- |
75 | 428 |
429 :ref:`C-Find requests <dicom-find>` are sometimes interpreted | |
77
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
430 differently by different DICOM servers (e.g. the ``*`` wildcard, as |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
431 `reported by users |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
432 <https://groups.google.com/d/msg/orthanc-users/3g7V7kqr3g0/IREL88RWAwAJ>`__), |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
433 and sometimes a querying modality might set unexpected DICOM tags |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
434 (cf. `this real-world example |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
435 <https://groups.google.com/d/msg/orthanc-users/PLWKqVVaXLs/n_0x4vKhAgAJ>`__). In |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
436 such situations, it is possible to dynamically fix incoming or |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
437 outgoing C-Find queries using a Lua script. |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
438 |
165
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
439 Sanitizing incoming C-Find requests can be done by implementing the |
77
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
440 ``IncomingFindRequestFilter(query, origin)`` callback that is called |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
441 whenever the Orthanc C-Find SCP is queried by a remote modality. For |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
442 instance, here is Lua script to remove a private tag that is specified |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
443 by some manufacturer:: |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
444 |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
445 function IncomingFindRequestFilter(query, origin) |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
446 -- First display the content of the C-Find query |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
447 PrintRecursive(query) |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
448 PrintRecursive(origin) |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
449 |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
450 -- Remove the "PrivateCreator" tag from the query |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
451 local v = query |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
452 v['5555,0010'] = nil |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
453 |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
454 return v |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
455 end |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
456 |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
457 The ``origin`` argument contains information about which modality has |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
458 issued the request. |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
459 |
165
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
460 Note that the ``IncomingFindRequestFilter`` callback is not applied to |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
461 C-Find requests targeting :ref:`modality worklists |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
462 <worklists-plugin>`. Since Orthanc 1.4.2, the corresponding |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
463 ``IncomingWorklistRequestFilter`` callback can be used to sanitize |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
464 C-FIND requests against worklists:: |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
465 |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
466 function IncomingWorklistRequestFilter(query, origin) |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
467 PrintRecursive(query) |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
468 PrintRecursive(origin) |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
469 |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
470 -- Implements the same behavior as the "FilterIssuerAet" |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
471 -- option of the sample worklist plugin |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
472 query['0040,0100'][1]['0040,0001'] = origin['RemoteAet'] |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
473 |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
474 return query |
b879a6274065
IncomingWorklistRequestFilter
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
105
diff
changeset
|
475 end |
105
268ec482f051
IncomingFindRequestFilter not applied to worklist plugin
amazy
parents:
99
diff
changeset
|
476 |
77
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
477 Similarly, the callback ``OutgoingFindRequestFilter(query, modality)`` |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
478 is invoked whenever Orthanc acts as a C-Find SCU, which gives the |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
479 opportunity to dynamically fix outgoing C-Find requests before they |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
480 are actually sent to the queried modality. For instance, here is a |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
481 sample Lua callback that would replace asterisk wildcards (i.e. ``*``) |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
482 by an empty string for any query/retrieve issued by Orthanc (including |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
483 from Orthanc Explorer):: |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
484 |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
485 function OutgoingFindRequestFilter(query, modality) |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
486 for key, value in pairs(query) do |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
487 if value == '*' then |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
488 query[key] = '' |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
489 end |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
490 end |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
491 |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
492 return query |
48c7d2eb98da
Fixing C-Find requests
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
75
diff
changeset
|
493 end |