Skip to content

Using BLISS as a library

BLISS is primarily a Python library, thus BLISS can be embedded into any Python program.

BLISS modules can be used in any python environment. In order to work correctly the environment variables BEACON_HOST and potentially TANGO_HOST must be present.

Start a simple python shell e.g. like this:

$ TANGO_HOST=localhost:20000 BEACON_HOST=localhost python

of course the values of TANGO_HOST and BEACON_HOST have to be changed to the appropriate values - or not to be set at all if they are available in the environment already.

The entry-point to work with objects configured in Beacon is:

>>> from bliss.config import static
>>> config = static.get_config()

Using objects provided by Beacon

To work with specific objects they can be imported via config.get:

>>> transfocator = config.get('transfocator_simulator')
>>> print(transfocator.__info__())
Transfocator transfocator_simulator:
P0   L1  L2  L3   L4  L5  L6   L7  L8
OUT  IN  IN  OUT  IN  IN  OUT  IN  IN

Using a BLISS session in library mode

To be able to run scans it is best practice to import a defined BLISS session and access objects defined in the session via session.env_dict

>>> session =  config.get('test_session')
>>> session.setup()
>>> roby = session.env_dict['roby']
>>> roby.position
2.1

To run a standard scan the module bliss.common.scans can be used:

>>> from bliss.common.scans import loopscan
>>> loopscan(3,.1,session.env_dict['diode'])
Scan(number=32, name=loopscan, path=/tmp/scans/test_session/data.h5)

Saving related settings can be configured via session.scan_saving:

>>> session.scan_saving.data_filename='my_new_file'
>>> print(session.scan_saving.__info__())
Parameters (default) - 

  .base_path            = '/tmp/scans'
  .data_filename        = 'my_new_file'
  .user_name            = 'pithan'
  .template             = '{session}/'
  .images_path_relative = True
  .images_path_template = 'scan{scan_number}'
  .images_prefix        = '{img_acq_device}_'
  .date_format          = '%Y%m%d'
  .scan_number_format   = '%04d'
  .session              = 'test_session'
  .date                 = '20191211'
  .scan_name            = 'scan name'
  .scan_number          = 'scan number'
  .img_acq_device       = '<images_* only> acquisition device name'
  .writer               = 'hdf5'
  .creation_date        = '2019-11-12-15:32'
  .last_accessed        = '2019-12-11-12:00'
------  ---------  -------------------------------
exists  filename   /tmp/scans/test_session/data.h5
exists  root_path  /tmp/scans/test_session/
------  ---------  -------------------------------

Another example to execute user code.

import os
from bliss.config import static
from bliss import setup_globals
from bliss.common import standard

# Select Beacon server and Tango database
os.environ["BEACON_HOST"] = "localhost:10001"
os.environ["TANGO_HOST"] = "localhost:10000"

# Instantiate a Bliss session
config = static.get_config()
bliss_session = config.get("demo_session")

# Run setup scripts and populate the session environment
env_dict = {k: standard.__dict__[k] for k in standard.__all__}
assert bliss_session.setup(env_dict), "Session setup failed"
assert env_dict is bliss_session.env_dict

# Execute commands as if you were in the Bliss shell
setup_globals.user_script_homedir_oda()
ns = setup_globals.user_script_load("xrpd", export_global=False)
ns.xrpd_demo()
print("ok")

BLISS and IPython

A BLISS-friendly IPython console can be started like this:

python -c "import gevent.monkey; gevent.monkey.patch_all(thread=False); import IPython; IPython.start_ipython()"

Using BLISS shell and BLISS in library mode in parallel

It is possible to run e.g. a session in the “BLISS command line” and access it at the same time in library mode. In this case there are two different python processes running that don’t share the same object instances however states are shared via Beacon (wherever implemented) e.g. the position and state of an axis will be in sync in the two processes.

Concurrent hardware access

Each of the two python processes may communicate directly with hardware. As for now there is no locking mechanism implemented to prevent concurrent hardware access.

Technical details

BLISS is built on top of gevent, a coroutine-based asynchronous networking library. Under the hood, gevent works with a very fast control loop based on libev (or libuv).

The loop has to be running in the host program. When BLISS is imported, gevent monkey-patching is applied automatically (except for the threading module). In most cases, this is transparent and does not require anything from the host Python program.

Note

When using BLISS from a command line or from a graphical interface, gevent needs to be inserted into the events loop.

The line above launches Python, makes sure Python standard library is patched, without replacing system threads by gevent greenlets (which seems like a reasonable option), then starts the IPython interpreter.

From now on it is possible to use BLISS as any Python library:

from bliss.common.axis import Axis

from bliss.controllers.motors import icepap

ice = icepap.Icepap("iceid2322", {"host": "iceid2322"},
                   [("mbv4mot", Axis,
                   {"address":1,"steps_per_unit":817,
                   "velocity": 0.3, "acceleration": 3
                   })], [], [], [])

ice.initialize()

mbv4 = ice.get_axis("mbv4mot")

mbv4.position
>>>  0.07099143206854346

The example above creates an IcePAP motor controller instance, configured with a mbv4mot axis on IcePAP channel 1. Then, the controller is initialized and the axis object is retrieved to read the motor position.

Note

This example is meant to demystify BLISS – the only recommended way to use BLISS is to rely on BLISS Beacon to get configuration and to use the BLISS shell as the preferred command line interface.