Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 52 additions & 16 deletions mxcubecore/HardwareObjects/BlissShutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,15 @@
actuator_name: safshut
type: tango
username: Safety shutter
objects:
controller: bliss.yaml
"""

import logging
from enum import (
Enum,
unique,
)

import gevent

from mxcubecore import HardwareRepository as HWR
from mxcubecore.BaseHardwareObjects import HardwareObjectState
from mxcubecore.HardwareObjects.abstract.AbstractShutter import AbstractShutter

Expand All @@ -66,6 +64,9 @@ class BlissShutter(AbstractShutter):
"""BLISS implementation of AbstractShutter"""

SPECIFIC_STATES = BlissShutterStates
# Only OPEN and CLOSED are user-commandable; the other values in VALUES
# (MOVING, DISABLE, STANDBY, FAULT) are read-only status indicators.
COMMANDABLE_VALUES = ("OPEN", "CLOSED")

def __init__(self, name):
super().__init__(name)
Expand All @@ -75,9 +76,20 @@ def __init__(self, name):

def init(self):
"""Initialise the predefined values"""
self.controller = self.get_object_by_role("controller")
super().init()
self._bliss_obj = getattr(self.controller, self.actuator_name)
try:
bliss_proxy = HWR.beamline.bliss_proxy
bliss_proxy.hardware.register(self.actuator_name)
self._bliss_obj = bliss_proxy.get_object(self.actuator_name)
except Exception as exc:
logging.getLogger("MX3.HWR").warning(
"BlissShutter '%s': BLISS object '%s' not available (%s)",
self.actuator_name,
self.actuator_name,
exc,
)
self._bliss_obj = None
return
# for now we only treat tango type shutter
self.shutter_type = self.get_property("type", "tango")
try:
Expand All @@ -88,14 +100,29 @@ def init(self):
pass
if self.shutter_type == "tango":
self._initialise_values()
self._poll_task = gevent.spawn(self._poll_state)

self.update_state()
self._bliss_obj.subscribe("property", self._on_property_changed)
self._bliss_obj.subscribe("online", self._on_online_changed)

self.update_state(self.get_state())
self.update_value(self.get_value())

def _on_online_changed(self, online: bool) -> None:
"""Callback for shutter online/offline events received via blissclient."""
if not online:
self.update_state(HardwareObjectState.UNKNOWN)
else:
self._update_state()

def _poll_state(self):
while True:
self.update_value(self.get_value())
gevent.sleep(0.5)
def _on_property_changed(self, data: dict) -> None:
"""Callback for property changes received via blissclient."""
if "state" in data:
self._update_state()

def _update_state(self):
"""Refresh state and value from the shutter object."""
self.update_value(self.get_value())
self.update_state(self.get_state())

def _initialise_values(self):
"""Add the tango states to VALUES"""
Expand All @@ -116,21 +143,30 @@ def get_state(self):
(enum 'HardwareObjectState'): Device state.
"""
try:
_state = self._bliss_obj.state.name
_state = self._bliss_obj.state or "UNKNOWN"
return self.SPECIFIC_STATES[_state].value[0]
except (AttributeError, KeyError):
return self.STATES.UNKNOWN
return self.SPECIFIC_STATES[_state].value[0]
except Exception:
return self.STATES.UNKNOWN

def get_value(self):
"""Get the device value
Returns:
(Enum): Enum member, corresponding to the value or UNKNOWN.
"""
# the return from BLISS value is an Enum
_val = self._bliss_obj.state.name
try:
_val = self._bliss_obj.state or "UNKNOWN"
except Exception:
_val = "UNKNOWN"
return self.value_to_enum(_val)

def _set_value(self, value):
if self._bliss_obj is None:
raise RuntimeError(
f"BlissShutter '{self.actuator_name}' is offline — "
"BLISS object not available"
)
if value.name == "OPEN":
self._bliss_obj.open()
elif value.name == "CLOSED":
Expand Down
Loading