diff Sphinx/source/users/lua.rst @ 0:901e8961f46e

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 22 Apr 2016 12:57:38 +0200
parents
children c98317fedf87
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sphinx/source/users/lua.rst	Fri Apr 22 12:57:38 2016 +0200
@@ -0,0 +1,367 @@
+.. _lua:
+
+Server-side scripting with Lua
+==============================
+
+.. contents::
+
+Since release 0.5.2, Orthanc supports server-side scripting through
+the `Lua <http://en.wikipedia.org/wiki/Lua_(programming_language)>`__
+scripting language. Thanks to this major feature, Orthanc can be tuned
+to specific medical workflows without being driven by an external
+script. This page summarizes the possibilities of Orthanc server-side
+scripting.
+
+Many other examples are `available in the source distribution
+<https://bitbucket.org/sjodogne/orthanc/src/default/Resources/Samples/Lua/>`__.
+
+
+Installing a Lua Script
+-----------------------
+
+.. highlight:: bash
+
+A custom Lua script can be installed either by the :ref:`configuration
+file <configuration>`, or by uploading it
+through the :ref:`REST API <rest-samples>`.
+
+To install it by the **configuration file** method, you just have to
+specify the path to the file containing the Lua script in the
+``LuaScripts`` variable.
+
+To upload a script stored in the file "``script.lua``" through the
+**REST API**, use the following command::
+
+    $ curl -X POST http://localhost:8042/tools/execute-script --data-binary @script.lua
+
+Pay attention to the fact that, contrarily to the scripts installed
+from the configuration file, the scripts installed through the REST
+API are non-persistent: They are discarded after a restart of Orthanc,
+which makes them useful for script prototyping. You can also interpret
+a single Lua command through the REST API::
+
+    $ curl -X POST http://localhost:8042/tools/execute-script --data-binary "print(42)"
+
+*Note:* The ``--data-binary`` cURL option is used instead of
+``--data`` to prevent the interpretation of newlines by cURL, which is
+`mandatory for the proper evaluation
+<http://stackoverflow.com/q/3872427/881731>`__ of the possible
+comments inside the Lua script.
+
+
+Lua API
+-------
+
+
+.. _lua-callbacks:
+
+Callbacks to react to events
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The Lua engine of Orthanc comes invokes the following callbacks that
+are triggered on various events. Here are the **generic events**:
+
+* ``function Initialize()``: Invoked as soon as the Orthanc server is started.
+* ``function Finalize()``: Invoked just before the Orthanc server is stopped.
+
+Some **permission-related events** allow to filter incoming requests:
+
+* ``function ReceivedInstanceFilter(dicom, origin)``:
+  Invoked to known whether an incoming DICOM instance should be
+  accepted. :ref:`See this section <lua-filter-dicom>`. The ``origin``
+  parameter is :ref:`documented separately <lua-origin>`.
+* ``function IncomingHttpRequestFilter(method, uri, ip, username,
+  httpHeaders)``: Invoked to known whether a REST request should be
+  accepted. :ref:`See this section <lua-filter-rest>`.
+
+Some **DICOM-related events** allow to react to the reception of
+new medical images:
+
+* ``function OnStoredInstance(instanceId, tags, metadata, origin)``:
+  Invoked whenever a new instance has been stored into Orthanc. 
+  This is especially useful for :ref:`lua-auto-routing`. The ``origin``
+  parameter is :ref:`documented separately <lua-origin>`.
+* ``function OnStablePatient(patientId, tags, metadata)``: Invoked
+  whenever a patient has not received any new instance for a certain
+  amount of time (cf. the option ``StableAge`` in the
+  :ref:`configuration file <configuration>`). The :ref:`identifier
+  <orthanc-ids>` of the patient is provided, together with her DICOM
+  tags and her metadata.
+* ``function OnStableSeries(seriesId, tags, metadata)``: Invoked
+  whenever a series has not received any new instance for a certain
+  amount of time.
+* ``function OnStableStudy(studyId, tags, metadata)``: Invoked
+  whenever a study has not received any new instance for a certain
+  amount of time.
+* ``function IncomingFindRequestFilter(source, origin)``: Invoked
+  whenever Orthanc receives an incoming C-Find query through the DICOM
+  protocol. This allows to inspect the content of the C-Find query,
+  and possibly modify it if a patch is needed for some manufacturer. A
+  `sample script is available
+  <https://bitbucket.org/sjodogne/orthanc/src/default/Resources/Samples/Lua/IncomingFindRequestFilter.lua>`__.
+
+Furthermore, whenever a DICOM association is negociated for C-Store
+SCP, several callbacks are successively invoked to specify which
+**transfer syntaxes** are accepted for the association. These
+callbacks are listed in `this sample script
+<https://bitbucket.org/sjodogne/orthanc/src/default/Resources/Samples/Lua/TransferSyntaxEnable.lua>`__.
+
+*Note:* All of these callbacks are guaranteed to be **invoked in
+mutual exclusion**. This implies that Lua scripting in Orthanc does
+not support any kind of concurrency.
+
+
+.. _lua-rest:
+
+Calling the REST API of Orthanc
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Lua scripts have :ref:`full access to the REST API <rest>` of Orthanc
+through the following functions:
+
+* ``RestApiGet(uri, builtin)``
+* ``RestApiPost(uri, body, builtin)``
+* ``RestApiPut(uri, body, builtin)``
+* ``RestApiDelete(uri, builtin)``
+
+The ``uri`` arguments specifies the URI against which to make the
+request, and ``body`` is a string containing the body of POST/PUT
+request.  The ``builtin`` parameter is an optional Boolean that
+specifies whether the request targets only the built-in REST API of
+Orthanc (if set to ``true``), or the full the REST API after being
+tainted by the plugins (if set to ``false``).
+
+
+General-purpose functions
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The Lua engine of Orthanc contain several general-purpose ancillary
+functions:
+
+* ``PrintRecursive(v)`` recursively prints the content of a `Lua table
+  <http://www.lua.org/pil/2.5.html>`__ to the log file of Orthanc.
+* ``ParseJson(s)`` converts a string encoded in the `JSON format
+  <https://en.wikipedia.org/wiki/JSON>`__ to a Lua table.
+* ``DumpJson(v, keepStrings)`` encodes a Lua table as a JSON string.
+  Setting the optional argument ``keepStrings`` (available from
+  release 0.9.5) to ``true`` prevents the automatic conversion of
+  strings to integers.
+* ``GetOrthancConfiguration()`` returns a Lua table containing the
+  content of the :ref:`configuration files <configuration>` of
+  Orthanc.
+
+
+Similarly to the functions to :ref:`call the REST API of Orthanc
+<lua-rest>`, several functions are available to make generic HTTP
+requests to Web services:
+
+* ``HttpGet(url)``
+* ``HttpPost(url, body)``
+* ``HttpPut(url, body)``
+* ``HttpDelete(url)``
+* ``SetHttpCredentials(username, password)`` can be used to setup the
+  HTTP credentials.
+
+
+.. _lua-origin:
+
+Origin of the instances
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Whenever Orthanc decides whether it should should store a new instance
+(cf. the ``ReceivedInstanceFilter()`` callback), or whenever it has
+actually stored a new instance (cf. the ``OnStoredInstance``
+callback), an ``origin`` parameter is provided. This parameter is a
+`Lua table <http://www.lua.org/pil/2.5.html>`__ that describes from
+which Orthanc subsystem the new instance comes from.
+
+There are 4 possible subsystems, that can be distinguished according
+to the value of ``origin["RequestOrigin"]``:
+
+* ``RestApi``: The instance originates from some HTTP request to the REST
+  API. In this case, the ``RemoteIp`` and ``Username`` fields are
+  available in ``origin``. They respectively describe the IP address
+  of the HTTP client, and the username that was used for HTTP
+  authentication (as defined in the ``RegisteredUsers``
+  :ref:`configuration variable <configuration>`).
+* ``DicomProtocol``: The instance originates from a DICOM C-Store.
+  The fields ``RemoteIp``, ``RemoteAet`` and ``CalledAet``
+  respectively provide the IP address of the DICOM SCU (client), the
+  application entity title of the DICOM SCU client, and the
+  application entity title of the Orthanc SCP server. The
+  ``CalledAet`` can be used for :ref:`advanced auto-routing scenarios
+  <lua-auto-routing>`, when a single instance of Orthanc acts as a
+  proxy for several DICOM SCU clients.
+* ``Lua``: The instance originates from a Lua script.
+* ``Plugins``: The instance originates from a plugin.
+
+
+.. _lua-filter-dicom:
+
+Filtering Incoming DICOM Instances
+----------------------------------
+
+.. highlight:: lua
+
+Each time a DICOM instance is received by Orthanc (either through the
+DICOM protocol or through the REST API), the
+``ReceivedInstanceFilter()`` Lua function is invoked. If this callback
+returns ``true``, the instance is accepted for storage. If it returns
+``false``, the instance is discarded. This mechanism can be used to
+filter the incoming DICOM instances. Here is an example of a Lua
+filter that only allows incoming instances of MR modality::
+
+ function ReceivedInstanceFilter(dicom, origin) 
+    -- Only allow incoming MR images   
+    if dicom.Modality == 'MR' then
+       return true 
+    else
+       return false
+    end
+ end
+
+The argument dicom corresponds to a `Lua table
+<http://www.lua.org/pil/2.5.html>`__ (i.e. an associative array) that
+contains the DICOM tags of the incoming instance. For debugging
+purpose, you can print this structure as follows::
+
+ function ReceivedInstanceFilter(dicom, origin) 
+    PrintRecursive(dicom)
+    -- Accept all incoming instances (default behavior)
+    return true 
+ end
+
+The argument ``origin`` is :ref:`documented separately <lua-origin>`.
+
+
+.. _lua-filter-rest:
+
+Filtering Incoming REST Requests
+--------------------------------
+
+.. highlight:: lua
+
+Lua scripting can be used to control the access to the various URI of
+the REST API. Each time an incoming HTTP request is received, the
+``IncomingHttpRequestFilter()`` Lua function is called. The access to
+the resource is granted if and only if this callback script returns
+``true``.
+
+This mechanism can be used to implement fine-grained `access control
+lists <http://en.wikipedia.org/wiki/Access_control_list>`__. Here is
+an example of a Lua script that limits POST, PUT and DELETE requests
+to an user that is called "admin"::
+
+ function IncomingHttpRequestFilter(method, uri, ip, username, httpHeaders)
+    -- Only allow GET requests for non-admin users
+ 
+   if method == 'GET' then
+       return true
+    elseif username == 'admin' then
+       return true
+    else
+       return false
+    end
+ end
+
+Here is a description of the arguments of this Lua callback:
+
+* ``method``: The HTTP method (GET, POST, PUT or DELETE).
+* ``uri``: The path to the resource (e.g. ``/tools/generate-uid``).
+* ``ip``: The IP address of the host that has issued the HTTP request (e.g. ``127.0.0.1``).
+* ``username``: If HTTP Basic Authentication is enabled in the
+  :ref:`configuration file <configuration>`, the name of the user that
+  has issued the HTTP request (as defined in the ``RegisteredUsers``
+  configuration variable). If the authentication is disabled, this
+  argument is set to the empty string.
+* ``httpHeaders``: The HTTP headers of the incoming request. This
+  argument is available since Orthanc 1.0.1. It is useful if the
+  authentication should be achieved through tokens, for instance
+  against a `LDAP
+  <https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol>`__
+  or `OAuth2 <https://en.wikipedia.org/wiki/OAuth>`__ server.
+
+
+.. _lua-auto-routing:
+
+Auto-Routing of DICOM Images
+----------------------------
+
+.. highlight:: lua
+
+Since release 0.8.0, the routing of DICOM flows can be very easily
+automated with Orthanc. All you have to do is to declare your
+destination modality in the :ref:`configuration file <configuration>`
+(section ``DicomModalities``), then to create and install a Lua
+script. For instance, here is a sample script::
+
+    function OnStoredInstance(instanceId, tags, metadata)
+      Delete(SendToModality(instanceId, 'sample'))
+    end
+
+If this script is loaded into Orthanc, whenever a new DICOM instance
+is received by Orthanc, it will be routed to the modality whose
+symbolic name is ``sample`` (through a Store-SCU command), then it
+will be removed from Orthanc. In other words, this is a **one-liner
+script to implement DICOM auto-routing**.
+
+Very importantly, thanks to this feature, you do not have to use the
+REST API or to create external scripts in order to automate simple
+imaging flows. The scripting engine is entirely contained inside the
+Orthanc core system.
+
+Thanks to Lua expressiveness, you can also implement conditional
+auto-routing. For instance, if you wish to route only patients whose
+name contains "David", you would simply write::
+
+ function OnStoredInstance(instanceId, tags, metadata)
+    -- Extract the value of the "PatientName" DICOM tag
+    local patientName = string.lower(tags['PatientName'])
+ 
+   if string.find(patientName, 'david') ~= nil then
+       -- Only route patients whose name contains "David"
+       Delete(SendToModality(instanceId, 'sample'))
+ 
+   else
+       -- Delete the patients that are not called "David"
+       Delete(instanceId)
+    end
+ end
+
+Besides ``SendToModality()``, a mostly identical function with the
+same arguments called ``SendToPeer()`` can be used to route instances
+to :ref:`Orthanc peers <peers>`.  It is also possible to modify the
+received instances before routing them. For instance, here is how you
+would replace the ``StationName`` DICOM tag::
+
+ function OnStoredInstance(instanceId, tags, metadata)
+    -- Ignore the instances that result from a modification to avoid
+    -- infinite loops
+    if (metadata['ModifiedFrom'] == nil and
+        metadata['AnonymizedFrom'] == nil) then
+ 
+      -- The tags to be replaced
+       local replace = {}
+       replace['StationName'] = 'My Medical Device'
+ 
+      -- The tags to be removed
+       local remove = { 'MilitaryRank' }
+
+      -- Modify the instance, send it, then delete the modified instance
+       Delete(SendToModality(ModifyInstance(instanceId, replace, remove, true), 'sample'))
+
+      -- Delete the original instance
+       Delete(instanceId)
+    end
+ end
+
+
+**Important remark:** The ``SendToModality()``, ``SendToPeer()``,
+``ModifyInstance()`` and ``Delete()`` functions are for the most
+common cases of auto-routing (implying a single DICOM instance, and
+possibly a basic modification of this instance). For more evolved
+auto-routing scenarios, remember that Lua scripts :ref:`have full to
+the REST API of Orthanc <lua-rest>`, and that :ref:`other callbacks
+are available <lua-callbacks>` to react to other events than the
+reception of a single instance (notably ``OnStablePatient()``,
+``OnStableStudy()`` and ``OnStableSeries()``).