Commits (21)
[![build status](https://gitlab.esrf.fr/bliss/bliss/badges/master/build.svg)](http://bliss.gitlab-pages.esrf.fr/bliss)
[![coverage report](https://gitlab.esrf.fr/bliss/bliss/badges/master/coverage.svg)](http://bliss.gitlab-pages.esrf.fr/bliss/htmlcov)
[![build status](https://gitlab.esrf.fr/bliss/bliss/badges/master/build.svg)](https://gitlab.esrf.fr/bliss/bliss/pipelines/master/latest)
[![coverage report](https://gitlab.esrf.fr/bliss/bliss/badges/master/coverage.svg)](https://bliss.gitlab-pages.esrf.fr/bliss/master/htmlcov)
The bliss control library.
Latest documentation from master can be found [here](http://bliss.gitlab-pages.esrf.fr/bliss/master)
Latest documentation from master can be found [here](https://bliss.gitlab-pages.esrf.fr/bliss/master)
In short
To update BLISS from source:
conda install --file ./requirements-conda.txt
pip install --no-deps -e .
conda install --file ./requirements-conda.txt
exit and re-enter into conda environment
pip install --no-deps -e .
......@@ -278,14 +278,14 @@ class Session:
class_name = object_config.get("class", "")
if class_name.lower() == "session":
f"Session {self.name} 'config-objects' list contains session {name}, ignoring (hint: add session in 'include-sessions' list)",
class_name = object_config.get("class", "")
if class_name.lower() == "session":
f"Session {self.name} 'config-objects' list contains session {name}, ignoring (hint: add session in 'include-sessions' list)",
for name in self.__exclude_objects_names:
This diff is collapsed.
......@@ -7,9 +7,9 @@
from bliss.controllers.temp import Controller
from bliss.common.temperature import Input, Output, Loop, lazy_init
from bliss.common.logtools import *
from bliss.common.logtools import log_info, log_debug
from bliss.common.utils import autocomplete_property
from bliss.common import session
from bliss import global_map
import os
import re
import sys
......@@ -253,7 +253,7 @@ class LakeshoreBase(Controller):
Controller.__init__(self, config, *args)
session.get_current().map.register(handler._comm, parents_list=[self, "comms"])
global_map.register(handler._comm, parents_list=[self, "comms"])
def model(self):
......@@ -501,6 +501,9 @@ class LakeshoreBase(Controller):
self.__kp, self.__ki, self.__kd = self._lakeshore.pid(channel)
return self.__kd
def __info__(self):
return "\n".join(self._show())
def _show(self, name=None):
""" Display all main parameters and values for the temperature controller
......@@ -611,7 +611,7 @@ def _interlock_channel_info_from_plc(
info["dac_offset"] = get_conf_output[7]
logical_device_key, logical_device_channel = modules_config.devhard2log(
(info["type"]["register_type"], offset)
(register_type_to_int(info["type"]["register_type"]), offset)
logical_device = modules_config.devkey2name(logical_device_key)
......@@ -304,7 +304,16 @@ class TangoWago:
key = self.modules_config.devname2key(name)
val = self.comm.command_inout("DevReadNoCachePhys", key)
return flatten(values)
values = flatten(values)
if not values:
return None
if len(values) == 1:
return values[0]
return values
def connect(self):
"""Added for compatibility"""
......@@ -390,7 +399,7 @@ class ModulesConfig:
if channels:
# if channels are specified, check it corresponds
# to the number of available channels
if module_info[N_CHANNELS] != len(channels):
if module_info.n_channels != len(channels):
if not ignore_missing:
raise RuntimeError(
"Missing mapped channels on module %d: %r"
......@@ -398,8 +407,14 @@ class ModulesConfig:
for _ in range(module_info[j]):
if module_info[N_CHANNELS] == 1:
if module_info.reading_type in ("ssi24", "ssi32", "637"):
# those modules need 2 words per value
total_channels = range(int(module_info[j] / 2))
total_channels = range(module_info[j])
for _ in total_channels:
if module_info.n_channels == 1:
......@@ -544,6 +559,8 @@ class ModulesConfig:
Returns: (logical_device_key, logical_device_channel)
channel_type, offset = array_in
if channel_type not in (0x4942, 0x4f42, 0x4f57, 0x4957):
raise RuntimeError("Wrong I/O type: (ex: ('I'<<8 + 'W') )")
if isinstance(channel_type, str):
# converto to integer if receiving types like 'TC' or 'IB'
channel_type = (ord(channel_type[0]) << 8) + ord(channel_type[1])
......@@ -557,6 +574,8 @@ class ModulesConfig:
if offset_ == offset and channel_base_address == channel_type:
return logical_device_key, logical_channel
raise RuntimeError("Invalid offset")
def devlog2hard(self, array_in):
device_key, logical_channel = array_in
......@@ -989,7 +1008,7 @@ class WagoController:
reading_info = read_table[READING_INFO]
if reading_type.startswith("fs"):
return self._read_fs(raw_value, **reading_info)
if reading_type == "ssi":
if reading_type in ("ssi24", "ssi32", "637"):
return self._read_ssi(raw_value, **reading_info)
if reading_type == "thc":
return self._read_thc(raw_value, **reading_info)
......@@ -1098,7 +1117,7 @@ class WagoController:
raw_values = ana_in_reading[i : i + n]
if not convert_values:
elif module_read_table[READING_TYPE] == "ssi":
elif module_read_table[READING_TYPE] in ("ssi24", "ssi32", "637"):
tuple(self._read_value(raw_values, module_read_table))
......@@ -1733,7 +1752,13 @@ class Wago(SamplingCounterController):
# instantiating comm and controller class
if config_tree.get("tango"):
comm = get_comm(config_tree)
# if tango url is provided do not consider modbustcp
new_config_tree = config_tree.copy()
del new_config_tree["modbustcp"]
except KeyError:
comm = get_comm(new_config_tree)
except Exception as exc:
log_exception(self, "Can't connect to tango host")
......@@ -56,7 +56,7 @@ class FlintWindow(qt.QMainWindow):
logWindow.setAttribute(qt.Qt.WA_QuitOnClose, False)
logWindow.setWindowTitle("Log messages")
self.__logWindow = logWindow
def __initMenus(self):
exitAction = qt.QAction("&Exit", self)
......@@ -21,6 +21,7 @@ from tango.server import Device, device_property, attribute, command
from bliss.comm.util import get_comm
from bliss.controllers.wago.wago import *
from bliss.common.utils import flatten
from bliss.config.static import get_config
# Device States Description
# ON : The motor powered on and is ready to move.
......@@ -55,8 +56,8 @@ access_conv_tab_inv = dict((v, k) for k, v in access_conv_tab.items())
class Wago(Device):
iphost = device_property(dtype=str, doc="ip address of Wago PLC")
protocol = device_property(dtype=str, default_value="tcp")
beacon_name = device_property(dtype=str, doc="Object name inside Beacon")
iphost = device_property(dtype=str, default_value="", doc="ip address of Wago PLC")
TCPTimeout = device_property(dtype=int, default_value=1000, doc="timeout in ms")
config = device_property(
dtype=tango.DevVarCharArray, default_value="", doc="I/O modules attached to PLC"
......@@ -72,6 +73,24 @@ class Wago(Device):
def init_device(self, *args, **kwargs):
super().init_device(*args, **kwargs)
# configuration can be given through Beacon if beacon_name is provided
# this will generate self.iphost and self.config
if self.beacon_name:
config = get_config()
yml_config = config.get_config(self.beacon_name)
if yml_config is None:
raise RuntimeError(
f"Could not find a Beacon object with name {self.beacon_name}"
self.iphost = yml_config["modbustcp"]["url"]
except KeyError:
raise RuntimeError(
"modbustcp url should be given in Beacon configuration"
self.config = ModulesConfig.from_config_tree(yml_config).mapping_str
self.TurnOn() # automatic turn on to mimic C++ Device Server
......@@ -253,34 +272,32 @@ class Wago(Device):
# determination of variable type
if type_ in ("thc", "fs"):
if type_ in ("thc", "fs10", "fs20", "fs4-20"):
# temperature and Analog requires Float
var_type = tango.DevDouble
elif type_ in ("ssi", "637"):
elif type_ in ("ssi24", "ssi32", "637"):
# encoder requires Long
var_type = tango.DevLong
elif type_ in ("digital"):
# digital requires boolean
var_type = tango.DevBoolean
raise NotImplementedError
module_info = MODULES_CONFIG[module_type]
# determination of read/write type
if type_ in ("thc",):
read_write = "r"
elif type_ in ("fs",):
elif type_ in ("thc", "fs10", "fs20", "fs4-20"):
if module_info[ANA_IN] == module_info[N_CHANNELS]:
read_write = "r"
elif module_info[ANA_OUT] == module_info[N_CHANNELS]:
read_write = "rw"
raise NotImplementedError
elif type_ in ("ssi", "637"):
elif type_ in ("ssi24", "ssi32", "637"):
read_write = "r"
if module_info[ANA_OUT] > 0:
read_write += "w"
raise NotImplementedError
elif type_ in ("digital"):
if module_info[DIGI_IN] == module_info[N_CHANNELS]:
read_write = "r"
......@@ -290,6 +307,8 @@ class Wago(Device):
raise NotImplementedError(
f"Digital I/O number of channels should be equal to total for {module_type}"
raise NotImplementedError
# define read and write methods
_read_channel = lambda: None
......@@ -410,7 +429,7 @@ class Wago(Device):
long *error - pointer to error code (in the case of failure)
return self.wago.logical_keys[name]
return self.wago.devname2key(name)
......@@ -434,7 +453,12 @@ class Wago(Device):
def DevReadNoCacheDigi(self, key):
return self.wago.devreadnocachedigi(key)
value = self.wago.get(self.wago.devkey2name(key), convert_values=False)
except TypeError:
value = [value]
return value
......@@ -446,7 +470,12 @@ class Wago(Device):
def DevReadNoCachePhys(self, key):
return self.wago.devreadnocachephys(key)
value = self.wago.get(self.wago.devkey2name(key))
except TypeError:
value = [value]
return value
......@@ -18,10 +18,59 @@ Unknown exception while trying to fill database cache...
Ready to accept request
## Different run possibilities ##
The Wago Device server can be run:
- with Beacon
- as a replacement of Taco/Tango C++ device servers (having working MySql and DatabaseDS instances)
## Configuration ##
### With Beacon ###
The configuration of this device server is written inside Beacon as
an yaml file, here we have the example contained by test_configuration.
an yaml file, here we have the example:
- tango_name: 1/1/mywago
class: Wago
beacon_name: mywago
personal_name: wago_mywago
server: Wago
- tango_name: is the Tango Fully Qualified Domain Name (FQDN) in the form *domain/family/member*
- personal_name: this will be the name you will use in the command line to launch the Device Server using
**Wago personal_name**
- beacon_name: should coresponds to another Beacon object defined in yml that will
define Wago mapping and host.
Refer to the documentation for creating the Wago bliss configuration
This is the most obvious solution where the device server configuration is at minimum.
Be aware that with this solution you will have to specify connection settings and
mapping `inside the Bliss wago client yml configuration.`
In particular be sure that you specify both connection types:
url: host:port
url: tango://domain/family/member
This will instruct the Device Server to connect to the proper modbustcp host, so when launching it using for example:
`Wago wago_mywago` it will connect to Wago through modbustcp.
Instead when instantiating a client in Bliss shell or script they will use `tango` connection.
The other solution is to provide full configuration inside this yml file
(give the example contained in default_session):
......@@ -52,3 +101,17 @@ server: Wago
- personal_name: this will be the name you will use in the command line to launch the Device Server using
**Wago personal_name**
### Bliss WagoDS as old C++ server replacement ###
If we want to use Bliss WagoDS as a replacement for an existing C++ server we just have to register the **Tango Server** through **Jive** or similar and than follow the example on first paragraph "Launching the device server".
After doing we will have to create/fill the properties:
- Iphost
- config
- Protocol (not necessary, default value is TCP and is always the same)
- TCPTimeout (default value is 1000ms)
And finally restarting the device server should do the job.
......@@ -34,6 +34,13 @@ The configuration is a matter of defining the following:
### Connection informations ###
We can connect to Wago in two ways:
- direct connection
- through Tango Device Server
The connection can be direct with the following configuration:
......@@ -60,7 +67,6 @@ tango:
Normally host:port can also be omitted if we define the global variable `TANGO_HOST`.
Note: If the connection is through Tango and the property `config` is set we can omit the mapping.
### Mapping PLC input/output plugged modules ###
......@@ -32,9 +32,11 @@ For development, i.e in `bliss_env` Conda environement:
`conda install --file ./requirements-conda.txt`
* Pip-install BLISS making a link from conda environment directory pointing to
git repository:
* Exit and re-enter into the conda environment to ensure using up-to-date modules.
* Pip-install BLISS by creating a link in conda environment directory pointing to
the git repository:
`pip install --no-deps -e .`
!!! note
......@@ -60,8 +62,6 @@ For more details, see: https://bliss.gitlab-pages.esrf.fr/ansible/local_code.htm
## Installation outside ESRF beamlines
### Using a Conda environment