Commit 66a8fc23 authored by Sebastien Petitdemange's avatar Sebastien Petitdemange
Browse files

shutter: base class for shutter control

parent 5d9627f0
# -*- coding: utf-8 -*-
# This file is part of the bliss project
# Copyright (c) 2017 Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.
import functools
from gevent import lock
from bliss.config.conductor.client import Lock
from bliss.config.channels import Cache
from bliss.config.settings import HashObjSetting
class Shutter(object):
MODE2STR = {MANUAL: ("MANUAL", "Manual mode"),
EXTERNAL: ("EXTERNAL", "External trigger mode"),
CONFIGURATION: ("CONFIGURATION", "Configuration mode"),
OPEN,CLOSED,UNKNOWN = range(3) # state
STATE2STR = { OPEN: ("OPEN", "Shutter is open"),
CLOSED: ("CLOSED", "Shutter is closed"),
UNKNOWN: ("UNKNOWN", "Unknown shutter state"),
Generic shutter object
This interface should be used for all type of shutter (motor,fast...)
You may want to link this shutter with an external
control i.e: wago,musst.... in that case you have to put
in configuration **external-control** with the object reference.
This external control should be compatible with the Switch object
and have an OPEN/CLOSED states.
def lazy_init(func):
def func_wrapper(self,*args,**kwargs):
with Lock(self):
return func(self,*args,**kwargs)
return func_wrapper
def __init__(self,name,config):
self.__name = name
self.__config = config
self._external_ctrl = config.get('external-control')
self.__settings = HashObjSetting('shutter:%s' % name)
self.__initialized_hw = Cache(self,"initialized",
default_value = False)
self.__state = Cache(self,"state",
default_value = Shutter.UNKNOWN)
self._init_flag = False
self.__lock = lock.Semaphore()
def init(self):
initialize the shutter in the current mode.
this is method is called by lazy_init
if self._external_ctrl is not None:
# Check if the external control is compatible
# with a switch object and if it has open/close state
ext_ctrl = self._external_ctrl
name = if hasattr(ext_ctrl,'name') else "unknown"
states = ext_ctrl.states_list()
except AttributeError:
raise ValueError('external-ctrl : {0} is not compatible '
'with a switch object'.format(name))
if(not 'OPEN' in states or
not 'CLOSED' in states):
raise ValueError("external-ctrl : {0} doesn't"
" have 'OPEN' and 'CLOSED' states".format(name))
if not self._init_flag:
self._init_flag = True
with Lock(self):
with self.__lock:
if not self.__initialized_hw.value:
self.__initialized_hw.value = True
self._init_flag = False
def _init(self):
This method should contains all software initialization
like communication, internal state...
raise NotImplementedError
def _initialize_hardware(self):
This method should contains all commands needed to
initialize the hardware.
It's will be call only once (by the first client).
def name(self):
return self.__name
def config(self):
return self.__config
def settings(self):
return self.__settings
def mode(self):
In CONFIGURATION mode, shutter can't be open/close.
**CONFIGURATION** could mean that the shutter is in tuning mode
i.e: changing open/close position in case of a motor.
In EXTERNAL mode, the shutter will be controlled
through the external-control handler.
If no external control is configured open/close
wont be authorized.
return self.__settings.get('mode',Shutter.MANUAL)
def mode(self,value):
if value not in self.MODE2STR:
raise ValueError("Mode can only be: %s" %\
','.join((x[0] for x in self.MODE2STR.values())))
if value in (self.CONFIGURATION,self.EXTERNAL):
# Can't cache the state if external or configuration
self.__state.value = self.UNKNOWN
self.__settings['mode'] = value
def state(self):
mode = self.mode
if mode == self.MANUAL and self.__state.value == self.UNKNOWN:
return_state = self._state()
self.__state.value = return_state
return return_state
if mode == self.EXTERNAL:
if self.external_control is not None:
switch_state = self.external_control.get()
return self.OPEN if switch_state == "OPEN" else self.CLOSED
return self.UNKNOWN
elif mode == self.CONFIGURATION:
return self.UNKNOWN
return self.__state.value
def _state(self):
raise NotImplementedError
def state_string(self):
state = self.state()
return self.STATE2STR.get(state,self.STATE2STR[self.UNKNOWN])
def external_control(self):
return self._external_ctrl
def opening_time(self):
Return the opening time if available or None
return self._opening_time()
def _opening_time(self):
return self.__settings.get('opening_time')
def closing_time(self):
Return the closing time if available or None
return self._closing_time()
def _closing_time(self):
return self.__settings.get('closing_time')
def measure_open_close_time(self):
This small procedure will in basic usage do an open and close
of the shutter to measure the opening and closing time.
Those timing will be register into the settings.
returns (opening,closing) time
previous_mode = self.mode()
if previous_mode != self.MANUAL:
opening_time,closing_time = self._measure_open_close_time()
self.__settings['opening_time'] = opening_time
self.__settings['closing_time'] = closing_time
return open_time,close_time
if previous_mode != self.MANUAL:
def _measure_open_close_time(self):
This method can be overloaded if needed.
Basic timing on
self.close() # ensure it's closed
start_time = time.time()
opening_time = time.time() - start_time
start_time = time.time()
closing_time = time.time() - start_time
return opening_time,closing_time
def open(self):
mode = self.mode
if mode == self.EXTERNAL:
if self._external_ctrl is None:
raise RuntimeError("Can't open the shutter because no "
"external-control is configured")
return self._external_ctrl.set("OPEN")
elif mode != self.MANUAL:
raise RuntimeError("Can't open the shutter, in %s" %\
return self._open()
def _open(self):
raise NotImplementedError
def close(self):
mode = self.mode
if mode == self.EXTERNAL:
if self._external_ctrl is None:
raise RuntimeError("Can't close the shutter because no "
"external-control is configured")
return self._external_ctrl.set("CLOSED")
elif mode != self.MANUAL:
raise RuntimeError("Can't close the shutter, in %s" %\
return self._close()
def _close(self):
raise NotImplementedError
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