Commit 6fdbf411 authored by Matias Guijarro's avatar Matias Guijarro
Browse files

Merge branch '2750-fe-shutter-to-display-error-message-on-mode-setting-error' into 'master'

Resolve "FE shutter to display error message on mode setting error"

Closes #2750 and #2607

See merge request bliss/bliss!3692
parents 6f96ca9b e0fcfc80
Pipeline #47085 failed with stages
in 106 minutes and 30 seconds
......@@ -6,25 +6,17 @@
# Distributed under the GNU LGPLv3. See LICENSE for more info.
"""
Tango shutter is used to control frontend or safety shutter or a valve.
Some commands/attributes (like automatic/manual) are only implemented in the
front end device server, set by the _frontend variable.
Tango shutter is used to control Tango safety shutter or valve.
Example yml file:
.. code-block::
-
# front end shutter
class: TangoShutter
name: frontend
uri: //orion:10000/fe/master/id30
-
# safety shutter
class: TangoShutter
name: safshut
uri: id30/bsh/1
uri: id42/bsh/1
"""
from enum import Enum
......@@ -51,39 +43,151 @@ TangoShutterState = Enum(
)
""" FRONTEND
The state of a Tango FrontEnd server can be (according to JLP):
* Tango::OPEN: FrontEnd is open with the automtic mode disabled
* Tango::RUNNING: FrontEnd is open with the automtic mode enabled
=> OPEN
* Tango::CLOSE: FrontEnd is close with the injection mode disabled
* Tango::STANDBY: FrontEnd is close with the injection mode enabled
=> CLOSED
* Tango::FAULT: FrontEnd in fault
* Tango::DISABLE: No operation permission
=> ???
"""
""" SAFETY SHUTTER
Overloading of `is_open` and `is_closed` properties to deal with Tango
states.
The state of a Safety Shutter Tango server can be:
* Tango::OPEN
=> OPEN
* Tango::CLOSE (not CLOSED)
* Tango::DISABLE
=> CLOSED
* ON OFF INSERT EXTRACT MOVING STANDBY FAULT INIT RUNNING ALARM UNKNOWN
=> ???
"""
""" Valve
Overloading of `is_open` and `is_closed` properties to deal with Tango
Vacuum Valves
The state of a Valve Tango server can be:
* Tango::OPEN
=> OPEN
* Tango::CLOSE (not CLOSED)
* Tango::DISABLE
=> CLOSED
* ON OFF INSERT EXTRACT MOVING STANDBY FAULT INIT RUNNING ALARM UNKNOWN
=> ???
"""
TANGO_OPEN_STATES = {
"SafetyShutter": ["OPEN", "RUNNING"],
"FrontEnd": ["OPEN", "RUNNING"],
"Valve": ["OPEN", "RUNNING"],
"Default": ["OPEN", "RUNNING"],
}
TANGO_CLOSED_STATES = {
"SafetyShutter": ["CLOSE", "STANDBY", "FAULT"],
"FrontEnd": ["CLOSE", "STANDBY"],
"Valve": ["CLOSE", "STANDBY", "FAULT", "DISABLE"],
"Default": ["CLOSE", "STANDBY"],
}
class TangoShutter(BaseShutter):
""" Handle Tango frontend or safety shutter or a valve"""
def __init__(self, name, config):
tango_uri = config.get("uri")
def __init__(self, name, config, shutter_type=None):
self._tango_uri = config.get("uri")
if shutter_type is None:
self.__shutter_type = config.get("shutter_type", "Default")
else:
self.__shutter_type = shutter_type
self.__name = name
self.__config = config
self.__control = DeviceProxy(tango_uri)
self.__control = DeviceProxy(self._tango_uri)
global_map.register(self, children_list=[self.__control], tag=f"Shutter:{name}")
self._frontend = None
self._mode = None
self._init_type()
self._state_channel = Channel(
f"{name}:state", default_value="UNKNOWN", callback=self._state_changed
)
def _init_type(self):
self._frontend = "FrontEnd" in self.__control.info().dev_class
@property
def shutter_type(self):
"""
Set / Get shutter type in
Parameters:
stype (str): shutter type in "FrontEnd" "SafetyShutter" "Valve"
"""
return self.__shutter_type
@shutter_type.setter
def shutter_type(self, stype):
self.__shutter_type = stype
"""
Overloading of `is_open` and `is_closed` properties to deal with Tango
states.
"""
@property
def proxy(self):
return self.__control
def is_open(self):
"""Check if the Tango Shutter is open"""
_state = self._tango_state
_open = _state in TANGO_OPEN_STATES[self.shutter_type]
_closed = _state in TANGO_CLOSED_STATES[self.shutter_type]
if _open and _closed:
user_print(
f"WARNING: {self.shutter_type} state coherency problem: state is : {_state} (please report this error)"
)
if not _open and not _closed:
user_print(
f"WARNING: {self.shutter_type} state coherency problem: state is : {_state} (please report this error)"
)
return _open
@property
def frontend(self):
""" Check if the device is a front end type
Returns:
(bool): True if it is a front end, False otherwise
"""
if self._frontend is None:
self._init_type()
return self._frontend
def is_closed(self):
"""Check if the Tango Shutter is closed"""
_state = self._tango_state
_open = _state in TANGO_OPEN_STATES[self.shutter_type]
_closed = _state in TANGO_CLOSED_STATES[self.shutter_type]
if _open and _closed:
user_print(
f"WARNING: {self.shutter_type} state coherency problem: state is : {_state} (please report this error)"
)
if not _open and not _closed:
user_print(
f"WARNING: {self.shutter_type} state coherency problem: state is : {_state} (please report this error)"
)
return _closed
@property
def proxy(self):
return self.__control
@property
def name(self):
......@@ -97,11 +201,11 @@ class TangoShutter(BaseShutter):
@property
def _tango_state(self):
""" Read the tango state. Available PyTango states: 'ALARM', 'CLOSE',
""" Read the tango state. PyTango states: 'ALARM', 'CLOSE',
'DISABLE', 'EXTRACT', 'FAULT', 'INIT', 'INSERT', 'MOVING', 'OFF',
'ON', 'OPEN', 'RUNNING', 'STANDBY', 'UNKNOWN'.
Returns:
(str): The state from the device server.
(str): The state read from the device server.
"""
return self.__control.state().name
......@@ -120,6 +224,9 @@ class TangoShutter(BaseShutter):
(enum): state as enum
Raises:
RuntimeError: If DevFailed from the device server
Notes:
Use this state with care: values do not match with Tango States.
"""
try:
state = self._tango_state
......@@ -143,7 +250,12 @@ class TangoShutter(BaseShutter):
return self.state.value, self._tango_status
def __info__(self):
return self._tango_status.rstrip("\n")
if self.is_closed:
info_str = f"{self.shutter_type} `{self.name}` is closed\n"
else:
info_str = f"{self.shutter_type} `{self.name}` is open\n"
info_str += self._tango_status.rstrip("\n")
return info_str
def open(self, timeout=60):
"""Open
......@@ -196,11 +308,18 @@ class TangoShutter(BaseShutter):
@property
def mode(self):
""" Get the opening mode. (only for FrontEnd).
"""
Get or set the opening mode of the FrontEnd.
state is read from tango attribute: `automatic_mode`.
Only available for FrontEnd shutters.
Parameters:
mode: (str): 'MANUAL' or 'AUTOMATIC'
Raises:
NotImplementedError: Not a Frontend shutter
"""
if not self.frontend:
if self.shutter_type != "FrontEnd":
raise NotImplementedError("Not a Frontend shutter")
try:
......@@ -212,12 +331,7 @@ class TangoShutter(BaseShutter):
@mode.setter
def mode(self, mode):
"""Set the opening mode (only for FrontEnd).
Args:
mode (str): MANUAL or AUTOMATIC
Raises: NotImplementedError: Not a Fronend shutter.
"""
if not self.frontend:
if self.shutter_type != "FrontEnd":
raise NotImplementedError("Not a Frontend shutter")
try:
......@@ -225,9 +339,12 @@ class TangoShutter(BaseShutter):
self.__control.manual()
elif mode == "AUTOMATIC":
self.__control.automatic()
else:
raise RuntimeError(f"Unknown mode: {mode}")
self._wait_mode(mode=mode)
except DevFailed:
raise RuntimeError(f"Cannot set {mode} opening")
except DevFailed as df_err:
raise RuntimeError(f"Cannot set {mode} opening") from df_err
def reset(self):
"""Reset
......@@ -251,9 +368,25 @@ class TangoShutter(BaseShutter):
self._state_changed(self.state)
def _wait_mode(self, mode, timeout=3):
with Timeout(timeout, RuntimeError(f"Cannot set {mode} opening")):
"""
Wait until set mode is equal to read mode.
"""
with Timeout(timeout, RuntimeError(f"Cannot set {mode} opening mode")):
# FE tango server feature: 'automatic_mode' goes True even
# if it's not allowed (ex: MDT)
# It switches back to False after ~1 second.
# So this method can return without error even if AUTOMATIC
# mode is not set properly.
sleep(2) # to be removed when FE tango server will be fixed.
while self.mode != mode:
sleep(1)
# print(f"{self.mode} != {mode}") # to be removed when FE tango server will be fixed.
sleep(0.2)
# for i in range(100): # to be removed when FE tango server will be fixed.
# print(f"{self.mode} =? {mode}") # to be removed when FE tango server will be fixed.
# sleep(0.05) # to be removed when FE tango server will be fixed.
def __enter__(self):
self.open()
......
......@@ -178,12 +178,6 @@ Some commands/attributes (like `automatic`/`manual`) are only implemented in the
front end device server, set by the `_frontend` variable.
### FrontEnd mode
If a `TangoShutter` is a FrontEnd, a special attribute `mode` is usable.
It can be : `MANUAL` `AUTOMATIC` or `UNKNOWN`
### Usage examples
Example with a safety shutter:
......@@ -215,7 +209,50 @@ DEMO [12]: rv9.open()
WARNING 2020-03-19 00:13:23,937 global.controllers.rv9: rv9 already open, command ignored
```
Example with a FrontEnd shutter:
### Configuration examples
Safety shutter and FrontEnd:
```yaml
- name: safshut
class: TangoShutter
shutter_type: SafetyShutter
uri: id42/bsh/1
- name: frontend
class: TangoShutter
shutter_type: FrontEnd
uri: acs.esrf.fr:10000/fe/master/id42
```
Remote valves:
```yaml
- name: rv0
class: TangoShutter
shutter_type: Valve
uri: id42/v-rv/0
- name: rv1
class: TangoShutter
shutter_type: Valve
uri: id42/v-rv/1
- name: rv2
class: TangoShutter
shutter_type: Valve
uri: id42/v-rv/2
```
### FrontEnd mode
If a `TangoShutter` is a FrontEnd, a special attribute `mode` is usable to
activate or deactivate the automatic openning mode.
It can be : `MANUAL` `AUTOMATIC` or `UNKNOWN`
Example:
```python
DEMO [3]: fe
Out [3]: State : Fault on Front End
......@@ -234,44 +271,12 @@ DEMO [3]: fe
To change the opening mode of a `FrontEnd` shutter:
```python
DEMO [3]: fe.mode = fe.MANUAL
DEMO [3]: fe.mode = "MANUAL"
fe mode was AUTOMATIC and is now MANUAL
```
Example (during a shutdown):
```python
DEMO [7]: fe.mode = fe.AUTOMATIC
DEMO [7]: fe.mode = "AUTOMATIC"
!!! === RuntimeError: Cannot set AUTOMATIC opening === !!!
```
### Configuration examples
```yaml
-
name: safshut
class: TangoShutter
uri: id42/bsh/1
-
# front end shutter
class: TangoShutter
name: frontend
uri: acs.esrf.fr:10000/fe/master/id42
```
```yaml
-
name: rv0
class: TangoShutter
uri: id42/v-rv/0
-
name: rv1
class: TangoShutter
uri: id42/v-rv/1
-
name: rv2
class: TangoShutter
uri: id42/v-rv/2
```
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment