Skip to content

Beacon settings

Settings

Beacon settings are helper classes to store data in the Redis database at runtime from Python code, using BLISS.

Use cases for settings:

  • on top of static configuration parameters

    • motor velocity, acceleration, limits
    • auto-gain on a Keithley
  • to persistently keep (or share) information across executions (or across processes)

    • selected counter for plot
    • enabled loggers, log level
    • scan saving path, file template…

Direct access to the Redis database

redis-cli is a command-line tool to directly access to the Redis database.

redis-cli -s /tmp/redis.sock -n 0

WARNING: redis-cli must be used with care, alteration of the database integrity can lead to very strange behaviour.

Never forget: “With great power comes great responsibility”

BLISS provides different classes for different kinds of settings.

SimpleSetting

The SimpleSetting class is used to store a scalar value:

  • int
  • float
  • string
  • bool

Main usages are:

  • to create the setting with a default value:

    sss = SimpleSetting(<key>, default_value=<value>)

  • to set a value to store: sss.set()

  • to read the stored value: sss.get()
  • to reset the value to the default one: sss.clear()

SimpleSetting Example

from bliss.config import settings
magicNumber = 63825363
sss = settings.SimpleSetting('myIntkey', default_value=magicNumber)
print(sss)
# <SimpleSetting name=myIntkey value=None>
print(sss.get())
# 63825363    # this is the default value.
sss.set(42)

print(sss)
# <SimpleSetting name=myIntkey value=b'42'>

print(sss.get())
# 42

sss.clear()
print(sss.get())
# 63825363

sss.set(3.14)

redis-cli can be used to inspect the redis database:

% redis-cli -s /tmp/redis.sock -n 0
redis /tmp/redis.sock> keys my*
1) "myHkey"
2) "myIntkey"

redis /tmp/redis.sock> get myIntkey
"3.14"

After a restart of the session (or from another session):

from bliss.config import settings
sss = settings.SimpleSetting('myIntkey')
print(sss.get())
# 3.14

After a .clear(), the key is removed from Redis database:

sss.clear()
redis /tmp/redis.sock> keys myIntkey
(empty list or set)

SimpleSetting behaviour

After instanciation (sss = SimpleSetting('aKey', default_value=42)), the SimpleSetting object returns the default_value. This default value is NOT saved in Redis database.

After a value has been set to the SimpleSetting object (sss.set(3.14)):

  • this value is stored in the Redis database
  • SimpleSetting object returns this value (sss.get())

After a clear() (sss.clear()), the key is removed from Redis database and the SimpleSetting object returns again the default_value.

Note

default_value can be None, but a SimpleSetting cannot be set to None.

BaseHashSetting and HashSetting

The BaseHashSetting class is used to represent a dictionary of scalar values. HashSetting simply adds a kwarg (keyword argument) default_values that is a dictionary containing values taken as a fallback.

from bliss.config import settings
myDict = {"key1":"value1", "key2":"value2"}
hs = settings.HashSetting('myHkey', default_values=myDict)
                                      # note the 's' in default_values
# 'hs' acts now as a dictionary.
print(hs["key1"])
# value1
print(hs["key2"])
# value2
hs["key1"]=3333
print(hs["key1"])
# 3333

Redis stores only key/value pairs that have changed:

% redis-cli -s /tmp/redis.sock -n 0
redis /tmp/redis.sock> hgetall myHkey
1) "key1"
2) "3333"

After a .clear(), the key is removed from Redis database.

A key can be added and removed:

bliss session:

hs["newKey"]=45

redis:

hgetall myHkey
1) "key1"
2) "3333"
3) "newKey"
4) "45"

bliss session:

hs.remove("newKey")

redis:

redis /tmp/redis.sock> hgetall myHkey
1) "key1"
2) "3333"

QueueSetting

The QueueSetting class is used to represent a list of scalar values:

>>> myList = ["a", "b", "c", "d"]
>>> sqs = settings.QueueSetting('myQkey')
>>> sqs.set(myList)

ParametersWardrobe

ParametersWardrobe is a more advanced object used to group simple settings and to be able to easily switch from one set of values to another set.

Different sets are addressed by instances.

Every instance has a name and shares the same attributes with other instances.

A ParameterWardrobe can be visualized as a table where rows are the attributes and column are the instances. the values from ONE column/instance can be used at a time.

Note

The name of ParameterWardrobe comes from the idea of having some parts of the body to dress and suits of clothes to choose from, for example: working suit, night suit, swimming suit.

dress object example:

 BLISS [9]: from bliss.config.wardrobe import ParametersWardrobe
            # Create a Wardrobe with a 'default' instance
BLISS [10]: dress = ParametersWardrobe(name='dress')
BLISS [11]: dress.current_instance
            'default'
BLISS [12]: dress.add('body','shirt')  # adding some slots default dressing
BLISS [13]: dress.add('head','nothing')
BLISS [14]: dress.add('feet','tennis shoes')
BLISS [15]: dress.add('legs','jeans')
BLISS [16]: dress
  Out [17]: Parameters (default) -

              .body           = 'shirt'
              .creation_date  = '2019-06-05-12:38'
              .feet           = 'tennis shoes'
              .head           = 'nothing'
              .last_accessed  = '2019-06-05-12:38'
              .legs           = 'jeans'

There is a default suit of dress, that can be used with dot notation.

BLISS [25]: dress.body
  Out [25]: 'shirt'

BLISS [26]: dress.body = "T shirt"
BLISS [27]: dress.body
  Out [27]: 'T shirt'

Another suit of clothes can be added:

