Source code for hvl_ccb.dev.cube.switches

#  Copyright (c) ETH Zurich, SIS ID and HVL D-ITET
#
"""
Switches of the different "Cubes".
"""
import logging
from abc import ABC
from typing import TYPE_CHECKING, Optional

from aenum import IntEnum

from hvl_ccb.utils.enum import BoolEnum

from .constants import SafetyStatus
from .errors import SwitchOperationError

if TYPE_CHECKING:
    from . import BaseCube  # pragma: no cover

logger = logging.getLogger(__name__)


[docs] class SwitchStatus(IntEnum): """ Status of a switch. These are the possible values in the status integer e.g. in :attr:`_Switch.status`. """ # Switch is deselected and not enabled in safety circuit. To get out of # this state, the earthing has to be enabled in the BaseCube HMI setup. INACTIVE = 0 # Earthing is closed (safe). CLOSED = 1 # Earthing is open (not safe). OPEN = 2 # Earthing is in error, e.g. when the stick did not close correctly or could not # open. ERROR = 3
[docs] class SwitchOperatingStatus(IntEnum): """ Operating Status of a switch. Switch can be in auto or manual mode. """ AUTO = 0 MANUAL = 1
[docs] class SwitchOperation(BoolEnum): """ Operation of a switch in manual operating mode. Can be closed or opened. """ OPEN = False CLOSE = True
class _Switch(ABC): """ Switch with status, operating status (manual and auto) and manual operate. """ _CLS_ERROR = SwitchOperationError _SWITCHABLE_AT_STATES: Optional[tuple[SafetyStatus]] = None def __init__(self, handle, device_name: str): self._handle: BaseCube = handle self._device_name: str = device_name self._CMD_STATUS: Optional[str] = None self._CMD_OPERATING_STATUS: Optional[str] = None self._CMD_MANUAL: Optional[str] = None @property def status(self) -> SwitchStatus: """ Position status of a switch. :return: Status of the switch. """ if self._CMD_STATUS is None: msg = ( f"Tried to query position status of {self._device_name}, " f"but {self._device_name} does not have a position status feedback." ) logger.error(msg) raise self._CLS_ERROR(msg) value = SwitchStatus(self._handle.read(self._CMD_STATUS)) logger.info(f"Status of {self._device_name} is {value.name}") return value @property def operating_status(self) -> SwitchOperatingStatus: """ Switch operating status, if 'manual' the stick can be controlled by the user. :return: Switch operating status, can be either auto or manual """ if self._CMD_OPERATING_STATUS is None: msg = ( f"Tried to query operating status of {self._device_name}, " f"but {self._device_name} does not have an operating status." ) logger.error(msg) raise self._CLS_ERROR(msg) value = SwitchOperatingStatus(self._handle.read(self._CMD_OPERATING_STATUS)) logger.info(f"Operating Status of {self._device_name} is {value.name}") return value def operation_conditions_fulfilled(self) -> None: """ Method to be called before an operation is performed. It will raise an exception if not """ if ( self._SWITCHABLE_AT_STATES and self._handle.status not in self._SWITCHABLE_AT_STATES ): switchable_states = " or ".join( [f'"{_}"' for _ in self._SWITCHABLE_AT_STATES] ) msg = ( f"Cube needs to be in state {switchable_states} " f"to operate {self._device_name} manually, " f'but is in "{self._handle.status.name}".' ) logger.error(msg) raise self._CLS_ERROR(msg) if not self.operating_status == SwitchOperatingStatus.MANUAL: msg = ( f"Operation of the {self._device_name} is not possible, " "as the feature is not activated in the Cube Setup." ) logger.error(msg) raise self._CLS_ERROR(msg) @property def operate(self) -> SwitchOperation: """ Operation of a switch which has a manual operating status. :return: Switch operation setting, can be open or close """ if self._CMD_MANUAL is None: msg = ( f"Tried to query manual operation setting of {self._device_name}, " f"but {self._device_name} cannot be operated manually." ) logger.error(msg) raise self._CLS_ERROR(msg) value = SwitchOperation(self._handle.read(self._CMD_MANUAL)) logger.info(f"Manual operation status of {self._device_name} is {value}") return value @operate.setter def operate(self, operation: SwitchOperation) -> None: """ Operation of a switch which has a manual operating status. :param operation: switch operation setting (close or open) :raises SwitchOperationError: when the operation conditions of the switch are not fulfilled, or if the switch is not allowed to be manually operated """ self.operation_conditions_fulfilled() if self._CMD_MANUAL is None: msg = ( # pragma: no cover f"Tried to operate {self._device_name}, " f"but {self._device_name} cannot" "be operated manually." ) logger.error(msg) # pragma: no cover raise self._CLS_ERROR(msg) # pragma: no cover operation = SwitchOperation(operation) self._handle.write(self._CMD_MANUAL, operation) logger.info(f"{self._device_name} is set to {operation}")