...
 
Commits (21)
Bliss
======
[![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:
RuntimeWarning,
)
names_list.remove(name)
class_name = object_config.get("class", "")
if class_name.lower() == "session":
warnings.warn(
f"Session {self.name} 'config-objects' list contains session {name}, ignoring (hint: add session in 'include-sessions' list)",
RuntimeWarning,
)
names_list.remove(name)
else:
class_name = object_config.get("class", "")
if class_name.lower() == "session":
warnings.warn(
f"Session {self.name} 'config-objects' list contains session {name}, ignoring (hint: add session in 'include-sessions' list)",
RuntimeWarning,
)
names_list.remove(name)
for name in self.__exclude_objects_names:
try:
......
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"])
@property
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
Prints:
......
......@@ -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)
values.append(val)
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 j in (DIGI_IN, DIGI_OUT, ANA_IN, ANA_OUT):
channels_map.append([])
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))
else:
total_channels = range(module_info[j])
for _ in total_channels:
if module_info.n_channels == 1:
channels_map[-1].append(channels[0])
else:
try:
......@@ -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:
readings.append(raw_values)
elif module_read_table[READING_TYPE] == "ssi":
elif module_read_table[READING_TYPE] in ("ssi24", "ssi32", "637"):
readings.append(
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"):
try:
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:
pass
try:
comm = get_comm(new_config_tree)
except Exception as exc:
log_exception(self, "Can't connect to tango host")
raise
......
......@@ -56,7 +56,7 @@ class FlintWindow(qt.QMainWindow):
logWindow.setAttribute(qt.Qt.WA_QuitOnClose, False)
logWindow.setWindowTitle("Log messages")
self.__logWindow = logWindow
logWidget.connect_logger(_logger)
logWidget.connect_logger(logging.root)
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)
self.set_state(DevState.STANDBY)
# 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}"
)
try:
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
@command
......@@ -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
else:
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"
else:
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"
else:
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}"
)
else:
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)
@command(
dtype_in=tango.DevShort,
......@@ -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)
try:
len(value)
except TypeError:
value = [value]
return value
@command(
dtype_in=tango.DevShort,
......@@ -446,7 +470,12 @@ class Wago(Device):
def DevReadNoCachePhys(self, key):
"""
"""
return self.wago.devreadnocachephys(key)
value = self.wago.get(self.wago.devkey2name(key))
try:
len(value)
except TypeError:
value = [value]
return value
@command(
dtype_in=tango.DevShort,
......
......@@ -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:
```yaml
device:
- tango_name: 1/1/mywago
class: Wago
properties:
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
[Wago](config_wago.md)
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:
```yml
modbustcp:
url: host:port
tango:
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):
```yaml
device:
......@@ -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:
```yaml
......@@ -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
......