BLISS [28]: dress.switch("Night dress")
BLISS [29]: dress
  Out [29]: Parameters (Night dress) - default

              .body           = 'T shirt'
              .creation_date  = '2019-06-05-14:30'
              .feet           = 'tennis shoes'
              .head           = 'nothing'
              .last_accessed  = '2019-06-05-14:30'
              .legs           = 'jeans'


BLISS [30]: dress.body = 'Jacket'
BLISS [31]: dress.feet = "Nice shoes"
BLISS [32]: dress.legs = "Black trousers"
BLISS [33]: dress
  Out [33]: Parameters (Night dress) - default

              .body           = 'Jacket'
              .creation_date  = '2019-06-05-14:30'
              .feet           = 'Nice shoes'
              .head           = 'nothing'
              .last_accessed  = '2019-06-05-14:30'
              .legs           = 'Black trousers'

There are now two instances of dress and the Night dress is currently used.

Values can be changed and visualized. If no value is assigned to the instance, the default is taken.

With .show_table() we have a complete vision of what is contained in all instances.

Example: add another instance (swim) and use the provided .show_table() method to visualize all instances in a tabular form:

BLISS [34]: dress.switch('swim')
BLISS [35]: dress.feet = "nothing"
BLISS [36]: dress.head = "swim glasses"
BLISS [37]: dress.body = "nothing"
BLISS [38]: dress.legs = "swimsuit"
BLISS [39]: dress.instances
  Out [39]: ['swim', 'Night dress', 'default']
BLISS [40]: dress.show_table()
* asterisks means value not stored in database (default is taken)
# hash means a computed attribute (property)

                  swim (SELECTED)         Night dress             default
-------------  ------------------  ------------------  ------------------
         body             nothing              Jacket             T shirt
creation_date  # 2019-06-05-14:37  # 2019-06-05-14:30  # 2019-06-05-12:38
         feet             nothing          Nice shoes        tennis shoes
         head        swim glasses           * nothing             nothing
last_accessed  # 2019-06-05-14:37  # 2019-06-05-14:30  # 2019-06-05-12:38
         legs            swimsuit      Black trousers               jeans

A ParametersWardrobe can handle all basic types of data like:

  • string
  • bool
  • list (including numpy array)
  • tuple
  • dict
  • set

Another example with numpy objects:

BLISS [40]: all = ParametersWardrobe('all')
BLISS [41]: all.add('bool',True)
BLISS [42]: all.add('dict', {'index':'value'})
BLISS [43]: all.add('list')  # without passing a default value (will be None)
BLISS [44]: all.list = [1,2,3]  # assigning a value later
BLISS [45]: import numpy
BLISS [46]: numpy.ndarray((1,2,3))
  Out [46]: array([[[ 6.915285e-3,  4.645814e-3,  5.862452e-1],
                    [ 6.915285e-3,  4.645814e-3, -1.391518e+1]]])

BLISS [47]: all.list = numpy.ndarray((1,2,3))
BLISS [48]: all.list
  Out [48]: array([[[6.915285e-3, 4.645814e-3, 5.862452e-1],
                    [6.915285e-3, 4.645814e-3, 1.391518e+1]]])

BLISS [49]: all
  Out [49]: Parameters (default) -

      .bool           = True
      .creation_date  = '2019-06-05-14:43'
      .dict           = {'index': 'value'}
      .last_accessed  = '2019-06-05-14:43'
      .list           = array([[[6.915285e-3, 4.645814e-3, 5.862452e-1],
            [6.91528554e-3, 4.645814e-3, 1.391518e+1]]])

Useful methods of ParameterWardrobe are:

Here is a list of methods and purposes of ParameterWardrobe. To understand the usage details, just read the docstring with python’s help(...) method from the Bliss shell.

.add()

To add a new attribute to the Wardrobe.

.switch()

To switch from the current instance to another one and to create it if needed.

.remove() or .purge()

.remove() allow deleting instances or attributes. .purge() completely remove a ParametersWardrobe from redis.

.freeze()

Freeze the current instance copying values the default instance.

.to_dict() and .from_dict()

Allow to easily import/export instances, the main purpose is to have data in a form that can be used inside shell or scripts to do computation.

.to_file() and .from_file()

Save and import data using yaml format to and from a file.

The purpose is to dump instances and to keep a copy for backup reasons.

Not created for manipulation purposes.

Keep in mind that exporting to file in fact freezes the data in the file,

This means that: if an instance that takes some values from default instance is used, the act of writing to the file will hardcode these values to file.

An instance imported from_file will have her own values for every attribute and will not use default ones.

Example:

BLISS [39]: gp.show_table()
* asterisks means value not stored in database (default is taken)
# hash means a computed attribute (property)


                  default (SELECTED)
-------------  ---------------------
           AC                     30
           KP                     10
           KI                    150
creation_date  # 2023-05-19 13:29:18
last_accessed  # 2023-05-19 18:00:00
BLISS [40]: gp.to_file("/tmp/galil_param_nano_sxm.txt")
[(bliss) guilloud@pcsht]$ cat /tmp/galil_param_nano_sxm.txt
WardrobeName: galil params
instances:
  default:
    _creation_date: '2023-05-19 13:29:18'
    AC: 30
    KP: 10
    KI: 150

.to_beacon() and .from_beacon()

The same purpose and behaviour than to_file() and from_file(), but the files are saved in Beacon on a subfolder called wardrobe as .dat files. for example, in /users/blissadm/local/beamline_configuration/wardrobe/

.instances

Gives the list of all instance names.

.creation_date and .last_accessed

Read only values (properties) that gives some information for ParameterWardrobe maintenance.