.. index:: pair: idcp; parameters pair: idcp; StructuredData =========================== The IDCP parameter database =========================== IDCP Parameters are stored and managed with `StructuredData `__. This is a concept of storing the data in a single file in `YAML `_ format with some type checking. StructuredData provides the programs *SDview* and *SDshell*. SDview is a GUI based program which is used to display and query the data. SDshell is a powerful text based shell which is used to query or modify the data. Since the data stored in a text file it can also be displayed or modified with a simple text editor. The data file is part of a mercurial repository which is publicly available and has it's public homepage here: `id_db `_. .. note:: The parameter file has always the name 'id_db.SDCyml'. There are several ways how the the parameter file may be accessible: Parameter database locations ---------------------------- Globally installed file +++++++++++++++++++++++ .. hint:: The following information is specific for sites where IDCP is installed, see also :doc:`Sites `. As an example, at all :doc:`Sites ` where IDCP is installed, the parameter file 'id_db.SDCyml' is found on the IOC boot server at:: /opt/OPI/idcp/id_db.SDCyml At the BESSY II site (see :doc:`Sites `) it is also installed on the following hosts at the same file path: - stretch.acc.bessy.de - bullseye.acc.bessy.de - nfs.blc.bessy.de - nfs.ctl.bessy.de - hscon1l.blc.bessy.de - hscon2l.blc.bessy.de From working copy of the id-db repository +++++++++++++++++++++++++++++++++++++++++ In this case, you have a working copy of the id_db project. The file 'id_db.SDCyml' can then be found in directory 'data' of the working copy. You get a working copy like this: From the internal network of the `Helmholtz-Zentrum Berlin `_ at the BESSY II site with this command:: hg clone http://repo.acc.bessy.de/hg/id_db From anywhere else with this command:: hg clone http://hg.code.sf.net/p/id-db/code id_db .. note:: In the :doc:`IDCP ` application the configuration script creates a working copy of the id_db repository in directory 'add_ons/src/id_db', so you can find the parameter file there, too. XML-RPC +++++++ At the BESSY II site, there is an XML-RPC server that gives access to the parameter file from anywhere in the network of the facility. The server is gwc2c.acc.bessy.de and uses port 7643. You usually use the :ref:`iddb ` utility (see below) to access the data in this case. .. _iddb_doc: Database queries with the iddb tool ----------------------------------- At the BESSY II site (see :doc:`Sites `) the 'iddb' utility is already installed on the boot servers and development servers. In all other cases, you have to install iddb first. Installing iddb +++++++++++++++ You can install iddb with the 'bii_scripts' project or as a single file. Installing with bii_scripts ::::::::::::::::::::::::::: iddb is part of bii_scripts. You can install the complete project, here is a link to the public repository: `bii_scripts `_ Direct install :::::::::::::: You can download iddb directly since it is a single python script. .. note:: If iddb needs to read the parameter database file instead of using XML-RPC, you need to have `StructuredData `_ installed. From the internal network at the BESSY II site ############################################## :: wget http://repo.acc.bessy.de/idcp/iddb chmod u+x iddb At the BESSY II site these extra steps cause the program to contact the XML-RPC server:: export IDDB_SOURCE=server:gwc2c.acc.bessy.de:7643 You should copy iddb to a directory that is defined in your ``PATH`` variable, e.g. ``$HOME/bin``. From anywhere else ################## Enter:: wget https://sourceforge.net/p/bii-scripts/code/ci/master/tree/bin/iddb?format=raw -O iddb chmod u+x iddb .. note:: You need to have the parameter file 'id_db.SDCyml' installed at some location or you need a working copy of the id_db repository. In the repository, file 'id_db.SDCyml' is in directory 'data'. In the example below, '[PATH]' is the directory where 'id_db.SDCyml' can be found. Set:: export IDDB_SOURCE=file:[PATH]/id_db.SDCyml You should copy iddb to a directory that is defined in your ``PATH`` variable, e.g. ``$HOME/bin``. iddb Usage ++++++++++ iddb allows many kinds of queries of the parameter database. It has many useful options, you get help with ``iddb -h``. If you call iddb without any parameters you get an overview of all insertion devices:: $ iddb name devicename key prefix application group status -------------------------------------------------------------- BAM-WLS W7IT1R 0 WLS installed HMI-WLS W7IT2R 0 WLS test PSF-WLS W7IT7R 0 WLS installed U125/1 U125IL2RP 90 idcp90 idcp MLS installed U125/2 U125ID2R 3 idcp3 idcp BII installed U139 U139ID6R 110 idcp110 idcp BII installed U15 U15IV 80 idcp80 idcp DESY-U15 test U17 U17IT6R 120 idcp120 idcp BII installed U41 U41IT3R 6 idcp6 idcp BII installed U49/1 U49ID4R 7 idcp7 idcp BII installed U49/2 U49ID3R 5 idcp5 idcp BII installed UE112 UE112ID7R 13 idcp13 idcp BII installed UE46 UE46IT5R 10 idcp10 idcp BII installed UE48 UE48IT6R 12 idcp12 idcp BII installed UE49 UE49IT4R 8 idcp8 idcp BII installed UE52 UE52ID5R 9 idcp9 idcp BII installed UE56/1 UE56ID6R 11 idcp11 idcp BII installed UE56/2 UE56ID8R 15 idcp15 idcp BII installed UE56S UE56IS 95 idcp95 idcp BII installed Ubonsai U1IV 99 idcp99 idcp BII simulated With "find" you can query data for a given StructuredData path, here is an example:: $ iddb -i ue112 find 'global.*' id-data.UE112.global.application : idcp id-data.UE112.global.build_enabled : True id-data.UE112.global.description : UE112 ,UE112ID7R,idcp13 id-data.UE112.global.device_status : installed id-data.UE112.global.instance_no : 0 id-data.UE112.global.primary_key : 39 id-data.UE112.global.rsync_dist_group: BII id-data.UE112.global.undulator : UE112 With "rxfind" alone you get all the data for an undlator:: $ iddb -i ue112 rxfind id-data.UE112.accp-can-protocol.facility : bessy id-data.UE112.accp-can-protocol.node.inhibit : 500 id-data.UE112.accp-can-protocol.node.nid : 13 id-data.UE112.accp-can-protocol.node.timeout : 1500 id-data.UE112.accp-can-protocol.vars.command.fields.cmd : 0 id-data.UE112.accp-can-protocol.vars.command.index : 0 id-data.UE112.accp-can-protocol.vars.command.server : self id-data.UE112.accp-can-protocol.vars.command.type : RW,MULTIPLEX,CHAR,UNSIGNED id-data.UE112.accp-can-protocol.vars.position.fields.aparshift: 3 (many more lines follow). You can use this with a regular expression:: $ iddb --id ue112 rxfind '.*config.*max' id-data.UE112.config.h_max_diff : 0.1 id-data.UE112.config.h_max_pos : 56.0 id-data.UE112.config.h_max_velocity : 0.42 id-data.UE112.config.max_par_is_time : True id-data.UE112.config.v_max_diff : 0.1 id-data.UE112.config.v_max_pos : 185 id-data.UE112.config.v_max_velocity : 0.42 With the "dump" command you get the complete data for an insertion device as a python structure (only the first line is shown here for the UE112 undulator):: iddb dump ue112 | less {'UE112': {'accp-can-protocol': {'facility': 'bessy', ... Using an editor to view the data -------------------------------- On some hosts, the StructuredData data file is installed on the local host at a fixed location. On these hosts you can directly open the data file with you favorite editor, for example:: gedit /opt/OPI/idcp/id_db.SDCyml The file format is `YAML `_, if your editor supports syntax highlighting for this you use this option. When you want to find some parameters, with a simple editor, you are on your own. The tools described below offer more support for querying the data. Using SDview to query the data ------------------------------ If you have `StructuredData `_ installed, you can use ``SDview`` to query the data. The program is described here: `SDview `__. Starting SDview +++++++++++++++ If you have SDview installed enter:: SDview -f [PATH]/id_db.SDCyml This starts SDview and loads the file 'idcp_db.SDCyml'. SDview basics +++++++++++++ Here is the initial window of SDview .. image:: SDview-idcp_db-initialwin.png Organization of the data ++++++++++++++++++++++++ For a detailed explanation on the organization of the data please look here: OrganizationAnchor_. A first overview ++++++++++++++++ After clicking on the button "expand" and enlarging the window a bit you get this picture: .. image:: SDview-idcp_db-overview.png You see all undulators as sub-nodes of node "id-data". Showing all parameter groups of a single undulator ++++++++++++++++++++++++++++++++++++++++++++++++++ If we now click for example on the small "+" near "U125/2" we get this picture: .. image:: SDview-idcp_db-u1252.png Here you see all parameter groups of that undulator. Showing a single parameter group of an undulator ++++++++++++++++++++++++++++++++++++++++++++++++ If we now click on the small "+" near "names" within the parameter group of the U125/2 undulator we get this picture: .. image:: SDview-idcp_db-u1252-names.png Here we see all parameters of the group "names" of the undulator "U125/2". On the right side of the small white icons there is the parameter name followed by a colon ":" and the parameter value. Note that for string values (e.g. "devicename") the value is enclosed in single quotes. Numbers like the value of "key" have no quotes. Showing a single parameter group of an undulator ++++++++++++++++++++++++++++++++++++++++++++++++ Here we show only some of the search capabilities of SDview. For a detailed description of all search options look here: `SDview `__ Searching for a given parameter name ++++++++++++++++++++++++++++++++++++ We now want to find all parameters named "devicename" of all undulators. First we click on "collapse all" to collapse the tree. We select "ipattern" as a type and check "report only leaves" and "show paths and values" at "result window:". We now enter "devicename" in the entry field after "pattern" and press . This is how the program now looks like: .. image:: SDview-idcp_db-devicename-search.png All matching parts in the tree are now marked red, although not all are currently visible. If you grab the scroll bar with your mouse and scroll you can see the other matches. You can also press the "up" or "down" buttons to move between matches. Note that if you click with the mouse into the tree, the selection is cleared. If have have done this by accident, simply click again on "search". Since we selected "show paths and values" an additional window has appeared which shows the search results in a text window. It looks like this: .. image:: SDview-idcp_db-devicename-searchresult.png Searching for a value +++++++++++++++++++++ In this example we look for a given number. Note that we can also search values by ipatterns and regular expressions. Here however, we simply look for the number "180". We select "find value" at "type:" and enter 180 after "pattern:". Since there are many parameters in the parameter group "config" the first found value is visible first, we have to scroll to see it. The main window now looks like this: .. image:: SDview-idcp_db-val180-search.png Since we selected "show paths and values" an additional window has appeared which shows the search results in a text window. It looks like this: .. image:: SDview-idcp_db-val180-searchresult.png Using SDpyshell to query the data --------------------------------- If you have `StructuredData `_ installed, you can use ``SDpyshell`` to query the data. You should be familiar with the basics of the SDpyshell, it is described in the chapter "Basic Syntax" here: `SDpyshell `_ Starting SDpyshell ++++++++++++++++++ Log on to one of the development hosts and enter:: SDpyshell -f /opt/OPI/idcp/id_db.SDCyml This starts the SDshell and loads the file "idcp_db.SDCyml". SDpyshell basics ++++++++++++++++ SDpyshell has a history. You can retrieve previously entered commands by pressing the "up-arrow" key on your keyboard. You can use the "left-arrow" key in order to modify the command. You leave SDpyshell by pressing Control-D or entering "quit". If you enter "help()" SDpyshell shows you a list of all commands and help topics. If you enter "help("command") (note he double quotes) SDpyshell shows a help for the given command. Here is an example:: >>> help("help") help **** help() ^^^^^^ This command for interactive use displays help for a given help topic or command. If no arguments are given it displays all help topics. The command takes the following parameters: - item: This specifies the help topic which must be a string. This parameter is optional. If it is not provided or None the command displays a list of all help topics. - level: This specifies the help level. This parameter is mandatory if a help topic with the same name occurs at more than one place in the list of help topics. It is then used to specify exactly which help topic is requested. If you request a topic that is more than once in the list, the help command shows you all topics together with their level parameter. txt.help() ^^^^^^^^^^ This command returns the text that `help()`_ prints to the console as a string. For an explanation of parameters look at the description of `help()`_. fun.help() ^^^^^^^^^^ This command is identical to `txt.help()`_. .. _OrganizationAnchor: Organization of the data ++++++++++++++++++++++++ IDCP parameters are organized in a hierarchical structure much like files in a directory tree. Similar to a filesystem each parameter can be identified by a *path*. Each *value* has a unique path. The two keys at the top of the structure are "id-data" and "id-metadata". Below "id-data" are the parameters for each insertion device, below "id-metadata" are descriptions of the parameters. The keys below "id-data" are the human readable undulator names. Here is an example how to list all undulators with the SDshell:: >>> paths("id-data.*") - id-data.U125/1 - id-data.U125/2 - id-data.U139 - id-data.U2 - id-data.U3 - id-data.U4 - id-data.U41 - id-data.U48 - id-data.U49/1 - id-data.U49/2 - id-data.UE112 - id-data.UE46 - id-data.UE49 - id-data.UE52 - id-data.UE56/1 - id-data.UE56/2 - id-data.UE56R - id-data.Ubonsai For each undulator parameters are organized in groups. Here is a command that shows the parameter groups for the U125/1 undulator:: >>> paths("id-data.U125/1.*") - id-data.U125/1.accp-can-protocol - id-data.U125/1.config - id-data.U125/1.correction - id-data.U125/1.feedback - id-data.U125/1.global - id-data.U125/1.interface - id-data.U125/1.measurement - id-data.U125/1.names - id-data.U125/1.network - id-data.U125/1.operation - id-data.U125/1.physical - id-data.U125/1.referencing This example shows all parameters of the group "network" for the U125/1 undulator:: >>> paths("id-data.U125/1.network.*") - id-data.U125/1.network.bootserver - id-data.U125/1.network.gateway - id-data.U125/1.network.ioc - id-data.U125/1.network.login - id-data.U125/1.network.mount_filesystem - id-data.U125/1.network.ntpserver - id-data.U125/1.network.sec_lswitch_hosts Simple queries ++++++++++++++ You can get all parameters of a group with the command "get":: >>> get("id-data.U125/1.network", "yaml") bootserver: nfs.mlscs.bessy.de gateway: charm.mlscs.bessy.de ioc: eis4gp.mlscs.bessy.de login: epics mount_filesystem: 0 ntpserver: 192.168.48.1 sec_lswitch_hosts: '' If you use "find", the complete parameter path is shown for each parameter:: >>> find("id-data.U125/1.network.**") id-data.U125/1.network.bootserver : nfs.mlscs.bessy.de id-data.U125/1.network.gateway : charm.mlscs.bessy.de id-data.U125/1.network.ioc : eis4gp.mlscs.bessy.de id-data.U125/1.network.login : epics id-data.U125/1.network.mount_filesystem : 0 id-data.U125/1.network.ntpserver : 192.168.48.1 id-data.U125/1.network.sec_lswitch_hosts: '' You can of course query the value of a single parameter, too:: >>> get("id-data.U125/1.network.ioc") eis4gp.mlscs.bessy.de Parameter metadata ++++++++++++++++++ Extra information for parameters such as a description is stored below "id-metadata". There are two sub keys here, "id-path-info" and "parameter-info":: >>> paths("id-metadata.*") - id-metadata.id-path-info - id-metadata.parameter-info Parameter metadata: parameter-info :::::::::::::::::::::::::::::::::: Below "parameter-info" descriptions are stored for most parameters. Here you do have not to know the parameter group name. This is an example for the parameter network.ioc:: >>> find("id-metadata.parameter-info.ioc.**") id-metadata.parameter-info.ioc.description: the IOC's name id-metadata.parameter-info.ioc.group : network Parameter metadata: id-path-info :::::::::::::::::::::::::::::::: Below "id-path-info" descriptions are stored for parameter-paths. This is needed for parameters that are organized in a deeper structure than "group-name.parameter-name". Here is an example for one of the parameters of the ACCP protocol, which defines the CAN bus communication with the BESSY controlsystem. First we query the value of the parameter "vars.command.type" of the group "accp-can-protocol" of the U125/2 undulator:: >>> get("id-data.U125/2.accp-can-protocol.vars.command.type") RW,MULTIPLEX,CHAR,UNSIGNED Here we read the description of this parameter. Note that the *parameter path* is a simple key here, so we have to prepend the dots "." in the path with a backslash:: >>> get("id-metadata.id-path-info.accp-can-protocol\.vars\.command\.type.**") the type of the CAN variable Finding a parameter if the exact name is not known ++++++++++++++++++++++++++++++++++++++++++++++++++ Suppose we want to find a parameter that contains the string "max" for the U125/1 undulator. In order to perform this search we use the command "ifind". It takes a string as parameter which consists of words separated by spaces. It finds all paths were all the words can be found in any order. We have to search all parameter paths, so we must match "id-data". We want the U125/1 undulator, so we match "U125/1" and we want everything where "max" is in the path. We do the search like this:: >>> ifind("id-data U125/1 max") id-data.U125/1.config.v_max_diff : 0.1 id-data.U125/1.config.v_max_pos : 185 id-data.U125/1.config.v_max_velocity : 3500 id-data.U125/1.correction.ps_maxcurr_0: 3.0 id-data.U125/1.correction.ps_maxcurr_1: 3.0 id-data.U125/1.correction.ps_maxcurr_2: 3.0 id-data.U125/1.correction.ps_maxcurr_3: 3.0 id-data.U125/1.correction.ps_maxcurr_4: 3.0 id-data.U125/1.correction.ps_maxcurr_5: 3.0 There is also the command *rxfind* which can search all paths with a perl compatible regular expression. Comparing a parameter for all insertion devices +++++++++++++++++++++++++++++++++++++++++++++++ Suppose we want a list of all device names for all undulators. We can use ifind again:: >>> ifind("devicename") id-data.U125/1.names.devicename : U125IL2RP id-data.U125/2.names.devicename : U125ID2R id-data.U139.names.devicename : U139ID6R id-data.U2.names.devicename : U2IV id-data.U3.names.devicename : U3IVP id-data.U4.names.devicename : U4IV id-data.U41.names.devicename : U41IT6R id-data.U48.names.devicename : U48IV id-data.U49/1.names.devicename : U49ID4R id-data.U49/2.names.devicename : U49ID3R id-data.UE112.names.devicename : UE112ID7R id-data.UE46.names.devicename : UE46IT5R id-data.UE49.names.devicename : UE49IT4R id-data.UE52.names.devicename : UE52ID5R id-data.UE56/1.names.devicename : UE56ID6R id-data.UE56/2.names.devicename : UE56ID8R id-data.UE56R.names.devicename : UE56IV id-data.Ubonsai.names.devicename: U1IV Querying a group of parameters ++++++++++++++++++++++++++++++ We want to see all parameters whose paths start with "id-data.[undulator].names". We specify this with a *pattern*. We use a *wildcard* at the end of the path "**" that indicates that we want all paths that *start* with the given pattern. We do the query like this: >>> find("id-data.U125/1.names.**") id-data.U125/1.names.devicename: U125IL2RP id-data.U125/1.names.id : U125-1:Mls id-data.U125/1.names.key : 90 id-data.U125/1.names.name : U125/1 id-data.U125/1.names.prefix : idcp90 Find parameters that have a certain value +++++++++++++++++++++++++++++++++++++++++ Suppose we want to find all parameters that have the value "installed". This is done with the command findval:: >>> findval("installed") id-data.U125/2.global.device_status: installed id-data.U139.global.device_status : installed id-data.U41.global.device_status : installed id-data.U49/1.global.device_status : installed id-data.U49/2.global.device_status : installed id-data.UE112.global.device_status : installed id-data.UE46.global.device_status : installed id-data.UE49.global.device_status : installed id-data.UE52.global.device_status : installed id-data.UE56/1.global.device_status: installed id-data.UE56/2.global.device_status: installed A complex query +++++++++++++++ Here is an example of a more complex query. You are not supposed to understand all details here, for more information look at the documentation of `StructuredData `__ We want to have all device names of all insertion devices where the parameter "device_status" has the value "installed":: >>> findval("installed", pattern="id-data.*.global.device_status") id-data.U125/2.global.device_status: installed id-data.U139.global.device_status : installed id-data.U41.global.device_status : installed id-data.U49/1.global.device_status : installed id-data.U49/2.global.device_status : installed id-data.UE112.global.device_status : installed id-data.UE46.global.device_status : installed id-data.UE49.global.device_status : installed id-data.UE52.global.device_status : installed id-data.UE56/1.global.device_status: installed id-data.UE56/2.global.device_status: installed If we call "fun.findval" instead of "findval" the results are not printed to the screen but returned. We now use "substpath" to modify the returned paths:: >>> substpath(fun.findval("installed", pattern="id-data.*.global.device_status") ,"*.*.names.devicename") ['id-data.U49/1.names.devicename', 'id-data.U49/2.names.devicename', 'id-data.UE49.names.devicename', 'id-data.UE52.names.devicename', 'id-data.U41.names.devicename', 'id-data.U125/2.names.devicename', 'id-data.U139.names.devicename', 'id-data.UE112.names.devicename', 'id-data.UE56/1.names.devicename', 'id-data.UE46.names.devicename', 'id-data.UE56/2.names.devicename'] If we call "fun.substpath" instead "substpath" the paths are returned and can be stored in a new variable "p":: >>> p= fun.substpath (fun.findval("installed", pattern="id-data.*.global.device_status"), "*.*.names.devicename") We now call "get" with this list of paths in order to retrieve the values, the undulator device names in this case. We use use the better readable "yaml" format for the output. The first parameter ":\*\*" matches any path, this is currently needed since the first parameter of "get" must never be omitted:: >>> get("**", paths=p, formatspec="yaml") - U125ID2R - U139ID6R - U41IT6R - U49ID4R - U49ID3R - UE112ID7R - UE46IT5R - UE49IT4R - UE52ID5R - UE56ID6R - UE56ID8R Programming +++++++++++ Since SDpyshell is based on the python programming language, you can define functions to make things easier. The following function for example returns the paths for all insertion devices where a certain parameter has a certain value:: >>> def idfind(par, val): . p= fun.addpaths("id-data.*", par) . found= fun.findval(val, pattern=p) . return fun.substpath(found, "*.*") . We can now use this function to get paths for insertion devices that have a shift drive:: >>> idfind("physical.has_hdrive", 1) ['id-data.UE49', 'id-data.UE52', 'id-data.UE56R', 'id-data.U2', 'id-data.UE112', 'id-data.UE56/1', 'id-data.UE46', 'id-data.UE56/2'] We can also print the results in YAML format:: >>> format(idfind("physical.has_hdrive", 1), "yaml") - id-data.UE49 - id-data.UE52 - id-data.UE56R - id-data.U2 - id-data.UE112 - id-data.UE56/1 - id-data.UE46 - id-data.UE56/2 Query the data with xmlrpc -------------------------- At the BESSY II site (see also :doc:`Sites `) there is a xmlrpc server for the insertion device data running on host gwc2c.acc.bessy.de, port 7643. Below we show a short example how to query the data from the interactive python shell. Note that you should only call functions from the text and functional layer. So function names should start with "txt." or "fun.". Unused parameters must be empty strings, the container name should always be "id_db":: $ python3 Python 3.5.3 (default, Nov 4 2021, 15:29:10) [GCC 6.3.0 20170516] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import pprint >>> import xmlrpc.client >>> s = xmlrpc.client.ServerProxy('http://gwc2c.acc.bessy.de:7643') >>> pprint.pprint(s.fun.paths("id-data.*","","id_db")) ['id-data.BAM-WLS', 'id-data.HMI-WLS', 'id-data.PSF-WLS', 'id-data.U125/1', 'id-data.U125/2', 'id-data.U139', 'id-data.U15', 'id-data.U17', 'id-data.U41', 'id-data.U49/1', 'id-data.U49/2', 'id-data.UE112', 'id-data.UE46', 'id-data.UE48', 'id-data.UE49', 'id-data.UE52', 'id-data.UE56/1', 'id-data.UE56/2', 'id-data.UE56IV', 'id-data.Ubonsai'] >>> s.fun.get("id-data.U125/1.network.ioc",True,"","id_db") 'eis4gp.mlscs.bessy.de' As convenience functions for the BII-Controls application, there are some additional functions in module "id" defined:: >>> pprint.pprint(s.id.p_ids(["installed"])) ['id-data.U49/1', 'id-data.U49/2', 'id-data.UE49', 'id-data.UE52', 'id-data.UE46', 'id-data.PSF-WLS', 'id-data.U41', 'id-data.U139', 'id-data.UE112', 'id-data.BAM-WLS', 'id-data.UE56/1', 'id-data.U125/2', 'id-data.UE56/2', 'id-data.U125/1'] >>> pprint.pprint(s.id.cioc_data()['UE48IT6R']) {'device_status': 'test', 'name': 'UE48', 'nid': 12, 'vars': {'command': {'cid': 0, 'fields': {'cmd': {'description': 'mux of the ID command', 'mux': 0}}, 'server': 'id-ioc', 'type': 'RW,MULTIPLEX,CHAR,UNSIGNED'}, 'position': {'fields': {'aparshift': {'adel': 0.01, 'description': 'mux of the antiparallel shift', 'eguf': 218.447, 'egul': -218.453, 'hyst': 0.10000000000000001, 'max': 24.0, 'mdel': 0.01, 'min': -24.0, 'mux': 3, 'prec': 2, 'signal': 'ashift', 'unit': 'mm', 'value_description': 'Antiparallel Shift'}, 'gap': {'adel': 0.01, 'description': 'mux of the gap', 'eguf': 218.447,annotated_ 'egul': -218.453, 'hyst': 0.10000000000000001, 'max': 185, 'mdel': 0.01, 'min': 16.0, 'mux': 0, 'prec': 2, 'signal': 'gap', 'unit': 'mm', 'value_description': 'Gap'}, 'parshift': {'adel': 0.01, 'description': 'mux of the parallel shift', 'eguf': 218.447, 'egul': -218.453, 'hyst': 0.10000000000000001, 'max': 24.0, 'mdel': 0.01, 'min': -24.0, 'mux': 2, 'prec': 2, 'signal': 'pshift', 'unit': 'mm', 'value_description': 'Parallel Shift'}, 'reserved': {'adel': 0.01, 'description': 'unused mux', 'eguf': 218.447, 'egul': -218.453, 'hyst': 0.10000000000000001, 'mdel': 0.01, 'mux': 1, 'prec': 2}}, 'rsob': 12, 'server': 'cioc', 'type': 'WO,MULTIPLEX,SHORT,SIGNED'}, 'status': {'cid': 12, 'fields': {'stat': {'description': 'mux of the ID status', 'mux': 0}}, 'server': 'id-ioc', 'type': 'RW,MULTIPLEX,CHAR,UNSIGNED'}}} Note that cioc_data is intended to be called only once, since it returns a data structure for *all* installed insertion devices at the Bessy II storage ring.