diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..f3223383 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +ea93cd189c476ebbc52350088130655249441ae1 +82d39f767abb8a964f2f5bd6edb7d754e37d7672 diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml new file mode 100644 index 00000000..82ad245b --- /dev/null +++ b/.github/workflows/linters.yml @@ -0,0 +1,7 @@ +name: Linter +on: [pull_request] +jobs: + call-workflow: + uses: ISISComputingGroup/reusable-workflows/.github/workflows/linters.yml@main + with: + compare-branch: origin/master diff --git a/ReflectometryServer/ChannelAccess/constants.py b/ReflectometryServer/ChannelAccess/constants.py index 987d581a..e0916415 100644 --- a/ReflectometryServer/ChannelAccess/constants.py +++ b/ReflectometryServer/ChannelAccess/constants.py @@ -1,6 +1,7 @@ """ constants for the reflectometry server. """ + import os @@ -9,7 +10,7 @@ def _get_env_var(name): # Prefix for PVs on this instrument -MYPVPREFIX = _get_env_var('MYPVPREFIX') +MYPVPREFIX = _get_env_var("MYPVPREFIX") REFL_IOC_NAME = "REFL_01" @@ -17,15 +18,19 @@ def _get_env_var(name): REFLECTOMETRY_PREFIX = "{}{}:".format(MYPVPREFIX, REFL_IOC_NAME) # Reflectometry configuration file path -REFL_CONFIG_PATH = os.path.abspath(os.path.join("{}".format(_get_env_var('ICPCONFIGROOT')), "refl")) +REFL_CONFIG_PATH = os.path.abspath(os.path.join("{}".format(_get_env_var("ICPCONFIGROOT")), "refl")) # Reflectometry configuration file path -REFL_AUTOSAVE_PATH = os.path.join("{}".format(_get_env_var('ICPVARDIR')), "refl") +REFL_AUTOSAVE_PATH = os.path.join("{}".format(_get_env_var("ICPVARDIR")), "refl") # Path to security access rules file -DEFAULT_ASG_RULES = os.path.join("{}".format( - os.environ.get('KIT_ROOT', os.path.join(r'C:\Instrument', 'Apps', 'EPICS'))), - "support", "AccessSecurity", "master", "default.acf") +DEFAULT_ASG_RULES = os.path.join( + "{}".format(os.environ.get("KIT_ROOT", os.path.join(r"C:\Instrument", "Apps", "EPICS"))), + "support", + "AccessSecurity", + "master", + "default.acf", +) # alias motor DMOV values MTR_MOVING = 0 @@ -38,4 +43,4 @@ def _get_env_var(name): MAX_ALARM_ID = 15 # standard field for float pvs -STANDARD_FLOAT_PV_FIELDS = {'type': 'float', 'prec': 3, 'value': 0.0} +STANDARD_FLOAT_PV_FIELDS = {"type": "float", "prec": 3, "value": 0.0} diff --git a/ReflectometryServer/ChannelAccess/driver_utils.py b/ReflectometryServer/ChannelAccess/driver_utils.py index 7d1f2d62..b0170092 100644 --- a/ReflectometryServer/ChannelAccess/driver_utils.py +++ b/ReflectometryServer/ChannelAccess/driver_utils.py @@ -1,13 +1,13 @@ from enum import Enum -from typing import Tuple, Union, Dict +from typing import Dict, Tuple, Union -from ReflectometryServer import Beamline -from ReflectometryServer.ChannelAccess.constants import STANDARD_FLOAT_PV_FIELDS, MAX_ALARM_ID +from server_common.channel_access import AlarmSeverity, AlarmStatus +from server_common.utilities import SEVERITY, print_and_log +from ReflectometryServer import Beamline +from ReflectometryServer.ChannelAccess.constants import MAX_ALARM_ID, STANDARD_FLOAT_PV_FIELDS from ReflectometryServer.parameters import BeamlineParameterType, ParameterUpdateBase from ReflectometryServer.server_status_manager import STATUS_MANAGER -from server_common.utilities import print_and_log, SEVERITY -from server_common.channel_access import AlarmSeverity, AlarmStatus # field for in beam parameter OUT_IN_ENUM_TEXT = ["OUT", "IN"] @@ -15,9 +15,10 @@ # Field for the various type of beamline parameter PARAMS_FIELDS_BEAMLINE_TYPES = { - BeamlineParameterType.IN_OUT: {'type': 'enum', 'enums': OUT_IN_ENUM_TEXT}, + BeamlineParameterType.IN_OUT: {"type": "enum", "enums": OUT_IN_ENUM_TEXT}, BeamlineParameterType.FLOAT: STANDARD_FLOAT_PV_FIELDS, - BeamlineParameterType.ENUM: {'type': 'enum', 'enums': []}} + BeamlineParameterType.ENUM: {"type": "enum", "enums": []}, +} def convert_from_epics_pv_value(parameter_type, value, pv_fields): @@ -39,8 +40,13 @@ def convert_from_epics_pv_value(parameter_type, value, pv_fields): return value -def _convert_to_epics_pv_value(parameter_type: BeamlineParameterType, value: Union[float, int, str, bool], - alarm_severity: AlarmSeverity, alarm_status: AlarmStatus, pv_fields: Dict): +def _convert_to_epics_pv_value( + parameter_type: BeamlineParameterType, + value: Union[float, int, str, bool], + alarm_severity: AlarmSeverity, + alarm_status: AlarmStatus, + pv_fields: Dict, +): """ Convert from parameter value to the epic pv value Args: @@ -70,7 +76,9 @@ def _convert_to_epics_pv_value(parameter_type: BeamlineParameterType, value: Uni pv_value = -1 status = AlarmStatus.State severity = AlarmSeverity.Invalid - STATUS_MANAGER.update_error_log("Value set of parameter which is not in pv options {}".format(value)) + STATUS_MANAGER.update_error_log( + "Value set of parameter which is not in pv options {}".format(value) + ) else: if value is None: pv_value = float("NaN") @@ -83,6 +91,7 @@ class PvSort(Enum): """ Enum for the type of PV """ + RBV = 0 ACTION = 1 SP_RBV = 2 @@ -138,7 +147,9 @@ def what(pv_sort): elif pv_sort == PvSort.READ_ONLY: return "(Is not settable by user)" else: - print_and_log("Unknown pv sort!! {}".format(pv_sort), severity=SEVERITY.MAJOR, src="REFL") + print_and_log( + "Unknown pv sort!! {}".format(pv_sort), severity=SEVERITY.MAJOR, src="REFL" + ) return "(unknown)" def get_from_parameter(self, parameter, pv_fields): @@ -153,18 +164,33 @@ def get_from_parameter(self, parameter, pv_fields): severity = AlarmSeverity.No status = AlarmStatus.No if self == PvSort.SP: - value, severity, status = _convert_to_epics_pv_value(parameter.parameter_type, parameter.sp, - AlarmSeverity.No, AlarmStatus.No, pv_fields) + value, severity, status = _convert_to_epics_pv_value( + parameter.parameter_type, parameter.sp, AlarmSeverity.No, AlarmStatus.No, pv_fields + ) elif self == PvSort.SP_RBV: - value, severity, status = _convert_to_epics_pv_value(parameter.parameter_type, parameter.sp_rbv, - AlarmSeverity.No, AlarmStatus.No, pv_fields) + value, severity, status = _convert_to_epics_pv_value( + parameter.parameter_type, + parameter.sp_rbv, + AlarmSeverity.No, + AlarmStatus.No, + pv_fields, + ) elif self == PvSort.RBV: - value, severity, status = _convert_to_epics_pv_value(parameter.parameter_type, parameter.rbv, - parameter.alarm_severity, parameter.alarm_status, - pv_fields) + value, severity, status = _convert_to_epics_pv_value( + parameter.parameter_type, + parameter.rbv, + parameter.alarm_severity, + parameter.alarm_status, + pv_fields, + ) elif self == PvSort.SET_AND_NO_ACTION: - value, severity, status = _convert_to_epics_pv_value(parameter.parameter_type, parameter.sp_no_move, - AlarmSeverity.No, AlarmStatus.No, pv_fields) + value, severity, status = _convert_to_epics_pv_value( + parameter.parameter_type, + parameter.sp_no_move, + AlarmSeverity.No, + AlarmStatus.No, + pv_fields, + ) elif self == PvSort.CHANGED: value = parameter.sp_changed elif self == PvSort.ACTION: @@ -230,25 +256,35 @@ def param_write(self, pv_name, value): if param_sort == PvSort.ACTION and not param.is_disabled and not param.is_locked: param.move = 1 elif param_sort == PvSort.SP and not param.is_disabled and not param.is_locked: - param.sp = convert_from_epics_pv_value(param.parameter_type, value, self._pv_manager.PVDB[pv_name]) + param.sp = convert_from_epics_pv_value( + param.parameter_type, value, self._pv_manager.PVDB[pv_name] + ) elif param_sort == PvSort.SET_AND_NO_ACTION and not param.is_locked: - param.sp_no_move = convert_from_epics_pv_value(param.parameter_type, value, self._pv_manager.PVDB[pv_name]) + param.sp_no_move = convert_from_epics_pv_value( + param.parameter_type, value, self._pv_manager.PVDB[pv_name] + ) elif param_sort == PvSort.DEFINE_POS_SP: - param.define_current_value_as.new_value_sp_rbv = convert_from_epics_pv_value(param.parameter_type, value, - self._pv_manager.PVDB[pv_name]) + param.define_current_value_as.new_value_sp_rbv = convert_from_epics_pv_value( + param.parameter_type, value, self._pv_manager.PVDB[pv_name] + ) elif param_sort == PvSort.DEFINE_POS_SET_AND_NO_ACTION: - param.define_current_value_as.new_value_sp = convert_from_epics_pv_value(param.parameter_type, value, - self._pv_manager.PVDB[pv_name]) + param.define_current_value_as.new_value_sp = convert_from_epics_pv_value( + param.parameter_type, value, self._pv_manager.PVDB[pv_name] + ) elif param_sort == PvSort.DEFINE_POS_ACTION: param.define_current_value_as.do_action() elif param_sort == PvSort.LOCKED: - param.is_locked = convert_from_epics_pv_value(param.parameter_type, value, self._pv_manager.PVDB[pv_name]) + param.is_locked = convert_from_epics_pv_value( + param.parameter_type, value, self._pv_manager.PVDB[pv_name] + ) else: STATUS_MANAGER.update_error_log("Error: PV {} is read only".format(pv_name)) value_accepted = False return value_accepted - def get_param_monitor_updates(self) -> Tuple[str, Union[float, int, str, bool], AlarmSeverity, AlarmStatus]: + def get_param_monitor_updates( + self, + ) -> Tuple[str, Union[float, int, str, bool], AlarmSeverity, AlarmStatus]: """ This is a generator over the names and values (with alarms) of the parameters Returns: tuple of @@ -261,11 +297,14 @@ def get_param_monitor_updates(self) -> Tuple[str, Union[float, int, str, bool], parameter = self._beamline.parameter(param_name) if param_sort not in [PvSort.IN_MODE, PvSort.CHANGING]: pv_fields = self._pv_manager.PVDB[pv_name] - value, alarm_severity, alarm_status = param_sort.get_from_parameter(parameter, pv_fields) + value, alarm_severity, alarm_status = param_sort.get_from_parameter( + parameter, pv_fields + ) yield pv_name, value, alarm_severity, alarm_status - def get_param_update_from_event(self, pv_name: str, param_type: BeamlineParameterType, update: ParameterUpdateBase)\ - -> Tuple[str, Union[float, int, str, bool], AlarmSeverity, AlarmStatus]: + def get_param_update_from_event( + self, pv_name: str, param_type: BeamlineParameterType, update: ParameterUpdateBase + ) -> Tuple[str, Union[float, int, str, bool], AlarmSeverity, AlarmStatus]: """ Given an update event get the update information for updating a pv field Args: @@ -281,6 +320,7 @@ def get_param_update_from_event(self, pv_name: str, param_type: BeamlineParamete """ pv_fields = self._pv_manager.PVDB[pv_name] - value, severity, status = _convert_to_epics_pv_value(param_type, update.value, update.alarm_severity, - update.alarm_status, pv_fields) + value, severity, status = _convert_to_epics_pv_value( + param_type, update.value, update.alarm_severity, update.alarm_status, pv_fields + ) return pv_name, value, severity, status diff --git a/ReflectometryServer/ChannelAccess/pv_manager.py b/ReflectometryServer/ChannelAccess/pv_manager.py index e8f78324..608e12cc 100644 --- a/ReflectometryServer/ChannelAccess/pv_manager.py +++ b/ReflectometryServer/ChannelAccess/pv_manager.py @@ -1,23 +1,25 @@ """ Reflectometry pv manager """ + +import json import logging -from typing import Optional +from collections import OrderedDict from pcaspy import Severity +from pcaspy.alarm import SeverityStrings +from server_common.ioc_data_source import ( + DESCRIPTION_LENGTH, + PV_DESCRIPTION_NAME, + PV_INFO_FIELD_NAME, +) +from server_common.utilities import compress_and_hex, create_pv_name, remove_from_end -import ReflectometryServer from ReflectometryServer.ChannelAccess.constants import STANDARD_FLOAT_PV_FIELDS -from ReflectometryServer.ChannelAccess.driver_utils import PvSort, \ - PARAMS_FIELDS_BEAMLINE_TYPES +from ReflectometryServer.ChannelAccess.driver_utils import PARAMS_FIELDS_BEAMLINE_TYPES, PvSort +from ReflectometryServer.footprint_manager import FP_RBV_KEY, FP_SP_KEY, FP_SP_RBV_KEY +from ReflectometryServer.parameters import BeamlineParameterGroup, BeamlineParameterType from ReflectometryServer.server_status_manager import STATUS, STATUS_MANAGER, ProblemInfo -from ReflectometryServer.footprint_manager import FP_SP_KEY, FP_SP_RBV_KEY, FP_RBV_KEY -from pcaspy.alarm import SeverityStrings -from ReflectometryServer.parameters import BeamlineParameterType, BeamlineParameterGroup -from server_common.ioc_data_source import PV_INFO_FIELD_NAME, PV_DESCRIPTION_NAME, DESCRIPTION_LENGTH -from server_common.utilities import create_pv_name, remove_from_end, compress_and_hex -import json -from collections import OrderedDict logger = logging.getLogger(__name__) @@ -66,13 +68,14 @@ DRIVER_INFO = "DRIVER_INFO" BEAMLINE_CONSTANT_INFO = "CONST_INFO" ALIGN_INFO = "ALIGN_INFO" -PARAM_INFO_LOOKUP = {BeamlineParameterGroup.ALL: PARAM_INFO, - BeamlineParameterGroup.COLLIMATION_PLANE: PARAM_INFO_COLLIMATION, - BeamlineParameterGroup.TOGGLE: PARAM_INFO_TOGGLE, - BeamlineParameterGroup.SLIT: PARAM_INFO_SLITS, - BeamlineParameterGroup.MISC: PARAM_INFO_MISC, - BeamlineParameterGroup.FOOTPRINT_PARAMETER: PARAM_INFO_FOOTPRINT, - } +PARAM_INFO_LOOKUP = { + BeamlineParameterGroup.ALL: PARAM_INFO, + BeamlineParameterGroup.COLLIMATION_PLANE: PARAM_INFO_COLLIMATION, + BeamlineParameterGroup.TOGGLE: PARAM_INFO_TOGGLE, + BeamlineParameterGroup.SLIT: PARAM_INFO_SLITS, + BeamlineParameterGroup.MISC: PARAM_INFO_MISC, + BeamlineParameterGroup.FOOTPRINT_PARAMETER: PARAM_INFO_FOOTPRINT, +} IN_MODE_SUFFIX = ":IN_MODE" @@ -97,9 +100,24 @@ DISP_FIELD = ".DISP" EGU_FIELD = ".EGU" -ALL_PARAM_SUFFIXES = [VAL_FIELD, STAT_FIELD, SEVR_FIELD, DISP_FIELD, EGU_FIELD, DESC_FIELD, SP_SUFFIX, SP_RBV_SUFFIX, - SET_AND_NO_ACTION_SUFFIX, CHANGED_SUFFIX, ACTION_SUFFIX, CHANGING, IN_MODE_SUFFIX, RBV_AT_SP, - LOCKED, READ_ONLY] +ALL_PARAM_SUFFIXES = [ + VAL_FIELD, + STAT_FIELD, + SEVR_FIELD, + DISP_FIELD, + EGU_FIELD, + DESC_FIELD, + SP_SUFFIX, + SP_RBV_SUFFIX, + SET_AND_NO_ACTION_SUFFIX, + CHANGED_SUFFIX, + ACTION_SUFFIX, + CHANGING, + IN_MODE_SUFFIX, + RBV_AT_SP, + LOCKED, + READ_ONLY, +] CONST_PREFIX = "CONST" @@ -113,16 +131,16 @@ MANAGER_FIELD = {"asg": "MANAGER"} STANDARD_FLOAT_PV_FIELDS_WITH_MANAGER = STANDARD_FLOAT_PV_FIELDS | MANAGER_FIELD -PARAM_FIELDS_BINARY = {'type': 'enum', 'enums': ["NO", "YES"]} +PARAM_FIELDS_BINARY = {"type": "enum", "enums": ["NO", "YES"]} PARAM_FIELDS_BINARY_WITH_MANAGER = PARAM_FIELDS_BINARY | MANAGER_FIELD -PARAM_IN_MODE = {'type': 'enum', 'enums': ["NO", "YES"]} -PARAM_FIELDS_ACTION = {'type': 'int', 'count': 1, 'value': 0} +PARAM_IN_MODE = {"type": "enum", "enums": ["NO", "YES"]} +PARAM_FIELDS_ACTION = {"type": "int", "count": 1, "value": 0} PARAM_FIELDS_ACTION_WITH_MANAGER = PARAM_FIELDS_ACTION | MANAGER_FIELD -STANDARD_2048_CHAR_WF_FIELDS = {'type': 'char', 'count': 2048, 'value': ""} -STANDARD_STRING_FIELDS = {'type': 'string', 'value': ""} -STANDARD_DISP_FIELDS = {'type': 'enum', 'enums': ["0", "1"], 'value': 0} -ALARM_STAT_PV_FIELDS = {'type': 'enum', 'enums': AlarmStringsTruncated} -ALARM_SEVR_PV_FIELDS = {'type': 'enum', 'enums': SeverityStrings} +STANDARD_2048_CHAR_WF_FIELDS = {"type": "char", "count": 2048, "value": ""} +STANDARD_STRING_FIELDS = {"type": "string", "value": ""} +STANDARD_DISP_FIELDS = {"type": "enum", "enums": ["0", "1"], "value": 0} +ALARM_STAT_PV_FIELDS = {"type": "enum", "enums": AlarmStringsTruncated} +ALARM_SEVR_PV_FIELDS = {"type": "enum", "enums": SeverityStrings} def is_pv_name_this_field(field_name, pv_name): @@ -137,6 +155,7 @@ def is_pv_name_this_field(field_name, pv_name): pv_name_no_val = remove_from_end(pv_name, VAL_FIELD) return pv_name_no_val == field_name + def check_if_pv_value_exceeds_max_size(value, max_size, pv): """ Args: @@ -148,15 +167,19 @@ def check_if_pv_value_exceeds_max_size(value, max_size, pv): """ if len(value) > max_size: - STATUS_MANAGER.update_error_log(f"Size of {pv} exceeded limit of {max_size} and was truncated. " - "Can you reduce the contents of the PV, if not increase the size of PV.") + STATUS_MANAGER.update_error_log( + f"Size of {pv} exceeded limit of {max_size} and was truncated. " + "Can you reduce the contents of the PV, if not increase the size of PV." + ) value = value[:max_size] return value + class PVManager: """ Holds reflectometry PVs and associated utilities. """ + def __init__(self): """ The constructor. @@ -175,16 +198,40 @@ def _add_status_pvs(self): """ PVs for server status """ - status_fields = {'type': 'enum', - 'enums': [code.display_string for code in STATUS.status_codes()], - 'states': [code.alarm_severity for code in STATUS.status_codes()]} - self._add_pv_with_fields(SERVER_STATUS, None, status_fields, "Status of the beam line", PvSort.RBV, - archive=True, interest="HIGH", alarm=True, on_init=True) - self._add_pv_with_fields(SERVER_MESSAGE, None, {'type': 'char', 'count': 400}, "Message about the beamline", - PvSort.RBV, interest="HIGH", on_init=True) - self._add_pv_with_fields(SERVER_ERROR_LOG, None, {'type': 'char', 'count': 10000}, - "Error log for the Reflectometry Server", PvSort.RBV, interest="HIGH", - on_init=True) + status_fields = { + "type": "enum", + "enums": [code.display_string for code in STATUS.status_codes()], + "states": [code.alarm_severity for code in STATUS.status_codes()], + } + self._add_pv_with_fields( + SERVER_STATUS, + None, + status_fields, + "Status of the beam line", + PvSort.RBV, + archive=True, + interest="HIGH", + alarm=True, + on_init=True, + ) + self._add_pv_with_fields( + SERVER_MESSAGE, + None, + {"type": "char", "count": 400}, + "Message about the beamline", + PvSort.RBV, + interest="HIGH", + on_init=True, + ) + self._add_pv_with_fields( + SERVER_ERROR_LOG, + None, + {"type": "char", "count": 10000}, + "Error log for the Reflectometry Server", + PvSort.RBV, + interest="HIGH", + on_init=True, + ) def set_beamline(self, beamline): """ @@ -210,32 +257,70 @@ def _add_global_pvs(self): Add PVs that affect the whole of the reflectometry system to the server's PV database. """ - self._add_pv_with_fields(BEAMLINE_MOVE, None, PARAM_FIELDS_ACTION, "Move the beam line", PvSort.RBV, - archive=True, interest="HIGH") + self._add_pv_with_fields( + BEAMLINE_MOVE, + None, + PARAM_FIELDS_ACTION, + "Move the beam line", + PvSort.RBV, + archive=True, + interest="HIGH", + ) # PVs for mode - mode_fields = {'type': 'enum', 'enums': self._beamline.mode_names} - self._add_pv_with_fields(BEAMLINE_MODE, None, mode_fields, "Beamline mode", PvSort.RBV, archive=True, - interest="HIGH") - self._add_pv_with_fields(BEAMLINE_MODE + SP_SUFFIX, None, mode_fields, "Beamline mode", PvSort.SP) - - self._add_pv_with_fields(REAPPLY_MODE_INITS, None, PARAM_FIELDS_BINARY, "Apply mode inits on move all", - PvSort.RBV, archive=True, interest="MEDIUM") + mode_fields = {"type": "enum", "enums": self._beamline.mode_names} + self._add_pv_with_fields( + BEAMLINE_MODE, + None, + mode_fields, + "Beamline mode", + PvSort.RBV, + archive=True, + interest="HIGH", + ) + self._add_pv_with_fields( + BEAMLINE_MODE + SP_SUFFIX, None, mode_fields, "Beamline mode", PvSort.SP + ) + + self._add_pv_with_fields( + REAPPLY_MODE_INITS, + None, + PARAM_FIELDS_BINARY, + "Apply mode inits on move all", + PvSort.RBV, + archive=True, + interest="MEDIUM", + ) def _add_footprint_calculator_pvs(self): """ Add PVs related to the footprint calculation to the server's PV database. """ - self._add_pv_with_fields(SAMPLE_LENGTH, None, STANDARD_FLOAT_PV_FIELDS, - "Sample Length", PvSort.SP_RBV, archive=True, interest="HIGH") + self._add_pv_with_fields( + SAMPLE_LENGTH, + None, + STANDARD_FLOAT_PV_FIELDS, + "Sample Length", + PvSort.SP_RBV, + archive=True, + interest="HIGH", + ) for prefix in FOOTPRINT_PREFIXES: - for template, description in [(FP_TEMPLATE, "Beam Footprint"), - (DQQ_TEMPLATE, "Beam Resolution dQ/Q"), - (QMIN_TEMPLATE, "Minimum measurable Q with current setup"), - (QMAX_TEMPLATE, "Maximum measurable Q with current setup")]: - self._add_pv_with_fields(template.format(prefix), None, - STANDARD_FLOAT_PV_FIELDS, - description, PvSort.RBV, archive=True, interest="HIGH") + for template, description in [ + (FP_TEMPLATE, "Beam Footprint"), + (DQQ_TEMPLATE, "Beam Resolution dQ/Q"), + (QMIN_TEMPLATE, "Minimum measurable Q with current setup"), + (QMAX_TEMPLATE, "Maximum measurable Q with current setup"), + ]: + self._add_pv_with_fields( + template.format(prefix), + None, + STANDARD_FLOAT_PV_FIELDS, + description, + PvSort.RBV, + archive=True, + interest="HIGH", + ) def _add_all_parameter_pvs(self): """ @@ -256,26 +341,45 @@ def _add_all_parameter_pvs(self): "name": param_info_record["name"], "prepended_alias": param_info_record["prepended_alias"], "type": "align", - "description": "" + "description": "", } align_info.append(align_info_record) except Exception as err: - STATUS_MANAGER.update_error_log("Error adding PV for parameter {}: {}".format(parameter.name, err), err) + STATUS_MANAGER.update_error_log( + "Error adding PV for parameter {}: {}".format(parameter.name, err), err + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Error adding parameter PV", parameter.name, Severity.MAJOR_ALARM)) + ProblemInfo("Error adding parameter PV", parameter.name, Severity.MAJOR_ALARM) + ) for param_group in BeamlineParameterGroup: value = compress_and_hex(json.dumps(param_info[param_group])) - value = check_if_pv_value_exceeds_max_size(value, STANDARD_2048_CHAR_WF_FIELDS["count"], param_group) - - self._add_pv_with_fields(PARAM_INFO_LOOKUP[param_group], None, STANDARD_2048_CHAR_WF_FIELDS, - BeamlineParameterGroup.description(param_group), None, value=value) + value = check_if_pv_value_exceeds_max_size( + value, STANDARD_2048_CHAR_WF_FIELDS["count"], param_group + ) + + self._add_pv_with_fields( + PARAM_INFO_LOOKUP[param_group], + None, + STANDARD_2048_CHAR_WF_FIELDS, + BeamlineParameterGroup.description(param_group), + None, + value=value, + ) value = compress_and_hex(json.dumps(align_info)) - value = check_if_pv_value_exceeds_max_size(value, STANDARD_2048_CHAR_WF_FIELDS["count"], ALIGN_INFO) - self._add_pv_with_fields(ALIGN_INFO, None, STANDARD_2048_CHAR_WF_FIELDS, "All alignment pvs information", - None, value=value) + value = check_if_pv_value_exceeds_max_size( + value, STANDARD_2048_CHAR_WF_FIELDS["count"], ALIGN_INFO + ) + self._add_pv_with_fields( + ALIGN_INFO, + None, + STANDARD_2048_CHAR_WF_FIELDS, + "All alignment pvs information", + None, + value=value, + ) def _add_parameter_pvs(self, parameter): """ @@ -299,74 +403,155 @@ def _add_parameter_pvs(self, parameter): fields["enums"] = parameter.options # Readback PV - self._add_pv_with_fields(prepended_alias, param_name, fields, description, PvSort.RBV, archive=True, - interest="HIGH") + self._add_pv_with_fields( + prepended_alias, + param_name, + fields, + description, + PvSort.RBV, + archive=True, + interest="HIGH", + ) # Setpoint PV - self._add_pv_with_fields(prepended_alias + SP_SUFFIX, param_name, fields, description, PvSort.SP, - archive=True) + self._add_pv_with_fields( + prepended_alias + SP_SUFFIX, param_name, fields, description, PvSort.SP, archive=True + ) # Setpoint readback PV - self._add_pv_with_fields(prepended_alias + SP_RBV_SUFFIX, param_name, fields, description, PvSort.SP_RBV) + self._add_pv_with_fields( + prepended_alias + SP_RBV_SUFFIX, param_name, fields, description, PvSort.SP_RBV + ) # Set value and do not action PV - self._add_pv_with_fields(prepended_alias + SET_AND_NO_ACTION_SUFFIX, param_name, fields, description, - PvSort.SET_AND_NO_ACTION, is_disabled_on_init=parameter.sp_mirrors_rbv) + self._add_pv_with_fields( + prepended_alias + SET_AND_NO_ACTION_SUFFIX, + param_name, + fields, + description, + PvSort.SET_AND_NO_ACTION, + is_disabled_on_init=parameter.sp_mirrors_rbv, + ) # Changed PV - self._add_pv_with_fields(prepended_alias + CHANGED_SUFFIX, param_name, PARAM_FIELDS_BINARY, description, - PvSort.CHANGED) + self._add_pv_with_fields( + prepended_alias + CHANGED_SUFFIX, + param_name, + PARAM_FIELDS_BINARY, + description, + PvSort.CHANGED, + ) # Action PV - self._add_pv_with_fields(prepended_alias + ACTION_SUFFIX, param_name, PARAM_FIELDS_ACTION, description, - PvSort.ACTION) + self._add_pv_with_fields( + prepended_alias + ACTION_SUFFIX, + param_name, + PARAM_FIELDS_ACTION, + description, + PvSort.ACTION, + ) # Moving state PV - self._add_pv_with_fields(prepended_alias + CHANGING, param_name, PARAM_FIELDS_BINARY, description, - PvSort.CHANGING) + self._add_pv_with_fields( + prepended_alias + CHANGING, + param_name, + PARAM_FIELDS_BINARY, + description, + PvSort.CHANGING, + ) # In mode PV - self._add_pv_with_fields(prepended_alias + IN_MODE_SUFFIX, param_name, PARAM_IN_MODE, description, - PvSort.IN_MODE) + self._add_pv_with_fields( + prepended_alias + IN_MODE_SUFFIX, param_name, PARAM_IN_MODE, description, PvSort.IN_MODE + ) # RBV to SP:RBV tolerance - self._add_pv_with_fields(prepended_alias + RBV_AT_SP, param_name, PARAM_FIELDS_BINARY, description, - PvSort.RBV_AT_SP) + self._add_pv_with_fields( + prepended_alias + RBV_AT_SP, + param_name, + PARAM_FIELDS_BINARY, + description, + PvSort.RBV_AT_SP, + ) # Locked PV - self._add_pv_with_fields(prepended_alias + LOCKED, param_name, PARAM_FIELDS_BINARY_WITH_MANAGER, description, - PvSort.LOCKED) + self._add_pv_with_fields( + prepended_alias + LOCKED, + param_name, + PARAM_FIELDS_BINARY_WITH_MANAGER, + description, + PvSort.LOCKED, + ) # Read Only PV - self._add_pv_with_fields(prepended_alias + READ_ONLY, param_name, PARAM_FIELDS_BINARY_WITH_MANAGER, description, - PvSort.READ_ONLY) + self._add_pv_with_fields( + prepended_alias + READ_ONLY, + param_name, + PARAM_FIELDS_BINARY_WITH_MANAGER, + description, + PvSort.READ_ONLY, + ) # define position at if parameter.define_current_value_as is not None: - self._add_pv_with_fields(prepended_alias + DEFINE_POS_SP, param_name, STANDARD_FLOAT_PV_FIELDS_WITH_MANAGER, description, - PvSort.DEFINE_POS_SP) - - self._add_pv_with_fields(prepended_alias + DEFINE_POS_SET_AND_NO_ACTION, param_name, STANDARD_FLOAT_PV_FIELDS_WITH_MANAGER, description, - PvSort.DEFINE_POS_SET_AND_NO_ACTION) - - self._add_pv_with_fields(prepended_alias + DEFINE_POS_ACTION, param_name, PARAM_FIELDS_ACTION_WITH_MANAGER, description, - PvSort.DEFINE_POS_ACTION) - - self._add_pv_with_fields(prepended_alias + DEFINE_POS_CHANGED, param_name, PARAM_FIELDS_BINARY_WITH_MANAGER, description, - PvSort.DEFINE_POS_CHANGED) + self._add_pv_with_fields( + prepended_alias + DEFINE_POS_SP, + param_name, + STANDARD_FLOAT_PV_FIELDS_WITH_MANAGER, + description, + PvSort.DEFINE_POS_SP, + ) + + self._add_pv_with_fields( + prepended_alias + DEFINE_POS_SET_AND_NO_ACTION, + param_name, + STANDARD_FLOAT_PV_FIELDS_WITH_MANAGER, + description, + PvSort.DEFINE_POS_SET_AND_NO_ACTION, + ) + + self._add_pv_with_fields( + prepended_alias + DEFINE_POS_ACTION, + param_name, + PARAM_FIELDS_ACTION_WITH_MANAGER, + description, + PvSort.DEFINE_POS_ACTION, + ) + + self._add_pv_with_fields( + prepended_alias + DEFINE_POS_CHANGED, + param_name, + PARAM_FIELDS_BINARY_WITH_MANAGER, + description, + PvSort.DEFINE_POS_CHANGED, + ) # Engineering Unit egu_fields = STANDARD_STRING_FIELDS.copy() egu_fields["value"] = parameter.engineering_unit self.PVDB[prepended_alias + EGU_FIELD] = egu_fields - return {"name": param_name, - "prepended_alias": prepended_alias, - "type": BeamlineParameterType.name_for_param_list(parameter_type), - "characteristic_value": parameter.characteristic_value, - "description": parameter.description} - - def _add_pv_with_fields(self, pv_name, param_name, pv_fields, description, sort, archive=False, interest=None, - alarm=False, value=None, on_init=False, is_disabled_on_init=False): + return { + "name": param_name, + "prepended_alias": prepended_alias, + "type": BeamlineParameterType.name_for_param_list(parameter_type), + "characteristic_value": parameter.characteristic_value, + "description": parameter.description, + } + + def _add_pv_with_fields( + self, + pv_name, + param_name, + pv_fields, + description, + sort, + archive=False, + interest=None, + alarm=False, + value=None, + on_init=False, + is_disabled_on_init=False, + ): """ Add param to pv list with .val and correct fields and to parm look up Args: @@ -405,7 +590,10 @@ def _add_pv_with_fields(self, pv_name, param_name, pv_fields, description, sort, self.PVDB[pv_name] = pv_fields_mod self.PVDB[pv_name + VAL_FIELD] = pv_fields - self.PVDB[pv_name + DESC_FIELD] = {'type': 'string', 'value': pv_fields[PV_DESCRIPTION_NAME]} + self.PVDB[pv_name + DESC_FIELD] = { + "type": "string", + "value": pv_fields[PV_DESCRIPTION_NAME], + } self.PVDB[pv_name + STAT_FIELD] = ALARM_STAT_PV_FIELDS.copy() self.PVDB[pv_name + SEVR_FIELD] = ALARM_SEVR_PV_FIELDS.copy() self.PVDB[pv_name + DISP_FIELD] = STANDARD_DISP_FIELDS.copy() @@ -439,23 +627,43 @@ def _add_all_driver_pvs(self): driver_info = [] for driver in self._beamline.drivers: if driver.has_engineering_correction: - correction_alias = create_pv_name(driver.name, list(self.PVDB.keys()), "COR", limit=12, - allow_colon=True) + correction_alias = create_pv_name( + driver.name, list(self.PVDB.keys()), "COR", limit=12, allow_colon=True + ) prepended_alias = "{}:{}".format("COR", correction_alias) - self._add_pv_with_fields(prepended_alias, None, STANDARD_FLOAT_PV_FIELDS, "Engineering Correction", - None, archive=True) - self._add_pv_with_fields("{}:DESC".format(prepended_alias), None, STANDARD_2048_CHAR_WF_FIELDS, - "Engineering Correction Full Description", None) + self._add_pv_with_fields( + prepended_alias, + None, + STANDARD_FLOAT_PV_FIELDS, + "Engineering Correction", + None, + archive=True, + ) + self._add_pv_with_fields( + "{}:DESC".format(prepended_alias), + None, + STANDARD_2048_CHAR_WF_FIELDS, + "Engineering Correction Full Description", + None, + ) self.drivers_pv[driver] = prepended_alias driver_info.append({"name": driver.name, "prepended_alias": prepended_alias}) value = compress_and_hex(json.dumps(driver_info)) - value = check_if_pv_value_exceeds_max_size(value, STANDARD_2048_CHAR_WF_FIELDS["count"], DRIVER_INFO) - self._add_pv_with_fields(DRIVER_INFO, None, STANDARD_2048_CHAR_WF_FIELDS, "All corrections information", - None, value=value) + value = check_if_pv_value_exceeds_max_size( + value, STANDARD_2048_CHAR_WF_FIELDS["count"], DRIVER_INFO + ) + self._add_pv_with_fields( + DRIVER_INFO, + None, + STANDARD_2048_CHAR_WF_FIELDS, + "All corrections information", + None, + value=value, + ) def _add_constants_pvs(self): """ @@ -466,8 +674,13 @@ def _add_constants_pvs(self): for beamline_constant in self._beamline.beamline_constants: try: - const_alias = create_pv_name(beamline_constant.name, list(self.PVDB.keys()), CONST_PREFIX, - limit=20, allow_colon=True) + const_alias = create_pv_name( + beamline_constant.name, + list(self.PVDB.keys()), + CONST_PREFIX, + limit=20, + allow_colon=True, + ) prepended_alias = "{}:{}".format(CONST_PREFIX, const_alias) # float_value works for cs studio's default display format, but not for strings. @@ -483,23 +696,52 @@ def _add_constants_pvs(self): value = float(beamline_constant.value) fields = STANDARD_FLOAT_PV_FIELDS - self._add_pv_with_fields(prepended_alias, None, fields, beamline_constant.description, None, - interest="MEDIUM", value=value) - logger.info("Adding Constant {} with value {}".format(beamline_constant.name, beamline_constant.value)) + self._add_pv_with_fields( + prepended_alias, + None, + fields, + beamline_constant.description, + None, + interest="MEDIUM", + value=value, + ) + logger.info( + "Adding Constant {} with value {}".format( + beamline_constant.name, beamline_constant.value + ) + ) beamline_constant_info.append( - {"name": beamline_constant.name, - "prepended_alias": prepended_alias, - "type": const_type, - "description": beamline_constant.description}) + { + "name": beamline_constant.name, + "prepended_alias": prepended_alias, + "type": const_type, + "description": beamline_constant.description, + } + ) except Exception as err: - STATUS_MANAGER.update_error_log("Error adding constant {}: {}".format(beamline_constant.name, err), err) + STATUS_MANAGER.update_error_log( + "Error adding constant {}: {}".format(beamline_constant.name, err), err + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Error adding PV for beamline constant", beamline_constant.name, Severity.MAJOR_ALARM)) + ProblemInfo( + "Error adding PV for beamline constant", + beamline_constant.name, + Severity.MAJOR_ALARM, + ) + ) value = compress_and_hex(json.dumps(beamline_constant_info)) - value = check_if_pv_value_exceeds_max_size(value, STANDARD_2048_CHAR_WF_FIELDS["count"], BEAMLINE_CONSTANT_INFO) - self._add_pv_with_fields(BEAMLINE_CONSTANT_INFO, None, STANDARD_2048_CHAR_WF_FIELDS, "All value parameters", - None, value=value) + value = check_if_pv_value_exceeds_max_size( + value, STANDARD_2048_CHAR_WF_FIELDS["count"], BEAMLINE_CONSTANT_INFO + ) + self._add_pv_with_fields( + BEAMLINE_CONSTANT_INFO, + None, + STANDARD_2048_CHAR_WF_FIELDS, + "All value parameters", + None, + value=value, + ) def param_names_pv_names_and_sort(self): """ diff --git a/ReflectometryServer/ChannelAccess/reflectometry_driver.py b/ReflectometryServer/ChannelAccess/reflectometry_driver.py index 07441ae7..293a4f48 100644 --- a/ReflectometryServer/ChannelAccess/reflectometry_driver.py +++ b/ReflectometryServer/ChannelAccess/reflectometry_driver.py @@ -1,28 +1,57 @@ """ Driver for the reflectometry server. """ + import logging from functools import partial from typing import Optional -from pcaspy import Driver, Alarm, Severity -from pcaspy.driver import manager, Data +from pcaspy import Alarm, Driver, Severity +from pcaspy.driver import Data, manager +from server_common.loggers.isis_logger import IsisPutLog from ReflectometryServer import Beamline -from ReflectometryServer.ChannelAccess.constants import REFLECTOMETRY_PREFIX, REFL_IOC_NAME -from ReflectometryServer.ChannelAccess.driver_utils import DriverParamHelper -from ReflectometryServer.ChannelAccess.pv_manager import PvSort, is_pv_name_this_field, BEAMLINE_MODE, VAL_FIELD, \ - SERVER_STATUS, SERVER_MESSAGE, SP_SUFFIX, FP_TEMPLATE, DQQ_TEMPLATE, QMIN_TEMPLATE, QMAX_TEMPLATE, \ - IN_MODE_SUFFIX, SERVER_ERROR_LOG, SAMPLE_LENGTH, REAPPLY_MODE_INITS, BEAMLINE_MOVE, DISP_FIELD, \ - check_if_pv_value_exceeds_max_size from ReflectometryServer.beamline import ActiveModeUpdate -from ReflectometryServer.server_status_manager import STATUS_MANAGER, StatusUpdate, ProblemInfo, ErrorLogUpdate -from ReflectometryServer.footprint_manager import FootprintSort +from ReflectometryServer.ChannelAccess.constants import REFL_IOC_NAME, REFLECTOMETRY_PREFIX +from ReflectometryServer.ChannelAccess.driver_utils import DriverParamHelper +from ReflectometryServer.ChannelAccess.pv_manager import ( + BEAMLINE_MODE, + BEAMLINE_MOVE, + DISP_FIELD, + DQQ_TEMPLATE, + FP_TEMPLATE, + IN_MODE_SUFFIX, + QMAX_TEMPLATE, + QMIN_TEMPLATE, + REAPPLY_MODE_INITS, + SAMPLE_LENGTH, + SERVER_ERROR_LOG, + SERVER_MESSAGE, + SERVER_STATUS, + SP_SUFFIX, + VAL_FIELD, + PvSort, + is_pv_name_this_field, +) from ReflectometryServer.engineering_corrections import CorrectionUpdate -from ReflectometryServer.parameters import BeamlineParameterGroup, ParameterReadbackUpdate, \ - ParameterSetpointReadbackUpdate, ParameterAtSetpointUpdate, ParameterChangingUpdate, ParameterInitUpdate, \ - ParameterUpdateBase, BeamlineParameterType, ParameterDisabledUpdate -from server_common.loggers.isis_logger import IsisPutLog +from ReflectometryServer.footprint_manager import FootprintSort +from ReflectometryServer.parameters import ( + BeamlineParameterGroup, + BeamlineParameterType, + ParameterAtSetpointUpdate, + ParameterChangingUpdate, + ParameterDisabledUpdate, + ParameterInitUpdate, + ParameterReadbackUpdate, + ParameterSetpointReadbackUpdate, + ParameterUpdateBase, +) +from ReflectometryServer.server_status_manager import ( + STATUS_MANAGER, + ErrorLogUpdate, + ProblemInfo, + StatusUpdate, +) logger = logging.getLogger(__name__) @@ -32,6 +61,7 @@ class ReflectometryDriver(Driver): The driver which provides an interface for the reflectometry server to channel access by creating PVs and processing incoming CA get and put requests. """ + _driver_help: Optional[DriverParamHelper] _beamline: Optional[Beamline] @@ -117,10 +147,15 @@ def read(self, reason): elif is_pv_name_this_field(SERVER_MESSAGE, reason): # The server message has the active errors in the beginning so truncation happens at the end. truncated_string = "" - server_message_max_character_size = self._pv_manager.PVDB[SERVER_MESSAGE]["count"] + server_message_max_character_size = self._pv_manager.PVDB[SERVER_MESSAGE][ + "count" + ] message = STATUS_MANAGER.message if len(message) > server_message_max_character_size: - return message[:server_message_max_character_size - len(truncated_string)] + truncated_string + return ( + message[: server_message_max_character_size - len(truncated_string)] + + truncated_string + ) else: return message elif is_pv_name_this_field(SERVER_ERROR_LOG, reason): @@ -137,7 +172,8 @@ def read(self, reason): except Exception as e: STATUS_MANAGER.update_error_log("Exception when reading parameter {}".format(reason), e) STATUS_MANAGER.update_active_problems( - ProblemInfo("PV Value read caused exception.", reason, Severity.MAJOR_ALARM)) + ProblemInfo("PV Value read caused exception.", reason, Severity.MAJOR_ALARM) + ) return return self.getParam(reason) @@ -166,8 +202,11 @@ def write(self, reason, value): new_mode_name = beamline_mode_enums[value] self._beamline.active_mode = new_mode_name except ValueError: - STATUS_MANAGER.update_error_log("Invalid value entered for mode. (Possible modes: {})".format( - ",".join(self._beamline.mode_names))) + STATUS_MANAGER.update_error_log( + "Invalid value entered for mode. (Possible modes: {})".format( + ",".join(self._beamline.mode_names) + ) + ) value_accepted = False elif is_pv_name_this_field(SAMPLE_LENGTH, reason): self._footprint_manager.set_sample_length(value) @@ -181,9 +220,12 @@ def write(self, reason, value): self._update_param_both_pv_and_pv_val(reason, value) self.update_monitors() except Exception as e: - STATUS_MANAGER.update_error_log("PV Value {} for PV {} rejected by server: {}".format(value, reason, e), e) - STATUS_MANAGER.update_active_problems(ProblemInfo("PV Value rejected by server.", reason, - Severity.MINOR_ALARM)) + STATUS_MANAGER.update_error_log( + "PV Value {} for PV {} rejected by server: {}".format(value, reason, e), e + ) + STATUS_MANAGER.update_active_problems( + ProblemInfo("PV Value rejected by server.", reason, Severity.MINOR_ALARM) + ) value_accepted = False return value_accepted @@ -192,7 +234,12 @@ def update_monitors(self): Updates the PV values and alarms for each parameter so that changes are visible to monitors. """ # with self.monitor_lock: - for pv_name, value, alarm_severity, alarm_status in self._driver_help.get_param_monitor_updates(): + for ( + pv_name, + value, + alarm_severity, + alarm_status, + ) in self._driver_help.get_param_monitor_updates(): self._update_param_both_pv_and_pv_val(pv_name, value, alarm_severity, alarm_status) self._update_all_footprints() @@ -214,13 +261,23 @@ def _update_footprint(self, sort, _): sort{ReflectometryServer.pv_manager.FootprintSort): The sort of value for which to update the footprint PVs """ prefix = FootprintSort.prefix(sort) - self._update_param_both_pv_and_pv_val(FP_TEMPLATE.format(prefix), self._footprint_manager.get_footprint(sort)) - self._update_param_both_pv_and_pv_val(DQQ_TEMPLATE.format(prefix), self._footprint_manager.get_resolution(sort)) - self._update_param_both_pv_and_pv_val(QMIN_TEMPLATE.format(prefix), self._footprint_manager.get_q_min(sort)) - self._update_param_both_pv_and_pv_val(QMAX_TEMPLATE.format(prefix), self._footprint_manager.get_q_max(sort)) + self._update_param_both_pv_and_pv_val( + FP_TEMPLATE.format(prefix), self._footprint_manager.get_footprint(sort) + ) + self._update_param_both_pv_and_pv_val( + DQQ_TEMPLATE.format(prefix), self._footprint_manager.get_resolution(sort) + ) + self._update_param_both_pv_and_pv_val( + QMIN_TEMPLATE.format(prefix), self._footprint_manager.get_q_min(sort) + ) + self._update_param_both_pv_and_pv_val( + QMAX_TEMPLATE.format(prefix), self._footprint_manager.get_q_max(sort) + ) self.updatePVs() - def _update_param_both_pv_and_pv_val(self, pv_name, value, alarm_severity=None, alarm_status=None): + def _update_param_both_pv_and_pv_val( + self, pv_name, value, alarm_severity=None, alarm_status=None + ): """ Update a parameter value (both base and .VAL) and its alarms. @@ -236,15 +293,18 @@ def _update_param_both_pv_and_pv_val(self, pv_name, value, alarm_severity=None, self.setParam(pv_name + VAL_FIELD, value) self.setParamStatus(pv_name, alarm_status, alarm_severity) - def _update_param_listener(self, pv_name: str, param_type: BeamlineParameterType, update: ParameterUpdateBase): + def _update_param_listener( + self, pv_name: str, param_type: BeamlineParameterType, update: ParameterUpdateBase + ): """ Listener for responding to updates from the command line parameter Args: pv_name: name of the pv update (NamedTuple): update from this parameter, expected to have at least a "value" attribute. """ - pv_name, value, alarm_severity, alarm_status = \ + pv_name, value, alarm_severity, alarm_status = ( self._driver_help.get_param_update_from_event(pv_name, param_type, update) + ) self._update_param_both_pv_and_pv_val(pv_name, value, alarm_severity, alarm_status) for pv in self._pv_manager.get_all_pvs_for_param(pv_name): self.updatePV(pv) @@ -255,24 +315,37 @@ def add_param_listeners(self): """ for pv_name, (param_name, param_sort) in self._pv_manager.param_names_pv_names_and_sort(): parameter = self._beamline.parameter(param_name) - parameter.add_listener(ParameterInitUpdate, partial(self._update_param_listener, - pv_name, parameter.parameter_type)) + parameter.add_listener( + ParameterInitUpdate, + partial(self._update_param_listener, pv_name, parameter.parameter_type), + ) if param_sort in [PvSort.ACTION, PvSort.SP]: - parameter.add_listener(ParameterDisabledUpdate, - partial(self._update_param_disabled, pv_name + DISP_FIELD), run_listener=True) + parameter.add_listener( + ParameterDisabledUpdate, + partial(self._update_param_disabled, pv_name + DISP_FIELD), + run_listener=True, + ) if param_sort == PvSort.RBV: - parameter.add_listener(ParameterReadbackUpdate, partial(self._update_param_listener, - pv_name, parameter.parameter_type)) + parameter.add_listener( + ParameterReadbackUpdate, + partial(self._update_param_listener, pv_name, parameter.parameter_type), + ) if param_sort == PvSort.SP_RBV: - parameter.add_listener(ParameterSetpointReadbackUpdate, partial(self._update_param_listener, - pv_name, parameter.parameter_type)) + parameter.add_listener( + ParameterSetpointReadbackUpdate, + partial(self._update_param_listener, pv_name, parameter.parameter_type), + ) if param_sort == PvSort.CHANGING: - parameter.add_listener(ParameterChangingUpdate, partial(self._update_binary_listener, pv_name)) + parameter.add_listener( + ParameterChangingUpdate, partial(self._update_binary_listener, pv_name) + ) if param_sort == PvSort.RBV_AT_SP: - parameter.add_listener(ParameterAtSetpointUpdate, partial(self._update_binary_listener, pv_name)) + parameter.add_listener( + ParameterAtSetpointUpdate, partial(self._update_binary_listener, pv_name) + ) def _update_binary_listener(self, pv_name, update): self.setParam(pv_name, update.value) @@ -294,9 +367,9 @@ def _on_bl_mode_change(self, mode_update): if param_sort is PvSort.RBV: if param_name in params_in_mode: self._update_param_both_pv_and_pv_val(pv_name + IN_MODE_SUFFIX, 1) - else: + else: self._update_param_both_pv_and_pv_val(pv_name + IN_MODE_SUFFIX, 0) - + mode_value = self._beamline_mode_value(mode_update.mode.name) self._update_param_both_pv_and_pv_val(BEAMLINE_MODE, mode_value) self._update_param_both_pv_and_pv_val(BEAMLINE_MODE + SP_SUFFIX, mode_value) @@ -355,15 +428,20 @@ def add_footprint_param_listeners(self): if BeamlineParameterGroup.FOOTPRINT_PARAMETER in parameter.group_names: parameters_to_monitor.add(parameter) for parameter in parameters_to_monitor: - parameter.add_listener(ParameterReadbackUpdate, partial(self._update_footprint, FootprintSort.RBV)) - parameter.add_listener(ParameterSetpointReadbackUpdate, - partial(self._update_footprint, FootprintSort.SP_RBV)) + parameter.add_listener( + ParameterReadbackUpdate, partial(self._update_footprint, FootprintSort.RBV) + ) + parameter.add_listener( + ParameterSetpointReadbackUpdate, + partial(self._update_footprint, FootprintSort.SP_RBV), + ) def _add_trigger_on_engineering_correction_change(self): """ Add all the triggers on engineering corrections. """ + def _update_corrections_pv(name, correction_update): """ Update the driver engineering corrections PV with new value diff --git a/ReflectometryServer/__init__.py b/ReflectometryServer/__init__.py index b6eb44be..1c3677e9 100644 --- a/ReflectometryServer/__init__.py +++ b/ReflectometryServer/__init__.py @@ -1,29 +1,71 @@ """ Classes used by configuration for easy import """ -import sys + import os +import sys -epics_path = os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir, os.path.pardir, os.path.pardir) +epics_path = os.path.join( + os.path.dirname(__file__), os.path.pardir, os.path.pardir, os.path.pardir, os.path.pardir +) server_common_path = os.path.abspath(os.path.join(epics_path, "ISIS", "inst_servers", "master")) sys.path.insert(2, server_common_path) from ReflectometryServer.beamline import Beamline, BeamlineMode from ReflectometryServer.beamline_constant import BeamlineConstant -from ReflectometryServer.components import Component, ReflectingComponent, ThetaComponent, TiltingComponent, \ - BenchComponent, BenchSetup -from ReflectometryServer.engineering_corrections import ConstantCorrection, UserFunctionCorrection, \ - InterpolateGridDataCorrection, SymmetricEngineeringCorrection, NoCorrection, EngineeringCorrection, \ - GridDataFileReader, InterpolateGridDataCorrectionFromProvider, COLUMN_NAME_FOR_DRIVER_SETPOINT, \ - ModeSelectCorrection -from ReflectometryServer.footprint_manager import FootprintSetup, BaseFootprintSetup -from ReflectometryServer.geometry import PositionAndAngle, Position, ChangeAxis +from ReflectometryServer.components import ( + BenchComponent, + BenchSetup, + Component, + ReflectingComponent, + ThetaComponent, + TiltingComponent, +) +from ReflectometryServer.config_helper import ( + ConfigHelper, + SlitAxes, + add_beam_start, + add_component, + add_component_marker, + add_constant, + add_driver, + add_driver_marker, + add_footprint_setup, + add_mode, + add_parameter, + add_parameter_marker, + add_slit_parameters, + as_mode_correction, + get_configured_beamline, + optional_is_set, +) +from ReflectometryServer.engineering_corrections import ( + COLUMN_NAME_FOR_DRIVER_SETPOINT, + ConstantCorrection, + EngineeringCorrection, + GridDataFileReader, + InterpolateGridDataCorrection, + InterpolateGridDataCorrectionFromProvider, + ModeSelectCorrection, + NoCorrection, + SymmetricEngineeringCorrection, + UserFunctionCorrection, +) +from ReflectometryServer.footprint_manager import BaseFootprintSetup, FootprintSetup +from ReflectometryServer.geometry import ChangeAxis, Position, PositionAndAngle from ReflectometryServer.ioc_driver import IocDriver, PVWrapperForParameter from ReflectometryServer.out_of_beam import OutOfBeamPosition, OutOfBeamSequence -from ReflectometryServer.parameters import InBeamParameter, AxisParameter, DirectParameter, \ - SlitGapParameter, EnumParameter, VirtualParameter -from ReflectometryServer.pv_wrapper import MotorPVWrapper, JawsCentrePVWrapper, JawsGapPVWrapper, PVWrapper -from ReflectometryServer.config_helper import ConfigHelper, get_configured_beamline, add_constant, add_component, \ - add_parameter, add_mode, add_driver, add_slit_parameters, add_beam_start, add_footprint_setup, \ - add_component_marker, add_driver_marker, add_parameter_marker, optional_is_set, as_mode_correction, \ - SlitAxes +from ReflectometryServer.parameters import ( + AxisParameter, + DirectParameter, + EnumParameter, + InBeamParameter, + SlitGapParameter, + VirtualParameter, +) +from ReflectometryServer.pv_wrapper import ( + JawsCentrePVWrapper, + JawsGapPVWrapper, + MotorPVWrapper, + PVWrapper, +) diff --git a/ReflectometryServer/axis.py b/ReflectometryServer/axis.py index b67b9879..7861f830 100644 --- a/ReflectometryServer/axis.py +++ b/ReflectometryServer/axis.py @@ -1,21 +1,24 @@ """ Axis module, defining the position of a component. Contains associated events as well. """ + import logging from abc import ABCMeta, abstractmethod from collections import namedtuple from dataclasses import dataclass from typing import Optional -from ReflectometryServer.geometry import ChangeAxis -from server_common.channel_access import AlarmStatus, AlarmSeverity +from server_common.channel_access import AlarmSeverity, AlarmStatus from server_common.observable import observable +from ReflectometryServer.geometry import ChangeAxis + logger = logging.getLogger(__name__) # Event that is triggered when the physical position of this component changes. -PhysicalMoveUpdate = namedtuple("PhysicalMoveUpdate", [ - "source"]) # The source of the beam path change. (the axis itself) +PhysicalMoveUpdate = namedtuple( + "PhysicalMoveUpdate", ["source"] +) # The source of the beam path change. (the axis itself) # Event that is triggered when the changing state of the axis is updated (i.e. it starts or stops moving) AxisChangingUpdate = namedtuple("AxisChangingUpdate", []) @@ -26,13 +29,18 @@ class InitUpdate: """ Event that is triggered when the position or angle of the beam path calc gets an initial value. """ + pass # Event that happens when a value is redefine to a different value, e.g. offset is set from 2 to 3 -DefineValueAsEvent = namedtuple("DefineValueAsEvent", [ - "new_position", # the new value - "change_axis"]) # the axis it applies to of type ChangeAxis +DefineValueAsEvent = namedtuple( + "DefineValueAsEvent", + [ + "new_position", # the new value + "change_axis", + ], +) # the axis it applies to of type ChangeAxis @dataclass() @@ -40,6 +48,7 @@ class AddOutOfBeamPositionEvent: """ Event that is triggered an ioc driver with a parked position is added to an axis """ + source: "ComponentAxis" @@ -48,6 +57,7 @@ class AxisChangedUpdate: """ Event when the user has changed the parameter but not yet been moved to (e.g. the yellow background) """ + is_changed_update: bool # True if there is an unapplied updated; False otherwise @@ -56,6 +66,7 @@ class SetRelativeToBeamUpdate: """ Event when relative to beam has been updated """ + relative_to_beam: float @@ -64,18 +75,29 @@ class ParkingSequenceUpdate: """ Event when parking sequence has been updated """ + parking_sequence: Optional[int] # the parking sequence index set -@observable(DefineValueAsEvent, AxisChangingUpdate, PhysicalMoveUpdate, InitUpdate, AxisChangedUpdate, - SetRelativeToBeamUpdate, AddOutOfBeamPositionEvent, ParkingSequenceUpdate) +@observable( + DefineValueAsEvent, + AxisChangingUpdate, + PhysicalMoveUpdate, + InitUpdate, + AxisChangedUpdate, + SetRelativeToBeamUpdate, + AddOutOfBeamPositionEvent, + ParkingSequenceUpdate, +) class ComponentAxis(metaclass=ABCMeta): """ A components axis of movement, allowing setting in both mantid and relative coordinates. Transmits alarms, changed and changes. """ - _parking_index: Optional[int] # The last parking sequence position moved to (rbv)/about to be moved to (sp) + _parking_index: Optional[ + int + ] # The last parking sequence position moved to (rbv)/about to be moved to (sp) def __init__(self, axis: ChangeAxis): """ @@ -208,11 +230,11 @@ def is_changing(self): @is_changing.setter def is_changing(self, value): """ - Update the changing state of the component and notifies relevant listeners. Changing is usually caused by - the motor axis moving. + Update the changing state of the component and notifies relevant listeners. Changing is usually caused by + the motor axis moving. - Args: - value: the new rotating state + Args: + value: the new rotating state """ self._is_changing = value self.trigger_listeners(AxisChangingUpdate()) @@ -321,6 +343,7 @@ class DirectCalcAxis(ComponentAxis): """ Directly connect the relative and the mantid coordinates together """ + def __init__(self, axis: ChangeAxis): super(DirectCalcAxis, self).__init__(axis) self.can_define_axis_position_as = True @@ -389,6 +412,7 @@ class BeamPathCalcModificationAxis(DirectCalcAxis): This is basically a thin layer that calls the function on the direct calc axis but informs the beam path calc of what it's doing. This object is initialised with the functions to call. """ + def __init__(self, axis, update_calc_function): """ Initialiser. @@ -436,8 +460,17 @@ class BeamPathCalcAxis(ComponentAxis): This is basically a thin layer that calls the function on the axis and then delegates to the beam path calc. This object is initialised with the functions to call. """ - def __init__(self, axis, get_relative_to_beam, set_relative_to_beam, get_displacement_for=None, - get_displacement=None, set_displacement=None, init_displacement_from_motor=None): + + def __init__( + self, + axis, + get_relative_to_beam, + set_relative_to_beam, + get_displacement_for=None, + get_displacement=None, + set_displacement=None, + init_displacement_from_motor=None, + ): """ Initialiser. Args: @@ -517,11 +550,14 @@ def init_displacement_from_motor(self, value): class ReadOnlyBeamPathCalcAxis(BeamPathCalcAxis): - def __init__(self, axis, get_relative_to_beam): - super(ReadOnlyBeamPathCalcAxis, self).__init__(axis, get_relative_to_beam, self._do_nothing_function, - get_displacement=get_relative_to_beam, - set_displacement=self._do_nothing_function) + super(ReadOnlyBeamPathCalcAxis, self).__init__( + axis, + get_relative_to_beam, + self._do_nothing_function, + get_displacement=get_relative_to_beam, + set_displacement=self._do_nothing_function, + ) self.set_alarm(AlarmSeverity.No, AlarmStatus.No) def get_displacement(self): @@ -532,4 +568,3 @@ def init_displacement_from_motor(self, _): def _do_nothing_function(self, _): pass - diff --git a/ReflectometryServer/beam_path_calc.py b/ReflectometryServer/beam_path_calc.py index 0b5c8ff1..ae157d28 100644 --- a/ReflectometryServer/beam_path_calc.py +++ b/ReflectometryServer/beam_path_calc.py @@ -2,23 +2,34 @@ Objects to help with calculating the beam path when interacting with a component. This is used for instance for the set points or readbacks etc. """ -from dataclasses import dataclass - -from math import degrees, atan2, isnan -from typing import Dict, List, Tuple, Optional -from ReflectometryServer.axis import PhysicalMoveUpdate, AxisChangingUpdate, InitUpdate, ComponentAxis, \ - BeamPathCalcAxis, AddOutOfBeamPositionEvent, AxisChangedUpdate, BeamPathCalcModificationAxis, ParkingSequenceUpdate, \ - ReadOnlyBeamPathCalcAxis -from ReflectometryServer.geometry import PositionAndAngle, ChangeAxis, Position -from ReflectometryServer.exceptions import BeamlineConfigurationParkAutosaveInvalidException, \ - BeamlineConfigurationInvalidException import logging +from dataclasses import dataclass +from math import atan2, degrees, isnan +from typing import Dict, List, Optional, Tuple -from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo -from server_common.channel_access import maximum_severity, AlarmStatus, AlarmSeverity +from server_common.channel_access import AlarmSeverity, AlarmStatus, maximum_severity from server_common.observable import observable + +from ReflectometryServer.axis import ( + AddOutOfBeamPositionEvent, + AxisChangedUpdate, + AxisChangingUpdate, + BeamPathCalcAxis, + BeamPathCalcModificationAxis, + ComponentAxis, + InitUpdate, + ParkingSequenceUpdate, + PhysicalMoveUpdate, + ReadOnlyBeamPathCalcAxis, +) +from ReflectometryServer.exceptions import ( + BeamlineConfigurationInvalidException, + BeamlineConfigurationParkAutosaveInvalidException, +) from ReflectometryServer.file_io import disable_mode_autosave, parking_index_autosave +from ReflectometryServer.geometry import ChangeAxis, Position, PositionAndAngle +from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo logger = logging.getLogger(__name__) @@ -28,10 +39,10 @@ class BeamPathUpdate: """ Event that is triggered when the path of the beam has changed. """ + # The source of the beam path change. (the beam path calc itself); # None for whole beamline - source: Optional['TrackingBeamPathCalc'] - + source: Optional["TrackingBeamPathCalc"] @dataclass @@ -40,7 +51,10 @@ class BeamPathUpdateOnInit: Event that is triggered when the path of the beam has changed as a result of being initialised from file or motor rbv. """ - source: 'TrackingBeamPathCalc' # The source of the beam path change. (the beam path calc itself) + + source: ( + "TrackingBeamPathCalc" # The source of the beam path change. (the beam path calc itself) + ) @dataclass @@ -48,15 +62,24 @@ class ComponentInBeamUpdate: """ Event that is triggered when the in beam status of a component has changed. """ + value: bool -@observable(InitUpdate, AxisChangingUpdate, AxisChangedUpdate, PhysicalMoveUpdate, ParkingSequenceUpdate, ComponentInBeamUpdate) +@observable( + InitUpdate, + AxisChangingUpdate, + AxisChangedUpdate, + PhysicalMoveUpdate, + ParkingSequenceUpdate, + ComponentInBeamUpdate, +) class InBeamManager: """ Manages the in-beam status of a component as a whole by combining information from all axes on the component that can be parked. """ + # axes which are for associated with parking _parking_axes: List[ComponentAxis] @@ -81,8 +104,12 @@ def add_axes(self, axes: Dict[ChangeAxis, ComponentAxis]): axes: The axes to add """ for component_axis in axes.values(): - component_axis.add_listener(AddOutOfBeamPositionEvent, self._on_add_out_of_beam_position) - component_axis.add_listener(ParkingSequenceUpdate, self._on_axis_end_of_parking_sequence_change) + component_axis.add_listener( + AddOutOfBeamPositionEvent, self._on_add_out_of_beam_position + ) + component_axis.add_listener( + ParkingSequenceUpdate, self._on_axis_end_of_parking_sequence_change + ) def add_rbv_in_beam_manager(self, rbv_in_beam_manager): """ @@ -90,7 +117,9 @@ def add_rbv_in_beam_manager(self, rbv_in_beam_manager): Args: rbv_in_beam_manager: rbv in beam manager """ - rbv_in_beam_manager.add_listener(ParkingSequenceUpdate, self._on_at_parking_sequence_position) + rbv_in_beam_manager.add_listener( + ParkingSequenceUpdate, self._on_at_parking_sequence_position + ) def _on_add_out_of_beam_position(self, event: AddOutOfBeamPositionEvent): axis = event.source @@ -104,14 +133,20 @@ def _on_add_out_of_beam_position(self, event: AddOutOfBeamPositionEvent): # the sequence index on and cause that axis to move raise BeamlineConfigurationInvalidException( f"Beamline component {self._name} can not have parking sequences of different lengths, the axes are " - f"{[axis.get_name() for axis in self._parking_axes]}") + f"{[axis.get_name() for axis in self._parking_axes]}" + ) auto_saved_parking_index = parking_index_autosave.read_parameter(self._autosave_name, None) - if not (auto_saved_parking_index is None or auto_saved_parking_index + 1 >= self._maximum_sequence_count): - parking_index_autosave.write_parameter(self._autosave_name, None) # Make it so refl ioc can start next time - raise BeamlineConfigurationParkAutosaveInvalidException(self._name, axis.get_name(), - auto_saved_parking_index, - self._maximum_sequence_count) + if not ( + auto_saved_parking_index is None + or auto_saved_parking_index + 1 >= self._maximum_sequence_count + ): + parking_index_autosave.write_parameter( + self._autosave_name, None + ) # Make it so refl ioc can start next time + raise BeamlineConfigurationParkAutosaveInvalidException( + self._name, axis.get_name(), auto_saved_parking_index, self._maximum_sequence_count + ) axis.add_listener(AxisChangingUpdate, self._propagate_axis_event) axis.add_listener(AxisChangedUpdate, self._propagate_axis_event) @@ -175,16 +210,20 @@ def set_is_in_beam(self, is_in_beam): self._update_parking_index(new_index) # sequence 1 before last else: - logger.info(f"Set in beam; not set parking sequence is at {self.parking_index} " - f"(vals None, 0-{self._maximum_sequence_count+1})") + logger.info( + f"Set in beam; not set parking sequence is at {self.parking_index} " + f"(vals None, 0-{self._maximum_sequence_count+1})" + ) else: # if fully in the beam start out parking sequence if self.parking_index is None: self._update_parking_index(0) else: - logger.info(f"Set out of beam; not set parking sequence is at {self.parking_index} " - f"(vals None, 0-{self._maximum_sequence_count+1})") + logger.info( + f"Set out of beam; not set parking sequence is at {self.parking_index} " + f"(vals None, 0-{self._maximum_sequence_count+1})" + ) self._parking_sequence_started = True self.trigger_listeners(ComponentInBeamUpdate(is_in_beam)) @@ -214,7 +253,10 @@ def _on_at_parking_sequence_position(self, parking_sequence_update: ParkingSeque Args: parking_sequence_update: update indicating that a parking sequence has ended """ - if parking_sequence_update.parking_sequence == self.parking_index and self._parking_sequence_started: + if ( + parking_sequence_update.parking_sequence == self.parking_index + and self._parking_sequence_started + ): if self.get_is_in_beam(): # axes are unparking if self.parking_index == 0: @@ -226,11 +268,17 @@ def _on_at_parking_sequence_position(self, parking_sequence_update: ParkingSeque else: # axes are parking if self.parking_index is None: - STATUS_MANAGER.update_error_log("Parking sequence error - the parking index is None but we are " - "parking the axis, this should not be possible") + STATUS_MANAGER.update_error_log( + "Parking sequence error - the parking index is None but we are " + "parking the axis, this should not be possible" + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Next park sequence triggered but manager is in beam (report error)", - "InBeamManager", AlarmSeverity.MINOR_ALARM)) + ProblemInfo( + "Next park sequence triggered but manager is in beam (report error)", + "InBeamManager", + AlarmSeverity.MINOR_ALARM, + ) + ) if self.parking_index + 1 < self._maximum_sequence_count: self._move_axis_to(self.parking_index + 1) else: @@ -242,8 +290,10 @@ def _move_axis_to(self, new_parking_index): Args: new_parking_index: new index to move to """ - logger.info(f"MOVE {self._name} to next parking sequence {new_parking_index} " - f"(vals None, 0-{self._maximum_sequence_count+1})") + logger.info( + f"MOVE {self._name} to next parking sequence {new_parking_index} " + f"(vals None, 0-{self._maximum_sequence_count+1})" + ) self._update_parking_index(new_parking_index) for axis in self._parking_axes: axis.is_changed = True @@ -255,8 +305,10 @@ def _update_parking_index(self, new_parking_index): Args: new_parking_index: parking index to set """ - logger.info(f"MOVE {self._name} to parking sequence {new_parking_index} " - f"(vals None, 0-{self._maximum_sequence_count+1})") + logger.info( + f"MOVE {self._name} to parking sequence {new_parking_index} " + f"(vals None, 0-{self._maximum_sequence_count+1})" + ) self.parking_index = new_parking_index parking_index_autosave.write_parameter(self._autosave_name, self.parking_index) @@ -273,6 +325,7 @@ class TrackingBeamPathCalc: """ Calculator for the beam path when it interacts with a component that can be displaced relative to the beam. """ + axis: Dict[ChangeAxis, ComponentAxis] def __init__(self, name, movement_strategy): @@ -300,18 +353,21 @@ def __init__(self, name, movement_strategy): self.in_beam_manager.add_listener(AxisChangedUpdate, self._on_in_beam_status_update) self.axis = { - ChangeAxis.POSITION: BeamPathCalcAxis(ChangeAxis.POSITION, - self._get_position_relative_to_beam, - self._set_position_relative_to_beam, - self._get_displacement_for, - self._get_displacement, - self._displacement_update, - self._init_displacement_from_motor), - - ChangeAxis.LONG_AXIS: BeamPathCalcModificationAxis(ChangeAxis.LONG_AXIS, - self._on_long_axis_change), - ChangeAxis.DISPLACEMENT_POSITION: ReadOnlyBeamPathCalcAxis(ChangeAxis.DISPLACEMENT_POSITION, - self._get_displacement_at_intersect), + ChangeAxis.POSITION: BeamPathCalcAxis( + ChangeAxis.POSITION, + self._get_position_relative_to_beam, + self._set_position_relative_to_beam, + self._get_displacement_for, + self._get_displacement, + self._displacement_update, + self._init_displacement_from_motor, + ), + ChangeAxis.LONG_AXIS: BeamPathCalcModificationAxis( + ChangeAxis.LONG_AXIS, self._on_long_axis_change + ), + ChangeAxis.DISPLACEMENT_POSITION: ReadOnlyBeamPathCalcAxis( + ChangeAxis.DISPLACEMENT_POSITION, self._get_displacement_at_intersect + ), } def _init_displacement_from_motor(self, value): @@ -325,7 +381,9 @@ def _init_displacement_from_motor(self, value): if self.axis[ChangeAxis.POSITION].autosaved_value is None: logger.debug(f"Setting {self._name} displacement initial value from motor to {value}") self._movement_strategy.set_displacement(value) - self.axis[ChangeAxis.POSITION].trigger_listeners(InitUpdate()) # Tell Parameter layer and Theta + self.axis[ChangeAxis.POSITION].trigger_listeners( + InitUpdate() + ) # Tell Parameter layer and Theta def set_incoming_beam(self, incoming_beam, force=False, on_init=False): """ @@ -347,13 +405,17 @@ def set_incoming_beam(self, incoming_beam, force=False, on_init=False): # beam path update autosaved_value = self.axis[ChangeAxis.POSITION].autosaved_value if autosaved_value is not None: - self._movement_strategy.set_distance_relative_to_beam(self._incoming_beam, autosaved_value) + self._movement_strategy.set_distance_relative_to_beam( + self._incoming_beam, autosaved_value + ) self.trigger_listeners(BeamPathUpdateOnInit(self)) else: self.trigger_listeners(BeamPathUpdate(self)) def _update_beam_path_axes(self): - self.axis[ChangeAxis.DISPLACEMENT_POSITION].set_relative_to_beam(self._get_displacement_at_intersect()) + self.axis[ChangeAxis.DISPLACEMENT_POSITION].set_relative_to_beam( + self._get_displacement_at_intersect() + ) def _on_set_incoming_beam(self, incoming_beam, on_init): """ @@ -414,7 +476,9 @@ def calculate_beam_interception(self): Returns: the position at the point where the components possible movement intercepts the beam (the beam is the theta beam if set or the incoming beam if not) """ - return self._movement_strategy.calculate_interception(self._theta_incoming_beam_if_set_else_incoming_beam()) + return self._movement_strategy.calculate_interception( + self._theta_incoming_beam_if_set_else_incoming_beam() + ) def _set_position_relative_to_beam(self, displacement): """ @@ -434,7 +498,8 @@ def _get_position_relative_to_beam(self): """ return self._movement_strategy.get_distance_relative_to_beam( - self._theta_incoming_beam_if_set_else_incoming_beam()) + self._theta_incoming_beam_if_set_else_incoming_beam() + ) def _theta_incoming_beam_if_set_else_incoming_beam(self): """ @@ -476,8 +541,9 @@ def _get_displacement_for(self, position_relative_to_beam): Returns (float): displacement in mantid coordinates """ - return self._movement_strategy.get_displacement_relative_to_beam_for(self._incoming_beam, - position_relative_to_beam) + return self._movement_strategy.get_displacement_relative_to_beam_for( + self._incoming_beam, position_relative_to_beam + ) def _get_displacement_at_intersect(self): return self._get_displacement_for(0) @@ -494,7 +560,8 @@ def get_distance_relative_to_beam_in_mantid_coordinates(self): is the theta beam if set otherwise incoming beam """ return self._movement_strategy.get_distance_relative_to_beam_in_mantid_coordinates( - self._theta_incoming_beam_if_set_else_incoming_beam()) + self._theta_incoming_beam_if_set_else_incoming_beam() + ) def intercept_in_mantid_coordinates(self, on_init=False): """ @@ -560,7 +627,9 @@ def init_beam_from_autosave(self): incoming_beam = disable_mode_autosave.read_parameter(self._name, None) if incoming_beam is None: incoming_beam = PositionAndAngle(0, 0, 0) - logger.error("Incoming beam was not initialised for component {}".format(self._name)) + logger.error( + "Incoming beam was not initialised for component {}".format(self._name) + ) self.set_incoming_beam(incoming_beam, force=True, on_init=True) # re trigger on init specifically so that if this is the component theta depends on theta get reset for axis in self.axis.values(): @@ -575,6 +644,7 @@ class _BeamPathCalcWithAngle(TrackingBeamPathCalc): Calculator for the beam path when it interacts with a component that can be displaced and rotated relative to the beam. The rotation angle is set implicitly and is read only. """ + def __init__(self, name, movement_strategy, is_reflecting): """ Initialise. @@ -587,12 +657,15 @@ def __init__(self, name, movement_strategy, is_reflecting): super(_BeamPathCalcWithAngle, self).__init__(name, movement_strategy) self._angular_displacement = 0.0 self._is_reflecting = is_reflecting - self.axis[ChangeAxis.DISPLACEMENT_ANGLE] = ReadOnlyBeamPathCalcAxis(ChangeAxis.DISPLACEMENT_ANGLE, - self._get_angular_displacement_at_intersect) + self.axis[ChangeAxis.DISPLACEMENT_ANGLE] = ReadOnlyBeamPathCalcAxis( + ChangeAxis.DISPLACEMENT_ANGLE, self._get_angular_displacement_at_intersect + ) def _update_beam_path_axes(self): super()._update_beam_path_axes() - self.axis[ChangeAxis.DISPLACEMENT_ANGLE].set_relative_to_beam(self._get_angular_displacement_at_intersect()) + self.axis[ChangeAxis.DISPLACEMENT_ANGLE].set_relative_to_beam( + self._get_angular_displacement_at_intersect() + ) def _set_angular_displacement(self, angle): """ @@ -646,7 +719,7 @@ def get_outgoing_beam(self): return self._incoming_beam target_position = self.calculate_beam_interception() - angle_between_beam_and_component = (self._angular_displacement - self._incoming_beam.angle) + angle_between_beam_and_component = self._angular_displacement - self._incoming_beam.angle angle = angle_between_beam_and_component * 2 + self._incoming_beam.angle outgoing_beam = PositionAndAngle(target_position.y, target_position.z, angle) return outgoing_beam @@ -657,15 +730,18 @@ class SettableBeamPathCalcWithAngle(_BeamPathCalcWithAngle): Calculator for the beam path when it interacts with a component that can be displaced and rotated relative to the beam, and where the angle can be both read and set explicitly. """ + def __init__(self, name, movement_strategy, is_reflecting): super(SettableBeamPathCalcWithAngle, self).__init__(name, movement_strategy, is_reflecting) - self.axis[ChangeAxis.ANGLE] = BeamPathCalcAxis(ChangeAxis.ANGLE, - self._get_angle_relative_to_beam, - self._set_angle_relative_to_beam, - self._get_angle_for, - self._get_angular_displacement, - self._set_angular_displacement, - self._init_angle_from_motor) + self.axis[ChangeAxis.ANGLE] = BeamPathCalcAxis( + ChangeAxis.ANGLE, + self._get_angle_relative_to_beam, + self._set_angle_relative_to_beam, + self._get_angle_for, + self._get_angular_displacement, + self._set_angular_displacement, + self._init_angle_from_motor, + ) def _init_angle_from_motor(self, angle): """ @@ -696,6 +772,7 @@ class BeamPathCalcThetaRBV(_BeamPathCalcWithAngle): A reflecting beam path calculator which has a read only angle based on the angle to a list of beam path calculations. This is used for example for Theta where the angle is the angle to the next enabled component. """ + _angle_to: List[Tuple[TrackingBeamPathCalc, TrackingBeamPathCalc, List[ChangeAxis]]] def __init__(self, name, movement_strategy, theta_setpoint_beam_path_calc): @@ -709,14 +786,16 @@ def __init__(self, name, movement_strategy, theta_setpoint_beam_path_calc): """ super(BeamPathCalcThetaRBV, self).__init__(name, movement_strategy, is_reflecting=True) - self.axis[ChangeAxis.ANGLE] = BeamPathCalcAxis(ChangeAxis.ANGLE, - self._get_angle_relative_to_beam, - self._set_angle_relative_to_beam) + self.axis[ChangeAxis.ANGLE] = BeamPathCalcAxis( + ChangeAxis.ANGLE, self._get_angle_relative_to_beam, self._set_angle_relative_to_beam + ) self.axis[ChangeAxis.ANGLE].can_define_axis_position_as = False self._angle_to = [] self.theta_setpoint_beam_path_calc = theta_setpoint_beam_path_calc - self._add_pre_trigger_function(BeamPathUpdate, self._set_incoming_beam_at_next_angled_to_component) + self._add_pre_trigger_function( + BeamPathUpdate, self._set_incoming_beam_at_next_angled_to_component + ) def add_angle_to(self, readback_beam_path_calc, setpoint_beam_path_calc, axes): """ @@ -734,13 +813,17 @@ def add_angle_to(self, readback_beam_path_calc, setpoint_beam_path_calc, axes): # and add to beamline change of set point because no loop is created from the setpoint action for axis in axes: readback_beam_path_calc.axis[axis].add_listener(PhysicalMoveUpdate, self._angle_update) - readback_beam_path_calc.axis[axis].add_listener(AxisChangingUpdate, self._on_is_changing_change) + readback_beam_path_calc.axis[axis].add_listener( + AxisChangingUpdate, self._on_is_changing_change + ) readback_beam_path_calc.in_beam_manager.add_listener(PhysicalMoveUpdate, self._angle_update) setpoint_beam_path_calc.add_listener(BeamPathUpdate, self._angle_update) setpoint_beam_path_calc.add_listener(BeamPathUpdateOnInit, self._angle_update) - readback_beam_path_calc.in_beam_manager.add_listener(AxisChangingUpdate, self._on_is_changing_change) + readback_beam_path_calc.in_beam_manager.add_listener( + AxisChangingUpdate, self._on_is_changing_change + ) def _on_is_changing_change(self, _): """ @@ -763,7 +846,9 @@ def _on_is_changing_change(self, _): self.axis[ChangeAxis.ANGLE].is_changing = theta_is_changing def _angle_update(self, _): - self._set_angular_displacement(self._set_up_next_component_and_return_angle_set_by_it(self._incoming_beam)) + self._set_angular_displacement( + self._set_up_next_component_and_return_angle_set_by_it(self._incoming_beam) + ) def _set_up_next_component_and_return_angle_set_by_it(self, incoming_beam): """ @@ -799,15 +884,18 @@ def _set_up_next_component_and_return_angle_set_by_it(self, incoming_beam): elif ChangeAxis.ANGLE in axes: # rbv should not include setpoint offset angle other_angle = readback_beam_path_calc.axis[ChangeAxis.ANGLE].get_displacement() - other_setpoint_offset = set_point_beam_path_calc.axis[ChangeAxis.ANGLE].get_relative_to_beam() + other_setpoint_offset = set_point_beam_path_calc.axis[ + ChangeAxis.ANGLE + ].get_relative_to_beam() angle_of_outgoing_beam = other_angle - other_setpoint_offset else: raise RuntimeError("Theta can not depend on the {} axes".format(axes)) angle = (angle_of_outgoing_beam - incoming_beam.angle) / 2.0 + incoming_beam.angle # set the beam path for the selected component - readback_beam_path_calc.substitute_incoming_beam_for_displacement = \ + readback_beam_path_calc.substitute_incoming_beam_for_displacement = ( self.theta_setpoint_beam_path_calc.get_outgoing_beam() + ) severity, status = self.calculate_alarm_based_on_axes(axes, readback_beam_path_calc) self.axis[ChangeAxis.ANGLE].set_alarm(severity, status) @@ -832,7 +920,9 @@ def calculate_alarm_based_on_axes(axes, readback_beam_path_calc): all_undefined = True for axis in axes: axis_severity, axis_status = readback_beam_path_calc.axis[axis].alarm - undefined = (axis_severity == AlarmSeverity.Invalid) and (axis_status == AlarmStatus.UDF) + undefined = (axis_severity == AlarmSeverity.Invalid) and ( + axis_status == AlarmStatus.UDF + ) all_undefined &= undefined if not undefined and axis_severity > max_severity: max_severity, max_status = axis_severity, axis_status @@ -850,14 +940,19 @@ def _on_set_incoming_beam(self, incoming_beam, on_init): incoming_beam(PositionAndAngle): incoming beam on_init(bool): True if during initialisation """ - self._angular_displacement = self._set_up_next_component_and_return_angle_set_by_it(incoming_beam) + self._angular_displacement = self._set_up_next_component_and_return_angle_set_by_it( + incoming_beam + ) def _set_incoming_beam_at_next_angled_to_component(self): """ Sets the incoming beam at the next disabled component in beam that this theta component is angled to. """ for readback_beam_path_calc, _, _ in self._angle_to: - if not readback_beam_path_calc.incoming_beam_can_change and readback_beam_path_calc.is_in_beam: + if ( + not readback_beam_path_calc.incoming_beam_can_change + and readback_beam_path_calc.is_in_beam + ): readback_beam_path_calc.set_incoming_beam(self.get_outgoing_beam(), force=True) break @@ -868,6 +963,7 @@ class BeamPathCalcThetaSP(SettableBeamPathCalcWithAngle): beam on the component it is pointing at when in disable mode. It will only change the beam if the component is in the beam. """ + _angle_to: List[Tuple[TrackingBeamPathCalc, List[ChangeAxis]]] def __init__(self, name, movement_strategy): @@ -879,8 +975,12 @@ def __init__(self, name, movement_strategy): """ super(BeamPathCalcThetaSP, self).__init__(name, movement_strategy, is_reflecting=True) self._angle_to = [] - self._add_pre_trigger_function(BeamPathUpdate, self._set_incoming_beam_at_next_angled_to_component) - self._add_pre_trigger_function(BeamPathUpdateOnInit, self._set_incoming_beam_at_next_angled_to_component) + self._add_pre_trigger_function( + BeamPathUpdate, self._set_incoming_beam_at_next_angled_to_component + ) + self._add_pre_trigger_function( + BeamPathUpdateOnInit, self._set_incoming_beam_at_next_angled_to_component + ) def add_angle_to(self, beam_path_calc, axes): """ @@ -925,7 +1025,9 @@ def _calc_angle_from_next_component(self, incoming_beam): for setpoint_beam_path_calc, axis in self._angle_to: if setpoint_beam_path_calc.is_in_beam: if ChangeAxis.POSITION in axis: - other_pos = setpoint_beam_path_calc.intercept_in_mantid_coordinates(on_init=True) + other_pos = setpoint_beam_path_calc.intercept_in_mantid_coordinates( + on_init=True + ) this_pos = self._movement_strategy.calculate_interception(incoming_beam) opp = other_pos.y - this_pos.y @@ -937,11 +1039,15 @@ def _calc_angle_from_next_component(self, incoming_beam): angle_of_outgoing_beam = degrees(atan2(opp, adj)) elif ChangeAxis.ANGLE in axis: - angle_of_outgoing_beam = setpoint_beam_path_calc.axis[ChangeAxis.ANGLE].get_displacement() + angle_of_outgoing_beam = setpoint_beam_path_calc.axis[ + ChangeAxis.ANGLE + ].get_displacement() # if there is an auto saved offset then take this off before calculating theta offset = setpoint_beam_path_calc.axis[ChangeAxis.ANGLE].autosaved_value if offset is not None: - init_from_motor = setpoint_beam_path_calc.axis[ChangeAxis.ANGLE].init_from_motor + init_from_motor = setpoint_beam_path_calc.axis[ + ChangeAxis.ANGLE + ].init_from_motor angle_of_outgoing_beam = init_from_motor - offset else: raise RuntimeError("Can not set theta angle based on {} axis".format(axis)) diff --git a/ReflectometryServer/beamline.py b/ReflectometryServer/beamline.py index a26a4acc..9cfc9073 100644 --- a/ReflectometryServer/beamline.py +++ b/ReflectometryServer/beamline.py @@ -1,25 +1,29 @@ """ Resources at a beamline level """ + import logging from collections import OrderedDict, defaultdict from dataclasses import dataclass from functools import partial from pcaspy import Severity +from server_common.channel_access import UnableToConnectToPVException +from server_common.observable import observable from ReflectometryServer.beam_path_calc import BeamPathUpdate, BeamPathUpdateOnInit -from ReflectometryServer.exceptions import BeamlineConfigurationInvalidException, ParameterNotInitializedException, AxisNotWithinSoftLimitsException -from ReflectometryServer.geometry import PositionAndAngle, ChangeAxis -from ReflectometryServer.file_io import mode_autosave, MODE_KEY +from ReflectometryServer.exceptions import ( + AxisNotWithinSoftLimitsException, + BeamlineConfigurationInvalidException, + ParameterNotInitializedException, +) +from ReflectometryServer.file_io import MODE_KEY, mode_autosave from ReflectometryServer.footprint_calc import BaseFootprintSetup from ReflectometryServer.footprint_manager import FootprintManager -from ReflectometryServer.parameters import RequestMoveEvent, AxisParameter +from ReflectometryServer.geometry import ChangeAxis, PositionAndAngle +from ReflectometryServer.parameters import AxisParameter, RequestMoveEvent from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo -from server_common.channel_access import UnableToConnectToPVException -from server_common.observable import observable - logger = logging.getLogger(__name__) @@ -35,7 +39,9 @@ def check_long_axis_before_position(axes_per_component, error_string): for component, axes in axes_per_component.items(): if ChangeAxis.LONG_AXIS in axes and ChangeAxis.POSITION in axes: if axes.index(ChangeAxis.LONG_AXIS) > axes.index(ChangeAxis.POSITION): - errors.append(f"Long axis {error_string} must be added before position for '{component}'") + errors.append( + f"Long axis {error_string} must be added before position for '{component}'" + ) return errors @@ -75,7 +81,7 @@ def __init__(self, name, beamline_parameters_to_calculate, sp_inits=None, is_dis def names_of_parameters_in_mode(self): return self._beamline_parameters_to_calculate - #TODO move this to use above! + # TODO move this to use above! def get_parameters_in_mode(self, beamline_parameters, first_parameter=None): """ Returns, in order, all those parameters which are in this mode. Starting with the parameter after the first @@ -118,19 +124,28 @@ def validate(self, beamline_parameters): errors = [] for beamline_parameter in self._beamline_parameters_to_calculate: if beamline_parameter not in beamline_parameters: - errors.append("Beamline parameter '{}' in mode '{}' not in beamline".format( - beamline_parameters, self.name)) + errors.append( + "Beamline parameter '{}' in mode '{}' not in beamline".format( + beamline_parameters, self.name + ) + ) for sp_init in self._sp_inits.keys(): if sp_init not in beamline_parameters: - errors.append("SP Init '{}' in mode '{}' not in beamline".format(sp_init, self.name)) + errors.append( + "SP Init '{}' in mode '{}' not in beamline".format(sp_init, self.name) + ) return errors def __repr__(self): return "{}({}, disabled={}, beamline_parameters{!r}, sp_inits{!r})".format( - self.__class__.__name__, self.name, self.is_disabled, self._beamline_parameters_to_calculate, - self._sp_inits) + self.__class__.__name__, + self.name, + self.is_disabled, + self._beamline_parameters_to_calculate, + self._sp_inits, + ) @dataclass @@ -138,6 +153,7 @@ class ActiveModeUpdate: """ Event that is triggered when the active mode is changed """ + mode: BeamlineMode # mode that has been changed to @@ -147,8 +163,16 @@ class Beamline: The collection of all beamline components. """ - def __init__(self, components, beamline_parameters, drivers, modes, incoming_beam=None, - footprint_setup=None, beamline_constants=None): + def __init__( + self, + components, + beamline_parameters, + drivers, + modes, + incoming_beam=None, + footprint_setup=None, + beamline_constants=None, + ): """ The initializer. Args: @@ -174,7 +198,9 @@ def __init__(self, components, beamline_parameters, drivers, modes, incoming_bea self.footprint_manager = FootprintManager(footprint_setup) for beamline_parameter in beamline_parameters: self._beamline_parameters[beamline_parameter.name] = beamline_parameter - beamline_parameter.add_listener(RequestMoveEvent, self._move_for_single_beamline_parameters) + beamline_parameter.add_listener( + RequestMoveEvent, self._move_for_single_beamline_parameters + ) self._modes = OrderedDict() for mode in modes: @@ -185,15 +211,26 @@ def __init__(self, components, beamline_parameters, drivers, modes, incoming_bea for component in components: self._beam_path_calcs_set_point.append(component.beam_path_set_point) self._beam_path_calcs_rbv.append(component.beam_path_rbv) - component.beam_path_set_point.add_listener(BeamPathUpdateOnInit, self.update_next_beam_component_on_init) - component.beam_path_set_point.add_listener(BeamPathUpdate, partial( - self.update_next_beam_component, calc_path_list=self._beam_path_calcs_set_point)) - component.beam_path_rbv.add_listener(BeamPathUpdate, partial( - self.update_next_beam_component, calc_path_list=self._beam_path_calcs_rbv)) + component.beam_path_set_point.add_listener( + BeamPathUpdateOnInit, self.update_next_beam_component_on_init + ) + component.beam_path_set_point.add_listener( + BeamPathUpdate, + partial( + self.update_next_beam_component, calc_path_list=self._beam_path_calcs_set_point + ), + ) + component.beam_path_rbv.add_listener( + BeamPathUpdate, + partial(self.update_next_beam_component, calc_path_list=self._beam_path_calcs_rbv), + ) component.beam_path_set_point.in_beam_manager.add_rbv_in_beam_manager( - component.beam_path_rbv.in_beam_manager) + component.beam_path_rbv.in_beam_manager + ) - self._incoming_beam = incoming_beam if incoming_beam is not None else PositionAndAngle(0, 0, 0) + self._incoming_beam = ( + incoming_beam if incoming_beam is not None else PositionAndAngle(0, 0, 0) + ) self.update_next_beam_component(BeamPathUpdate(None), self._beam_path_calcs_rbv) self.update_next_beam_component(BeamPathUpdate(None), self._beam_path_calcs_set_point) @@ -228,10 +265,14 @@ def __init__(self, components, beamline_parameters, drivers, modes, incoming_bea def _validate(self, beamline_parameters, modes, drivers): errors = [] - beamline_parameters_names = [beamline_parameter.name for beamline_parameter in beamline_parameters] + beamline_parameters_names = [ + beamline_parameter.name for beamline_parameter in beamline_parameters + ] for name in beamline_parameters_names: if beamline_parameters_names.count(name) > 1: - errors.append("Beamline parameters must be uniquely named. Duplicate '{}'".format(name)) + errors.append( + "Beamline parameters must be uniquely named. Duplicate '{}'".format(name) + ) mode_names = [mode.name for mode in modes] for mode in mode_names: @@ -256,8 +297,11 @@ def _validate(self, beamline_parameters, modes, drivers): if len(errors) > 0: STATUS_MANAGER.update_error_log( - "Beamline configuration is invalid:\n {}".format("\n ".join(errors))) - raise BeamlineConfigurationInvalidException("Beamline configuration invalid: {}".format(";".join(errors))) + "Beamline configuration is invalid:\n {}".format("\n ".join(errors)) + ) + raise BeamlineConfigurationInvalidException( + "Beamline configuration invalid: {}".format(";".join(errors)) + ) @property def parameters(self): @@ -335,8 +379,7 @@ def __getitem__(self, item): return self._components[item] def get_param_names_in_mode(self): - """ Returns a list of the name of params in the current mode, in order. - """ + """Returns a list of the name of params in the current mode, in order.""" param_names_in_mode = [] parameters = self._beamline_parameters.values() @@ -403,8 +446,12 @@ def _move_for_all_beamline_parameters(self): beamline_parameter.move_to_sp_no_callback() except ParameterNotInitializedException: STATUS_MANAGER.update_active_problems( - ProblemInfo("Parameter not initialized. Is the configuration correct?", beamline_parameter.name, - Severity.MAJOR_ALARM)) + ProblemInfo( + "Parameter not initialized. Is the configuration correct?", + beamline_parameter.name, + Severity.MAJOR_ALARM, + ) + ) return self._move_drivers() @@ -422,7 +469,9 @@ def _move_for_single_beamline_parameters(self, request: RequestMoveEvent): logger.info("PARAMETER MOVE TRIGGERED (source: {})".format(request.source.name)) if self._active_mode.has_beamline_parameter(request.source): parameters = self._beamline_parameters.values() - parameters_in_mode = self._active_mode.get_parameters_in_mode(parameters, request.source) + parameters_in_mode = self._active_mode.get_parameters_in_mode( + parameters, request.source + ) for beamline_parameter in parameters_in_mode: beamline_parameter.move_to_sp_rbv_no_callback() @@ -446,10 +495,15 @@ def _init_params_from_mode(self): current_parameter = self._beamline_parameters[key] if current_parameter.read_only: STATUS_MANAGER.update_error_log( - f"Could not apply mode init for: {current_parameter.name} (parameter is read only)") + f"Could not apply mode init for: {current_parameter.name} (parameter is read only)" + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Could not apply mode init (parameter is read only)", current_parameter.name, - Severity.MINOR_ALARM)) + ProblemInfo( + "Could not apply mode init (parameter is read only)", + current_parameter.name, + Severity.MINOR_ALARM, + ) + ) else: self._beamline_parameters[key].sp_no_move = value logger.info("Default value applied for param {}: {}".format(key, value)) @@ -465,12 +519,14 @@ def _move_drivers(self): except (ZeroDivisionError, AxisNotWithinSoftLimitsException) as e: STATUS_MANAGER.update_error_log("Failed to perform beamline move: {}".format(e), e) STATUS_MANAGER.update_active_problems( - ProblemInfo("Failed to move driver", "beamline", Severity.MAJOR_ALARM)) + ProblemInfo("Failed to move driver", "beamline", Severity.MAJOR_ALARM) + ) return except (ValueError, UnableToConnectToPVException) as e: STATUS_MANAGER.update_error_log("Unable to connect to PV: {}".format(str(e))) STATUS_MANAGER.update_active_problems( - ProblemInfo("Unable to connect to PV", "beamline", Severity.MAJOR_ALARM)) + ProblemInfo("Unable to connect to PV", "beamline", Severity.MAJOR_ALARM) + ) return def _perform_move_for_all_drivers(self, move_duration): @@ -487,7 +543,8 @@ def _check_limits_for_all_drivers(self): (inside_limits, component_sp, hlm, llm) = driver.check_limits_against_sps() if not inside_limits: drivers_outside_of_limits.append( - f"{driver.name} setpoint {component_sp} outside of limits ({llm},{hlm}), not moving") + f"{driver.name} setpoint {component_sp} outside of limits ({llm},{hlm}), not moving" + ) if len(drivers_outside_of_limits) > 0: raise AxisNotWithinSoftLimitsException(str(drivers_outside_of_limits)) @@ -517,7 +574,9 @@ def _initialise_mode(self, modes): initial_mode = self._modes[mode_name] except KeyError: - STATUS_MANAGER.update_error_log("Mode {} not found in configuration. Setting default.".format(mode_name)) + STATUS_MANAGER.update_error_log( + "Mode {} not found in configuration. Setting default.".format(mode_name) + ) if len(modes) > 0: initial_mode = modes[0] else: diff --git a/ReflectometryServer/beamline_configuration.py b/ReflectometryServer/beamline_configuration.py index 612817cc..b44e7f08 100644 --- a/ReflectometryServer/beamline_configuration.py +++ b/ReflectometryServer/beamline_configuration.py @@ -1,19 +1,22 @@ """ Objects to Create a beamline from the configuration. """ -import logging -import os + import sys import traceback from importlib import import_module from pcaspy import Severity +from server_common.utilities import SEVERITY, print_and_log +from ReflectometryServer.beamline import ( + Beamline, + BeamlineConfigurationInvalidException, + BeamlineMode, +) from ReflectometryServer.ChannelAccess.constants import REFL_CONFIG_PATH, REFL_IOC_NAME -from ReflectometryServer.beamline import Beamline, BeamlineMode, BeamlineConfigurationInvalidException from ReflectometryServer.exceptions import BeamlineConfigurationParkAutosaveInvalidException from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo -from server_common.utilities import print_and_log, SEVERITY DEFAULT_CONFIG_FILE = "config.py" @@ -29,7 +32,12 @@ def _create_beamline_in_error(error_message): beamline = Beamline([], [], [], [error_mode]) try: STATUS_MANAGER.update_active_problems( - ProblemInfo("Error reading configuration: {}".format(error_message), "Configuration", Severity.MAJOR_ALARM)) + ProblemInfo( + "Error reading configuration: {}".format(error_message), + "Configuration", + Severity.MAJOR_ALARM, + ) + ) except Exception as e: print(e) return beamline @@ -68,34 +76,49 @@ def create_beamline_from_configuration(macros): """ try: - print_and_log("Importing get_beamline function from config.py in {}".format(REFL_CONFIG_PATH), - SEVERITY.INFO, src=REFL_IOC_NAME) + print_and_log( + "Importing get_beamline function from config.py in {}".format(REFL_CONFIG_PATH), + SEVERITY.INFO, + src=REFL_IOC_NAME, + ) sys.path.insert(0, REFL_CONFIG_PATH) # noinspection PyUnresolvedReferences config_to_load = _get_config_to_load(macros) - print_and_log("Importing get_beamline function from {} in {}".format(config_to_load, REFL_CONFIG_PATH), - SEVERITY.INFO, src=REFL_IOC_NAME) + print_and_log( + "Importing get_beamline function from {} in {}".format( + config_to_load, REFL_CONFIG_PATH + ), + SEVERITY.INFO, + src=REFL_IOC_NAME, + ) config = import_module(_get_config_to_load(macros)) beamline = config.get_beamline(macros) except ImportError as error: - - print_and_log(error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME) + print_and_log( + error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME + ) beamline = _create_beamline_in_error("Configuration not found.") except BeamlineConfigurationParkAutosaveInvalidException as error: - print_and_log(error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME) + print_and_log( + error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME + ) traceback.print_exc(file=sys.stdout) beamline = _create_beamline_in_error(str(error)) except BeamlineConfigurationInvalidException as error: - print_and_log(error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME) + print_and_log( + error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME + ) traceback.print_exc(file=sys.stdout) beamline = _create_beamline_in_error("Beamline configuration is invalid.") except Exception as error: - print_and_log(error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME) + print_and_log( + error.__class__.__name__ + ": " + str(error), SEVERITY.MAJOR, src=REFL_IOC_NAME + ) traceback.print_exc(file=sys.stdout) beamline = _create_beamline_in_error("Can not read configuration.") diff --git a/ReflectometryServer/beamline_constant.py b/ReflectometryServer/beamline_constant.py index 5dab5861..4d5aac53 100644 --- a/ReflectometryServer/beamline_constant.py +++ b/ReflectometryServer/beamline_constant.py @@ -7,6 +7,7 @@ class BeamlineConstant: """ A parameter of the beamline which is constant """ + def __init__(self, name, value, description=None): """ Initialiser. diff --git a/ReflectometryServer/components.py b/ReflectometryServer/components.py index a107e5af..7d8cf0cd 100644 --- a/ReflectometryServer/components.py +++ b/ReflectometryServer/components.py @@ -1,18 +1,31 @@ """ Components on a beam """ -from math import atan, cos, tan, radians, degrees + import logging +from math import atan, cos, degrees, radians, tan + +from server_common.channel_access import maximum_severity -from ReflectometryServer.beam_path_calc import TrackingBeamPathCalc, SettableBeamPathCalcWithAngle, \ - BeamPathCalcThetaRBV, BeamPathCalcThetaSP -from ReflectometryServer.axis import DirectCalcAxis, AxisChangedUpdate, \ - AxisChangingUpdate, PhysicalMoveUpdate, SetRelativeToBeamUpdate, DefineValueAsEvent, InitUpdate +from ReflectometryServer.axis import ( + AxisChangedUpdate, + AxisChangingUpdate, + DefineValueAsEvent, + DirectCalcAxis, + InitUpdate, + PhysicalMoveUpdate, + SetRelativeToBeamUpdate, +) +from ReflectometryServer.beam_path_calc import ( + BeamPathCalcThetaRBV, + BeamPathCalcThetaSP, + SettableBeamPathCalcWithAngle, + TrackingBeamPathCalc, +) +from ReflectometryServer.geometry import ChangeAxis, PositionAndAngle from ReflectometryServer.ioc_driver import CorrectedReadbackUpdate from ReflectometryServer.movement_strategy import LinearMovementCalc -from ReflectometryServer.geometry import ChangeAxis, PositionAndAngle from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo, Severity -from server_common.channel_access import maximum_severity logger = logging.getLogger(__name__) @@ -31,7 +44,13 @@ def __init__(self, name, setup): """ self._name = name self._init_beam_path_calcs(setup) - for axis_to_add in [ChangeAxis.PHI, ChangeAxis.CHI, ChangeAxis.PSI, ChangeAxis.TRANS, ChangeAxis.HEIGHT]: + for axis_to_add in [ + ChangeAxis.PHI, + ChangeAxis.CHI, + ChangeAxis.PSI, + ChangeAxis.TRANS, + ChangeAxis.HEIGHT, + ]: self._beam_path_set_point.axis[axis_to_add] = DirectCalcAxis(axis_to_add) self._beam_path_rbv.axis[axis_to_add] = DirectCalcAxis(axis_to_add) @@ -41,11 +60,16 @@ def __init__(self, name, setup): def __repr__(self): return "{}({} beampath sp:{!r}, beampath rbv:{!r})), ".format( - self.__class__.__name__, self._name, self._beam_path_set_point, self._beam_path_rbv) + self.__class__.__name__, self._name, self._beam_path_set_point, self._beam_path_rbv + ) def _init_beam_path_calcs(self, setup): - self._beam_path_set_point = TrackingBeamPathCalc("{}_sp".format(self.name), LinearMovementCalc(setup)) - self._beam_path_rbv = TrackingBeamPathCalc("{}_rbv".format(self.name), LinearMovementCalc(setup)) + self._beam_path_set_point = TrackingBeamPathCalc( + "{}_sp".format(self.name), LinearMovementCalc(setup) + ) + self._beam_path_rbv = TrackingBeamPathCalc( + "{}_rbv".format(self.name), LinearMovementCalc(setup) + ) @property def name(self): @@ -109,16 +133,19 @@ def __init__(self, name, setup): super(TiltingComponent, self).__init__(name, setup) def _init_beam_path_calcs(self, setup): - self._beam_path_set_point = SettableBeamPathCalcWithAngle("{}_sp".format(self.name), LinearMovementCalc(setup), - is_reflecting=False) - self._beam_path_rbv = SettableBeamPathCalcWithAngle("{}_rbv".format(self.name), LinearMovementCalc(setup), - is_reflecting=False) + self._beam_path_set_point = SettableBeamPathCalcWithAngle( + "{}_sp".format(self.name), LinearMovementCalc(setup), is_reflecting=False + ) + self._beam_path_rbv = SettableBeamPathCalcWithAngle( + "{}_rbv".format(self.name), LinearMovementCalc(setup), is_reflecting=False + ) class ReflectingComponent(Component): """ Components which reflects the beam from an reflecting surface at an angle. """ + def __init__(self, name, setup): """ Initializer. @@ -129,10 +156,12 @@ def __init__(self, name, setup): super(ReflectingComponent, self).__init__(name, setup) def _init_beam_path_calcs(self, setup): - self._beam_path_set_point = SettableBeamPathCalcWithAngle("{}_sp".format(self.name), LinearMovementCalc(setup), - is_reflecting=True) - self._beam_path_rbv = SettableBeamPathCalcWithAngle("{}_rbv".format(self.name), LinearMovementCalc(setup), - is_reflecting=True) + self._beam_path_set_point = SettableBeamPathCalcWithAngle( + "{}_sp".format(self.name), LinearMovementCalc(setup), is_reflecting=True + ) + self._beam_path_rbv = SettableBeamPathCalcWithAngle( + "{}_rbv".format(self.name), LinearMovementCalc(setup), is_reflecting=True + ) class ThetaComponent(ReflectingComponent): @@ -159,7 +188,9 @@ def add_angle_to(self, component): """ axes = [ChangeAxis.POSITION, ChangeAxis.LONG_AXIS] self._beam_path_set_point.add_angle_to(component.beam_path_set_point, axes) - self._beam_path_rbv.add_angle_to(component.beam_path_rbv, component.beam_path_set_point, axes) + self._beam_path_rbv.add_angle_to( + component.beam_path_rbv, component.beam_path_set_point, axes + ) def add_angle_of(self, component): """ @@ -171,22 +202,39 @@ def add_angle_of(self, component): """ self._beam_path_set_point.add_angle_to(component.beam_path_set_point, [ChangeAxis.ANGLE]) - self._beam_path_rbv.add_angle_to(component.beam_path_rbv, component.beam_path_set_point, [ChangeAxis.ANGLE]) + self._beam_path_rbv.add_angle_to( + component.beam_path_rbv, component.beam_path_set_point, [ChangeAxis.ANGLE] + ) def _init_beam_path_calcs(self, setup): linear_movement_calc = LinearMovementCalc(setup) - self._beam_path_set_point = BeamPathCalcThetaSP("{}_sp".format(self.name), linear_movement_calc) - self._beam_path_rbv = BeamPathCalcThetaRBV("{}_rbv".format(self.name), linear_movement_calc, - self._beam_path_set_point) + self._beam_path_set_point = BeamPathCalcThetaSP( + "{}_sp".format(self.name), linear_movement_calc + ) + self._beam_path_rbv = BeamPathCalcThetaRBV( + "{}_rbv".format(self.name), linear_movement_calc, self._beam_path_set_point + ) class BenchSetup(PositionAndAngle): """ Setup parameters for the bench component """ - def __init__(self, y, z, angle, jack_front_z, jack_rear_z, initial_table_angle, pivot_to_beam, - min_angle_for_slide, max_angle_for_slide, vertical_mode=False): + + def __init__( + self, + y, + z, + angle, + jack_front_z, + jack_rear_z, + initial_table_angle, + pivot_to_beam, + min_angle_for_slide, + max_angle_for_slide, + vertical_mode=False, + ): """ Initialise. Args: @@ -236,8 +284,12 @@ def __init__(self, name: str, setup: BenchSetup): else: self._angle_axis = ChangeAxis.ANGLE self._position_axis = ChangeAxis.POSITION - _, _, self._min_slide_position = self._calculate_motor_positions(0.0, self._min_angle_for_slide, 0.0) - _, _, self._max_slide_position = self._calculate_motor_positions(0.0, self._max_angle_for_slide, 0.0) + _, _, self._min_slide_position = self._calculate_motor_positions( + 0.0, self._min_angle_for_slide, 0.0 + ) + _, _, self._max_slide_position = self._calculate_motor_positions( + 0.0, self._max_angle_for_slide, 0.0 + ) super(TiltingComponent, self).__init__(name, setup) self._add_axis_listeners() @@ -291,7 +343,9 @@ def on_motor_axis_changed(self, update: AxisChangedUpdate): """ if not update.is_changed_update: set_point_axes = self.beam_path_set_point.axis - any_have_unapplied_update = any([set_point_axes[axis].is_changed for axis in self._motor_axes]) + any_have_unapplied_update = any( + [set_point_axes[axis].is_changed for axis in self._motor_axes] + ) if not any_have_unapplied_update: for axis in self._control_axes: set_point_axes[axis].is_changed = False @@ -321,13 +375,21 @@ def _on_physical_move(self, _): rbv_axis = self.beam_path_rbv.axis height, pivot_angle, seesaw = self._calculate_motor_rbvs(rbv_axis) - alarm_severity, alarm_status = maximum_severity(rbv_axis[ChangeAxis.JACK_FRONT].alarm, - rbv_axis[ChangeAxis.JACK_REAR].alarm, - rbv_axis[ChangeAxis.SLIDE].alarm) - - rbv_axis[self._position_axis].set_displacement(CorrectedReadbackUpdate(height, alarm_severity, alarm_status)) - rbv_axis[self._angle_axis].set_displacement(CorrectedReadbackUpdate(pivot_angle, alarm_severity, alarm_status)) - rbv_axis[ChangeAxis.SEESAW].set_displacement(CorrectedReadbackUpdate(seesaw, alarm_severity, alarm_status)) + alarm_severity, alarm_status = maximum_severity( + rbv_axis[ChangeAxis.JACK_FRONT].alarm, + rbv_axis[ChangeAxis.JACK_REAR].alarm, + rbv_axis[ChangeAxis.SLIDE].alarm, + ) + + rbv_axis[self._position_axis].set_displacement( + CorrectedReadbackUpdate(height, alarm_severity, alarm_status) + ) + rbv_axis[self._angle_axis].set_displacement( + CorrectedReadbackUpdate(pivot_angle, alarm_severity, alarm_status) + ) + rbv_axis[ChangeAxis.SEESAW].set_displacement( + CorrectedReadbackUpdate(seesaw, alarm_severity, alarm_status) + ) def _calculate_motor_rbvs(self, rbv_axis): """ @@ -343,16 +405,22 @@ def _calculate_motor_rbvs(self, rbv_axis): seesaw_sp = self.beam_path_set_point.axis[ChangeAxis.SEESAW].get_displacement() if seesaw_sp == 0.0: # assume seesaw readback is 0 - height, pivot_angle, seesaw = \ - self._calculate_pivot_height_and_angle_with_fixed_seesaw(front_jack, rear_jack, 0) + height, pivot_angle, seesaw = self._calculate_pivot_height_and_angle_with_fixed_seesaw( + front_jack, rear_jack, 0 + ) else: # assume angle is set correctly and any variation is because of seesaw and height angle_sp = self.beam_path_set_point.axis[self._angle_axis].get_displacement() - height, pivot_angle, seesaw = \ - self._calculate_pivot_height_and_seesaw_with_fixed_pivot_angle(front_jack, rear_jack, angle_sp) + height, pivot_angle, seesaw = ( + self._calculate_pivot_height_and_seesaw_with_fixed_pivot_angle( + front_jack, rear_jack, angle_sp + ) + ) return height, pivot_angle, seesaw - def _calculate_pivot_height_and_seesaw_with_fixed_pivot_angle(self, front_jack, rear_jack, pivot_angle): + def _calculate_pivot_height_and_seesaw_with_fixed_pivot_angle( + self, front_jack, rear_jack, pivot_angle + ): """ Calculate the pivot height and the seesaw value given a fixed pivot angle Args: @@ -366,10 +434,16 @@ def _calculate_pivot_height_and_seesaw_with_fixed_pivot_angle(self, front_jack, """ angle_sp_from_initial_position = pivot_angle - self._initial_table_angle tan_bench_angle_sp = tan(radians(angle_sp_from_initial_position)) - one_minus_cos_angle_sp = (1 - cos(radians(angle_sp_from_initial_position))) - height = (front_jack + rear_jack - (self._jack_front_z + self._jack_rear_z) * tan_bench_angle_sp - + 2 * self._pivot_to_beam * one_minus_cos_angle_sp) / 2.0 - seesaw = ((self._jack_rear_z - self._jack_front_z) * tan_bench_angle_sp + front_jack - rear_jack) / 2.0 + one_minus_cos_angle_sp = 1 - cos(radians(angle_sp_from_initial_position)) + height = ( + front_jack + + rear_jack + - (self._jack_front_z + self._jack_rear_z) * tan_bench_angle_sp + + 2 * self._pivot_to_beam * one_minus_cos_angle_sp + ) / 2.0 + seesaw = ( + (self._jack_rear_z - self._jack_front_z) * tan_bench_angle_sp + front_jack - rear_jack + ) / 2.0 return height, pivot_angle, seesaw def _calculate_pivot_height_and_angle_with_fixed_seesaw(self, front_jack, rear_jack, seesaw): @@ -385,10 +459,15 @@ def _calculate_pivot_height_and_angle_with_fixed_seesaw(self, front_jack, rear_j """ front_jack -= seesaw rear_jack += seesaw - tan_angle_from_initial_position = (front_jack - rear_jack) / (self._jack_front_z - self._jack_rear_z) + tan_angle_from_initial_position = (front_jack - rear_jack) / ( + self._jack_front_z - self._jack_rear_z + ) angle_from_initial_position = atan(tan_angle_from_initial_position) - height = front_jack - self._jack_front_z * tan_angle_from_initial_position + \ - self._pivot_to_beam * (1 - cos(angle_from_initial_position)) + height = ( + front_jack + - self._jack_front_z * tan_angle_from_initial_position + + self._pivot_to_beam * (1 - cos(angle_from_initial_position)) + ) pivot_angle = degrees(angle_from_initial_position) + self._initial_table_angle seesaw = 0 return height, pivot_angle, seesaw @@ -402,8 +481,9 @@ def on_set_relative_to_beam(self, _): pivot_angle = set_point_axes[self._angle_axis].get_displacement() seesaw = set_point_axes[ChangeAxis.SEESAW].get_displacement() - front_jack_height, rear_jack_height, horizontal_position = \ - self._calculate_motor_positions(pivot_height, pivot_angle, seesaw) + front_jack_height, rear_jack_height, horizontal_position = self._calculate_motor_positions( + pivot_height, pivot_angle, seesaw + ) set_point_axes[ChangeAxis.JACK_FRONT].set_relative_to_beam(front_jack_height) set_point_axes[ChangeAxis.JACK_REAR].set_relative_to_beam(rear_jack_height) @@ -422,7 +502,7 @@ def _calculate_motor_positions(self, pivot_height, pivot_angle, seesaw): """ angle_from_initial_position = pivot_angle - self._initial_table_angle tan_bench_angle = tan(radians(angle_from_initial_position)) - one_minus_cos_angle = (1 - cos(radians(angle_from_initial_position))) + one_minus_cos_angle = 1 - cos(radians(angle_from_initial_position)) # jacks height1 = self._jack_front_z * tan_bench_angle height2 = self._jack_rear_z * tan_bench_angle @@ -462,12 +542,16 @@ def on_define_position(self, define_position: DefineValueAsEvent): seesaw = define_position.new_position else: - STATUS_MANAGER.update_error_log("Define on bench using axis {} is not allowed".format(change_axis)) + STATUS_MANAGER.update_error_log( + "Define on bench using axis {} is not allowed".format(change_axis) + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Invalid bench update axis", self.name, Severity.MINOR_ALARM)) + ProblemInfo("Invalid bench update axis", self.name, Severity.MINOR_ALARM) + ) - front_jack_height, rear_jack_height, horizontal_position = \ - self._calculate_motor_positions(height, pivot_angle, seesaw) + front_jack_height, rear_jack_height, horizontal_position = self._calculate_motor_positions( + height, pivot_angle, seesaw + ) rbv_axis[ChangeAxis.JACK_FRONT].define_axis_position_as(front_jack_height) rbv_axis[ChangeAxis.JACK_REAR].define_axis_position_as(rear_jack_height) @@ -486,8 +570,9 @@ def on_init_update(self, _): # if autosave is corrupt we must default to 0 (set this on parameter too) seesaw = 0 sp_axis[ChangeAxis.SEESAW].init_displacement_from_motor(seesaw) - pivot_height, pivot_angle, seesaw = \ + pivot_height, pivot_angle, seesaw = ( self._calculate_pivot_height_and_angle_with_fixed_seesaw(front_jack, rear_jack, seesaw) + ) sp_axis[self._position_axis].init_displacement_from_motor(pivot_height) sp_axis[self._angle_axis].init_displacement_from_motor(pivot_angle) diff --git a/ReflectometryServer/config_helper.py b/ReflectometryServer/config_helper.py index 663608de..e1b8374a 100644 --- a/ReflectometryServer/config_helper.py +++ b/ReflectometryServer/config_helper.py @@ -1,31 +1,46 @@ """ Objects to help configure the beamline """ -from typing import Dict, Optional, List, Any, Union -from pcaspy import Severity - -from ReflectometryServer import Beamline, BeamlineMode, SlitGapParameter, JawsGapPVWrapper, JawsCentrePVWrapper, \ - PVWrapper, MotorPVWrapper, ConstantCorrection, ModeSelectCorrection -from ReflectometryServer.geometry import PositionAndAngle import logging +from typing import Any, Dict, List, Optional, Union + +from pcaspy import Severity -from ReflectometryServer.parameters import DEFAULT_RBV_TO_SP_TOLERANCE, EnumParameter, DirectParameter, \ - BeamlineParameter +from ReflectometryServer import ( + Beamline, + BeamlineMode, + ConstantCorrection, + JawsCentrePVWrapper, + JawsGapPVWrapper, + ModeSelectCorrection, + MotorPVWrapper, + PVWrapper, + SlitGapParameter, +) +from ReflectometryServer.parameters import ( + DEFAULT_RBV_TO_SP_TOLERANCE, + BeamlineParameter, + DirectParameter, + EnumParameter, +) from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo logger = logging.getLogger(__name__) -BLADE_DETAILS = {"N": {"name": "North", "pair": "S"}, - "S": {"name": "South", "pair": "N"}, - "E": {"name": "East", "pair": "W"}, - "W": {"name": "West", "pair": "E"}} +BLADE_DETAILS = { + "N": {"name": "North", "pair": "S"}, + "S": {"name": "South", "pair": "N"}, + "E": {"name": "East", "pair": "W"}, + "W": {"name": "West", "pair": "E"}, +} class ConfigHelper: """ Class holding configuration as it is built up """ + parameters = [] constants = [] components = [] @@ -60,10 +75,14 @@ def get_configured_beamline(): """ modes = [] for name in ConfigHelper.modes: - modes.append(BeamlineMode(name, - ConfigHelper.mode_params[name], - ConfigHelper.mode_initial_values[name], - ConfigHelper.mode_is_disabled[name])) + modes.append( + BeamlineMode( + name, + ConfigHelper.mode_params[name], + ConfigHelper.mode_initial_values[name], + ConfigHelper.mode_is_disabled[name], + ) + ) return Beamline( components=ConfigHelper.components, @@ -72,7 +91,8 @@ def get_configured_beamline(): modes=modes, incoming_beam=ConfigHelper.beam_start, footprint_setup=ConfigHelper.footprint_setup, - beamline_constants=ConfigHelper.constants) + beamline_constants=ConfigHelper.constants, + ) def add_constant(constant): @@ -133,12 +153,16 @@ def add_parameter(parameter, modes=None, mode_inits=None, marker=None): Add name to nr and polarised mode >>> nr = add_mode("NR") >>> polarised = add_mode("POLARISED") - >>> add_parameter(AxisParameter(ChangeAxis.POSITION,"name", component), modes=(nr, polarised)) + >>> add_parameter(AxisParameter(ChangeAxis.POSITION, "name", component), modes=(nr, polarised)) Add name to nr and polarised mode but with an initial value in nr of 0 >>> nr = add_mode("NR") >>> polarised = add_mode("POLARISED") - >>> add_parameter(AxisParameter(ChangeAxis.POSITION,"name", component), modes=(nr, polarised), mode_inits=(nr, 0.0)) + >>> add_parameter( + ... AxisParameter(ChangeAxis.POSITION, "name", component), + ... modes=(nr, polarised), + ... mode_inits=(nr, 0.0), + ... ) """ if marker is None: @@ -247,10 +271,12 @@ def create_blade_pv_driver(jaws_pv_prefix: str, blade: str) -> PVWrapper: """ return MotorPVWrapper("{}:J{}:MTR".format(jaws_pv_prefix, blade)) + class SlitAxes: """ Parameters relevant to slits. This is to avoid potential typos in the configuration file. """ + VertGap = "VG" VertCent = "VC" HorGap = "HG" @@ -281,9 +307,15 @@ def centres(): return [SlitAxes.VertCent, SlitAxes.HorCent] -def add_slit_parameters(slit_number: Union[str, int], rbv_to_sp_tolerance: float = DEFAULT_RBV_TO_SP_TOLERANCE, - modes: Optional[List[str]] = None, mode_inits: Optional[Dict[str, Any]] = None, - exclude: List[str] = None, include_centres: bool = False, beam_blocker: Optional[str] = None): +def add_slit_parameters( + slit_number: Union[str, int], + rbv_to_sp_tolerance: float = DEFAULT_RBV_TO_SP_TOLERANCE, + modes: Optional[List[str]] = None, + mode_inits: Optional[Dict[str, Any]] = None, + exclude: List[str] = None, + include_centres: bool = False, + beam_blocker: Optional[str] = None, +): """ Add parameters for a slit, this is horizontal and vertical gaps and centres. Also add modes, mode inits and tolerance if needed. @@ -311,7 +343,9 @@ def add_slit_parameters(slit_number: Union[str, int], rbv_to_sp_tolerance: float jaws_pv_prefix = "MOT:JAWS{}".format(slit_number) - parameters = _add_beam_block(slit_number, modes, mode_inits, exclude, beam_blocker, jaws_pv_prefix) + parameters = _add_beam_block( + slit_number, modes, mode_inits, exclude, beam_blocker, jaws_pv_prefix + ) for name in names: is_vertical = name[0] == "V" @@ -326,8 +360,9 @@ def add_slit_parameters(slit_number: Union[str, int], rbv_to_sp_tolerance: float return parameters -def _add_beam_block(slit_number, modes, mode_inits, exclude: List[str], beam_blocker: str, jaws_pv_prefix) \ - -> Dict[str, BeamlineParameter]: +def _add_beam_block( + slit_number, modes, mode_inits, exclude: List[str], beam_blocker: str, jaws_pv_prefix +) -> Dict[str, BeamlineParameter]: """ Add beam block parameters and drivers Args: @@ -345,8 +380,11 @@ def _add_beam_block(slit_number, modes, mode_inits, exclude: List[str], beam_blo if beam_blocker is not None: options = ["No"] options.extend([BLADE_DETAILS[blade]["name"] for blade in beam_blocker]) - parameter = EnumParameter("S{}BLOCK".format(slit_number), options, - description="Which blade is blocking the beam; No for none") + parameter = EnumParameter( + "S{}BLOCK".format(slit_number), + options, + description="Which blade is blocking the beam; No for none", + ) add_parameter(parameter, modes, mode_inits) parameters["block"] = parameter blade_names = set() @@ -359,7 +397,9 @@ def _add_beam_block(slit_number, modes, mode_inits, exclude: List[str], beam_blo for name in ["N", "S", "E", "W"]: if name in blade_names: driver = create_blade_pv_driver(jaws_pv_prefix, name) - add_parameter(DirectParameter("S{}{}".format(slit_number, name), driver), modes, mode_inits) + add_parameter( + DirectParameter("S{}{}".format(slit_number, name), driver), modes, mode_inits + ) parameters[name] = parameter return parameters @@ -418,9 +458,15 @@ def optional_is_set(optional_id, macros): return False else: STATUS_MANAGER.update_error_log( - ("Invalid value for IOC macro {}: {} (Expected: True/False)".format(macro_name, macro_value))) + ( + "Invalid value for IOC macro {}: {} (Expected: True/False)".format( + macro_name, macro_value + ) + ) + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Invalid IOC macro value", macro_name, Severity.MINOR_ALARM)) + ProblemInfo("Invalid IOC macro value", macro_name, Severity.MINOR_ALARM) + ) return False except KeyError: return False @@ -437,5 +483,7 @@ def as_mode_correction(correction: float, modes: List[str], default: float = 0.0 Returns: True if macro with given ID is set to True, otherwise False """ - return ModeSelectCorrection(default_correction=ConstantCorrection(default), - corrections_for_mode={mode: ConstantCorrection(correction) for mode in modes}) + return ModeSelectCorrection( + default_correction=ConstantCorrection(default), + corrections_for_mode={mode: ConstantCorrection(correction) for mode in modes}, + ) diff --git a/ReflectometryServer/engineering_corrections.py b/ReflectometryServer/engineering_corrections.py index 3fa10e2f..7f8085e9 100644 --- a/ReflectometryServer/engineering_corrections.py +++ b/ReflectometryServer/engineering_corrections.py @@ -1,6 +1,7 @@ """ Engineering correction to positions. """ + import abc import csv import logging @@ -12,11 +13,11 @@ from attr import dataclass from pcaspy import Severity from scipy.interpolate import griddata +from server_common.observable import observable -from ReflectometryServer import beamline_configuration, BeamlineMode +from ReflectometryServer import beamline_configuration from ReflectometryServer.beamline import ActiveModeUpdate from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo -from server_common.observable import observable logger = logging.getLogger(__name__) @@ -31,6 +32,7 @@ class CorrectionUpdate: """ Type for correction updates """ + correction: float # amount value is corrected by description: str # description of correction @@ -40,6 +42,7 @@ class CorrectionRecalculate: """ Correction has changed and the values need to calculating """ + reason_for_recalculate: str # reason that we need to recalculate @@ -105,6 +108,7 @@ class SymmetricEngineeringCorrection(EngineeringCorrection, metaclass=abc.ABCMet Base class for engineering corrections which are symmetric for both to axis and from axis directions. Correction is added to the value when sent to an axis. """ + @abc.abstractmethod def correction(self, setpoint): """ @@ -160,6 +164,7 @@ class ConstantCorrection(SymmetricEngineeringCorrection): """ A correction which adds a constant to the set point value when it is passed to the motor. """ + def __init__(self, offset): """ Initialize. @@ -194,6 +199,7 @@ class UserFunctionCorrection(SymmetricEngineeringCorrection): """ A correction which is calculated from a user function. """ + def __init__(self, user_correction_function, *beamline_parameters): """ Initialise. @@ -205,7 +211,8 @@ def __init__(self, user_correction_function, *beamline_parameters): user function, listed in the order they should be used in the user function """ super(UserFunctionCorrection, self).__init__( - "User function correction {}".format(user_correction_function.__name__)) + "User function correction {}".format(user_correction_function.__name__) + ) self._user_correction_function = user_correction_function self._beamline_parameters = beamline_parameters @@ -217,22 +224,41 @@ def correction(self, setpoint): Returns: the correction calculated using the users function. """ try: - return self._user_correction_function(setpoint, *[param.sp_rbv for param in self._beamline_parameters]) + return self._user_correction_function( + setpoint, *[param.sp_rbv for param in self._beamline_parameters] + ) except Exception as ex: if setpoint is None or None in [param.sp_rbv for param in self._beamline_parameters]: - non_initialised_params = [param.name for param in self._beamline_parameters if param.sp_rbv is None] + non_initialised_params = [ + param.name for param in self._beamline_parameters if param.sp_rbv is None + ] STATUS_MANAGER.update_error_log( "Engineering correction, '{}', raised exception '{}' is this because you have not coped with " - "non-autosaved value, {}".format(self.description, ex, non_initialised_params), ex) + "non-autosaved value, {}".format(self.description, ex, non_initialised_params), + ex, + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Invalid engineering correction (uses non autosaved value?)", self.description, - Severity.MINOR_ALARM)) + ProblemInfo( + "Invalid engineering correction (uses non autosaved value?)", + self.description, + Severity.MINOR_ALARM, + ) + ) else: STATUS_MANAGER.update_error_log( - "Engineering correction, '{}', raised exception '{}' ".format(self.description, ex), ex) + "Engineering correction, '{}', raised exception '{}' ".format( + self.description, ex + ), + ex, + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Engineering correction throws exception", self.description, Severity.MINOR_ALARM)) + ProblemInfo( + "Engineering correction throws exception", + self.description, + Severity.MINOR_ALARM, + ) + ) return 0 @@ -241,6 +267,7 @@ class GridDataFileReader: """ Read a file with point data in. """ + def __init__(self, filename): """ Initialise. @@ -262,10 +289,17 @@ def read(self): self._read_header(next(reader)) self._read_data(reader) except (csv.Error, ValueError) as e: - raise IOError("Problem with data in '{}', line {} error {}".format(self._filename, reader.line_num, e)) + raise IOError( + "Problem with data in '{}', line {} error {}".format( + self._filename, reader.line_num, e + ) + ) except StopIteration: - raise IOError("No data found in file for the grid data engineering correction '{}'".format( - self._filename)) + raise IOError( + "No data found in file for the grid data engineering correction '{}'".format( + self._filename + ) + ) def _read_data(self, reader): """ @@ -279,8 +313,10 @@ def _read_data(self, reader): corrections = [] for row in reader: if len(row) != len(self.variables) + 1: - raise IOError("Line {} should have the same number of entries as the header in " - "file '{}'".format(reader.line_num, self._filename)) + raise IOError( + "Line {} should have the same number of entries as the header in " + "file '{}'".format(reader.line_num, self._filename) + ) data_as_float = [float(item) for item in row] points.append(data_as_float[:-1]) corrections.append(data_as_float[-1]) @@ -298,7 +334,9 @@ def _read_header(self, row): if len(self.variables) < 1: raise IOError( "Header of file should be 'parameter names, correction' in file '{}'".format( - self._filename)) + self._filename + ) + ) @staticmethod @contextmanager @@ -313,7 +351,9 @@ def _open_file(filename): """ full_path = os.path.join(beamline_configuration.REFL_CONFIG_PATH, filename) if not os.path.isfile(full_path): - raise IOError("No such file for interpolation 1D engineering correction '{}'".format(full_path)) + raise IOError( + "No such file for interpolation 1D engineering correction '{}'".format(full_path) + ) with open(full_path) as correction_file: yield correction_file @@ -322,6 +362,7 @@ class _DummyBeamlineParameter: """ A dummy beamline parameter which returns the axis setpoint to make the list of beamline parameters easy to construct """ + def __init__(self): self.sp_rbv = 0 self.name = COLUMN_NAME_FOR_DRIVER_SETPOINT @@ -344,8 +385,10 @@ def __init__(self, grid_data_provider, *beamline_parameters, description="Interp self._grid_data_provider = grid_data_provider self._grid_data_provider.read() self.set_point_value_as_parameter = _DummyBeamlineParameter() - self._beamline_parameters = [self._find_parameter(variable, beamline_parameters) - for variable in self._grid_data_provider.variables] + self._beamline_parameters = [ + self._find_parameter(variable, beamline_parameters) + for variable in self._grid_data_provider.variables + ] self._default_correction = 0 @@ -362,13 +405,21 @@ def _find_parameter(self, parameter_name, beamline_parameters): if parameter_name.upper() == COLUMN_NAME_FOR_DRIVER_SETPOINT: return self.set_point_value_as_parameter - named_parameters = [beamline_parameter for beamline_parameter in beamline_parameters - if beamline_parameter.name.upper() == parameter_name.upper()] + named_parameters = [ + beamline_parameter + for beamline_parameter in beamline_parameters + if beamline_parameter.name.upper() == parameter_name.upper() + ] if len(named_parameters) != 1: - parameter_names = [beamline_parameter.name for beamline_parameter in beamline_parameters] - raise ValueError("Data for Interpolate Grid Data has column name '{}' which does not match either " - "'{}' or one of the beamline parameter '{}'". - format(parameter_name, COLUMN_NAME_FOR_DRIVER_SETPOINT, parameter_names)) + parameter_names = [ + beamline_parameter.name for beamline_parameter in beamline_parameters + ] + raise ValueError( + "Data for Interpolate Grid Data has column name '{}' which does not match either " + "'{}' or one of the beamline parameter '{}'".format( + parameter_name, COLUMN_NAME_FOR_DRIVER_SETPOINT, parameter_names + ) + ) return named_parameters[0] def correction(self, setpoint): @@ -381,17 +432,31 @@ def correction(self, setpoint): self.set_point_value_as_parameter.sp_rbv = setpoint evaluation_point = [param.sp_rbv for param in self._beamline_parameters] if None in evaluation_point: - non_initialised_params = [param.name for param in self._beamline_parameters if param.sp_rbv is None] - STATUS_MANAGER.update_error_log("Engineering correction, '{}', evaluated for non-autosaved value, {}" - .format(self.description, non_initialised_params)) + non_initialised_params = [ + param.name for param in self._beamline_parameters if param.sp_rbv is None + ] + STATUS_MANAGER.update_error_log( + "Engineering correction, '{}', evaluated for non-autosaved value, {}".format( + self.description, non_initialised_params + ) + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Engineering correction evaluated for non-autosaved value", self.description, - Severity.MINOR_ALARM)) + ProblemInfo( + "Engineering correction evaluated for non-autosaved value", + self.description, + Severity.MINOR_ALARM, + ) + ) interpolated_value = [0.0] else: - interpolated_value = griddata(self._grid_data_provider.points, self._grid_data_provider.corrections, - evaluation_point, 'linear', self._default_correction) + interpolated_value = griddata( + self._grid_data_provider.points, + self._grid_data_provider.corrections, + evaluation_point, + "linear", + self._default_correction, + ) return interpolated_value[0] @@ -401,8 +466,11 @@ class InterpolateGridDataCorrection(InterpolateGridDataCorrectionFromProvider): """ def __init__(self, filename, *beamline_parameters): - super(InterpolateGridDataCorrection, self).__init__(GridDataFileReader(filename), *beamline_parameters, - description="Interpolated from file {}".format(filename)) + super(InterpolateGridDataCorrection, self).__init__( + GridDataFileReader(filename), + *beamline_parameters, + description="Interpolated from file {}".format(filename), + ) class ModeSelectCorrection(EngineeringCorrection): @@ -410,8 +478,11 @@ class ModeSelectCorrection(EngineeringCorrection): This will select an engineering correction based on the current mode. """ - def __init__(self, default_correction: EngineeringCorrection, - corrections_for_mode: Dict[str, EngineeringCorrection]): + def __init__( + self, + default_correction: EngineeringCorrection, + corrections_for_mode: Dict[str, EngineeringCorrection], + ): """ Initialisation. Args: diff --git a/ReflectometryServer/example_beamline.py b/ReflectometryServer/example_beamline.py index dd073678..e20afec8 100644 --- a/ReflectometryServer/example_beamline.py +++ b/ReflectometryServer/example_beamline.py @@ -1,6 +1,6 @@ from ReflectometryServer.beamline import Beamline, BeamlineMode from ReflectometryServer.components import Component, ReflectingComponent -from ReflectometryServer.geometry import PositionAndAngle, PositionAndAngle, ChangeAxis +from ReflectometryServer.geometry import ChangeAxis, PositionAndAngle from ReflectometryServer.parameters import AxisParameter @@ -16,10 +16,14 @@ def create_beamline(): s1 = Component("s1", setup=PositionAndAngle(0, 1, perp_to_floor)) frame_overlap_mirror = ReflectingComponent("FOM", setup=PositionAndAngle(0, 2, perp_to_floor)) frame_overlap_mirror.beam_path_set_point.is_in_beam = False - polarising_mirror = ReflectingComponent("Polarising mirror", setup=PositionAndAngle(0, 3, perp_to_floor)) + polarising_mirror = ReflectingComponent( + "Polarising mirror", setup=PositionAndAngle(0, 3, perp_to_floor) + ) polarising_mirror.beam_path_set_point.is_in_beam = False s2 = Component("s2", setup=PositionAndAngle(0, 4, perp_to_floor)) - ideal_sample_point = ReflectingComponent("Ideal Sample Point", setup=PositionAndAngle(0, 5, perp_to_floor)) + ideal_sample_point = ReflectingComponent( + "Ideal Sample Point", setup=PositionAndAngle(0, 5, perp_to_floor) + ) s3 = Component("s3", setup=PositionAndAngle(0, 6, perp_to_floor)) analyser = ReflectingComponent("analyser", setup=PositionAndAngle(0, 7, perp_to_floor)) analyser.beam_path_set_point.is_in_beam = False @@ -29,10 +33,23 @@ def create_beamline(): theta = AxisParameter("theta", ideal_sample_point, ChangeAxis.ANGLE) nr_mode = BeamlineMode("NR", ["theta"]) beamline = Beamline( - [s0, s1, frame_overlap_mirror, polarising_mirror, s2, ideal_sample_point, s3, analyser, s4, detector], + [ + s0, + s1, + frame_overlap_mirror, + polarising_mirror, + s2, + ideal_sample_point, + s3, + analyser, + s4, + detector, + ], [theta], [], - [nr_mode], beam_start) + [nr_mode], + beam_start, + ) beamline.active_mode = nr_mode.name @@ -41,14 +58,18 @@ def create_beamline(): def generate_theta_movement(): beamline = create_beamline() - positions_z = [component.beam_path_set_point.calculate_beam_interception().z for component in beamline] + positions_z = [ + component.beam_path_set_point.calculate_beam_interception().z for component in beamline + ] positions_z.insert(0, "z position") positions = [ positions_z, ] for theta in range(0, 20, 1): beamline.parameter("theta").sp_move = theta * 1.0 - positions_y = [component.beam_path_set_point.calculate_beam_interception().y for component in beamline] + positions_y = [ + component.beam_path_set_point.calculate_beam_interception().y for component in beamline + ] positions_y.insert(0, "theta {}".format(theta)) positions.append(positions_y) @@ -57,14 +78,16 @@ def generate_theta_movement(): beamline[3].beam_path_set_point.set_angular_displacement(sm_angle) for theta in range(0, 20, 1): beamline.parameter("theta").sp_move = theta * 1.0 - positions_y = [component.beam_path_set_point.calculate_beam_interception().y for component in beamline] + positions_y = [ + component.beam_path_set_point.calculate_beam_interception().y for component in beamline + ] positions_y.insert(0, "theta {} sman{}".format(theta, sm_angle)) positions.append(positions_y) return positions -if __name__ == '__main__': +if __name__ == "__main__": thetas = generate_theta_movement() with open("example.csv", mode="w") as f: diff --git a/ReflectometryServer/exceptions.py b/ReflectometryServer/exceptions.py index ff563c2c..cebd529f 100644 --- a/ReflectometryServer/exceptions.py +++ b/ReflectometryServer/exceptions.py @@ -7,6 +7,7 @@ class BeamlineConfigurationInvalidException(Exception): """ Exception for when a parameter is not initialized. """ + def __init__(self, err): self.message = str(err) @@ -18,6 +19,7 @@ class ParameterNotInitializedException(Exception): """ Exception for when a parameter is not initialized. """ + def __init__(self, err): self.message = str(err) @@ -37,18 +39,21 @@ def __init__(self, component_name, axis_name, sequence, max_sequence): self.max_sequence = max_sequence def __str__(self): - return f"The component {self.component_name} appears to be in the middle of a parking sequence, " \ - f"{self.sequence} of {self.max_sequence} steps. The error was found on axis {self.axis_name} but " \ - f"other axes may be involved. Please contact an instrument scientists and have them move the " \ - f"component back into the beam before anything else is moved. The positions can be found in the " \ - f"configuration file. The autosave position has now been overwritten so restarting the reflectometry " \ - f"server will not work." + return ( + f"The component {self.component_name} appears to be in the middle of a parking sequence, " + f"{self.sequence} of {self.max_sequence} steps. The error was found on axis {self.axis_name} but " + f"other axes may be involved. Please contact an instrument scientists and have them move the " + f"component back into the beam before anything else is moved. The positions can be found in the " + f"configuration file. The autosave position has now been overwritten so restarting the reflectometry " + f"server will not work." + ) class AxisNotWithinSoftLimitsException(Exception): """ Raised when a component's proposed setpoint is outside its soft limits. """ + def __init__(self, err): self.message = str(err) diff --git a/ReflectometryServer/file_io.py b/ReflectometryServer/file_io.py index f46178ee..d4622354 100644 --- a/ReflectometryServer/file_io.py +++ b/ReflectometryServer/file_io.py @@ -4,11 +4,16 @@ import logging +from server_common.autosave import ( + AutosaveFile, + BoolConversion, + FloatConversion, + OptionalIntConversion, + StringConversion, +) -from ReflectometryServer.geometry import PositionAndAngle from ReflectometryServer.ChannelAccess.constants import REFL_AUTOSAVE_PATH -from server_common.autosave import AutosaveFile, FloatConversion, BoolConversion, StringConversion, \ - OptionalIntConversion +from ReflectometryServer.geometry import PositionAndAngle logger = logging.getLogger(__name__) @@ -21,31 +26,61 @@ MODE_KEY = "mode" # the mode autosave service -mode_autosave = AutosaveFile(service_name="refl", file_name=MODE_AUTOSAVE_FILE, folder=REFL_AUTOSAVE_PATH) +mode_autosave = AutosaveFile( + service_name="refl", file_name=MODE_AUTOSAVE_FILE, folder=REFL_AUTOSAVE_PATH +) # the disable mode autosave service -disable_mode_autosave = AutosaveFile(service_name="refl", file_name=DISABLE_MODE_AUTOSAVE_FILE, - conversion=PositionAndAngle, folder=REFL_AUTOSAVE_PATH) +disable_mode_autosave = AutosaveFile( + service_name="refl", + file_name=DISABLE_MODE_AUTOSAVE_FILE, + conversion=PositionAndAngle, + folder=REFL_AUTOSAVE_PATH, +) # the parameter autosave service for floats -param_float_autosave = AutosaveFile(service_name="refl", file_name=PARAM_AUTOSAVE_FILE, - conversion=FloatConversion, folder=REFL_AUTOSAVE_PATH) +param_float_autosave = AutosaveFile( + service_name="refl", + file_name=PARAM_AUTOSAVE_FILE, + conversion=FloatConversion, + folder=REFL_AUTOSAVE_PATH, +) # the parameter autosave service for booleans -param_bool_autosave = AutosaveFile(service_name="refl", file_name=PARAM_AUTOSAVE_FILE, - conversion=BoolConversion, folder=REFL_AUTOSAVE_PATH) +param_bool_autosave = AutosaveFile( + service_name="refl", + file_name=PARAM_AUTOSAVE_FILE, + conversion=BoolConversion, + folder=REFL_AUTOSAVE_PATH, +) # the parameter autosave service for strings -param_string_autosave = AutosaveFile(service_name="refl", file_name=PARAM_AUTOSAVE_FILE, - conversion=StringConversion, folder=REFL_AUTOSAVE_PATH) +param_string_autosave = AutosaveFile( + service_name="refl", + file_name=PARAM_AUTOSAVE_FILE, + conversion=StringConversion, + folder=REFL_AUTOSAVE_PATH, +) # the velocity autosave service for floats -velocity_float_autosave = AutosaveFile(service_name="refl", file_name=VELOCITY_AUTOSAVE_FILE, - conversion=FloatConversion, folder=REFL_AUTOSAVE_PATH) +velocity_float_autosave = AutosaveFile( + service_name="refl", + file_name=VELOCITY_AUTOSAVE_FILE, + conversion=FloatConversion, + folder=REFL_AUTOSAVE_PATH, +) # the velocity autosave service for booleans -velocity_bool_autosave = AutosaveFile(service_name="refl", file_name=VELOCITY_AUTOSAVE_FILE, - conversion=BoolConversion, folder=REFL_AUTOSAVE_PATH) +velocity_bool_autosave = AutosaveFile( + service_name="refl", + file_name=VELOCITY_AUTOSAVE_FILE, + conversion=BoolConversion, + folder=REFL_AUTOSAVE_PATH, +) -parking_index_autosave = AutosaveFile(service_name="refl", file_name=COMPONENT_AUTOSAVE_FILE, - conversion=OptionalIntConversion, folder=REFL_AUTOSAVE_PATH) +parking_index_autosave = AutosaveFile( + service_name="refl", + file_name=COMPONENT_AUTOSAVE_FILE, + conversion=OptionalIntConversion, + folder=REFL_AUTOSAVE_PATH, +) diff --git a/ReflectometryServer/footprint_calc.py b/ReflectometryServer/footprint_calc.py index 9ffb30d0..0179daaa 100644 --- a/ReflectometryServer/footprint_calc.py +++ b/ReflectometryServer/footprint_calc.py @@ -1,8 +1,9 @@ """ Footprint calculations. """ + from itertools import combinations -from math import sin, tan, atan, degrees, radians, pi +from math import atan, degrees, pi, radians, sin, tan S1_ID = "SLIT1" S2_ID = "SLIT2" @@ -15,6 +16,7 @@ class BaseFootprintSetup: """ Blank setup for a footprint calculation (for default use). """ + def __init__(self, theta=None, lambda_min=0, lambda_max=0): self.lambda_min = float(lambda_min) self.lambda_max = float(lambda_max) @@ -28,8 +30,22 @@ class FootprintSetup(BaseFootprintSetup): """ Normal setup for a footprint calculation. """ - def __init__(self, pos_s1, pos_s2, pos_s3, pos_s4, pos_sample, s1vg, s2vg, s3vg, s4vg, theta, - lambda_min, lambda_max): + + def __init__( + self, + pos_s1, + pos_s2, + pos_s3, + pos_s4, + pos_sample, + s1vg, + s2vg, + s3vg, + s4vg, + theta, + lambda_min, + lambda_max, + ): """ Args: pos_s1: Z Position of slit 1 (along the beam) @@ -46,17 +62,19 @@ def __init__(self, pos_s1, pos_s2, pos_s3, pos_s4, pos_sample, s1vg, s2vg, s3vg, lambda_max: Maximum lambda for this beamline """ super(FootprintSetup, self).__init__(theta, lambda_min, lambda_max) - self.positions = {S1_ID: 0.0, - S2_ID: float(pos_s2 - pos_s1), - S3_ID: float(pos_s3 - pos_s1), - S4_ID: float(pos_s4 - pos_s1), - SA_ID: float(pos_sample - pos_s1), - } - self.gap_params = {S1_ID: s1vg, - S2_ID: s2vg, - S3_ID: s3vg, - S4_ID: s4vg, - } + self.positions = { + S1_ID: 0.0, + S2_ID: float(pos_s2 - pos_s1), + S3_ID: float(pos_s3 - pos_s1), + S4_ID: float(pos_s4 - pos_s1), + SA_ID: float(pos_sample - pos_s1), + } + self.gap_params = { + S1_ID: s1vg, + S2_ID: s2vg, + S3_ID: s3vg, + S4_ID: s4vg, + } class FootprintCalculator: @@ -78,7 +96,7 @@ def get_param_value(self, param): Returns: correct value of the parameter to use for calculation. """ - raise NotImplemented("This must be implemented in the sub class") + raise NotImplementedError("This must be implemented in the sub class") def update_gaps(self): """ @@ -161,7 +179,7 @@ def get_gap(self, comp): Args: comp (String): The key of the component for which to get the gap size - + Returns: The gap size of the component or its equivalent for the sample reflection. """ if comp is SA_ID: @@ -231,8 +249,13 @@ def calc_gaps(self, theta_rad, resolution, footprint): Returns: The slit gaps for slit 1 and 2 """ theta_deg = degrees(theta_rad) - sv1 = 2 * self.distance(S1_ID, SA_ID) * tan(resolution * theta_deg) - footprint * sin(theta_deg) - sv2 = self.distance(S1_ID, S2_ID) * (footprint * sin(theta_deg)) / self.distance(S1_ID, SA_ID) - sv1 + sv1 = 2 * self.distance(S1_ID, SA_ID) * tan(resolution * theta_deg) - footprint * sin( + theta_deg + ) + sv2 = ( + self.distance(S1_ID, S2_ID) * (footprint * sin(theta_deg)) / self.distance(S1_ID, SA_ID) + - sv1 + ) return sv1, sv2 @@ -240,6 +263,7 @@ class FootprintCalculatorSetpoint(FootprintCalculator): """ Calculates the footprint based on the setpoint values of beamline parameters. """ + def __init__(self, setup): """ Args: @@ -258,6 +282,7 @@ class FootprintCalculatorSetpointReadback(FootprintCalculator): """ Calculates the footprint based on the setpoint readback values of beamline parameters. """ + def __init__(self, setup): """ Args: @@ -276,6 +301,7 @@ class FootprintCalculatorReadback(FootprintCalculator): """ Calculates the footprint based on the readback values of beamline parameters. """ + def __init__(self, setup): """ Args: diff --git a/ReflectometryServer/footprint_manager.py b/ReflectometryServer/footprint_manager.py index 8dff7a86..1a215582 100644 --- a/ReflectometryServer/footprint_manager.py +++ b/ReflectometryServer/footprint_manager.py @@ -1,6 +1,7 @@ """ Manager for the footprint calc. """ + from enum import Enum from ReflectometryServer.footprint_calc import * @@ -14,6 +15,7 @@ class FootprintSort(Enum): """ Enum for the type of footprint calculator """ + SP = 0 RBV = 1 SP_RBV = 2 @@ -39,6 +41,7 @@ class FootprintManager: """ Holds instances of the footprint calculator and manages access to them. """ + def __init__(self, footprint_setup): """ Args: @@ -136,5 +139,3 @@ def _get_footprint_calc_by_sort(self, sort): elif sort is FootprintSort.RBV: return self._footprint_calc_rbv return None - - diff --git a/ReflectometryServer/geometry.py b/ReflectometryServer/geometry.py index 6c171dc0..cf3cfe44 100644 --- a/ReflectometryServer/geometry.py +++ b/ReflectometryServer/geometry.py @@ -1,15 +1,16 @@ """ Objects and classes that handle geometry """ -from math import radians, sin, cos from enum import Enum +from math import cos, radians, sin class Position: """ The beam position and direction """ + def __init__(self, y, z): self.z = float(z) self.y = float(y) @@ -64,7 +65,7 @@ def autosave_convert_for_read(autosave_read_value): """ try: - y, z, angle = autosave_read_value[len(PositionAndAngle.__name__)+1:-1].split(",") + y, z, angle = autosave_read_value[len(PositionAndAngle.__name__) + 1 : -1].split(",") return PositionAndAngle(float(y), float(z), float(angle)) except (TypeError, ValueError): raise ValueError("Converting from string to {}".format(PositionAndAngle.__name__)) @@ -95,17 +96,18 @@ class ChangeAxis(Enum): NB Usually POSITION is used instead of HEIGHT and ANGLE instead of PHI so they track the beam. """ - POSITION = 0 # tracking position in collimation axis (i.e. height for horizontal samples) - ANGLE = 1 # tracking angle in plane of collimation - SEESAW = 2 # Tip of bench - PHI = 3 # angle like pitch - CHI = 4 # angle like yaw - PSI = 5 # angle like roll - HEIGHT = 6 # height axis perpendicular to beam and the floor - TRANS = 7 # translation axis perpendicular to beam and parallel to the floor - LONG_AXIS = 8 # axis along the beam + + POSITION = 0 # tracking position in collimation axis (i.e. height for horizontal samples) + ANGLE = 1 # tracking angle in plane of collimation + SEESAW = 2 # Tip of bench + PHI = 3 # angle like pitch + CHI = 4 # angle like yaw + PSI = 5 # angle like roll + HEIGHT = 6 # height axis perpendicular to beam and the floor + TRANS = 7 # translation axis perpendicular to beam and parallel to the floor + LONG_AXIS = 8 # axis along the beam JACK_FRONT = 9 # on the bench the jack at the front of the table JACK_REAR = 10 # on the bench the jack at the rear of the table - SLIDE = 11 # on the bench the horizontal slide + SLIDE = 11 # on the bench the horizontal slide DISPLACEMENT_POSITION = 12 DISPLACEMENT_ANGLE = 13 diff --git a/ReflectometryServer/ioc_driver.py b/ReflectometryServer/ioc_driver.py index 06d68a98..c0802f1c 100644 --- a/ReflectometryServer/ioc_driver.py +++ b/ReflectometryServer/ioc_driver.py @@ -1,35 +1,48 @@ """ The driving layer communicates between the component layer and underlying pvs. """ -import math + import logging +import math from collections import namedtuple from dataclasses import dataclass -from typing import Dict, Any, List, Optional, TYPE_CHECKING, Tuple +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple from pcaspy import Severity if TYPE_CHECKING: from ReflectometryServer.components import Component -from ReflectometryServer.axis import DefineValueAsEvent, ParkingSequenceUpdate -from ReflectometryServer.out_of_beam import OutOfBeamLookup, OutOfBeamPosition -from ReflectometryServer.engineering_corrections import NoCorrection, CorrectionUpdate, CorrectionRecalculate, \ - EngineeringCorrection +from server_common.observable import observable +from ReflectometryServer.axis import DefineValueAsEvent, ParkingSequenceUpdate +from ReflectometryServer.engineering_corrections import ( + CorrectionRecalculate, + CorrectionUpdate, + EngineeringCorrection, + NoCorrection, +) from ReflectometryServer.geometry import ChangeAxis +from ReflectometryServer.out_of_beam import OutOfBeamLookup, OutOfBeamPosition from ReflectometryServer.parameters import BeamlineParameter, ParameterSetpointReadbackUpdate -from ReflectometryServer.pv_wrapper import SetpointUpdate, ReadbackUpdate, IsChangingUpdate, PVWrapper +from ReflectometryServer.pv_wrapper import ( + IsChangingUpdate, + PVWrapper, + ReadbackUpdate, + SetpointUpdate, +) from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo -from server_common.observable import observable - logger = logging.getLogger(__name__) # Event that is triggered when a new readback value is read from the axis (with corrections applied) -CorrectedReadbackUpdate = namedtuple("CorrectedReadbackUpdate", [ - "value", # The new (corrected) readback value of the axis (float) - "alarm_severity", # The alarm severity of the axis, represented as an integer (see Channel Access doc) - "alarm_status"]) # The alarm status of the axis, represented as an integer (see Channel Access doc) +CorrectedReadbackUpdate = namedtuple( + "CorrectedReadbackUpdate", + [ + "value", # The new (corrected) readback value of the axis (float) + "alarm_severity", # The alarm severity of the axis, represented as an integer (see Channel Access doc) + "alarm_status", + ], +) # The alarm status of the axis, represented as an integer (see Channel Access doc) @dataclass @@ -37,6 +50,7 @@ class PVWrapperForParameter: """ Setting to allow a PV Wrapper to be changed based on the value of a parameter """ + parameter: BeamlineParameter # the parameter whose value the axis depends on value_wrapper_map: Dict[Any, PVWrapper] # the wrapper that should be used for each value @@ -46,12 +60,19 @@ class IocDriver: """ Drives an actual motor axis based on a component in the beamline model. """ + _motor_axis: Optional[PVWrapper] - def __init__(self, component: 'Component', component_axis: ChangeAxis, motor_axis: PVWrapper, - out_of_beam_positions: Optional[List[OutOfBeamPosition]|float|int] = None, synchronised: bool = True, - engineering_correction: Optional[EngineeringCorrection] = None, - pv_wrapper_for_parameter: Optional[PVWrapperForParameter] = None): + def __init__( + self, + component: "Component", + component_axis: ChangeAxis, + motor_axis: PVWrapper, + out_of_beam_positions: Optional[List[OutOfBeamPosition] | float | int] = None, + synchronised: bool = True, + engineering_correction: Optional[EngineeringCorrection] = None, + pv_wrapper_for_parameter: Optional[PVWrapperForParameter] = None, + ): """ Drive the IOC based on a component Args: @@ -71,7 +92,9 @@ def __init__(self, component: 'Component', component_axis: ChangeAxis, motor_axi self._set_motor_axis(motor_axis, False) self._pv_wrapper_for_parameter = pv_wrapper_for_parameter if pv_wrapper_for_parameter is not None: - pv_wrapper_for_parameter.parameter.add_listener(ParameterSetpointReadbackUpdate, self._on_parameter_update) + pv_wrapper_for_parameter.parameter.add_listener( + ParameterSetpointReadbackUpdate, self._on_parameter_update + ) if out_of_beam_positions is None: self._out_of_beam_lookup = None @@ -82,16 +105,20 @@ def __init__(self, component: 'Component', component_axis: ChangeAxis, motor_axi out_of_beam_positions = [OutOfBeamPosition(out_of_beam_positions)] self._out_of_beam_lookup = OutOfBeamLookup(out_of_beam_positions) - self.component.beam_path_rbv.axis[component_axis].park_sequence_count = \ - self._out_of_beam_lookup.get_max_sequence_count() - self.component.beam_path_set_point.axis[component_axis].park_sequence_count = \ - self._out_of_beam_lookup.get_max_sequence_count() - self.component.beam_path_set_point.axis[component_axis].add_listener(ParkingSequenceUpdate, - self._move_to_parking_sequence) + self.component.beam_path_rbv.axis[ + component_axis + ].park_sequence_count = self._out_of_beam_lookup.get_max_sequence_count() + self.component.beam_path_set_point.axis[ + component_axis + ].park_sequence_count = self._out_of_beam_lookup.get_max_sequence_count() + self.component.beam_path_set_point.axis[component_axis].add_listener( + ParkingSequenceUpdate, self._move_to_parking_sequence + ) except ValueError as e: STATUS_MANAGER.update_error_log(str(e)) STATUS_MANAGER.update_active_problems( - ProblemInfo("Invalid Out Of Beam Positions", self.name, Severity.MINOR_ALARM)) + ProblemInfo("Invalid Out Of Beam Positions", self.name, Severity.MINOR_ALARM) + ) self._synchronised = synchronised if engineering_correction is None: @@ -101,12 +128,18 @@ def __init__(self, component: 'Component', component_axis: ChangeAxis, motor_axi self.has_engineering_correction = True self._engineering_correction = engineering_correction self._engineering_correction.add_listener(CorrectionUpdate, self._on_correction_update) - self._engineering_correction.add_listener(CorrectionRecalculate, self._retrigger_motor_axis_updates) + self._engineering_correction.add_listener( + CorrectionRecalculate, self._retrigger_motor_axis_updates + ) self._sp_cache = None - self._rbv_cache = self._engineering_correction.from_axis(self._motor_axis.rbv, self._get_component_sp(True)) + self._rbv_cache = self._engineering_correction.from_axis( + self._motor_axis.rbv, self._get_component_sp(True) + ) - self.component.beam_path_rbv.axis[component_axis].add_listener(DefineValueAsEvent, self._on_define_value_as) + self.component.beam_path_rbv.axis[component_axis].add_listener( + DefineValueAsEvent, self._on_define_value_as + ) def _set_motor_axis(self, pv_wrapper: PVWrapper, trigger_update: bool) -> None: """ @@ -146,10 +179,16 @@ def _on_define_value_as(self, new_event): """ correct_position = self._engineering_correction.to_axis(new_event.new_position) - logger.info("Defining position for axis {name} to {corrected_value} (uncorrected {new_value}). " - "From sp {sp} and rbv {rbv}".format(name=self._motor_axis.name, corrected_value=correct_position, - new_value=new_event.new_position, sp=self._sp_cache, - rbv=self._rbv_cache)) + logger.info( + "Defining position for axis {name} to {corrected_value} (uncorrected {new_value}). " + "From sp {sp} and rbv {rbv}".format( + name=self._motor_axis.name, + corrected_value=correct_position, + new_value=new_event.new_position, + sp=self._sp_cache, + rbv=self._rbv_cache, + ) + ) self._motor_axis.define_position_as(correct_position) def _on_correction_update(self, new_correction_value): @@ -159,12 +198,15 @@ def _on_correction_update(self, new_correction_value): Args: new_correction_value (CorrectionUpdate): the new correction value """ - description = "{} on {} for {}".format(new_correction_value.description, self.name, self.component.name) + description = "{} on {} for {}".format( + new_correction_value.description, self.name, self.component.name + ) self.trigger_listeners(CorrectionUpdate(new_correction_value.correction, description)) def __repr__(self): return "{} for axis pv {} and component {}".format( - self.__class__.__name__, self._motor_axis.name, self.component.name) + self.__class__.__name__, self._motor_axis.name, self.component.name + ) def initialise(self): """ @@ -183,18 +225,27 @@ def initialise_setpoint(self): beam_path_setpoint = self.component.beam_path_set_point autosaved_value = beam_path_setpoint.axis[self.component_axis].autosaved_value if autosaved_value is None: - corrected_axis_setpoint = self._engineering_correction.init_from_axis(self._motor_axis.sp) + corrected_axis_setpoint = self._engineering_correction.init_from_axis( + self._motor_axis.sp + ) else: - corrected_axis_setpoint = self._engineering_correction.from_axis(self._motor_axis.sp, autosaved_value) + corrected_axis_setpoint = self._engineering_correction.from_axis( + self._motor_axis.sp, autosaved_value + ) if self._out_of_beam_lookup is not None: beam_interception = beam_path_setpoint.calculate_beam_interception() - distance_relative_to_beam = \ - corrected_axis_setpoint - beam_path_setpoint.axis[self.component_axis].get_displacement_for(0) + distance_relative_to_beam = corrected_axis_setpoint - beam_path_setpoint.axis[ + self.component_axis + ].get_displacement_for(0) max_sequence_count = self._out_of_beam_lookup.get_max_sequence_count() - in_beam_status, is_at_sequence_end = self._get_in_beam_status(beam_interception, self._motor_axis.sp, - distance_relative_to_beam, max_sequence_count) + in_beam_status, is_at_sequence_end = self._get_in_beam_status( + beam_interception, + self._motor_axis.sp, + distance_relative_to_beam, + max_sequence_count, + ) beam_path_setpoint.axis[self.component_axis].is_in_beam = in_beam_status if in_beam_status: beam_path_setpoint.axis[self.component_axis].init_parking_index(None) @@ -205,7 +256,9 @@ def initialise_setpoint(self): if not in_beam_status: corrected_axis_setpoint = self._motor_axis.sp - beam_path_setpoint.axis[self.component_axis].init_displacement_from_motor(corrected_axis_setpoint) + beam_path_setpoint.axis[self.component_axis].init_displacement_from_motor( + corrected_axis_setpoint + ) def is_for_component(self, component): """ @@ -221,14 +274,18 @@ def _axis_will_move(self): """ Returns: True if the axis set point has changed and it will move any distance """ - return not self.at_target_setpoint() and \ - self.component.beam_path_set_point.axis[self.component_axis].is_changed + return ( + not self.at_target_setpoint() + and self.component.beam_path_set_point.axis[self.component_axis].is_changed + ) def _backlash_duration(self): """ Returns: the duration of the backlash move """ - backlash_distance, distance_to_move, is_within_backlash_distance = self._get_movement_distances() + backlash_distance, distance_to_move, is_within_backlash_distance = ( + self._get_movement_distances() + ) backlash_velocity = self._motor_axis.backlash_velocity if backlash_velocity is None or backlash_distance == 0 or backlash_distance is None: @@ -252,7 +309,9 @@ def _base_move_duration(self): if max_velocity is None: return 0.0 - backlash_distance, distance_to_move, is_within_backlash_distance = self._get_movement_distances() + backlash_distance, distance_to_move, is_within_backlash_distance = ( + self._get_movement_distances() + ) if is_within_backlash_distance: return 0.0 else: @@ -274,7 +333,9 @@ def _get_movement_distances(self): distance_to_move = 0.0 # if we are not moving then the distance to move is 0 else: distance_to_move = self.rbv_cache() - component_sp - is_within_backlash_distance = min([0.0, backlash_distance]) <= distance_to_move <= max([0.0, backlash_distance]) + is_within_backlash_distance = ( + min([0.0, backlash_distance]) <= distance_to_move <= max([0.0, backlash_distance]) + ) return backlash_distance, distance_to_move, is_within_backlash_distance def get_max_move_duration(self): @@ -291,8 +352,11 @@ def get_max_move_duration(self): base_move_duration = self._base_move_duration() duration = base_move_duration + backlash_duration - logger.debug("Shortest move duration for {}: {:.2f}s ({:.2f}s base; {:.2f}s backlash)" - .format(self.name, duration, base_move_duration, backlash_duration)) + logger.debug( + "Shortest move duration for {}: {:.2f}s ({:.2f}s base; {:.2f}s backlash)".format( + self.name, duration, base_move_duration, backlash_duration + ) + ) return duration def perform_move(self, move_duration, force=False): @@ -309,7 +373,9 @@ def perform_move(self, move_duration, force=False): move_duration -= self._backlash_duration() if move_duration > 1e-6 and self._synchronised and not is_to_from_park: self._motor_axis.cache_velocity() - self._motor_axis.velocity = max(self._motor_axis.min_velocity, self._get_distance() / move_duration) + self._motor_axis.velocity = max( + self._motor_axis.min_velocity, self._get_distance() / move_duration + ) else: self._motor_axis.record_no_cache_velocity() @@ -329,8 +395,11 @@ def rbv_cache(self): Returns: The cached readback value for the motor """ if self._rbv_cache is None: - raise ValueError("Axis {} not initialised. Check configuration is correct and motor IOC is running." - .format(self._motor_axis.name)) + raise ValueError( + "Axis {} not initialised. Check configuration is correct and motor IOC is running.".format( + self._motor_axis.name + ) + ) return self._rbv_cache def _get_distance(self): @@ -358,7 +427,9 @@ def _get_component_sp(self, for_correction=False) -> Optional[float]: component_sp, _ = self._get_component_sp_and_is_to_from_parking(for_correction) return component_sp - def _get_component_sp_and_is_to_from_parking(self, for_correction=False) -> Tuple[Optional[float], bool]: + def _get_component_sp_and_is_to_from_parking( + self, for_correction=False + ) -> Tuple[Optional[float], bool]: """ Args: for_correction (bool): Setpoint is for engineering correction @@ -368,27 +439,42 @@ def _get_component_sp_and_is_to_from_parking(self, for_correction=False) -> Tupl sequence with a None in it whether the movement is from or to a park position """ - if not self.component.beam_path_set_point.axis[self.component_axis].is_in_beam \ - and self._out_of_beam_lookup is None: + if ( + not self.component.beam_path_set_point.axis[self.component_axis].is_in_beam + and self._out_of_beam_lookup is None + ): STATUS_MANAGER.update_error_log( "An axis for component {} is set to out of beam but there is no out of beam position defined " - "for the driver for the associated motor axis {}".format(self.component.name, self._motor_axis.name)) + "for the driver for the associated motor axis {}".format( + self.component.name, self._motor_axis.name + ) + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("No out of beam position defined for motor_axis", self.name, Severity.MINOR_ALARM)) + ProblemInfo( + "No out of beam position defined for motor_axis", + self.name, + Severity.MINOR_ALARM, + ) + ) parking_index = self.component.beam_path_set_point.in_beam_manager.parking_index if parking_index is None or self._out_of_beam_lookup is None: - displacement = self.component.beam_path_set_point.axis[self.component_axis].get_displacement() + displacement = self.component.beam_path_set_point.axis[ + self.component_axis + ].get_displacement() is_to_from_park = not self.component.beam_path_rbv.axis[self.component_axis].is_in_beam else: is_to_from_park = True beam_interception = self.component.beam_path_set_point.calculate_beam_interception() - out_of_beam_position = self._out_of_beam_lookup.get_position_for_intercept(beam_interception) + out_of_beam_position = self._out_of_beam_lookup.get_position_for_intercept( + beam_interception + ) if out_of_beam_position.is_offset: - displacement = self.component.beam_path_set_point.axis[self.component_axis].\ - get_displacement_for(out_of_beam_position.get_sequence_position(parking_index)) + displacement = self.component.beam_path_set_point.axis[ + self.component_axis + ].get_displacement_for(out_of_beam_position.get_sequence_position(parking_index)) else: displacement = out_of_beam_position.get_sequence_position(parking_index) if displacement is None and for_correction: @@ -402,10 +488,13 @@ def _on_update_rbv(self, update): Args: update (ReflectometryServer.pv_wrapper.ReadbackUpdate): update of the readback value of the axis """ - corrected_new_value = self._engineering_correction.from_axis(update.value, self._get_component_sp(True)) + corrected_new_value = self._engineering_correction.from_axis( + update.value, self._get_component_sp(True) + ) self._rbv_cache = corrected_new_value self._propagate_rbv_change( - CorrectedReadbackUpdate(corrected_new_value, update.alarm_severity, update.alarm_status)) + CorrectedReadbackUpdate(corrected_new_value, update.alarm_severity, update.alarm_status) + ) def _retrigger_motor_axis_updates(self, _): """ @@ -431,11 +520,14 @@ def _propagate_rbv_change(self, update): if self._out_of_beam_lookup is not None: beam_interception = self.component.beam_path_rbv.calculate_beam_interception() - distance_relative_to_beam = self.component.beam_path_rbv.axis[self.component_axis].get_relative_to_beam() + distance_relative_to_beam = self.component.beam_path_rbv.axis[ + self.component_axis + ].get_relative_to_beam() parking_index = self.component.beam_path_set_point.in_beam_manager.parking_index in_beam_status, is_at_sequence_position = self._get_in_beam_status( - beam_interception, update.value, distance_relative_to_beam, parking_index) + beam_interception, update.value, distance_relative_to_beam, parking_index + ) self.component.beam_path_rbv.axis[self.component_axis].is_in_beam = in_beam_status if is_at_sequence_position: @@ -443,9 +535,9 @@ def _propagate_rbv_change(self, update): def _get_in_beam_status(self, beam_intersect, value, distance_relative_to_beam, parking_index): if self._out_of_beam_lookup is not None: - in_beam_status, is_at_sequence_position = \ - self._out_of_beam_lookup.out_of_beam_status(beam_intersect, value, - distance_relative_to_beam, parking_index) + in_beam_status, is_at_sequence_position = self._out_of_beam_lookup.out_of_beam_status( + beam_intersect, value, distance_relative_to_beam, parking_index + ) else: in_beam_status, is_at_sequence_position = True, False return in_beam_status, is_at_sequence_position @@ -457,7 +549,9 @@ def _on_update_sp(self, update): Args: update (ReflectometryServer.pv_wrapper.SetpointUpdate): update of the setpoint value of the axis """ - self._sp_cache = self._engineering_correction.from_axis(update.value, self._get_component_sp(True)) + self._sp_cache = self._engineering_correction.from_axis( + update.value, self._get_component_sp(True) + ) def _on_update_is_changing(self, update): """ @@ -498,7 +592,7 @@ def check_limits_against_sps(self): if self._sp_cache is None or component_sp is None: return True, self._sp_cache, hlm, llm - inside_limits = (llm <= component_sp <= hlm) + inside_limits = llm <= component_sp <= hlm return inside_limits, component_sp, hlm, llm def has_out_of_beam_position(self): @@ -507,7 +601,7 @@ def has_out_of_beam_position(self): """ return self._out_of_beam_lookup is not None - def _on_parameter_update(self, update: ParameterSetpointReadbackUpdate) -> None: + def _on_parameter_update(self, update: ParameterSetpointReadbackUpdate) -> None: """ When the parameter that the change axis updates see if we need to change axis diff --git a/ReflectometryServer/movement_strategy.py b/ReflectometryServer/movement_strategy.py index d2bec93a..bd880bee 100644 --- a/ReflectometryServer/movement_strategy.py +++ b/ReflectometryServer/movement_strategy.py @@ -1,11 +1,12 @@ """ Classes and objects describing the movement of items """ + from __future__ import division -from math import fabs, tan, radians, sqrt +from math import fabs, radians, sqrt, tan -from ReflectometryServer.geometry import PositionAndAngle, Position, position_from_radial_coords +from ReflectometryServer.geometry import Position, position_from_radial_coords # Tolerance to use when comparing an angle with another angle ANGULAR_TOLERANCE = 1e-12 @@ -52,14 +53,20 @@ def calculate_interception(self, beam): y, z = self._zero_angle(y_b, self._current_position_at_zero, self._angle) elif fabs(angle_m % 180.0) <= ANGULAR_TOLERANCE: y, z = self._zero_angle(y_m, beam, beam.angle) - elif fabs(angle_m % 180.0 - 90) <= ANGULAR_TOLERANCE or fabs(angle_m % 180.0 + 90) <= ANGULAR_TOLERANCE: + elif ( + fabs(angle_m % 180.0 - 90) <= ANGULAR_TOLERANCE + or fabs(angle_m % 180.0 + 90) <= ANGULAR_TOLERANCE + ): y, z = self._right_angle(z_m, beam, beam.angle) - elif fabs(angle_b % 180.0 - 90) <= ANGULAR_TOLERANCE or fabs(angle_b % 180.0 + 90) <= ANGULAR_TOLERANCE: + elif ( + fabs(angle_b % 180.0 - 90) <= ANGULAR_TOLERANCE + or fabs(angle_b % 180.0 + 90) <= ANGULAR_TOLERANCE + ): y, z = self._right_angle(z_b, self._current_position_at_zero, self._angle) else: tan_b = tan(radians(angle_b)) tan_m = tan(radians(angle_m)) - z = 1/(tan_m - tan_b) * (y_b - y_m + z_m * tan_m - z_b * tan_b) + z = 1 / (tan_m - tan_b) * (y_b - y_m + z_m * tan_m - z_b * tan_b) y = tan_b * tan_m / (tan_b - tan_m) * (y_m / tan_m - y_b / tan_b + z_b - z_m) return Position(y, z) @@ -124,7 +131,9 @@ def position_in_mantid_coordinates(self, given_displacement=None): if displacement is None: displacement = self._displacement - return self._current_position_at_zero + position_from_radial_coords(displacement, self._angle) + return self._current_position_at_zero + position_from_radial_coords( + displacement, self._angle + ) def set_displacement(self, displacement): """ diff --git a/ReflectometryServer/out_of_beam.py b/ReflectometryServer/out_of_beam.py index 901e3320..b0f39ac0 100644 --- a/ReflectometryServer/out_of_beam.py +++ b/ReflectometryServer/out_of_beam.py @@ -1,7 +1,9 @@ """ Module to define Out of beam position. """ -from typing import Optional, List, Tuple + +from typing import List, Optional, Tuple + from ReflectometryServer.geometry import Position @@ -9,7 +11,14 @@ class OutOfBeamPosition: """ The definition of a geometry component's out of beam position. """ - def __init__(self, position, tolerance: float = 1, threshold: Optional[float] = None, is_offset: bool = False): + + def __init__( + self, + position, + tolerance: float = 1, + threshold: Optional[float] = None, + is_offset: bool = False, + ): """ Params: position: The out-of-beam position along the movement axis. @@ -60,10 +69,14 @@ class OutOfBeamLookup: Facilitates lookup of out-of-beam positions / status for a single axis out of a list of possible positions depending on where the beam intersects with that movement axis. """ + def __init__(self, positions: List[OutOfBeamPosition]): self._validate(positions) - self._sorted_out_of_beam_positions = sorted(positions, key=lambda position: - (position.threshold is None, position.threshold), reverse=True) + self._sorted_out_of_beam_positions = sorted( + positions, + key=lambda position: (position.threshold is None, position.threshold), + reverse=True, + ) @staticmethod def _validate(positions): @@ -81,7 +94,9 @@ def _validate(positions): raise ValueError("ERROR: Multiple default Out Of Beam Position defined for lookup.") thresholds = [entry.threshold for entry in positions] if len(set(thresholds)) != len(thresholds): - raise ValueError("ERROR: Duplicate values for threshold in different Out Of Beam positions.") + raise ValueError( + "ERROR: Duplicate values for threshold in different Out Of Beam positions." + ) else: raise ValueError("ERROR: No positions defined.") @@ -96,16 +111,22 @@ def get_position_for_intercept(self, beam_intercept: Position): Returns: The out-of-beam position """ default_pos = self._sorted_out_of_beam_positions[0] - pos_with_threshold_below_intercept = [x for x in self._sorted_out_of_beam_positions[1:] if - x.threshold <= beam_intercept.y] + pos_with_threshold_below_intercept = [ + x for x in self._sorted_out_of_beam_positions[1:] if x.threshold <= beam_intercept.y + ] if len(pos_with_threshold_below_intercept) > 0: position_to_use = pos_with_threshold_below_intercept[0] else: position_to_use = default_pos return position_to_use - def out_of_beam_status(self, beam_intercept: Position, displacement: float, distance_from_beam: float, - parking_index: Optional[int]) -> Tuple[bool, bool]: + def out_of_beam_status( + self, + beam_intercept: Position, + displacement: float, + distance_from_beam: float, + parking_index: Optional[int], + ) -> Tuple[bool, bool]: """ Checks whether a given value for displacement represents an out of beam position or at the end of the current sequence for a given beam interception and parking sequence number. @@ -128,12 +149,17 @@ def out_of_beam_status(self, beam_intercept: Position, displacement: float, dist axis_position = distance_from_beam else: axis_position = displacement - in_beam = abs(axis_position - out_of_beam_position.get_final_position()) > out_of_beam_position.tolerance + in_beam = ( + abs(axis_position - out_of_beam_position.get_final_position()) + > out_of_beam_position.tolerance + ) is_at_sequence_position = out_of_beam_position.get_sequence_position(parking_index) if is_at_sequence_position is None: at_sequence_index = True else: - at_sequence_index = abs(axis_position - is_at_sequence_position) < out_of_beam_position.tolerance + at_sequence_index = ( + abs(axis_position - is_at_sequence_position) < out_of_beam_position.tolerance + ) return in_beam, at_sequence_index @@ -143,7 +169,12 @@ def get_max_sequence_count(self): Returns: maximum sequence length for any parking sequence """ - return max([position.get_parking_sequence_length() for position in self._sorted_out_of_beam_positions]) + return max( + [ + position.get_parking_sequence_length() + for position in self._sorted_out_of_beam_positions + ] + ) class OutOfBeamSequence(OutOfBeamPosition): @@ -151,7 +182,13 @@ class OutOfBeamSequence(OutOfBeamPosition): Out of Beam position which gives a sequence instead of just a fixed position. """ - def __init__(self, sequence, tolerance: float = 1, threshold: Optional[float] = None, is_offset: bool = False): + def __init__( + self, + sequence, + tolerance: float = 1, + threshold: Optional[float] = None, + is_offset: bool = False, + ): """ Initialise. Args: @@ -174,6 +211,8 @@ def _validate(self, sequence): found_non_none = False for val in sequence: if val is None and found_non_none: - raise ValueError("ERROR: Out of beam sequence has a None between values this is not allowed") + raise ValueError( + "ERROR: Out of beam sequence has a None between values this is not allowed" + ) elif val is not None: found_non_none = True diff --git a/ReflectometryServer/parameters.py b/ReflectometryServer/parameters.py index 1c6b12bc..d9c09e7f 100644 --- a/ReflectometryServer/parameters.py +++ b/ReflectometryServer/parameters.py @@ -1,35 +1,50 @@ """ Parameters that the user would interact with """ + from concurrent.futures.thread import ThreadPoolExecutor from dataclasses import dataclass -from typing import List, Optional, Union, TYPE_CHECKING, Callable, Any +from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union from pcaspy import Severity +from server_common.utilities import SEVERITY from ReflectometryServer.axis import SetRelativeToBeamUpdate -from server_common.utilities import SEVERITY if TYPE_CHECKING: - from ReflectometryServer.ioc_driver import IocDriver from ReflectometryServer.components import Component from ReflectometryServer.engineering_corrections import EngineeringCorrection + from ReflectometryServer.ioc_driver import IocDriver -from ReflectometryServer.beam_path_calc import BeamPathUpdate, AxisChangingUpdate, InitUpdate, PhysicalMoveUpdate, \ - ComponentInBeamUpdate -from ReflectometryServer.exceptions import ParameterNotInitializedException -from ReflectometryServer.file_io import param_float_autosave, param_bool_autosave, param_string_autosave +import abc import logging - from enum import Enum -from ReflectometryServer.geometry import ChangeAxis -import abc -from ReflectometryServer.pv_wrapper import ReadbackUpdate, IsChangingUpdate, PVWrapper, JawsAxisPVWrapper -from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo from server_common.channel_access import AlarmSeverity, AlarmStatus from server_common.observable import observable +from ReflectometryServer.beam_path_calc import ( + AxisChangingUpdate, + BeamPathUpdate, + ComponentInBeamUpdate, + InitUpdate, + PhysicalMoveUpdate, +) +from ReflectometryServer.exceptions import ParameterNotInitializedException +from ReflectometryServer.file_io import ( + param_bool_autosave, + param_float_autosave, + param_string_autosave, +) +from ReflectometryServer.geometry import ChangeAxis +from ReflectometryServer.pv_wrapper import ( + IsChangingUpdate, + JawsAxisPVWrapper, + PVWrapper, + ReadbackUpdate, +) +from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo + DEFAULT_RBV_TO_SP_TOLERANCE = 0.002 logger = logging.getLogger(__name__) @@ -44,8 +59,11 @@ class ParameterUpdateBase: """ An update of a parameter used as a base for other events """ + value: Union[float, bool, str] # The new value - alarm_severity: [AlarmSeverity] # The alarm severity of the parameter, represented as an integer + alarm_severity: [ + AlarmSeverity + ] # The alarm severity of the parameter, represented as an integer alarm_status: [AlarmStatus] # The alarm status of the parameter, represented as an integer @@ -75,6 +93,7 @@ class ParameterAtSetpointUpdate: """ An update of the parameter at-setpoint state """ + value: bool # The new state (boolean) @@ -83,6 +102,7 @@ class ParameterChangingUpdate: """ An update of the parameter is-changing state """ + value: bool # The new state @@ -91,6 +111,7 @@ class ParameterDisabledUpdate: """ An update of the parameters is-disabled state """ + value: bool # The new state @@ -99,7 +120,8 @@ class RequestMoveEvent: """ Called after a move has been requested on a parameter """ - source: 'BeamlineParameter' # beamline parameter which caused the move to be triggered + + source: "BeamlineParameter" # beamline parameter which caused the move to be triggered class DefineCurrentValueAsParameter: @@ -107,6 +129,7 @@ class DefineCurrentValueAsParameter: A helper class which allows the current parameter readback to be set to a particular value by passing it down to the lower levels. """ + def __init__(self, define_current_value_as_fn, set_point_change_fn, parameter): self._new_value_sp_rbv = 0.0 self._new_value_sp = 0.0 @@ -129,13 +152,16 @@ def new_value_sp_rbv(self, value): Args: value: the new value to set the parameter to """ - logger.info("Defining position for parameter {name} to {new_value}. " - "From sp {sp}, sp_rbv {sp_rbv} and rbv {rbv}" - .format(name=self._parameter.name, - new_value=value, - sp=self._parameter.sp, - sp_rbv=self._parameter.sp_rbv, - rbv=self._parameter.rbv)) + logger.info( + "Defining position for parameter {name} to {new_value}. " + "From sp {sp}, sp_rbv {sp_rbv} and rbv {rbv}".format( + name=self._parameter.name, + new_value=value, + sp=self._parameter.sp, + sp_rbv=self._parameter.sp_rbv, + rbv=self._parameter.rbv, + ) + ) self._new_value_sp_rbv = value self._define_current_value_as_fn(value) @@ -158,10 +184,12 @@ def do_action(self): def changed(self): return self._changed + class BeamlineParameterType(Enum): """ Types of beamline parameters """ + FLOAT = 0 IN_OUT = 1 ENUM = 2 @@ -185,6 +213,7 @@ class BeamlineParameterGroup(Enum): """ Types of groups a parameter can belong to """ + ALL = 1 COLLIMATION_PLANE = 2 FOOTPRINT_PARAMETER = 3 @@ -215,21 +244,39 @@ def description(parameter_group): elif parameter_group == BeamlineParameterGroup.MISC: return "Other beamline parameters" else: - logger.error("Unknown parameter group! {}".format(parameter_group), severity=SEVERITY.MAJOR, src="REFL") + logger.error( + "Unknown parameter group! {}".format(parameter_group), + severity=SEVERITY.MAJOR, + src="REFL", + ) return "(Unknown)" -@observable(ParameterReadbackUpdate, ParameterSetpointReadbackUpdate, ParameterAtSetpointUpdate, - ParameterChangingUpdate, ParameterDisabledUpdate, ParameterInitUpdate, RequestMoveEvent) +@observable( + ParameterReadbackUpdate, + ParameterSetpointReadbackUpdate, + ParameterAtSetpointUpdate, + ParameterChangingUpdate, + ParameterDisabledUpdate, + ParameterInitUpdate, + RequestMoveEvent, +) class BeamlineParameter(metaclass=abc.ABCMeta): """ General beamline parameter that can be set. Subclass must implement _move_component to decide what to do with the value that is set. """ - def __init__(self, name, description=None, autosave=True, rbv_to_sp_tolerance=0.01, - custom_function: Optional[Callable[[Any, Any], str]] = None, characteristic_value="", - sp_mirrors_rbv=False): + def __init__( + self, + name, + description=None, + autosave=True, + rbv_to_sp_tolerance=0.01, + custom_function: Optional[Callable[[Any, Any], str]] = None, + characteristic_value="", + sp_mirrors_rbv=False, + ): """ Initializer. Args: @@ -269,8 +316,9 @@ def __init__(self, name, description=None, autosave=True, rbv_to_sp_tolerance=0. self.sp_mirrors_rbv = sp_mirrors_rbv def __repr__(self): - return "{} '{}': sp={}, sp_rbv={}, rbv={}, changed={}".format(__name__, self.name, self._set_point, - self._set_point_rbv, self.rbv, self.sp_changed) + return "{} '{}': sp={}, sp_rbv={}, rbv={}, changed={}".format( + __name__, self.name, self._set_point, self._set_point_rbv, self.rbv, self.sp_changed + ) def _add_to_parameter_groups(self): """ @@ -303,7 +351,9 @@ def _set_initial_sp(self, sp_init): else: self._set_point = sp_init self._set_point_rbv = sp_init - self.trigger_listeners(ParameterInitUpdate(self._set_point, AlarmSeverity.No, AlarmStatus.No)) + self.trigger_listeners( + ParameterInitUpdate(self._set_point, AlarmSeverity.No, AlarmStatus.No) + ) @property def rbv(self): @@ -424,7 +474,9 @@ def move_to_sp_no_callback(self): raise if self._custom_function is not None: - CUSTOM_FUNCTION_POOL.submit(self._run_custom_function, self._set_point_rbv, original_set_point_rbv) + CUSTOM_FUNCTION_POOL.submit( + self._run_custom_function, self._set_point_rbv, original_set_point_rbv + ) self._sp_is_changed = False if self._autosave: @@ -441,15 +493,25 @@ def _run_custom_function(self, new_sp, original_sp): logger.debug(f"Running custom function on parameter {self.name} ...") try: message = self._custom_function(new_sp, original_sp) - logger.debug(f"... Finished running custom function on parameter {self.name} it returned: {message}") + logger.debug( + f"... Finished running custom function on parameter {self.name} it returned: {message}" + ) if message is not None: - STATUS_MANAGER.update_error_log(f"Custom function on parameter {self.name} returned: {message}") + STATUS_MANAGER.update_error_log( + f"Custom function on parameter {self.name} returned: {message}" + ) STATUS_MANAGER.update_active_problems( - ProblemInfo(f"Custom function returned: {message}", self.name, Severity.NO_ALARM)) + ProblemInfo( + f"Custom function returned: {message}", self.name, Severity.NO_ALARM + ) + ) except Exception as ex: - STATUS_MANAGER.update_error_log(f"Custom function on parameter {self.name} failed with {ex}") + STATUS_MANAGER.update_error_log( + f"Custom function on parameter {self.name} failed with {ex}" + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Custom function on parameter failed.", self.name, Severity.MAJOR_ALARM)) + ProblemInfo("Custom function on parameter failed.", self.name, Severity.MAJOR_ALARM) + ) def move_to_sp_rbv_no_callback(self): """ @@ -482,14 +544,14 @@ def _get_alarm_info(self): """ To be implemented in subclass """ - raise NotImplemented() + raise NotImplementedError() @property def is_changing(self): """ Returns: Is the parameter changing (rotating, displacing etc.) """ - raise NotImplemented() + raise NotImplementedError() def _on_update_changing_state(self, _: Optional[AxisChangingUpdate]): """ @@ -504,7 +566,9 @@ def _on_update_sp_rbv(self): """ Trigger all sp rbv listeners """ - self.trigger_listeners(ParameterSetpointReadbackUpdate(self._set_point_rbv, AlarmSeverity.No, AlarmStatus.No)) + self.trigger_listeners( + ParameterSetpointReadbackUpdate(self._set_point_rbv, AlarmSeverity.No, AlarmStatus.No) + ) self.trigger_listeners(ParameterAtSetpointUpdate(self.rbv_at_sp)) @property @@ -530,7 +594,10 @@ def _check_and_move_component(self): self._move_component() else: STATUS_MANAGER.update_active_problems( - ProblemInfo("No parameter initialization value found", self.name, Severity.MAJOR_ALARM)) + ProblemInfo( + "No parameter initialization value found", self.name, Severity.MAJOR_ALARM + ) + ) raise ParameterNotInitializedException(self.name) @abc.abstractmethod @@ -563,9 +630,13 @@ def _log_autosave_type_error(self): Logs an error that the autosave value this parameter was trying to read was of the wrong type. """ STATUS_MANAGER.update_error_log( - "Could not read autosave value for parameter {}: unexpected type.".format(self.name)) - STATUS_MANAGER.update_active_problems(ProblemInfo("Parameter autosave value has unexpected type", self.name, - Severity.MINOR_ALARM)) + "Could not read autosave value for parameter {}: unexpected type.".format(self.name) + ) + STATUS_MANAGER.update_active_problems( + ProblemInfo( + "Parameter autosave value has unexpected type", self.name, Severity.MINOR_ALARM + ) + ) def _trigger_listeners_disabled(self): value = self.is_disabled or self.is_locked or self.read_only @@ -649,10 +720,18 @@ class AxisParameter(BeamlineParameter): Beamline Parameter that reads and write values on an axis of a component. """ - def __init__(self, name: str, component: 'Component', axis: ChangeAxis, description: str = None, - autosave: bool = False, rbv_to_sp_tolerance: float = 0.002, - custom_function: Optional[Callable[[Any, Any], str]] = None, characteristic_value="", - sp_mirrors_rbv=False): + def __init__( + self, + name: str, + component: "Component", + axis: ChangeAxis, + description: str = None, + autosave: bool = False, + rbv_to_sp_tolerance: float = 0.002, + custom_function: Optional[Callable[[Any, Any], str]] = None, + characteristic_value="", + sp_mirrors_rbv=False, + ): """ Initialiser. Args: @@ -673,11 +752,23 @@ def __init__(self, name: str, component: 'Component', axis: ChangeAxis, descript self.axis = axis if description is None: description = name - super(AxisParameter, self).__init__(name, description, autosave, rbv_to_sp_tolerance=rbv_to_sp_tolerance, - custom_function=custom_function, characteristic_value=characteristic_value, - sp_mirrors_rbv=sp_mirrors_rbv) - - if axis in [ChangeAxis.ANGLE, ChangeAxis.PHI, ChangeAxis.PSI, ChangeAxis.CHI, ChangeAxis.DISPLACEMENT_ANGLE]: + super(AxisParameter, self).__init__( + name, + description, + autosave, + rbv_to_sp_tolerance=rbv_to_sp_tolerance, + custom_function=custom_function, + characteristic_value=characteristic_value, + sp_mirrors_rbv=sp_mirrors_rbv, + ) + + if axis in [ + ChangeAxis.ANGLE, + ChangeAxis.PHI, + ChangeAxis.PSI, + ChangeAxis.CHI, + ChangeAxis.DISPLACEMENT_ANGLE, + ]: self.engineering_unit = "deg" else: self.engineering_unit = "mm" @@ -685,7 +776,9 @@ def __init__(self, name: str, component: 'Component', axis: ChangeAxis, descript if axis in [ChangeAxis.DISPLACEMENT_POSITION, ChangeAxis.DISPLACEMENT_ANGLE]: self.read_only = True self._trigger_listeners_disabled() - self.component.beam_path_set_point.axis[self.axis].add_listener(SetRelativeToBeamUpdate, self._update_sp_from_component) + self.component.beam_path_set_point.axis[self.axis].add_listener( + SetRelativeToBeamUpdate, self._update_sp_from_component + ) self._initialise_setpoint() self._initialise_beam_path_sp_listeners() @@ -710,17 +803,20 @@ def _initialise_setpoint(self): if self._autosave: self._initialise_sp_from_file() if self._set_point_rbv is None: - self.component.beam_path_set_point.axis[self.axis].add_listener(InitUpdate, self._initialise_sp_from_motor) - self.component.beam_path_set_point.in_beam_manager.add_listener(InitUpdate, - self._initialise_sp_from_motor) + self.component.beam_path_set_point.axis[self.axis].add_listener( + InitUpdate, self._initialise_sp_from_motor + ) + self.component.beam_path_set_point.in_beam_manager.add_listener( + InitUpdate, self._initialise_sp_from_motor + ) def _initialise_beam_path_sp_listeners(self): """ Add listeners to the setpoint beam path calc. """ - self.component.beam_path_set_point.in_beam_manager.add_listener(ComponentInBeamUpdate, - self._on_update_in_beam_state, - run_listener=True) + self.component.beam_path_set_point.in_beam_manager.add_listener( + ComponentInBeamUpdate, self._on_update_in_beam_state, run_listener=True + ) def _initialise_beam_path_rbv_listeners(self): """ @@ -732,8 +828,9 @@ def _initialise_beam_path_rbv_listeners(self): rbv_axis.add_listener(PhysicalMoveUpdate, self._on_update_rbv) if rbv_axis.can_define_axis_position_as: - self.define_current_value_as = DefineCurrentValueAsParameter(rbv_axis.define_axis_position_as, - self._set_sp_perform_no_move, self) + self.define_current_value_as = DefineCurrentValueAsParameter( + rbv_axis.define_axis_position_as, self._set_sp_perform_no_move, self + ) def _on_update_in_beam_state(self, update: ComponentInBeamUpdate): """ @@ -765,10 +862,13 @@ def _initialise_sp_from_motor(self, _): else: # If the axis is out of the beam and there is not autosave the displacement should be set to 0 init_sp = 0.0 - STATUS_MANAGER.update_error_log("Parameter {} is parkable so should have an autosave value but " - "doesn't. Has been set to 0 check its value".format(self.name)) + STATUS_MANAGER.update_error_log( + "Parameter {} is parkable so should have an autosave value but " + "doesn't. Has been set to 0 check its value".format(self.name) + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Parameter has no autosave value", self.name, Severity.MINOR_ALARM)) + ProblemInfo("Parameter has no autosave value", self.name, Severity.MINOR_ALARM) + ) self.component.beam_path_set_point.axis[self.axis].set_relative_to_beam(init_sp) else: init_sp = self.component.beam_path_set_point.axis[self.axis].get_relative_to_beam() @@ -793,8 +893,10 @@ def rbv_at_sp(self): if self.rbv is None or self._set_point_rbv is None: return False - return not self.component.beam_path_set_point.axis[self.axis].is_in_beam or \ - abs(self.rbv - self._set_point_rbv) < self._rbv_to_sp_tolerance + return ( + not self.component.beam_path_set_point.axis[self.axis].is_in_beam + or abs(self.rbv - self._set_point_rbv) < self._rbv_to_sp_tolerance + ) def _get_alarm_info(self): """ @@ -828,8 +930,14 @@ class InBeamParameter(BeamlineParameter): Parameter which sets whether a given device is in the beam. """ - def __init__(self, name: str, component: 'Component', description: str = None, autosave: bool = False, - custom_function: Optional[Callable[[bool, bool], str]] = None): + def __init__( + self, + name: str, + component: "Component", + description: str = None, + autosave: bool = False, + custom_function: Optional[Callable[[bool, bool], str]] = None, + ): """ Initializer. Args: @@ -841,16 +949,21 @@ def __init__(self, name: str, component: 'Component', description: str = None, a """ if description is None: description = "{} component is in the beam".format(name) - super(InBeamParameter, self).__init__(name, description, autosave, rbv_to_sp_tolerance=0.001, - custom_function=custom_function) + super(InBeamParameter, self).__init__( + name, description, autosave, rbv_to_sp_tolerance=0.001, custom_function=custom_function + ) self._component = component if self._autosave: self._initialise_sp_from_file() if self._set_point_rbv is None: - self._component.beam_path_set_point.in_beam_manager.add_listener(InitUpdate, self._initialise_sp_from_motor) + self._component.beam_path_set_point.in_beam_manager.add_listener( + InitUpdate, self._initialise_sp_from_motor + ) self._component.beam_path_rbv.add_listener(BeamPathUpdate, self._on_update_rbv) - self._component.beam_path_rbv.in_beam_manager.add_listener(AxisChangingUpdate, self._on_update_changing_state) + self._component.beam_path_rbv.in_beam_manager.add_listener( + AxisChangingUpdate, self._on_update_changing_state + ) self.parameter_type = BeamlineParameterType.IN_OUT @@ -894,7 +1007,11 @@ def validate(self, drivers): if driver.has_out_of_beam_position(): break else: - errors.append("No driver found with out of beam position for component {}".format(self._component.name)) + errors.append( + "No driver found with out of beam position for component {}".format( + self._component.name + ) + ) return errors def _rbv(self): @@ -920,9 +1037,16 @@ class DirectParameter(BeamlineParameter): is just a wrapper to present a motor PV as a reflectometry style PV and does not track the beam path. """ - def __init__(self, name: str, pv_wrapper: PVWrapper, description: str = None, autosave: bool = False, - rbv_to_sp_tolerance: float = DEFAULT_RBV_TO_SP_TOLERANCE, - custom_function: Optional[Callable[[Any, Any], str]] = None, engineering_correction: 'EngineeringCorrection' = None): + def __init__( + self, + name: str, + pv_wrapper: PVWrapper, + description: str = None, + autosave: bool = False, + rbv_to_sp_tolerance: float = DEFAULT_RBV_TO_SP_TOLERANCE, + custom_function: Optional[Callable[[Any, Any], str]] = None, + engineering_correction: "EngineeringCorrection" = None, + ): """ Args: name: The name of the parameter @@ -935,10 +1059,18 @@ def __init__(self, name: str, pv_wrapper: PVWrapper, description: str = None, au """ # This is to avoid circular imports when instantiating NoCorrection() from ReflectometryServer.engineering_corrections import NoCorrection - self.engineering_correction = engineering_correction if engineering_correction is not None else NoCorrection() + + self.engineering_correction = ( + engineering_correction if engineering_correction is not None else NoCorrection() + ) self._pv_wrapper = pv_wrapper - super(DirectParameter, self).__init__(name, description, autosave, rbv_to_sp_tolerance=rbv_to_sp_tolerance, - custom_function=custom_function) + super(DirectParameter, self).__init__( + name, + description, + autosave, + rbv_to_sp_tolerance=rbv_to_sp_tolerance, + custom_function=custom_function, + ) self._last_update = None self._pv_wrapper.add_listener(ReadbackUpdate, self._cache_and_update_rbv) @@ -950,11 +1082,10 @@ def __init__(self, name: str, pv_wrapper: PVWrapper, description: str = None, au if self._set_point_rbv is None: self._initialise_sp_from_motor(None) - - self._no_move_because_is_define = False - self.define_current_value_as = DefineCurrentValueAsParameter(self._pv_wrapper.define_position_as, - self._set_sp_perform_no_move, self) + self.define_current_value_as = DefineCurrentValueAsParameter( + self._pv_wrapper.define_position_as, self._set_sp_perform_no_move, self + ) def _add_to_parameter_groups(self): super()._add_to_parameter_groups() @@ -1044,8 +1175,16 @@ class SlitGapParameter(DirectParameter): """ Parameter which sets the gap on a slit. """ - def __init__(self, name: str, pv_wrapper: JawsAxisPVWrapper, description: str = None, autosave: bool = False, - rbv_to_sp_tolerance: float = 0.002, custom_function: Optional[Callable[[Any, Any], str]] = None): + + def __init__( + self, + name: str, + pv_wrapper: JawsAxisPVWrapper, + description: str = None, + autosave: bool = False, + rbv_to_sp_tolerance: float = 0.002, + custom_function: Optional[Callable[[Any, Any], str]] = None, + ): """ Args: name: The name of the parameter @@ -1056,8 +1195,14 @@ def __init__(self, name: str, pv_wrapper: JawsAxisPVWrapper, description: str = parameter to be "at readback value" custom_function: custom function to run on move """ - super(SlitGapParameter, self).__init__(name, pv_wrapper, description, autosave, - rbv_to_sp_tolerance=rbv_to_sp_tolerance, custom_function=custom_function) + super(SlitGapParameter, self).__init__( + name, + pv_wrapper, + description, + autosave, + rbv_to_sp_tolerance=rbv_to_sp_tolerance, + custom_function=custom_function, + ) self.engineering_unit = "mm" def _add_to_parameter_groups(self): @@ -1072,8 +1217,13 @@ class EnumParameter(BeamlineParameter): and get set as soon as a move occurs. """ - def __init__(self, name: str, options: List[str], description: Optional[str] = None, - custom_function: Optional[Callable[[Any, Any], str]] = None): + def __init__( + self, + name: str, + options: List[str], + description: Optional[str] = None, + custom_function: Optional[Callable[[Any, Any], str]] = None, + ): """ Initializer. NB parameter is always autosaved @@ -1083,8 +1233,9 @@ def __init__(self, name: str, options: List[str], description: Optional[str] = N description: description of the parameter custom_function: custom function to run on move """ - super(EnumParameter, self).__init__(name, description=description, autosave=True, - custom_function=custom_function) + super(EnumParameter, self).__init__( + name, description=description, autosave=True, custom_function=custom_function + ) self.parameter_type = BeamlineParameterType.ENUM self.options = options if self._autosave: @@ -1094,7 +1245,7 @@ def _add_to_parameter_groups(self): super()._add_to_parameter_groups() self.group_names.append(BeamlineParameterGroup.TOGGLE) - def validate(self, drivers: List['IocDriver']) -> List[str]: + def validate(self, drivers: List["IocDriver"]) -> List[str]: """ Perform validation of this parameter returning a list of errors. @@ -1127,14 +1278,19 @@ def _initialise_sp_from_file(self): sp_init = param_string_autosave.read_parameter(self._name, self.options[0]) self._set_initial_sp(sp_init) except IndexError: - STATUS_MANAGER.update_error_log("No options for optional parameter, {}".format(self.name)) + STATUS_MANAGER.update_error_log( + "No options for optional parameter, {}".format(self.name) + ) def _initialise_sp_from_motor(self, _): # Optional parameters should always be autosaved and should not be initialised from the motor there is no code # to perform this operation so this is a major error if triggered. - STATUS_MANAGER.update_error_log("Optional parameter, {}, was asked up init from motor".format(self.name)) - STATUS_MANAGER.update_active_problems(ProblemInfo("Optional Parameter updating from motor", self.name, - Severity.MAJOR_ALARM)) + STATUS_MANAGER.update_error_log( + "Optional parameter, {}, was asked up init from motor".format(self.name) + ) + STATUS_MANAGER.update_active_problems( + ProblemInfo("Optional Parameter updating from motor", self.name, Severity.MAJOR_ALARM) + ) def _move_component(self): if self.sp_rbv not in self.options: diff --git a/ReflectometryServer/pv_wrapper.py b/ReflectometryServer/pv_wrapper.py index a04ad486..0b84d05f 100644 --- a/ReflectometryServer/pv_wrapper.py +++ b/ReflectometryServer/pv_wrapper.py @@ -1,23 +1,28 @@ """ Wrapper for motor PVs """ + import abc +import logging import threading -from collections import namedtuple import time +from collections import namedtuple from functools import partial from pcaspy import Severity - -from ReflectometryServer.server_status_manager import ProblemInfo, STATUS_MANAGER -from ReflectometryServer.ChannelAccess.constants import MYPVPREFIX, MTR_MOVING, MTR_STOPPED, MOTOR_MOVING_PV -from ReflectometryServer.file_io import velocity_float_autosave, velocity_bool_autosave -import logging - from server_common.channel_access import ChannelAccess, UnableToConnectToPVException from server_common.helpers import motor_in_set_mode from server_common.observable import observable +from ReflectometryServer.ChannelAccess.constants import ( + MOTOR_MOVING_PV, + MTR_MOVING, + MTR_STOPPED, + MYPVPREFIX, +) +from ReflectometryServer.file_io import velocity_bool_autosave, velocity_float_autosave +from ReflectometryServer.server_status_manager import STATUS_MANAGER, ProblemInfo + # Time between monitor update processing to allow for multiple monitors to be collected together providing a single # update trigger MIN_TIME_BETWEEN_MONITOR_UPDATES_FROM_MONITORS = 0.05 @@ -29,22 +34,34 @@ # An update of the setpoint value of this motor axis. -SetpointUpdate = namedtuple("SetpointUpdate", [ - "value", # The new setpoint value of the axis (float) - "alarm_severity", # The alarm severity of the axis, represented as an integer (see Channel Access doc) - "alarm_status"]) # The alarm status of the axis, represented as an integer (see Channel Access doc) +SetpointUpdate = namedtuple( + "SetpointUpdate", + [ + "value", # The new setpoint value of the axis (float) + "alarm_severity", # The alarm severity of the axis, represented as an integer (see Channel Access doc) + "alarm_status", + ], +) # The alarm status of the axis, represented as an integer (see Channel Access doc) # An update of the readback value of this motor axis. -ReadbackUpdate = namedtuple("ReadbackUpdate", [ - "value", # The new readback value of the axis (float) - "alarm_severity", # The alarm severity of the axis, represented as an integer (see Channel Access doc) - "alarm_status"]) # The alarm status of the axis, represented as an integer (see Channel Access doc) +ReadbackUpdate = namedtuple( + "ReadbackUpdate", + [ + "value", # The new readback value of the axis (float) + "alarm_severity", # The alarm severity of the axis, represented as an integer (see Channel Access doc) + "alarm_status", + ], +) # The alarm status of the axis, represented as an integer (see Channel Access doc) # An update of the is-changing state of this motor axis. -IsChangingUpdate = namedtuple("IsChangingUpdate", [ - "value", # The new is-changing state of the axis (boolean) - "alarm_severity", # The alarm severity of the axis, represented as an integer (see Channel Access doc) - "alarm_status"]) # The alarm status of the axis, represented as an integer (see Channel Access doc) +IsChangingUpdate = namedtuple( + "IsChangingUpdate", + [ + "value", # The new is-changing state of the axis (boolean) + "alarm_severity", # The alarm severity of the axis, represented as an integer (see Channel Access doc) + "alarm_status", + ], +) # The alarm status of the axis, represented as an integer (see Channel Access doc) class ProcessMonitorEvents: @@ -101,7 +118,8 @@ def _set_motor_moving_pv(self, value): except Exception as e: STATUS_MANAGER.update_error_log("Failed to set motor moving pv: {}".format(e), e) STATUS_MANAGER.update_active_problems( - ProblemInfo("Failed to update motor moving pv", "pv_wrapper", Severity.MAJOR_ALARM)) + ProblemInfo("Failed to update motor moving pv", "pv_wrapper", Severity.MAJOR_ALARM) + ) def process_current_triggers(self): """ @@ -132,6 +150,7 @@ class PVWrapper(metaclass=abc.ABCMeta): """ Wrap a single motor axis. Provides relevant listeners and synchronization utilities. """ + def __init__(self, base_pv, ca=None, min_velocity_scale_factor=None): """ Creates a wrapper around a PV. @@ -180,7 +199,9 @@ def _block_until_pv_available(self): while not self._ca.pv_exists(self._rbv_pv): STATUS_MANAGER.update_error_log( "{} does not exist. Check the PV is correct and the IOC is running. Retrying in {} s.".format( - self._rbv_pv, RETRY_INTERVAL)) + self._rbv_pv, RETRY_INTERVAL + ) + ) time.sleep(RETRY_INTERVAL) @abc.abstractmethod @@ -244,7 +265,8 @@ def _monitor_pv(self, pv, call_back_function): else: STATUS_MANAGER.update_error_log( "Error adding monitor to {}: PV does not exist. Check the PV is correct and the IOC is running. " - "Retrying in {} s.".format(pv, RETRY_INTERVAL)) + "Retrying in {} s.".format(pv, RETRY_INTERVAL) + ) time.sleep(RETRY_INTERVAL) def _read_pv(self, pv): @@ -259,7 +281,9 @@ def _read_pv(self, pv): return value else: STATUS_MANAGER.update_error_log("Could not connect to PV {}.".format(pv)) - raise UnableToConnectToPVException(pv, "Check configuration is correct and IOC is running.") + raise UnableToConnectToPVException( + pv, "Check configuration is correct and IOC is running." + ) def _write_pv(self, pv, value, wait=False): """ @@ -399,25 +423,43 @@ def _init_velocity_to_restore(self): """ autosave_value = velocity_float_autosave.read_parameter(self.name, None) if autosave_value is not None: - logger.debug("Restoring {pv_name} velocity_cache with auto-save value {value}" - .format(pv_name=self.name, value=autosave_value)) + logger.debug( + "Restoring {pv_name} velocity_cache with auto-save value {value}".format( + pv_name=self.name, value=autosave_value + ) + ) self._velocity_to_restore = autosave_value else: - logger.error("Error: Unable to initialise velocity cache from auto-save for {}." - .format(self.name)) + logger.error( + "Error: Unable to initialise velocity cache from auto-save for {}.".format( + self.name + ) + ) if autosave_value is not None: - autosave_value = velocity_bool_autosave.read_parameter(self.name + "_velocity_restored", None) + autosave_value = velocity_bool_autosave.read_parameter( + self.name + "_velocity_restored", None + ) if autosave_value is not None: - logger.debug("Restoring {pv_name} velocity_restored with auto-save value {value}" - .format(pv_name=self.name, value=autosave_value)) + logger.debug( + "Restoring {pv_name} velocity_restored with auto-save value {value}".format( + pv_name=self.name, value=autosave_value + ) + ) self._velocity_restored = autosave_value else: STATUS_MANAGER.update_error_log( "Error: Unable to initialise velocity_restored flag from auto-save for {}.".format( - self.name)) + self.name + ) + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Unable to read autosaved velocity_restored flag", self.name, Severity.MINOR_ALARM)) + ProblemInfo( + "Unable to read autosaved velocity_restored flag", + self.name, + Severity.MINOR_ALARM, + ) + ) def record_no_cache_velocity(self): """ @@ -438,11 +480,14 @@ def cache_velocity(self): self._velocity_to_restore = self.velocity velocity_float_autosave.write_parameter(self.name, self._velocity_to_restore) self._velocity_restored = False - velocity_bool_autosave.write_parameter(self.name + "_velocity_restored", self._velocity_restored) + velocity_bool_autosave.write_parameter( + self.name + "_velocity_restored", self._velocity_restored + ) elif not self._velocity_restored and self._moving_state_cache == MTR_STOPPED: STATUS_MANAGER.update_error_log( "Velocity for {pv_name} has not been cached as existing cache has not been restored and " - "is stationary.".format(pv_name=self.name)) + "is stationary.".format(pv_name=self.name) + ) elif not self._velocity_restored and self._moving_state_cache == MTR_MOVING: # Move interrupting current move. Leave the original cache so it can be restored once all # moves have been completed. @@ -457,20 +502,31 @@ def restore_pre_move_velocity(self): if self._velocity_restored: STATUS_MANAGER.update_error_log( "Velocity for {pv_name} has not been restored from cache. The cache has already been " - "restored previously. Hint: Are you moving the axis outside of the reflectometry server?" - .format(pv_name=self.name)) + "restored previously. Hint: Are you moving the axis outside of the reflectometry server?".format( + pv_name=self.name + ) + ) else: if self._velocity_to_restore is None: STATUS_MANAGER.update_error_log( - "Cannot restore velocity: velocity cache is None for {pv_name}".format(pv_name=self.name)) + "Cannot restore velocity: velocity cache is None for {pv_name}".format( + pv_name=self.name + ) + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Unable to restore axis velocity", self.name, Severity.MINOR_ALARM)) + ProblemInfo("Unable to restore axis velocity", self.name, Severity.MINOR_ALARM) + ) else: - logger.debug("Restoring velocity cache of {value} for {pv_name}" - .format(value=self._velocity_to_restore, pv_name=self.name)) + logger.debug( + "Restoring velocity cache of {value} for {pv_name}".format( + value=self._velocity_to_restore, pv_name=self.name + ) + ) self._write_pv(self._velo_pv, self._velocity_to_restore) self._velocity_restored = True - velocity_bool_autosave.write_parameter(self.name + "_velocity_restored", self._velocity_restored) + velocity_bool_autosave.write_parameter( + self.name + "_velocity_restored", self._velocity_restored + ) def _on_update_setpoint_value(self, new_value, alarm_severity, alarm_status): """ @@ -481,8 +537,9 @@ def _on_update_setpoint_value(self, new_value, alarm_severity, alarm_status): alarm_severity (server_common.channel_access.AlarmSeverity): severity of any alarm alarm_status (server_common.channel_access.AlarmCondition): the alarm status """ - PROCESS_MONITOR_EVENTS.add_trigger(self.trigger_listeners, - SetpointUpdate(new_value, alarm_severity, alarm_status)) + PROCESS_MONITOR_EVENTS.add_trigger( + self.trigger_listeners, SetpointUpdate(new_value, alarm_severity, alarm_status) + ) def _on_update_readback_value(self, new_value, alarm_severity, alarm_status): """ @@ -494,8 +551,9 @@ def _on_update_readback_value(self, new_value, alarm_severity, alarm_status): alarm_status (server_common.channel_access.AlarmCondition): the alarm status """ - PROCESS_MONITOR_EVENTS.add_trigger(self.trigger_listeners, - ReadbackUpdate(new_value, alarm_severity, alarm_status)) + PROCESS_MONITOR_EVENTS.add_trigger( + self.trigger_listeners, ReadbackUpdate(new_value, alarm_severity, alarm_status) + ) def _on_update_moving_state(self, new_value, alarm_severity, alarm_status): """ @@ -506,8 +564,11 @@ def _on_update_moving_state(self, new_value, alarm_severity, alarm_status): alarm_severity (server_common.channel_access.AlarmSeverity): severity of any alarm alarm_status (server_common.channel_access.AlarmCondition): the alarm status """ - if self._moving_state_cache == MTR_MOVING and new_value == MTR_STOPPED \ - and not self._moving_without_changing_velocity: + if ( + self._moving_state_cache == MTR_MOVING + and new_value == MTR_STOPPED + and not self._moving_without_changing_velocity + ): self.restore_pre_move_velocity() self._moving_state_cache = round(new_value) @@ -532,7 +593,9 @@ def _on_update_velocity(self, value, _alarm_severity, _alarm_status): alarm_status (server_common.channel_access.AlarmCondition): the alarm status """ self._velocity_cache = value - velocity_autosave_not_read_and_not_moving = (self._velocity_restored is None and not self.is_moving) + velocity_autosave_not_read_and_not_moving = ( + self._velocity_restored is None and not self.is_moving + ) if self._velocity_restored or velocity_autosave_not_read_and_not_moving: self._velocity_to_restore = value logger.info(f"{self._name}: Changed velocity to restore. New value: {value} ") @@ -612,6 +675,7 @@ class MotorPVWrapper(PVWrapper): """ Wrap a low level motor PV. Provides relevant listeners and synchronization utilities. """ + def __init__(self, base_pv, ca=None, min_velocity_scale_factor=None): """ Creates a wrapper around a low level motor PV. @@ -657,13 +721,15 @@ def define_position_as(self, new_position): except ValueError as ex: STATUS_MANAGER.update_error_log("Can not define zero: {}".format(ex), ex) STATUS_MANAGER.update_active_problems( - ProblemInfo("Failed to redefine position", self.name, Severity.MINOR_ALARM)) + ProblemInfo("Failed to redefine position", self.name, Severity.MINOR_ALARM) + ) class JawsAxisPVWrapper(PVWrapper, metaclass=abc.ABCMeta): """ Creates a wrapper around a jaws axis. """ + def __init__(self, base_pv, is_vertical, ca=None): """ Creates a wrapper around a jaws axis. @@ -697,7 +763,9 @@ def _set_resolution(self): Set the motor resolution for this axis. """ motor_resolutions_pvs = self._pv_names_for_directions("MTR.MRES") - motor_resolutions = [self._read_pv(motor_resolutions_pv) for motor_resolutions_pv in motor_resolutions_pvs] + motor_resolutions = [ + self._read_pv(motor_resolutions_pv) for motor_resolutions_pv in motor_resolutions_pvs + ] self._resolution = float(sum(motor_resolutions)) / len(motor_resolutions_pvs) def initialise(self): @@ -716,10 +784,12 @@ def initialise(self): # For jaws axes, soft limits don't apply as putting a huge number in will just open jaws up instead # of preventing a SP being taken - self._high_limit_cache = float('inf') - self._low_limit_cache = float('-inf') + self._high_limit_cache = float("inf") + self._low_limit_cache = float("-inf") - self._backlash_distance_cache = 0 # No backlash used as source of clash conditions on jaws sets + self._backlash_distance_cache = ( + 0 # No backlash used as source of clash conditions on jaws sets + ) self._add_monitors() def _add_monitors(self): @@ -731,8 +801,10 @@ def _add_monitors(self): self._monitor_pv(self._dmov_pv, self._on_update_moving_state) for velo_pv in self._pv_names_for_directions("MTR.VELO"): - self._monitor_pv(velo_pv, partial(self._on_update_individual_velocity, - source=self._strip_source_pv(velo_pv))) + self._monitor_pv( + velo_pv, + partial(self._on_update_individual_velocity, source=self._strip_source_pv(velo_pv)), + ) @property def velocity(self): @@ -751,11 +823,17 @@ def velocity(self, value): Args: value (float): The value to set """ - STATUS_MANAGER.update_error_log("Error: An attempt was made to write a velocity to a Jaws Axis. We do not " - "support this as we do not expect jaws to be synchronised.") + STATUS_MANAGER.update_error_log( + "Error: An attempt was made to write a velocity to a Jaws Axis. We do not " + "support this as we do not expect jaws to be synchronised." + ) STATUS_MANAGER.update_active_problems( - ProblemInfo("Drivers for Jaws axes should not be synchronised. Check configuration", self.name, - Severity.MINOR_ALARM)) + ProblemInfo( + "Drivers for Jaws axes should not be synchronised. Check configuration", + self.name, + Severity.MINOR_ALARM, + ) + ) @property def max_velocity(self): @@ -771,8 +849,10 @@ def _pv_names_for_directions(self, suffix): Returns (String): list of pv names for the different directions """ - return ["{}:{}:{}".format(self._prefixed_pv, direction, suffix) - for direction in self._directions] + return [ + "{}:{}:{}".format(self._prefixed_pv, direction, suffix) + for direction in self._directions + ] def _on_update_individual_velocity(self, value, _alarm_severity, _alarm_status, source=None): self._velocities[source] = value @@ -803,7 +883,8 @@ def _strip_source_pv(self, pv): if key in pv: return key STATUS_MANAGER.update_error_log( - "Wrapper for {} received event from unexpected source: {}".format(self.name, pv)) + "Wrapper for {} received event from unexpected source: {}".format(self.name, pv) + ) def define_position_as(self, new_position): """ @@ -813,13 +894,20 @@ def define_position_as(self, new_position): """ try: mtr1, mtr2 = self._pv_names_for_directions("MTR") - logger.info("Defining position for axis {name} to {corrected_value}. " - "From sp {sp} and rbv {rbv}.".format(name=self.name, corrected_value=new_position, - sp=self.sp, rbv=self.rbv)) + logger.info( + "Defining position for axis {name} to {corrected_value}. " + "From sp {sp} and rbv {rbv}.".format( + name=self.name, corrected_value=new_position, sp=self.sp, rbv=self.rbv + ) + ) for motor in self._pv_names_for_directions("MTR"): rbv = self._read_pv("{}.RBV".format(motor)) sp = self._read_pv("{}".format(motor)) - logger.info(" Motor {name} initially at rbv {rbv} sp {sp}".format(name=motor, rbv=rbv, sp=sp)) + logger.info( + " Motor {name} initially at rbv {rbv} sp {sp}".format( + name=motor, rbv=rbv, sp=sp + ) + ) with motor_in_set_mode(mtr1), motor_in_set_mode(mtr2): # Ensure the position has been redefined before leaving the "Set" context, otherwise the motor moves @@ -828,17 +916,21 @@ def define_position_as(self, new_position): for motor in self._pv_names_for_directions("MTR"): rbv = self._read_pv("{}.RBV".format(motor)) sp = self._read_pv("{}".format(motor)) - logger.info(" Motor {name} moved to rbv {rbv} sp {sp}".format(name=motor, rbv=rbv, sp=sp)) + logger.info( + " Motor {name} moved to rbv {rbv} sp {sp}".format(name=motor, rbv=rbv, sp=sp) + ) except ValueError as ex: STATUS_MANAGER.update_error_log("Can not define zero: {}".format(ex), ex) STATUS_MANAGER.update_active_problems( - ProblemInfo("Failed to redefine position", self.name, Severity.MAJOR_ALARM)) + ProblemInfo("Failed to redefine position", self.name, Severity.MAJOR_ALARM) + ) class JawsGapPVWrapper(JawsAxisPVWrapper): """ Wrap the axis PVs on top of a motor record to allow easy access to all axis PV values needed. """ + def __init__(self, base_pv, is_vertical, ca=None): """ Creates a wrapper around a motor PV for accessing its fields. @@ -884,4 +976,3 @@ def _set_pvs(self): self._sp_pv = "{}:{}CENT:SP".format(self._prefixed_pv, self._direction_symbol) self._rbv_pv = "{}:{}CENT".format(self._prefixed_pv, self._direction_symbol) self._dmov_pv = "{}:{}CENT:DMOV".format(self._prefixed_pv, self._direction_symbol) - diff --git a/ReflectometryServer/run_tests.py b/ReflectometryServer/run_tests.py index b9795510..7fa9d871 100644 --- a/ReflectometryServer/run_tests.py +++ b/ReflectometryServer/run_tests.py @@ -16,22 +16,30 @@ """ Run all the tests for refl server """ + # Standard imports -import unittest -import xmlrunner import argparse # Add root path for access to server_commons import os import sys +import unittest + +import xmlrunner -DEFAULT_DIRECTORY = os.path.join('..', '..', '..', '..', 'test-reports') +DEFAULT_DIRECTORY = os.path.join("..", "..", "..", "..", "test-reports") -if __name__ == '__main__': +if __name__ == "__main__": # get output directory from command line arguments parser = argparse.ArgumentParser() - parser.add_argument('-o', '--output_dir', nargs=1, type=str, default=[DEFAULT_DIRECTORY], - help='The directory to save the test reports') + parser.add_argument( + "-o", + "--output_dir", + nargs=1, + type=str, + default=[DEFAULT_DIRECTORY], + help="The directory to save the test reports", + ) args = parser.parse_args() xml_dir = args.output_dir[0] diff --git a/ReflectometryServer/server_status_manager.py b/ReflectometryServer/server_status_manager.py index c6da0580..2a7273f7 100644 --- a/ReflectometryServer/server_status_manager.py +++ b/ReflectometryServer/server_status_manager.py @@ -1,33 +1,48 @@ import logging from collections import namedtuple - from enum import Enum from typing import Optional from pcaspy import Severity - from server_common.observable import observable -StatusDescription = namedtuple("StatusDescription", [ - 'display_string', # A string representation of the server state - 'alarm_severity']) # The alarm severity associated to this state, represented as an int (see Channel Access doc) - -StatusUpdate = namedtuple("StatusUpdate", [ - 'server_status', # The server status - 'server_message']) # The server status display message - -ProblemInfo = namedtuple("ProblemInfo", [ - 'description', # The problem description - 'source', # The problem source - 'severity']) # The severity of the problem - -ActiveProblemsUpdate = namedtuple("ActiveProblemsUpdate", [ - 'errors', # Dictionary of errors (description:sources) - 'warnings', # Dictionary of warnings (description:sources) - 'other']) # Dictionary of other problems (description:sources) - -ErrorLogUpdate = namedtuple("ErrorLogUpdate", [ - 'log_as_string']) # The current error log as a list of strings +StatusDescription = namedtuple( + "StatusDescription", + [ + "display_string", # A string representation of the server state + "alarm_severity", + ], +) # The alarm severity associated to this state, represented as an int (see Channel Access doc) + +StatusUpdate = namedtuple( + "StatusUpdate", + [ + "server_status", # The server status + "server_message", + ], +) # The server status display message + +ProblemInfo = namedtuple( + "ProblemInfo", + [ + "description", # The problem description + "source", # The problem source + "severity", + ], +) # The severity of the problem + +ActiveProblemsUpdate = namedtuple( + "ActiveProblemsUpdate", + [ + "errors", # Dictionary of errors (description:sources) + "warnings", # Dictionary of warnings (description:sources) + "other", + ], +) # Dictionary of other problems (description:sources) + +ErrorLogUpdate = namedtuple( + "ErrorLogUpdate", ["log_as_string"] +) # The current error log as a list of strings logger = logging.getLogger(__name__) @@ -37,6 +52,7 @@ class STATUS(Enum): """ Beamline States. """ + INITIALISING = StatusDescription("INITIALISING", Severity.MINOR_ALARM) OKAY = StatusDescription("OKAY", Severity.NO_ALARM) WARNING = StatusDescription("WARNING", Severity.MINOR_ALARM) @@ -72,8 +88,11 @@ class _ServerStatusManager: """ Handler for setting the status of the reflectometry server. """ - INITIALISING_MESSAGE = "Reflectometry Server is initialising. Check configurations is correct and all motor IOCs " \ - "are running if this is taking longer than expected." + + INITIALISING_MESSAGE = ( + "Reflectometry Server is initialising. Check configurations is correct and all motor IOCs " + "are running if this is taking longer than expected." + ) def __init__(self): self._reset() @@ -153,7 +172,10 @@ def _trigger_status_update(self): def _trigger_active_problems_update(self): self.trigger_listeners( - ActiveProblemsUpdate(self.active_errors, self.active_warnings, self.active_other_problems)) + ActiveProblemsUpdate( + self.active_errors, self.active_warnings, self.active_other_problems + ) + ) def _trigger_error_log_update(self): self.trigger_listeners(ErrorLogUpdate(self._error_log_as_string())) diff --git a/ReflectometryServer/test_modules/data_mother.py b/ReflectometryServer/test_modules/data_mother.py index 90cbedbd..65ceb128 100644 --- a/ReflectometryServer/test_modules/data_mother.py +++ b/ReflectometryServer/test_modules/data_mother.py @@ -1,26 +1,52 @@ """ SimpleObservable data and classes. """ -from math import tan, radians, sin, cos -from mock import Mock +from math import cos, radians, sin, tan -from ReflectometryServer import GridDataFileReader, InterpolateGridDataCorrectionFromProvider, ChangeAxis, add_mode, \ - add_component, add_parameter, ConfigHelper, add_driver, add_beam_start, get_configured_beamline, OutOfBeamPosition -from ReflectometryServer.pv_wrapper import DEFAULT_SCALE_FACTOR -from ReflectometryServer.pv_wrapper import SetpointUpdate, ReadbackUpdate, IsChangingUpdate -from server_common.channel_access import AlarmStatus, AlarmSeverity +import numpy as np +from mock import Mock +from server_common.channel_access import AlarmSeverity, AlarmStatus from server_common.observable import observable -from .utils import DEFAULT_TEST_TOLERANCE, create_parameter_with_initial_value -from ReflectometryServer.beamline import BeamlineMode, Beamline -from ReflectometryServer.components import Component, TiltingComponent, ThetaComponent, ReflectingComponent, \ - BenchComponent, BenchSetup +from ReflectometryServer import ( + ChangeAxis, + ConfigHelper, + GridDataFileReader, + InterpolateGridDataCorrectionFromProvider, + OutOfBeamPosition, + add_beam_start, + add_component, + add_driver, + add_mode, + add_parameter, + get_configured_beamline, +) +from ReflectometryServer.beamline import Beamline, BeamlineMode +from ReflectometryServer.components import ( + BenchComponent, + BenchSetup, + Component, + ReflectingComponent, + ThetaComponent, + TiltingComponent, +) from ReflectometryServer.geometry import PositionAndAngle from ReflectometryServer.ioc_driver import IocDriver -from ReflectometryServer.parameters import BeamlineParameter, AxisParameter, \ - SlitGapParameter, InBeamParameter -import numpy as np +from ReflectometryServer.parameters import ( + AxisParameter, + BeamlineParameter, + InBeamParameter, + SlitGapParameter, +) +from ReflectometryServer.pv_wrapper import ( + DEFAULT_SCALE_FACTOR, + IsChangingUpdate, + ReadbackUpdate, + SetpointUpdate, +) + +from .utils import DEFAULT_TEST_TOLERANCE, create_parameter_with_initial_value class EmptyBeamlineParameter(BeamlineParameter): @@ -58,9 +84,10 @@ class DataMother: """ Test data for various tests. """ + BEAMLINE_MODE_NEUTRON_REFLECTION = BeamlineMode( - "Neutron reflection", - ["slit2height", "height", "theta", "detectorheight"]) + "Neutron reflection", ["slit2height", "height", "theta", "detectorheight"] + ) BEAMLINE_MODE_EMPTY = BeamlineMode("Empty", []) @@ -75,8 +102,12 @@ def beamline_with_3_empty_parameters(): two = EmptyBeamlineParameter("two") three = EmptyBeamlineParameter("three") beamline_parameters = [one, two, three] - mode = BeamlineMode("all", [beamline_parameter.name for beamline_parameter in beamline_parameters]) - naught_and_two = BeamlineMode("components1and3", [beamline_parameters[0].name, beamline_parameters[2].name]) + mode = BeamlineMode( + "all", [beamline_parameter.name for beamline_parameter in beamline_parameters] + ) + naught_and_two = BeamlineMode( + "components1and3", [beamline_parameters[0].name, beamline_parameters[2].name] + ) two = BeamlineMode("just2", [beamline_parameters[2].name]) beamline = Beamline([], beamline_parameters, [], [mode, naught_and_two, two]) @@ -107,7 +138,9 @@ def beamline_s1_s3_theta_detector(spacing, initilise_mode_nr=True): add_driver(IocDriver(s1, ChangeAxis.POSITION, s1_axis)) # theta - theta = add_component(ThetaComponent("ThetaComp_comp", PositionAndAngle(0.0, 2 * spacing, 90))) + theta = add_component( + ThetaComponent("ThetaComp_comp", PositionAndAngle(0.0, 2 * spacing, 90)) + ) add_parameter(AxisParameter("theta", theta, ChangeAxis.ANGLE), modes=[nr, disabled]) # s3 @@ -117,7 +150,9 @@ def beamline_s1_s3_theta_detector(spacing, initilise_mode_nr=True): add_driver(IocDriver(s3, ChangeAxis.POSITION, s3_axis)) # detector - detector = add_component(TiltingComponent("Detector_comp", PositionAndAngle(0.0, 4 * spacing, 90))) + detector = add_component( + TiltingComponent("Detector_comp", PositionAndAngle(0.0, 4 * spacing, 90)) + ) theta.add_angle_to(detector) add_parameter(AxisParameter("det", detector, ChangeAxis.POSITION), modes=[nr, disabled]) add_parameter(AxisParameter("det_angle", detector, ChangeAxis.ANGLE), modes=[nr, disabled]) @@ -126,10 +161,12 @@ def beamline_s1_s3_theta_detector(spacing, initilise_mode_nr=True): det_angle_axis = create_mock_axis("MOT:MTR0105", 0, 1) add_driver(IocDriver(detector, ChangeAxis.ANGLE, det_angle_axis)) - axes = {"s1_axis": s1_axis, - "s3_axis": s3_axis, - "det_axis": det_axis, - "det_angle_axis": det_angle_axis} + axes = { + "s1_axis": s1_axis, + "s3_axis": s3_axis, + "det_axis": det_axis, + "det_angle_axis": det_angle_axis, + } add_beam_start(PositionAndAngle(0.0, 0.0, 0.0)) bl = get_configured_beamline() @@ -161,10 +198,16 @@ def beamline_s1_gap_theta_s3_gap_detector(spacing): # BEAMLINE PARAMETERS s1_gap = create_parameter_with_initial_value(0, SlitGapParameter, "s1_gap", s1_gap_axis) - theta_ang = create_parameter_with_initial_value(0, AxisParameter, "theta", theta, ChangeAxis.ANGLE) + theta_ang = create_parameter_with_initial_value( + 0, AxisParameter, "theta", theta, ChangeAxis.ANGLE + ) s3_gap = create_parameter_with_initial_value(0, SlitGapParameter, "s3_gap", s3_gap_axis) - detector_position = create_parameter_with_initial_value(0, AxisParameter, "det", detector, ChangeAxis.POSITION) - detector_angle = create_parameter_with_initial_value(0, AxisParameter, "det_angle", detector, ChangeAxis.ANGLE) + detector_position = create_parameter_with_initial_value( + 0, AxisParameter, "det", detector, ChangeAxis.POSITION + ) + detector_angle = create_parameter_with_initial_value( + 0, AxisParameter, "det_angle", detector, ChangeAxis.ANGLE + ) params = [s1_gap, theta_ang, s3_gap, detector_position, detector_angle] # MODES @@ -180,7 +223,14 @@ def beamline_s1_gap_theta_s3_gap_detector(spacing): return bl, axes @staticmethod - def beamline_sm_theta_detector(sm_angle, theta, det_offset=0, autosave_theta_not_offset=True, beam_angle=0.0, sm_angle_engineering_correction=False): + def beamline_sm_theta_detector( + sm_angle, + theta, + det_offset=0, + autosave_theta_not_offset=True, + beam_angle=0.0, + sm_angle_engineering_correction=False, + ): """ Create beamline with supermirror, theta and a tilting detector. @@ -199,18 +249,28 @@ def beamline_sm_theta_detector(sm_angle, theta, det_offset=0, autosave_theta_not # COMPONENTS z_sm_to_sample = 1 z_sample_to_det = 2 - sm_comp = ReflectingComponent("sm_comp", PositionAndAngle(0.0, 0, perp_to_floor_angle_in_mantid)) - detector_comp = TiltingComponent("detector_comp", PositionAndAngle(0.0, z_sm_to_sample + z_sample_to_det, perp_to_floor_angle_in_mantid)) - theta_comp = ThetaComponent("theta_comp", PositionAndAngle(0.0, z_sm_to_sample, perp_to_floor_angle_in_mantid)) + sm_comp = ReflectingComponent( + "sm_comp", PositionAndAngle(0.0, 0, perp_to_floor_angle_in_mantid) + ) + detector_comp = TiltingComponent( + "detector_comp", + PositionAndAngle(0.0, z_sm_to_sample + z_sample_to_det, perp_to_floor_angle_in_mantid), + ) + theta_comp = ThetaComponent( + "theta_comp", PositionAndAngle(0.0, z_sm_to_sample, perp_to_floor_angle_in_mantid) + ) theta_comp.add_angle_to(detector_comp) comps = [sm_comp, theta_comp, detector_comp] # BEAMLINE PARAMETERS sm_angle_param = AxisParameter("sm_angle", sm_comp, ChangeAxis.ANGLE) - theta_param = AxisParameter("theta", theta_comp, ChangeAxis.ANGLE, autosave=autosave_theta_not_offset) - detector_position_param = AxisParameter("det_pos", detector_comp, ChangeAxis.POSITION, - autosave=not autosave_theta_not_offset) + theta_param = AxisParameter( + "theta", theta_comp, ChangeAxis.ANGLE, autosave=autosave_theta_not_offset + ) + detector_position_param = AxisParameter( + "det_pos", detector_comp, ChangeAxis.POSITION, autosave=not autosave_theta_not_offset + ) detector_angle_param = AxisParameter("det_angle", detector_comp, ChangeAxis.ANGLE) params = [sm_angle_param, theta_param, detector_position_param, detector_angle_param] @@ -220,7 +280,19 @@ def beamline_sm_theta_detector(sm_angle, theta, det_offset=0, autosave_theta_not if sm_angle_engineering_correction: grid_data_provider = GridDataFileReader("linear_theta") grid_data_provider.variables = ["Theta"] - grid_data_provider.points = np.array([[-90, ], [0.0, ], [90.0, ]]) + grid_data_provider.points = np.array( + [ + [ + -90, + ], + [ + 0.0, + ], + [ + 90.0, + ], + ] + ) grid_data_provider.corrections = np.array([-45, 0.0, 45]) grid_data_provider.read = lambda: None correction = InterpolateGridDataCorrectionFromProvider(grid_data_provider, theta_param) @@ -233,18 +305,22 @@ def beamline_sm_theta_detector(sm_angle, theta, det_offset=0, autosave_theta_not beam_angle_after_sample = theta * 2 + sm_angle * 2 supermirror_segment = (z_sm_to_sample, sm_angle) theta_segment = (z_sample_to_det, theta) - reflection_offset = DataMother._calc_reflection_offset(beam_angle, [supermirror_segment, theta_segment]) + reflection_offset = DataMother._calc_reflection_offset( + beam_angle, [supermirror_segment, theta_segment] + ) sm_axis = create_mock_axis("MOT:MTR0101", sm_angle + size_of_correction, 1) det_axis = create_mock_axis("MOT:MTR0104", reflection_offset + det_offset, 1) - det_angle_axis = create_mock_axis("MOT:MTR0105", beam_start.angle + beam_angle_after_sample, 1) + det_angle_axis = create_mock_axis( + "MOT:MTR0105", beam_start.angle + beam_angle_after_sample, 1 + ) - axes = {"sm_axis": sm_axis, - "det_axis": det_axis, - "det_angle_axis": det_angle_axis} + axes = {"sm_axis": sm_axis, "det_axis": det_axis, "det_angle_axis": det_angle_axis} - drives = [IocDriver(sm_comp, ChangeAxis.ANGLE, sm_axis, engineering_correction=correction), - IocDriver(detector_comp, ChangeAxis.POSITION, det_axis), - IocDriver(detector_comp, ChangeAxis.ANGLE, det_angle_axis)] + drives = [ + IocDriver(sm_comp, ChangeAxis.ANGLE, sm_axis, engineering_correction=correction), + IocDriver(detector_comp, ChangeAxis.POSITION, det_axis), + IocDriver(detector_comp, ChangeAxis.ANGLE, det_angle_axis), + ] # MODES nr_inits = {} @@ -275,18 +351,26 @@ def _calc_reflection_offset(beam_angle, segments): cumulative_reflection_angle = 0 for reflection_angle in reflection_angles: - cumulative_reflection_angle += 2*reflection_angle + cumulative_reflection_angle += 2 * reflection_angle offset_1 = distance * sin(radians(beam_angle)) - offset_2 = distance * cos(radians(beam_angle)) * tan(radians(cumulative_reflection_angle - beam_angle)) + offset_2 = ( + distance + * cos(radians(beam_angle)) + * tan(radians(cumulative_reflection_angle - beam_angle)) + ) total_offset += offset_1 + offset_2 return total_offset @staticmethod - def beamline_sm_theta_bench(sm_angle, theta_angle, driver_bench_offset, autosave_bench_not_theta=False, - natural_angle=0.0): - + def beamline_sm_theta_bench( + sm_angle, + theta_angle, + driver_bench_offset, + autosave_bench_not_theta=False, + natural_angle=0.0, + ): ConfigHelper.reset() test = add_mode("TEST") @@ -300,28 +384,46 @@ def beamline_sm_theta_bench(sm_angle, theta_angle, driver_bench_offset, autosave sm_axis.trigger_rbv_change() theta = add_component(ThetaComponent("THETA", PositionAndAngle(0, 10, perp_to_floor_angle))) - add_parameter(AxisParameter("theta", theta, ChangeAxis.ANGLE, autosave=not autosave_bench_not_theta)) + add_parameter( + AxisParameter("theta", theta, ChangeAxis.ANGLE, autosave=not autosave_bench_not_theta) + ) bench = add_component( - get_standard_bench(with_z_position=10, with_angle=0, perp_to_floor_angle=perp_to_floor_angle)) - add_parameter(AxisParameter("bench_angle", bench, ChangeAxis.ANGLE, autosave=autosave_bench_not_theta)) + get_standard_bench( + with_z_position=10, with_angle=0, perp_to_floor_angle=perp_to_floor_angle + ) + ) + add_parameter( + AxisParameter("bench_angle", bench, ChangeAxis.ANGLE, autosave=autosave_bench_not_theta) + ) add_parameter(AxisParameter("bench_offset", bench, ChangeAxis.POSITION)) bench_angle = radians(driver_bench_offset + theta_angle * 2 + sm_angle * 2) - bench_jack_front = create_mock_axis("MOT:MTR0102", tan(bench_angle) * PIVOT_TO_J1 - PIVOT_TO_BEAM * (1 - cos(bench_angle)), 1) - bench_jack_rear = create_mock_axis("MOT:MTR0103", tan(bench_angle) * PIVOT_TO_J2 - PIVOT_TO_BEAM * (1 - cos(bench_angle)), 1) + bench_jack_front = create_mock_axis( + "MOT:MTR0102", + tan(bench_angle) * PIVOT_TO_J1 - PIVOT_TO_BEAM * (1 - cos(bench_angle)), + 1, + ) + bench_jack_rear = create_mock_axis( + "MOT:MTR0103", + tan(bench_angle) * PIVOT_TO_J2 - PIVOT_TO_BEAM * (1 - cos(bench_angle)), + 1, + ) add_driver(IocDriver(bench, ChangeAxis.JACK_REAR, bench_jack_rear)) add_driver(IocDriver(bench, ChangeAxis.JACK_FRONT, bench_jack_front)) bench_jack_rear.trigger_rbv_change() bench_jack_front.trigger_rbv_change() theta.add_angle_of(bench) - return get_configured_beamline(), {"bench_jack_rear": bench_jack_rear, "bench_jack_front": bench_jack_front, "sm_angle": sm_axis} - - + return get_configured_beamline(), { + "bench_jack_rear": bench_jack_rear, + "bench_jack_front": bench_jack_front, + "sm_angle": sm_axis, + } @staticmethod - def beamline_sm_theta_ang_det(sm_angle, theta_angle, driver_comp_offset, autosave_bench_not_theta=False): - + def beamline_sm_theta_ang_det( + sm_angle, theta_angle, driver_comp_offset, autosave_bench_not_theta=False + ): ConfigHelper.reset() test = add_mode("TEST") @@ -334,11 +436,15 @@ def beamline_sm_theta_ang_det(sm_angle, theta_angle, driver_comp_offset, autosav sm_axis.trigger_rbv_change() theta = add_component(ThetaComponent("THETA", PositionAndAngle(0, 10, 90))) - add_parameter(AxisParameter("theta", theta, ChangeAxis.ANGLE, autosave=not autosave_bench_not_theta)) + add_parameter( + AxisParameter("theta", theta, ChangeAxis.ANGLE, autosave=not autosave_bench_not_theta) + ) DIST = 10 bench = add_component(TiltingComponent("comp", PositionAndAngle(0, DIST, 90))) - add_parameter(AxisParameter("comp_angle", bench, ChangeAxis.ANGLE, autosave=autosave_bench_not_theta)) + add_parameter( + AxisParameter("comp_angle", bench, ChangeAxis.ANGLE, autosave=autosave_bench_not_theta) + ) comp_angle = driver_comp_offset + theta_angle * 2 + sm_angle * 2 comp_height = create_mock_axis("MOT:MTR0102", tan(radians(comp_angle)) * DIST, 1) comp_ang = create_mock_axis("MOT:MTR0103", comp_angle, 1) @@ -348,28 +454,47 @@ def beamline_sm_theta_ang_det(sm_angle, theta_angle, driver_comp_offset, autosav comp_height.trigger_rbv_change() theta.add_angle_of(bench) - return get_configured_beamline(), {"comp_ang": comp_ang, "comp_height": comp_height, - "sm_angle": sm_axis} + return get_configured_beamline(), { + "comp_ang": comp_ang, + "comp_height": comp_height, + "sm_angle": sm_axis, + } @staticmethod - def beamline_theta_detector(out_of_beam_pos_z, inital_pos_z, out_of_beam_pos_ang, initial_pos_ang): + def beamline_theta_detector( + out_of_beam_pos_z, inital_pos_z, out_of_beam_pos_ang, initial_pos_ang + ): ConfigHelper.reset() theta_comp = add_component(ThetaComponent("theta", PositionAndAngle(0, 0, 90))) theta = add_parameter(AxisParameter("theta", theta_comp, ChangeAxis.ANGLE)) detector_comp = TiltingComponent("detector", PositionAndAngle(0, 1, 90)) axis_det_z = create_mock_axis("det_z", inital_pos_z, 1) - add_driver(IocDriver(detector_comp, ChangeAxis.POSITION, axis_det_z, - out_of_beam_positions=[OutOfBeamPosition(out_of_beam_pos_z)])) + add_driver( + IocDriver( + detector_comp, + ChangeAxis.POSITION, + axis_det_z, + out_of_beam_positions=[OutOfBeamPosition(out_of_beam_pos_z)], + ) + ) axis_det_ang = create_mock_axis("det_ang", initial_pos_ang, 1) - add_driver(IocDriver(detector_comp, ChangeAxis.ANGLE, axis_det_ang, - out_of_beam_positions=[OutOfBeamPosition(out_of_beam_pos_ang)])) + add_driver( + IocDriver( + detector_comp, + ChangeAxis.ANGLE, + axis_det_ang, + out_of_beam_positions=[OutOfBeamPosition(out_of_beam_pos_ang)], + ) + ) det_in = add_parameter(InBeamParameter("det_in", detector_comp)) theta_comp.add_angle_to(detector_comp) get_configured_beamline() return det_in, theta @staticmethod - def beamline_with_one_mode_init_param_in_mode_and_at_off_init(init_sm_angle, off_init, param_name): + def beamline_with_one_mode_init_param_in_mode_and_at_off_init( + init_sm_angle, off_init, param_name + ): super_mirror = ReflectingComponent("super mirror", PositionAndAngle(z=10, y=0, angle=90)) smangle = AxisParameter(param_name, super_mirror, ChangeAxis.ANGLE) beamline_mode = BeamlineMode("mode name", [smangle.name], {smangle.name: init_sm_angle}) @@ -379,7 +504,16 @@ def beamline_with_one_mode_init_param_in_mode_and_at_off_init(init_sm_angle, off return Beamline([super_mirror], [smangle], [], [beamline_mode]) -def create_mock_axis(name, init_position, max_velocity, backlash_distance=0, backlash_velocity=1, direction="Pos", llm=float('-inf'), hlm=float('inf')): +def create_mock_axis( + name, + init_position, + max_velocity, + backlash_distance=0, + backlash_velocity=1, + direction="Pos", + llm=float("-inf"), + hlm=float("inf"), +): """ Create a mock axis Args: @@ -395,12 +529,33 @@ def create_mock_axis(name, init_position, max_velocity, backlash_distance=0, bac mocked axis """ - return MockMotorPVWrapper(name, init_position, max_velocity, True, backlash_distance, backlash_velocity, direction, llm, hlm) + return MockMotorPVWrapper( + name, + init_position, + max_velocity, + True, + backlash_distance, + backlash_velocity, + direction, + llm, + hlm, + ) @observable(SetpointUpdate, ReadbackUpdate, IsChangingUpdate) class MockMotorPVWrapper: - def __init__(self, pv_name, init_position, max_velocity, is_vertical=True, backlash_distance=0, backlash_velocity=1, direction="Neg", llm=float('-inf'), hlm=float('inf')): + def __init__( + self, + pv_name, + init_position, + max_velocity, + is_vertical=True, + backlash_distance=0, + backlash_velocity=1, + direction="Neg", + llm=float("-inf"), + hlm=float("inf"), + ): self.name = pv_name self._value = init_position self.max_velocity = max_velocity @@ -477,7 +632,7 @@ def __init__(self, pvs): def pv_exists(self, pv): return pv in self._pvs.keys() - def add_monitor(self,pv, call_back_function): + def add_monitor(self, pv, call_back_function): pass def caget(self, pv): @@ -490,13 +645,17 @@ def caput(self, pv, value, wait=False, safe_not_quick=False): self._pvs[pv] = value -def create_mock_JawsCentrePVWrapper(name, init_position, max_velocity, backlash_distance=0, backlash_velocity=1, direction="Pos"): +def create_mock_JawsCentrePVWrapper( + name, init_position, max_velocity, backlash_distance=0, backlash_velocity=1, direction="Pos" +): """ Create a mock jaws centre pv wrapper for testing Returns: mock """ - mock_jaws_wrapper = create_mock_axis(name, init_position, max_velocity, backlash_distance, backlash_velocity, direction) + mock_jaws_wrapper = create_mock_axis( + name, init_position, max_velocity, backlash_distance, backlash_velocity, direction + ) mock_jaws_wrapper.define_position_as = Mock() mock_jaws_wrapper.is_vertical = False @@ -510,7 +669,10 @@ def create_mock_JawsCentrePVWrapper(name, init_position, max_velocity, backlash_ BENCH_MIN_ANGLE = 0 BENCH_MAX_ANGLE = 4.8 -def get_standard_bench(with_z_position=0, with_angle=ANGLE_OF_BENCH, vertical_mode=False, perp_to_floor_angle=90.0): + +def get_standard_bench( + with_z_position=0, with_angle=ANGLE_OF_BENCH, vertical_mode=False, perp_to_floor_angle=90.0 +): """ Get the standard bench setup as per POLREF Args: @@ -519,5 +681,18 @@ def get_standard_bench(with_z_position=0, with_angle=ANGLE_OF_BENCH, vertical_mo Returns: Bench setup correctly """ - return BenchComponent("rear_bench", BenchSetup(0, with_z_position, perp_to_floor_angle, PIVOT_TO_J1, PIVOT_TO_J2, with_angle, - PIVOT_TO_BEAM, BENCH_MIN_ANGLE, BENCH_MAX_ANGLE, vertical_mode)) + return BenchComponent( + "rear_bench", + BenchSetup( + 0, + with_z_position, + perp_to_floor_angle, + PIVOT_TO_J1, + PIVOT_TO_J2, + with_angle, + PIVOT_TO_BEAM, + BENCH_MIN_ANGLE, + BENCH_MAX_ANGLE, + vertical_mode, + ), + ) diff --git a/ReflectometryServer/test_modules/test_actual_positions.py b/ReflectometryServer/test_modules/test_actual_positions.py index f5ad230c..78e7ec5a 100644 --- a/ReflectometryServer/test_modules/test_actual_positions.py +++ b/ReflectometryServer/test_modules/test_actual_positions.py @@ -2,15 +2,14 @@ from hamcrest import * -from ReflectometryServer.components import ReflectingComponent, Component -from ReflectometryServer.geometry import PositionAndAngle, PositionAndAngle, ChangeAxis from ReflectometryServer.beamline import Beamline, BeamlineMode +from ReflectometryServer.components import Component, ReflectingComponent +from ReflectometryServer.geometry import ChangeAxis, PositionAndAngle from ReflectometryServer.parameters import AxisParameter from ReflectometryServer.test_modules.utils import create_parameter_with_initial_value class TestComponentBeamline(unittest.TestCase): - def setUp(self): beam_start = PositionAndAngle(y=0, z=0, angle=2.5) s0 = Component("s0", setup=PositionAndAngle(0, 0, 90)) @@ -20,39 +19,63 @@ def setUp(self): self.polarising_mirror = ReflectingComponent("Polariser", setup=PositionAndAngle(0, 3, 90)) self.polarising_mirror.beam_path_set_point.is_in_beam = False s2 = Component("s2", setup=PositionAndAngle(0, 4, 90)) - self.ideal_sample_point = ReflectingComponent("ideal sample point", setup=PositionAndAngle(0, 5, 90)) + self.ideal_sample_point = ReflectingComponent( + "ideal sample point", setup=PositionAndAngle(0, 5, 90) + ) s3 = Component("s3", setup=PositionAndAngle(0, 6, 90)) analyser = ReflectingComponent("analyser", setup=PositionAndAngle(0, 7, 90)) analyser.beam_path_set_point.is_in_beam = False s4 = Component("s4", setup=PositionAndAngle(0, 8, 90)) detector = Component("detector", setup=PositionAndAngle(0, 10, 90)) - theta = create_parameter_with_initial_value(0, AxisParameter, "theta", self.ideal_sample_point, ChangeAxis.ANGLE) + theta = create_parameter_with_initial_value( + 0, AxisParameter, "theta", self.ideal_sample_point, ChangeAxis.ANGLE + ) theta.sp_no_move = 0 - smangle = create_parameter_with_initial_value(0, AxisParameter, "smangle", self.polarising_mirror, ChangeAxis.ANGLE) + smangle = create_parameter_with_initial_value( + 0, AxisParameter, "smangle", self.polarising_mirror, ChangeAxis.ANGLE + ) smangle.sp_no_move = 0 self.nr_mode = BeamlineMode("NR Mode", [theta.name]) self.polarised_mode = BeamlineMode("Polarised Mode", [smangle.name, theta.name]) self.beamline = Beamline( - [s0, s1, frame_overlap_mirror, self.polarising_mirror, s2, self.ideal_sample_point, s3, analyser, s4, detector], + [ + s0, + s1, + frame_overlap_mirror, + self.polarising_mirror, + s2, + self.ideal_sample_point, + s3, + analyser, + s4, + detector, + ], [smangle, theta], [], [self.nr_mode, self.polarised_mode], - beam_start + beam_start, ) - def test_GIVEN_beam_line_contains_multiple_component_WHEN_set_theta_THEN_angle_between_incoming_and_outgoing_beam_is_correct(self): + def test_GIVEN_beam_line_contains_multiple_component_WHEN_set_theta_THEN_angle_between_incoming_and_outgoing_beam_is_correct( + self, + ): self.beamline.active_mode = self.nr_mode.name theta_set = 10.0 self.beamline.parameter("theta").sp = theta_set - reflection_angle = self.ideal_sample_point.beam_path_set_point.get_outgoing_beam().angle - self.ideal_sample_point.beam_path_set_point._incoming_beam.angle + reflection_angle = ( + self.ideal_sample_point.beam_path_set_point.get_outgoing_beam().angle + - self.ideal_sample_point.beam_path_set_point._incoming_beam.angle + ) assert_that(reflection_angle, is_(theta_set * 2.0)) - def test_GIVEN_beam_line_contains_active_super_mirror_WHEN_set_theta_THEN_angle_between_incoming_and_outgoing_beam_is_correct(self): + def test_GIVEN_beam_line_contains_active_super_mirror_WHEN_set_theta_THEN_angle_between_incoming_and_outgoing_beam_is_correct( + self, + ): self.beamline.active_mode = self.polarised_mode.name theta_set = 10.0 self.polarising_mirror.beam_path_set_point.is_in_beam = True @@ -60,10 +83,15 @@ def test_GIVEN_beam_line_contains_active_super_mirror_WHEN_set_theta_THEN_angle_ self.beamline.parameter("theta").sp = theta_set - reflection_angle = self.ideal_sample_point.beam_path_set_point.get_outgoing_beam().angle - self.ideal_sample_point.beam_path_set_point._incoming_beam.angle + reflection_angle = ( + self.ideal_sample_point.beam_path_set_point.get_outgoing_beam().angle + - self.ideal_sample_point.beam_path_set_point._incoming_beam.angle + ) assert_that(reflection_angle, is_(theta_set * 2.0)) - def test_GIVEN_beam_line_contains_active_super_mirror_WHEN_angle_set_THEN_angle_between_incoming_and_outgoing_beam_is_correct(self): + def test_GIVEN_beam_line_contains_active_super_mirror_WHEN_angle_set_THEN_angle_between_incoming_and_outgoing_beam_is_correct( + self, + ): self.beamline.active_mode = self.polarised_mode.name theta_set = 10.0 self.beamline.parameter("theta").sp = theta_set @@ -71,9 +99,12 @@ def test_GIVEN_beam_line_contains_active_super_mirror_WHEN_angle_set_THEN_angle_ self.beamline.parameter("smangle").sp = 10 - reflection_angle = self.ideal_sample_point.beam_path_set_point.get_outgoing_beam().angle - self.ideal_sample_point.beam_path_set_point._incoming_beam.angle + reflection_angle = ( + self.ideal_sample_point.beam_path_set_point.get_outgoing_beam().angle + - self.ideal_sample_point.beam_path_set_point._incoming_beam.angle + ) assert_that(reflection_angle, is_(theta_set * 2.0)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_beam_path_calc.py b/ReflectometryServer/test_modules/test_beam_path_calc.py index 22782a08..4b7b9b5f 100644 --- a/ReflectometryServer/test_modules/test_beam_path_calc.py +++ b/ReflectometryServer/test_modules/test_beam_path_calc.py @@ -1,71 +1,87 @@ import unittest from hamcrest import * +from mock import MagicMock +from server_common.channel_access import AlarmSeverity, AlarmStatus -from ReflectometryServer.beam_path_calc import BeamPathCalcThetaSP, BeamPathCalcThetaRBV, TrackingBeamPathCalc from ReflectometryServer.axis import BeamPathCalcAxis, ComponentAxis +from ReflectometryServer.beam_path_calc import ( + BeamPathCalcThetaRBV, + BeamPathCalcThetaSP, + TrackingBeamPathCalc, +) from ReflectometryServer.components import Component -from ReflectometryServer.geometry import PositionAndAngle, ChangeAxis +from ReflectometryServer.geometry import ChangeAxis, PositionAndAngle from ReflectometryServer.ioc_driver import CorrectedReadbackUpdate -from mock import MagicMock -from server_common.channel_access import AlarmStatus, AlarmSeverity class TestBeamPathCalc(unittest.TestCase): - def test_GIVEN_no_get_displacement_set_WHEN_get_displacement_THEN_Error(self): - calc_axis = BeamPathCalcAxis(None, None, None) - assert_that(calling(calc_axis.get_displacement), - raises(TypeError, pattern="Axis does not support get_displacement")) + assert_that( + calling(calc_axis.get_displacement), + raises(TypeError, pattern="Axis does not support get_displacement"), + ) def test_GIVEN_no_set_displacement_set_WHEN_set_displacement_THEN_Error(self): - calc_axis = BeamPathCalcAxis(None, None, None) - assert_that(calling(calc_axis.set_displacement).with_args(CorrectedReadbackUpdate(None, None, None)), - raises(TypeError, pattern="Axis does not support set_displacement")) + assert_that( + calling(calc_axis.set_displacement).with_args( + CorrectedReadbackUpdate(None, None, None) + ), + raises(TypeError, pattern="Axis does not support set_displacement"), + ) def test_GIVEN_no_get_displacement_for_set_WHEN_call_define_event_THEN_Error(self): - calc_axis = BeamPathCalcAxis(None, None, None) - assert_that(calling(calc_axis.define_axis_position_as).with_args(None), - raises(TypeError, pattern="Axis can not have its position defined")) + assert_that( + calling(calc_axis.define_axis_position_as).with_args(None), + raises(TypeError, pattern="Axis can not have its position defined"), + ) def test_GIVEN_can_define_position_as_is_false_WHEN_call_define_event_THEN_Error(self): - calc_axis = BeamPathCalcAxis(None, None, None, get_displacement_for=lambda x: x) calc_axis.can_define_axis_position_as = False - assert_that(calling(calc_axis.define_axis_position_as).with_args(None), - raises(TypeError, pattern="Axis can not have its position defined")) + assert_that( + calling(calc_axis.define_axis_position_as).with_args(None), + raises(TypeError, pattern="Axis can not have its position defined"), + ) def test_GIVEN_no_init_displacement_from_motor_WHEN_called_THEN_Error(self): - calc_axis = BeamPathCalcAxis(None, None, None) - assert_that(calling(calc_axis.init_displacement_from_motor).with_args(None), - raises(TypeError, pattern="Axis does not support init_displacement_from_motor")) + assert_that( + calling(calc_axis.init_displacement_from_motor).with_args(None), + raises(TypeError, pattern="Axis does not support init_displacement_from_motor"), + ) def test_GIVEN_theta_sp_beam_path_calc_WHEN_add_CHI_axis_and_update_THEN_error(self): - theta_sp = BeamPathCalcThetaSP("theta", None) comp = Component("comp", PositionAndAngle(0, 0, 0)) theta_sp.add_angle_to(comp.beam_path_rbv, [ChangeAxis.CHI]) - assert_that(calling(comp.beam_path_rbv.axis[ChangeAxis.CHI].init_displacement_from_motor).with_args(10), - raises(RuntimeError)) + assert_that( + calling(comp.beam_path_rbv.axis[ChangeAxis.CHI].init_displacement_from_motor).with_args( + 10 + ), + raises(RuntimeError), + ) def test_GIVEN_theta_rbv_beam_path_calc_WHEN_add_CHI_axis_and_set_displacement_THEN_error(self): - theta_sp = BeamPathCalcThetaRBV("theta", None, None) comp = Component("comp", PositionAndAngle(0, 0, 0)) theta_sp.add_angle_to(comp.beam_path_rbv, comp.beam_path_set_point, [ChangeAxis.CHI]) - assert_that(calling(comp.beam_path_rbv.axis[ChangeAxis.CHI].set_displacement).with_args( - CorrectedReadbackUpdate(10, None, None)), raises(RuntimeError)) + assert_that( + calling(comp.beam_path_rbv.axis[ChangeAxis.CHI].set_displacement).with_args( + CorrectedReadbackUpdate(10, None, None) + ), + raises(RuntimeError), + ) class TestBeamPathCalcThetaRBVAlarmCalc(unittest.TestCase): @@ -77,45 +93,72 @@ def set_up_alarms(self, first_alarm, second_alarm): long_axis = MagicMock(ComponentAxis) long_axis.alarm = second_alarm rbv_beam_path_calc = MagicMock(TrackingBeamPathCalc) - rbv_beam_path_calc.axis = {ChangeAxis.POSITION: position_axis, ChangeAxis.LONG_AXIS: long_axis} + rbv_beam_path_calc.axis = { + ChangeAxis.POSITION: position_axis, + ChangeAxis.LONG_AXIS: long_axis, + } return rbv_beam_path_calc def test_GIVEN_all_axis_ok_WHEN_alarm_calced_THEN_no_alarm(self): - rbv_beam_path = self.set_up_alarms((AlarmSeverity.No, AlarmStatus.No), (AlarmSeverity.No, AlarmStatus.No)) - severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes(self.axes, rbv_beam_path) + rbv_beam_path = self.set_up_alarms( + (AlarmSeverity.No, AlarmStatus.No), (AlarmSeverity.No, AlarmStatus.No) + ) + severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes( + self.axes, rbv_beam_path + ) self.assertEqual(severity, AlarmSeverity.No) self.assertEqual(status, AlarmStatus.No) def test_GIVEN_one_axis_undefined_WHEN_alarm_calced_THEN_no_alarm(self): - rbv_beam_path = self.set_up_alarms((AlarmSeverity.Invalid, AlarmStatus.UDF), (AlarmSeverity.No, AlarmStatus.No)) - severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes(self.axes, rbv_beam_path) + rbv_beam_path = self.set_up_alarms( + (AlarmSeverity.Invalid, AlarmStatus.UDF), (AlarmSeverity.No, AlarmStatus.No) + ) + severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes( + self.axes, rbv_beam_path + ) self.assertEqual(severity, AlarmSeverity.No) self.assertEqual(status, AlarmStatus.No) def test_GIVEN_both_axes_undefined_WHEN_alarm_calced_THEN_undefined_alarm(self): - rbv_beam_path = self.set_up_alarms((AlarmSeverity.Invalid, AlarmStatus.UDF), (AlarmSeverity.Invalid, AlarmStatus.UDF)) - severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes(self.axes, rbv_beam_path) + rbv_beam_path = self.set_up_alarms( + (AlarmSeverity.Invalid, AlarmStatus.UDF), (AlarmSeverity.Invalid, AlarmStatus.UDF) + ) + severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes( + self.axes, rbv_beam_path + ) self.assertEqual(severity, AlarmSeverity.Invalid) self.assertEqual(status, AlarmStatus.UDF) def test_GIVEN_one_axis_major_other_undefined_WHEN_alarm_calced_THEN_major_alarm(self): - rbv_beam_path = self.set_up_alarms((AlarmSeverity.Invalid, AlarmStatus.UDF), (AlarmSeverity.Major, AlarmStatus.HiHi)) - severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes(self.axes, rbv_beam_path) + rbv_beam_path = self.set_up_alarms( + (AlarmSeverity.Invalid, AlarmStatus.UDF), (AlarmSeverity.Major, AlarmStatus.HiHi) + ) + severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes( + self.axes, rbv_beam_path + ) self.assertEqual(severity, AlarmSeverity.Major) self.assertEqual(status, AlarmStatus.HiHi) def test_GIVEN_one_axis_major_other_no_alarm_WHEN_alarm_calced_THEN_major_alarm(self): - rbv_beam_path = self.set_up_alarms((AlarmSeverity.No, AlarmStatus.No), (AlarmSeverity.Major, AlarmStatus.HiHi)) - severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes(self.axes, rbv_beam_path) + rbv_beam_path = self.set_up_alarms( + (AlarmSeverity.No, AlarmStatus.No), (AlarmSeverity.Major, AlarmStatus.HiHi) + ) + severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes( + self.axes, rbv_beam_path + ) self.assertEqual(severity, AlarmSeverity.Major) self.assertEqual(status, AlarmStatus.HiHi) def test_GIVEN_one_axis_major_other_minor_WHEN_alarm_calced_THEN_major_alarm(self): - rbv_beam_path = self.set_up_alarms((AlarmSeverity.Minor, AlarmStatus.High), (AlarmSeverity.Major, AlarmStatus.HiHi)) - severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes(self.axes, rbv_beam_path) + rbv_beam_path = self.set_up_alarms( + (AlarmSeverity.Minor, AlarmStatus.High), (AlarmSeverity.Major, AlarmStatus.HiHi) + ) + severity, status = BeamPathCalcThetaRBV.calculate_alarm_based_on_axes( + self.axes, rbv_beam_path + ) self.assertEqual(severity, AlarmSeverity.Major) self.assertEqual(status, AlarmStatus.HiHi) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_beamline.py b/ReflectometryServer/test_modules/test_beamline.py index 3425a017..b00ee447 100644 --- a/ReflectometryServer/test_modules/test_beamline.py +++ b/ReflectometryServer/test_modules/test_beamline.py @@ -1,49 +1,88 @@ -import math import unittest +from math import radians, tan -from math import tan, radians from hamcrest import * -from mock import Mock, patch, call +from mock import Mock, patch from parameterized import parameterized from ReflectometryServer import * - from ReflectometryServer.beamline import BeamlineConfigurationInvalidException +from ReflectometryServer.beamline_constant import BeamlineConstant from ReflectometryServer.exceptions import BeamlineConfigurationParkAutosaveInvalidException from ReflectometryServer.ioc_driver import CorrectedReadbackUpdate from ReflectometryServer.out_of_beam import OutOfBeamSequence -from ReflectometryServer.test_modules.data_mother import DataMother, create_mock_axis, EmptyBeamlineParameter -from ReflectometryServer.beamline_constant import BeamlineConstant - -from ReflectometryServer.test_modules.utils import position_and_angle, no_autosave +from ReflectometryServer.test_modules.data_mother import ( + DataMother, + EmptyBeamlineParameter, + create_mock_axis, +) +from ReflectometryServer.test_modules.utils import no_autosave, position_and_angle # Three axes under auto save, first tuple is out of beam positions, # second tuple if initial positions # last tuple is expected position, # bool is whether in or out of beam TEST_PARAMETER_POSITION_SETS = [ - ((-2, -3, -4), (-2, -3, -4), (0, 0, 0), False), # all axes start out of beam, moving in moves to 0 - ((-2, -3, -4), (0, -3, -4), (0, -3, -4), True), # first axes in beam others out of beam, moving in moves to current positions - ((-2, -3, -4), (-2, 0, -4), (-2, 0, -4), True), # middle axes in beam others out of beam, moving in moves to current positions - ((-2, -3, -4), (-2, -3, 0), (-2, -3, 0), True), # last axes in beam others out of beam, moving in moves to current positions - ((-2, -3, -4), (0, -3, -4), (0, -3, -4), True), # first axes not in beam others in beam, moving in moves to current positions - ((-2, -3, -4), (-2, 0, -4), (-2, 0, -4), True), # middle axes not in beam others in beam, moving in moves to current positions - ((-2, -3, -4), (-2, -3, 0), (-2, -3, 0), True), # last axes not in beam others in beam, moving in moves to current positions + ( + (-2, -3, -4), + (-2, -3, -4), + (0, 0, 0), + False, + ), # all axes start out of beam, moving in moves to 0 + ( + (-2, -3, -4), + (0, -3, -4), + (0, -3, -4), + True, + ), # first axes in beam others out of beam, moving in moves to current positions + ( + (-2, -3, -4), + (-2, 0, -4), + (-2, 0, -4), + True, + ), # middle axes in beam others out of beam, moving in moves to current positions + ( + (-2, -3, -4), + (-2, -3, 0), + (-2, -3, 0), + True, + ), # last axes in beam others out of beam, moving in moves to current positions + ( + (-2, -3, -4), + (0, -3, -4), + (0, -3, -4), + True, + ), # first axes not in beam others in beam, moving in moves to current positions + ( + (-2, -3, -4), + (-2, 0, -4), + (-2, 0, -4), + True, + ), # middle axes not in beam others in beam, moving in moves to current positions + ( + (-2, -3, -4), + (-2, -3, 0), + (-2, -3, 0), + True, + ), # last axes not in beam others in beam, moving in moves to current positions ((-2, -3, -4), (1, 2, 3), (1, 2, 3), True), # all axes in the beam, move to current position - ] +] class TestComponentBeamline(unittest.TestCase): - def setup_beamline(self, initial_mirror_angle, mirror_position, beam_start): jaws = Component("jaws", setup=PositionAndAngle(0, 0, 90)) mirror = ReflectingComponent("mirror", setup=PositionAndAngle(0, mirror_position, 90)) - mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(initial_mirror_angle, None, None)) + mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(initial_mirror_angle, None, None) + ) jaws3 = Component("jaws3", setup=PositionAndAngle(0, 20, 90)) beamline = Beamline([jaws, mirror, jaws3], [], [], [BeamlineMode("mode", [])], beam_start) return beamline, mirror - def test_GIVEN_beam_line_contains_one_passive_component_WHEN_beam_set_THEN_component_has_beam_out_same_as_beam_in(self): + def test_GIVEN_beam_line_contains_one_passive_component_WHEN_beam_set_THEN_component_has_beam_out_same_as_beam_in( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) jaws = Component("jaws", setup=PositionAndAngle(0, 2, 90)) beamline = Beamline([jaws], [], [], [BeamlineMode("mode", [])], beam_start) @@ -52,7 +91,9 @@ def test_GIVEN_beam_line_contains_one_passive_component_WHEN_beam_set_THEN_compo assert_that(result, is_(position_and_angle(beam_start))) - def test_GIVEN_beam_line_contains_multiple_component_WHEN_beam_set_THEN_each_component_has_beam_out_which_is_effected_by_each_component_in_turn(self): + def test_GIVEN_beam_line_contains_multiple_component_WHEN_beam_set_THEN_each_component_has_beam_out_which_is_effected_by_each_component_in_turn( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) mirror_position = 10 initial_mirror_angle = 22.5 @@ -65,8 +106,9 @@ def test_GIVEN_beam_line_contains_multiple_component_WHEN_beam_set_THEN_each_com for index, (result, expected_beam) in enumerate(zip(results, expected_beams)): assert_that(result, position_and_angle(expected_beam), "in component {}".format(index)) - - def test_GIVEN_beam_line_contains_multiple_component_WHEN_angle_on_mirror_changed_THEN_beam_positions_are_all_recalculated(self): + def test_GIVEN_beam_line_contains_multiple_component_WHEN_angle_on_mirror_changed_THEN_beam_positions_are_all_recalculated( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) mirror_position = 10 initial_mirror_angle = 0 @@ -77,13 +119,19 @@ def test_GIVEN_beam_line_contains_multiple_component_WHEN_angle_on_mirror_change bounced_beam = PositionAndAngle(y=0, z=mirror_position, angle=mirror_final_angle * 2) expected_beams = [beam_start, bounced_beam, bounced_beam] - mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(mirror_final_angle, None, None)) + mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(mirror_final_angle, None, None) + ) results = [component.beam_path_set_point.get_outgoing_beam() for component in beamline] for index, (result, expected_beam) in enumerate(zip(results, expected_beams)): - assert_that(result, position_and_angle(expected_beam), "in component index {}".format(index)) + assert_that( + result, position_and_angle(expected_beam), "in component index {}".format(index) + ) - def test_GIVEN_beam_line_contains_multiple_component_WHEN_mirror_disabled_THEN_beam_positions_are_all_recalculated(self): + def test_GIVEN_beam_line_contains_multiple_component_WHEN_mirror_disabled_THEN_beam_positions_are_all_recalculated( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) mirror_position = 10 initial_mirror_angle = 22.5 @@ -96,26 +144,32 @@ def test_GIVEN_beam_line_contains_multiple_component_WHEN_mirror_disabled_THEN_b results = [component.beam_path_set_point.get_outgoing_beam() for component in beamline] for index, (result, expected_beam) in enumerate(zip(results, expected_beams)): - assert_that(result, position_and_angle(expected_beam), "in component index {}".format(index)) + assert_that( + result, position_and_angle(expected_beam), "in component index {}".format(index) + ) class TestComponentBeamlineReadbacks(unittest.TestCase): - - def test_GIVEN_components_in_beamline_WHEN_readback_changed_THEN_components_after_changed_component_updatereadbacks(self): + def test_GIVEN_components_in_beamline_WHEN_readback_changed_THEN_components_after_changed_component_updatereadbacks( + self, + ): comp1 = Component("comp1", PositionAndAngle(0, 1, 90)) comp2 = Component("comp2", PositionAndAngle(0, 2, 90)) beamline = Beamline([comp1, comp2], [], [], [DataMother.BEAMLINE_MODE_EMPTY]) callback = Mock() comp2.beam_path_rbv.set_incoming_beam = callback - comp1.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(1.0, None, None)) + comp1.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(1.0, None, None) + ) assert_that(callback.called, is_(True)) class TestRealistic(unittest.TestCase): - - def test_GIVEN_beam_line_where_all_items_track_WHEN_set_theta_THEN_motors_move_to_be_on_the_beam(self): + def test_GIVEN_beam_line_where_all_items_track_WHEN_set_theta_THEN_motors_move_to_be_on_the_beam( + self, + ): spacing = 2.0 bl, drives = DataMother.beamline_s1_s3_theta_detector(spacing) @@ -136,9 +190,11 @@ def test_GIVEN_beam_line_where_all_items_track_WHEN_set_theta_THEN_motors_move_t expected_det_value = 2 * spacing * tan(radians(theta_angle * 2.0)) assert_that(drives["det_axis"].sp, is_(expected_det_value)) - assert_that(drives["det_angle_axis"].sp, is_(2*theta_angle)) + assert_that(drives["det_angle_axis"].sp, is_(2 * theta_angle)) - def test_GIVEN_beam_line_where_all_items_have_offset_WHEN_set_theta_THEN_motors_move_to_be_correct_distance_from_the_beam_theta_readback_is_correct_and_does_not_include_offset(self): + def test_GIVEN_beam_line_where_all_items_have_offset_WHEN_set_theta_THEN_motors_move_to_be_correct_distance_from_the_beam_theta_readback_is_correct_and_does_not_include_offset( + self, + ): spacing = 2.0 bl, drives = DataMother.beamline_s1_s3_theta_detector(spacing) @@ -166,12 +222,14 @@ def test_GIVEN_beam_line_where_all_items_have_offset_WHEN_set_theta_THEN_motors_ assert_that(drives["det_axis"].sp, is_(expected_det_value)) assert_that(bl.parameter("det").rbv, is_(det_offset)) - assert_that(drives["det_angle_axis"].sp, is_(2*theta_angle + det_ang_offset)) + assert_that(drives["det_angle_axis"].sp, is_(2 * theta_angle + det_ang_offset)) assert_that(bl.parameter("det_angle").rbv, close_to(det_ang_offset, 1e-6)) assert_that(bl.parameter("theta").rbv, close_to(theta_angle, 1e-6)) - def test_GIVEN_beam_line_where_all_items_track_WHEN_set_theta_no_move_and_move_beamline_THEN_motors_move_to_be_on_the_beam(self): + def test_GIVEN_beam_line_where_all_items_track_WHEN_set_theta_no_move_and_move_beamline_THEN_motors_move_to_be_on_the_beam( + self, + ): spacing = 2.0 bl, drives = DataMother.beamline_s1_s3_theta_detector(spacing) @@ -193,9 +251,11 @@ def test_GIVEN_beam_line_where_all_items_track_WHEN_set_theta_no_move_and_move_b expected_det_value = 2 * spacing * tan(radians(theta_angle * 2.0)) assert_that(drives["det_axis"].sp, is_(expected_det_value)) - assert_that(drives["det_angle_axis"].sp, is_(2*theta_angle)) + assert_that(drives["det_angle_axis"].sp, is_(2 * theta_angle)) - def test_GIVEN_beam_line_where_all_items_track_but_one_axis_has_soft_limits_outside_of_range_WHEN_set_theta_no_move_and_move_beamline_THEN_motors_do_not_move(self): + def test_GIVEN_beam_line_where_all_items_track_but_one_axis_has_soft_limits_outside_of_range_WHEN_set_theta_no_move_and_move_beamline_THEN_motors_do_not_move( + self, + ): spacing = 2.0 bl, drives = DataMother.beamline_s1_s3_theta_detector(spacing) @@ -234,9 +294,9 @@ def test_GIVEN_beam_line_which_is_in_disabled_mode_WHEN_set_theta_THEN_nothing_e expected_det_value = 2 * spacing * tan(radians(theta_angle * 2.0)) assert_that(drives["det_axis"].sp, is_(expected_det_value)) - assert_that(drives["det_angle_axis"].sp, is_(2*theta_angle)) + assert_that(drives["det_angle_axis"].sp, is_(2 * theta_angle)) - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave') + @patch("ReflectometryServer.beam_path_calc.disable_mode_autosave") def test_GIVEN_beam_line_WHEN_set_disabled_THEN_incoming_beam_auto_saved(self, mock_auto_save): mock_auto_save.read_parameter.return_value = None spacing = 2.0 @@ -249,99 +309,125 @@ def test_GIVEN_beam_line_WHEN_set_disabled_THEN_incoming_beam_auto_saved(self, m bl.active_mode = "DISABLED" - calls_by_component = {call_arg[0][0]: call_arg[0][1] - for call_arg in mock_auto_save.write_parameter.call_args_list} + calls_by_component = { + call_arg[0][0]: call_arg[0][1] + for call_arg in mock_auto_save.write_parameter.call_args_list + } for component in bl: save_name = component.name + "_sp" - assert_that(calls_by_component[save_name], - is_(position_and_angle(component.beam_path_set_point._incoming_beam)), - "call autosaving {}".format(save_name)) + assert_that( + calls_by_component[save_name], + is_(position_and_angle(component.beam_path_set_point._incoming_beam)), + "call autosaving {}".format(save_name), + ) save_name = component.name + "_rbv" - assert_that(calls_by_component[save_name], - is_(position_and_angle(component.beam_path_rbv._incoming_beam)), - "call autosaving {}".format(save_name)) - - def test_GIVEN_negative_sm_angle_WHEN_calculating_bench_intercept_THEN_value_accepted_and_no_error_thrown(self): + assert_that( + calls_by_component[save_name], + is_(position_and_angle(component.beam_path_rbv._incoming_beam)), + "call autosaving {}".format(save_name), + ) + + def test_GIVEN_negative_sm_angle_WHEN_calculating_bench_intercept_THEN_value_accepted_and_no_error_thrown( + self, + ): expected_theta = 0.2 driver_bench_offset = 0 sm_angle_to_set = -0.1 - bl, drives = DataMother.beamline_sm_theta_bench(0.0, expected_theta, driver_bench_offset, - natural_angle=2.3) + bl, drives = DataMother.beamline_sm_theta_bench( + 0.0, expected_theta, driver_bench_offset, natural_angle=2.3 + ) bl.parameter("sm_angle").sp = sm_angle_to_set assert_that(bl.parameter("sm_angle").sp_rbv, is_(sm_angle_to_set)) class TestBeamlineValidation(unittest.TestCase): - def test_GIVEN_two_beamline_parameters_with_same_name_WHEN_construct_THEN_error(self): one = EmptyBeamlineParameter("same") two = EmptyBeamlineParameter("same") - assert_that(calling(Beamline).with_args([], [one, two], [], []), raises(BeamlineConfigurationInvalidException)) + assert_that( + calling(Beamline).with_args([], [one, two], [], []), + raises(BeamlineConfigurationInvalidException), + ) def test_GIVEN_two_modes_with_same_name_WHEN_construct_THEN_error(self): one = BeamlineMode("same", []) two = BeamlineMode("same", []) - assert_that(calling(Beamline).with_args([], [], [], [one, two]), raises(BeamlineConfigurationInvalidException)) + assert_that( + calling(Beamline).with_args([], [], [], [one, two]), + raises(BeamlineConfigurationInvalidException), + ) - def test_GIVEN_enable_disable_parameter_with_driver_that_has_no_offset_WHEN_construct_THEN_error(self): + def test_GIVEN_enable_disable_parameter_with_driver_that_has_no_offset_WHEN_construct_THEN_error( + self, + ): mode = BeamlineMode("mode", []) component = Component("comp", PositionAndAngle(0, 0, 0)) beamline_parameter = InBeamParameter("param", component) motor_axis = create_mock_axis("axis", 0, 0) driver = IocDriver(component, ChangeAxis.POSITION, motor_axis) - assert_that(calling(Beamline).with_args( - [component], - [beamline_parameter], - [driver], - [mode]), raises(BeamlineConfigurationInvalidException)) + assert_that( + calling(Beamline).with_args([component], [beamline_parameter], [driver], [mode]), + raises(BeamlineConfigurationInvalidException), + ) - def test_GIVEN_enable_disable_parameter_with_driver_that_has_only_angle_driver_WHEN_construct_THEN_error(self): + def test_GIVEN_enable_disable_parameter_with_driver_that_has_only_angle_driver_WHEN_construct_THEN_error( + self, + ): mode = BeamlineMode("mode", []) component = TiltingComponent("comp", PositionAndAngle(0, 0, 0)) beamline_parameter = InBeamParameter("param", component) motor_axis = create_mock_axis("axis", 0, 0) driver = IocDriver(component, ChangeAxis.ANGLE, motor_axis) - assert_that(calling(Beamline).with_args( - [component], - [beamline_parameter], - [driver], - [mode]), raises(BeamlineConfigurationInvalidException)) + assert_that( + calling(Beamline).with_args([component], [beamline_parameter], [driver], [mode]), + raises(BeamlineConfigurationInvalidException), + ) - def test_GIVEN_beamline_with_LONG_AXIS_parameter_before_POSITION_WHEN_construct_THEN_no_error(self): + def test_GIVEN_beamline_with_LONG_AXIS_parameter_before_POSITION_WHEN_construct_THEN_no_error( + self, + ): mode = BeamlineMode("mode", []) component = TiltingComponent("comp", PositionAndAngle(0, 0, 90)) - beamline_parameters = [AxisParameter("param_1", component, ChangeAxis.LONG_AXIS), - AxisParameter("param_2", component, ChangeAxis.POSITION)] + beamline_parameters = [ + AxisParameter("param_1", component, ChangeAxis.LONG_AXIS), + AxisParameter("param_2", component, ChangeAxis.POSITION), + ] try: Beamline([component], beamline_parameters, [], [mode]) except BeamlineConfigurationInvalidException: assert_that(True, is_(False), "Beamline construction raised unexpected error") - def test_GIVEN_beamline_with_POSITION_parameter_before_LONG_AXIS_WHEN_construct_THEN_error(self): + def test_GIVEN_beamline_with_POSITION_parameter_before_LONG_AXIS_WHEN_construct_THEN_error( + self, + ): mode = BeamlineMode("mode", []) component = TiltingComponent("comp", PositionAndAngle(0, 0, 90)) - beamline_parameters = [AxisParameter("param_1", component, ChangeAxis.POSITION), - AxisParameter("param_2", component, ChangeAxis.LONG_AXIS)] - assert_that(calling(Beamline).with_args( - [component], - beamline_parameters, - [], - [mode]), raises(BeamlineConfigurationInvalidException)) - - def test_GIVEN_beamline_with_LONG_AXIS_driver_before_POSITION_WHEN_construct_THEN_no_error(self): + beamline_parameters = [ + AxisParameter("param_1", component, ChangeAxis.POSITION), + AxisParameter("param_2", component, ChangeAxis.LONG_AXIS), + ] + assert_that( + calling(Beamline).with_args([component], beamline_parameters, [], [mode]), + raises(BeamlineConfigurationInvalidException), + ) + + def test_GIVEN_beamline_with_LONG_AXIS_driver_before_POSITION_WHEN_construct_THEN_no_error( + self, + ): mode = BeamlineMode("mode", []) component = TiltingComponent("comp", PositionAndAngle(0, 0, 90)) - motor_axis = [create_mock_axis("axis_1", 0, 0), - create_mock_axis("axis_2", 0, 0)] - drivers = [IocDriver(component, ChangeAxis.LONG_AXIS, motor_axis[0]), - IocDriver(component, ChangeAxis.POSITION, motor_axis[1])] + motor_axis = [create_mock_axis("axis_1", 0, 0), create_mock_axis("axis_2", 0, 0)] + drivers = [ + IocDriver(component, ChangeAxis.LONG_AXIS, motor_axis[0]), + IocDriver(component, ChangeAxis.POSITION, motor_axis[1]), + ] try: Beamline([component], [], drivers, [mode]) @@ -351,25 +437,26 @@ def test_GIVEN_beamline_with_LONG_AXIS_driver_before_POSITION_WHEN_construct_THE def test_GIVEN_beamline_with_POSITION_driver_before_LONG_AXIS_WHEN_construct_THEN_error(self): mode = BeamlineMode("mode", []) component = TiltingComponent("comp", PositionAndAngle(0, 0, 90)) - motor_axis = [create_mock_axis("axis_1", 0, 0), - create_mock_axis("axis_2", 0, 0)] - drivers = [IocDriver(component, ChangeAxis.POSITION, motor_axis[0]), - IocDriver(component, ChangeAxis.LONG_AXIS, motor_axis[1])] - assert_that(calling(Beamline).with_args( - [component], - [], - drivers, - [mode]), raises(BeamlineConfigurationInvalidException)) + motor_axis = [create_mock_axis("axis_1", 0, 0), create_mock_axis("axis_2", 0, 0)] + drivers = [ + IocDriver(component, ChangeAxis.POSITION, motor_axis[0]), + IocDriver(component, ChangeAxis.LONG_AXIS, motor_axis[1]), + ] + assert_that( + calling(Beamline).with_args([component], [], drivers, [mode]), + raises(BeamlineConfigurationInvalidException), + ) class TestBeamlineModeInitialization(unittest.TestCase): - def setUp(self): self.nr_mode = BeamlineMode("nr", []) self.pnr_mode = BeamlineMode("pnr", []) - @patch('ReflectometryServer.beamline.mode_autosave') - def test_GIVEN_no_autosaved_mode_WHEN_instantiating_beamline_THEN_defaults_to_first_in_list(self, mode_autosave): + @patch("ReflectometryServer.beamline.mode_autosave") + def test_GIVEN_no_autosaved_mode_WHEN_instantiating_beamline_THEN_defaults_to_first_in_list( + self, mode_autosave + ): mode_autosave.read_parameter.return_value = None # e.g. no value expected = "nr" beamline = Beamline([], [], [], [self.nr_mode, self.pnr_mode]) @@ -378,9 +465,10 @@ def test_GIVEN_no_autosaved_mode_WHEN_instantiating_beamline_THEN_defaults_to_fi self.assertEqual(expected, actual) - @patch('ReflectometryServer.beamline.mode_autosave') - def test_GIVEN_autosaved_mode_exists_WHEN_instantiating_beamline_THEN_active_mode_is_saved_mode(self, mode_autosave): - + @patch("ReflectometryServer.beamline.mode_autosave") + def test_GIVEN_autosaved_mode_exists_WHEN_instantiating_beamline_THEN_active_mode_is_saved_mode( + self, mode_autosave + ): expected = "pnr" mode_autosave.read_parameter.return_value = expected @@ -390,8 +478,10 @@ def test_GIVEN_autosaved_mode_exists_WHEN_instantiating_beamline_THEN_active_mod self.assertEqual(expected, actual) - @patch('ReflectometryServer.beamline.mode_autosave') - def test_GIVEN_autosaved_mode_does_not_exist_in_config_WHEN_instantiating_beamline_THEN_mode_defaults_to_first_in_list(self, mode_autosave): + @patch("ReflectometryServer.beamline.mode_autosave") + def test_GIVEN_autosaved_mode_does_not_exist_in_config_WHEN_instantiating_beamline_THEN_mode_defaults_to_first_in_list( + self, mode_autosave + ): expected = "nr" mode_autosave.read_parameter.return_value = "mode_nonexistent" beamline = Beamline([], [], [], [self.nr_mode, self.pnr_mode]) @@ -400,28 +490,32 @@ def test_GIVEN_autosaved_mode_does_not_exist_in_config_WHEN_instantiating_beamli self.assertEqual(expected, actual) - @patch('ReflectometryServer.beamline.mode_autosave') - def test_GIVEN_autosaved_mode_exists_WHEN_instantiating_beamline_THEN_mode_inits_are_not_applied(self, mode_autosave): + @patch("ReflectometryServer.beamline.mode_autosave") + def test_GIVEN_autosaved_mode_exists_WHEN_instantiating_beamline_THEN_mode_inits_are_not_applied( + self, mode_autosave + ): mode_autosave.read_parameter.return_value = "pnr" - with patch.object(Beamline, '_init_params_from_mode') as mock_mode_inits: + with patch.object(Beamline, "_init_params_from_mode") as mock_mode_inits: beamline = Beamline([], [], [], [self.nr_mode, self.pnr_mode]) mock_mode_inits.assert_not_called() - @patch('ReflectometryServer.beamline.mode_autosave') - def test_GIVEN_default_mode_applied_WHEN_instantiating_beamline_THEN_mode_inits_are_not_applied(self, mode_autosave): - + @patch("ReflectometryServer.beamline.mode_autosave") + def test_GIVEN_default_mode_applied_WHEN_instantiating_beamline_THEN_mode_inits_are_not_applied( + self, mode_autosave + ): mode_autosave.read_parameter.return_value = None - with patch.object(Beamline, '_init_params_from_mode') as mock_mode_inits: + with patch.object(Beamline, "_init_params_from_mode") as mock_mode_inits: beamline = Beamline([], [], [], [self.nr_mode, self.pnr_mode]) mock_mode_inits.assert_not_called() - @patch('ReflectometryServer.beamline.mode_autosave') - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave') - def test_GIVEN_beam_line_with_disable_autosave_position_WHEN_init_THEN_incoming_beams_set_correctly_on_start(self, mock_disable_mode_auto_save, mock_mode_auto_save): - + @patch("ReflectometryServer.beamline.mode_autosave") + @patch("ReflectometryServer.beam_path_calc.disable_mode_autosave") + def test_GIVEN_beam_line_with_disable_autosave_position_WHEN_init_THEN_incoming_beams_set_correctly_on_start( + self, mock_disable_mode_auto_save, mock_mode_auto_save + ): mock_mode_auto_save.read_parameter.return_value = "DISABLED" s1_comp_name = "s1_comp" @@ -432,12 +526,12 @@ def test_GIVEN_beam_line_with_disable_autosave_position_WHEN_init_THEN_incoming_ s1_comp_name: PositionAndAngle(0, 1, 0), s3_comp_name: PositionAndAngle(0, 1, 1), detector_comp_name: PositionAndAngle(2, 1, 4), - theta_comp_name: PositionAndAngle(3, 2, 1) + theta_comp_name: PositionAndAngle(3, 2, 1), } def autosave_value(key, default): if key.endswith("_sp"): - return autosave_values.get(key[:-len("_sp")], default) + return autosave_values.get(key[: -len("_sp")], default) else: return None @@ -445,306 +539,537 @@ def autosave_value(key, default): spacing = 2.0 bl, drives = DataMother.beamline_s1_s3_theta_detector(spacing, initilise_mode_nr=False) - result = {comp.name: (comp.beam_path_set_point._incoming_beam, comp.beam_path_set_point.get_outgoing_beam()) - for comp in bl} + result = { + comp.name: ( + comp.beam_path_set_point._incoming_beam, + comp.beam_path_set_point.get_outgoing_beam(), + ) + for comp in bl + } assert_that(result[s1_comp_name][0], is_(position_and_angle(autosave_values[s1_comp_name]))) assert_that(result[s3_comp_name][0], is_(position_and_angle(autosave_values[s3_comp_name]))) - assert_that(result[theta_comp_name][0], is_(position_and_angle(autosave_values[theta_comp_name]))) + assert_that( + result[theta_comp_name][0], is_(position_and_angle(autosave_values[theta_comp_name])) + ) # The detector incoming beam should be the same as the outgoing beam for theta because theta controls the # detector height - assert_that(result[detector_comp_name][0], is_(position_and_angle(result[theta_comp_name][1]))) + assert_that( + result[detector_comp_name][0], is_(position_and_angle(result[theta_comp_name][1])) + ) class TestRealisticWithAutosaveInit(unittest.TestCase): - @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_theta_at_0_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_where_autosave_theta_at_0_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): expected_sm_angle = 22.5 expected_theta = 0 file_io.read_parameter.return_value = expected_theta bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") assert_that(bl.parameter("det_pos").sp, is_(close_to(0, 1e-6)), "det position SP") assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(0, 1e-6)), "det position SP RBV") assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_theta_at_non_zero_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_where_autosave_theta_at_non_zero_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): expected_sm_angle = 22.5 expected_theta = 2 file_io.read_parameter.return_value = expected_theta bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") assert_that(bl.parameter("det_pos").sp, is_(close_to(0, 1e-6)), "det position SP") assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(0, 1e-6)), "det position SP RBV") assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_det_offset_at_zero_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_where_autosave_det_offset_at_zero_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): expected_sm_angle = 22.5 expected_theta = 0 expected_det_offset = 0 file_io.read_parameter.return_value = expected_det_offset - bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta, autosave_theta_not_offset=False) + bl, drives = DataMother.beamline_sm_theta_detector( + expected_sm_angle, expected_theta, autosave_theta_not_offset=False + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") assert_that(bl.parameter("det_pos").sp, is_(close_to(0, 1e-6)), "det position SP") assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(0, 1e-6)), "det position SP RBV") assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_det_offset_at_non_zero_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_where_autosave_det_offset_at_non_zero_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): expected_sm_angle = 22.5 expected_theta = 0 expected_det_offset = 1 file_io.read_parameter.return_value = expected_det_offset - bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta, expected_det_offset, autosave_theta_not_offset=False) + bl, drives = DataMother.beamline_sm_theta_detector( + expected_sm_angle, expected_theta, expected_det_offset, autosave_theta_not_offset=False + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") - assert_that(bl.parameter("det_pos").sp, is_(close_to(expected_det_offset, 1e-6)), "det position SP") + assert_that( + bl.parameter("det_pos").sp, is_(close_to(expected_det_offset, 1e-6)), "det position SP" + ) assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") - assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(expected_det_offset, 1e-6)), "det position SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) + assert_that( + bl.parameter("det_pos").sp_rbv, + is_(close_to(expected_det_offset, 1e-6)), + "det position SP RBV", + ) assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_with_nonzero_beam_start_with_all_values_at_0_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_with_nonzero_beam_start_with_all_values_at_0_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): beam_start_angle = -2.3 expected_sm_angle = 0 expected_theta = 0 file_io.read_parameter.return_value = expected_theta - bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta, beam_angle=beam_start_angle) + bl, drives = DataMother.beamline_sm_theta_detector( + expected_sm_angle, expected_theta, beam_angle=beam_start_angle + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") assert_that(bl.parameter("det_pos").sp, is_(close_to(0, 1e-6)), "det position SP") assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(0, 1e-6)), "det position SP RBV") assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_with_nonzero_beam_start_where_autosave_theta_at_0_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_with_nonzero_beam_start_where_autosave_theta_at_0_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): beam_start_angle = -2.3 expected_sm_angle = 22.5 expected_theta = 0 file_io.read_parameter.return_value = expected_theta - bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta, beam_angle=beam_start_angle) + bl, drives = DataMother.beamline_sm_theta_detector( + expected_sm_angle, expected_theta, beam_angle=beam_start_angle + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") assert_that(bl.parameter("det_pos").sp, is_(close_to(0, 1e-6)), "det position SP") assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(0, 1e-6)), "det position SP RBV") assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_with_nonzero_beam_start_where_autosave_theta_at_non_zero_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_with_nonzero_beam_start_where_autosave_theta_at_non_zero_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): beam_start_angle = -2.3 expected_sm_angle = 22.5 expected_theta = 2 file_io.read_parameter.return_value = expected_theta - bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta, beam_angle=beam_start_angle) + bl, drives = DataMother.beamline_sm_theta_detector( + expected_sm_angle, expected_theta, beam_angle=beam_start_angle + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") assert_that(bl.parameter("det_pos").sp, is_(close_to(0, 1e-6)), "det position SP") assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(0, 1e-6)), "det position SP RBV") assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_with_nonzero_beam_start_where_autosave_det_offset_at_zero_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_with_nonzero_beam_start_where_autosave_det_offset_at_zero_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): beam_start_angle = -2.3 expected_sm_angle = 22.5 expected_theta = 0 expected_det_offset = 0 file_io.read_parameter.return_value = expected_det_offset - bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta, autosave_theta_not_offset=False, beam_angle=beam_start_angle) + bl, drives = DataMother.beamline_sm_theta_detector( + expected_sm_angle, + expected_theta, + autosave_theta_not_offset=False, + beam_angle=beam_start_angle, + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") assert_that(bl.parameter("det_pos").sp, is_(close_to(0, 1e-6)), "det position SP") assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(0, 1e-6)), "det position SP RBV") assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_with_nonzero_beam_start_where_autosave_det_offset_at_non_zero_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_with_nonzero_beam_start_where_autosave_det_offset_at_non_zero_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): beam_start_angle = -2.3 expected_sm_angle = 22.5 expected_theta = 0 expected_det_offset = 1 file_io.read_parameter.return_value = expected_det_offset - bl, drives = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta, expected_det_offset, autosave_theta_not_offset=False, beam_angle=beam_start_angle) - - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + bl, drives = DataMother.beamline_sm_theta_detector( + expected_sm_angle, + expected_theta, + expected_det_offset, + autosave_theta_not_offset=False, + beam_angle=beam_start_angle, + ) + + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") - assert_that(bl.parameter("det_pos").sp, is_(close_to(expected_det_offset, 1e-6)), "det position SP") + assert_that( + bl.parameter("det_pos").sp, is_(close_to(expected_det_offset, 1e-6)), "det position SP" + ) assert_that(bl.parameter("det_angle").sp, is_(close_to(0, 1e-6)), "det angle SP") - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") - assert_that(bl.parameter("det_pos").sp_rbv, is_(close_to(expected_det_offset, 1e-6)), "det position SP RBV") + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) + assert_that( + bl.parameter("det_pos").sp_rbv, + is_(close_to(expected_det_offset, 1e-6)), + "det position SP RBV", + ) assert_that(bl.parameter("det_angle").sp_rbv, is_(close_to(0, 1e-6)), "det angle SP RBV") class TestRealisticWithAutosaveInitAndBench(unittest.TestCase): - @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_theta_at_0p1_and_bench_offset_0_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_where_autosave_theta_at_0p1_and_bench_offset_0_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): expected_sm_angle = 0 expected_theta = 0.1 driver_bench_offset = 0 file_io.read_parameter.return_value = expected_theta - bl, drives = DataMother.beamline_sm_theta_bench(expected_sm_angle, expected_theta, driver_bench_offset) + bl, drives = DataMother.beamline_sm_theta_bench( + expected_sm_angle, expected_theta, driver_bench_offset + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") - assert_that(bl.parameter("bench_angle").sp, is_(close_to(driver_bench_offset, 1e-6)), "bench angle SP") - - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") - assert_that(bl.parameter("bench_angle").sp_rbv, is_(close_to(driver_bench_offset, 1e-6)), "bench angle SP RBV") - - assert_that(bl.parameter("sm_angle").rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle RBV") + assert_that( + bl.parameter("bench_angle").sp, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle SP", + ) + + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) + assert_that( + bl.parameter("bench_angle").sp_rbv, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle SP RBV", + ) + + assert_that( + bl.parameter("sm_angle").rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle RBV" + ) assert_that(bl.parameter("theta").rbv, is_(close_to(expected_theta, 1e-6)), "theta RBV") - assert_that(bl.parameter("bench_angle").rbv, is_(close_to(driver_bench_offset, 1e-6)), "bench angle RBV") + assert_that( + bl.parameter("bench_angle").rbv, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle RBV", + ) @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_theta_at_0p1_and_bench_offset_2_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_where_autosave_theta_at_0p1_and_bench_offset_2_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): expected_sm_angle = 0 expected_theta = 0.1 driver_bench_offset = 2 file_io.read_parameter.return_value = expected_theta - bl, drives = DataMother.beamline_sm_theta_bench(expected_sm_angle, expected_theta, driver_bench_offset) + bl, drives = DataMother.beamline_sm_theta_bench( + expected_sm_angle, expected_theta, driver_bench_offset + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") - assert_that(bl.parameter("bench_angle").sp, is_(close_to(driver_bench_offset, 1e-6)), "bench angle SP") - - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") - assert_that(bl.parameter("bench_angle").sp_rbv, is_(close_to(driver_bench_offset, 1e-6)), "bench angle SP RBV") - - assert_that(bl.parameter("sm_angle").rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle RBV") + assert_that( + bl.parameter("bench_angle").sp, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle SP", + ) + + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) + assert_that( + bl.parameter("bench_angle").sp_rbv, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle SP RBV", + ) + + assert_that( + bl.parameter("sm_angle").rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle RBV" + ) assert_that(bl.parameter("theta").rbv, is_(close_to(expected_theta, 1e-6)), "theta RBV") - assert_that(bl.parameter("bench_angle").rbv, is_(close_to(driver_bench_offset, 1e-6)), "bench angle RBV") + assert_that( + bl.parameter("bench_angle").rbv, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle RBV", + ) @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_bench_offset_2_and_theta_0p1_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_where_autosave_bench_offset_2_and_theta_0p1_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): expected_sm_angle = 0 expected_theta = 0.1 driver_bench_offset = 2 file_io.read_parameter.return_value = driver_bench_offset - bl, drives = DataMother.beamline_sm_theta_bench(expected_sm_angle, expected_theta, driver_bench_offset, autosave_bench_not_theta=True) + bl, drives = DataMother.beamline_sm_theta_bench( + expected_sm_angle, expected_theta, driver_bench_offset, autosave_bench_not_theta=True + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") - assert_that(bl.parameter("bench_angle").sp, is_(close_to(driver_bench_offset, 1e-6)), "bench angle SP") - - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") - assert_that(bl.parameter("bench_angle").sp_rbv, is_(close_to(driver_bench_offset, 1e-6)), "bench angle SP RBV") - - assert_that(bl.parameter("sm_angle").rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle RBV") + assert_that( + bl.parameter("bench_angle").sp, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle SP", + ) + + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) + assert_that( + bl.parameter("bench_angle").sp_rbv, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle SP RBV", + ) + + assert_that( + bl.parameter("sm_angle").rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle RBV" + ) assert_that(bl.parameter("theta").rbv, is_(close_to(expected_theta, 1e-6)), "theta RBV") - assert_that(bl.parameter("bench_angle").rbv, is_(close_to(driver_bench_offset, 1e-6)), "bench angle RBV") + assert_that( + bl.parameter("bench_angle").rbv, + is_(close_to(driver_bench_offset, 1e-6)), + "bench angle RBV", + ) @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_ang_offset_2_and_theta_0p1_WHEN_init_THEN_beamline_is_at_given_place(self, file_io): + def test_GIVEN_beam_line_where_autosave_ang_offset_2_and_theta_0p1_WHEN_init_THEN_beamline_is_at_given_place( + self, file_io + ): expected_sm_angle = 0 expected_theta = 0.1 angle_offset = 2 file_io.read_parameter.return_value = angle_offset - bl, drives = DataMother.beamline_sm_theta_ang_det(expected_sm_angle, expected_theta, angle_offset, autosave_bench_not_theta=True) + bl, drives = DataMother.beamline_sm_theta_ang_det( + expected_sm_angle, expected_theta, angle_offset, autosave_bench_not_theta=True + ) for drive in drives.values(): drive.trigger_rbv_change() - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) assert_that(bl.parameter("theta").sp, is_(close_to(expected_theta, 1e-6)), "theta SP") - assert_that(bl.parameter("comp_angle").sp, is_(close_to(angle_offset, 1e-6)), "comp angle SP") - - assert_that(bl.parameter("sm_angle").sp_rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP RBV") - assert_that(bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV") - assert_that(bl.parameter("comp_angle").sp_rbv, is_(close_to(angle_offset, 1e-6)), "comp angle SP RBV") - - assert_that(bl.parameter("sm_angle").rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle RBV") + assert_that( + bl.parameter("comp_angle").sp, is_(close_to(angle_offset, 1e-6)), "comp angle SP" + ) + + assert_that( + bl.parameter("sm_angle").sp_rbv, + is_(close_to(expected_sm_angle, 1e-6)), + "sm angle SP RBV", + ) + assert_that( + bl.parameter("theta").sp_rbv, is_(close_to(expected_theta, 1e-6)), "theta SP RBV" + ) + assert_that( + bl.parameter("comp_angle").sp_rbv, + is_(close_to(angle_offset, 1e-6)), + "comp angle SP RBV", + ) + + assert_that( + bl.parameter("sm_angle").rbv, is_(close_to(expected_sm_angle, 1e-6)), "sm angle RBV" + ) assert_that(bl.parameter("theta").rbv, is_(close_to(expected_theta, 1e-6)), "theta RBV") - assert_that(bl.parameter("comp_angle").rbv, is_(close_to(angle_offset, 1e-6)), "comp angle RBV") + assert_that( + bl.parameter("comp_angle").rbv, is_(close_to(angle_offset, 1e-6)), "comp angle RBV" + ) -class TestBeamlineReadOnlyParameters(unittest.TestCase): +class TestBeamlineReadOnlyParameters(unittest.TestCase): def setup_beamline(self, parameters): - beamline = Beamline([], [], [], [], beamline_constants=parameters) return beamline - def test_GIVEN_there_are_no_beamline_constant_set_empty_list_WHEN_get_beamline_constant_THEN_empty(self): - + def test_GIVEN_there_are_no_beamline_constant_set_empty_list_WHEN_get_beamline_constant_THEN_empty( + self, + ): beamline = self.setup_beamline([]) result = beamline.beamline_constants assert_that(result, is_([])) - def test_GIVEN_there_are_no_beamline_constant_set_using_none_WHEN_get_beamline_constant_THEN_empty(self): - + def test_GIVEN_there_are_no_beamline_constant_set_using_none_WHEN_get_beamline_constant_THEN_empty( + self, + ): beamline = self.setup_beamline(None) result = beamline.beamline_constants assert_that(result, is_([])) - def test_GIVEN_there_are_beamline_constant_set_WHEN_get_beamline_constant_THEN_parameters_returned(self): - expected_parameters = [ - BeamlineConstant("NAME", 2, "description") - ] + def test_GIVEN_there_are_beamline_constant_set_WHEN_get_beamline_constant_THEN_parameters_returned( + self, + ): + expected_parameters = [BeamlineConstant("NAME", 2, "description")] beamline = self.setup_beamline(expected_parameters) result = beamline.beamline_constants @@ -753,16 +1078,31 @@ def test_GIVEN_there_are_beamline_constant_set_WHEN_get_beamline_constant_THEN_p class TestComponentOutOfBeam(unittest.TestCase): - @no_autosave def setUp(self): self.comp = Component("test_component", PositionAndAngle(0, 0, 90)) self.IN_BEAM_VALUE = 0 self.OUT_OF_BEAM_VALUE = -5 - @parameterized.expand([(ChangeAxis.PHI,), (ChangeAxis.CHI,), (ChangeAxis.PSI,), (ChangeAxis.TRANS,), (ChangeAxis.HEIGHT,), (ChangeAxis.POSITION,)]) - def test_GIVEN_driver_on_component_has_no_out_of_beam_position_THEN_appropriate_change_axes_reports_not_having_out_of_beam_position(self, change_axis_to_set): - IocDriver(self.comp, change_axis_to_set, create_mock_axis("axis", 0, 1), out_of_beam_positions=None) + @parameterized.expand( + [ + (ChangeAxis.PHI,), + (ChangeAxis.CHI,), + (ChangeAxis.PSI,), + (ChangeAxis.TRANS,), + (ChangeAxis.HEIGHT,), + (ChangeAxis.POSITION,), + ] + ) + def test_GIVEN_driver_on_component_has_no_out_of_beam_position_THEN_appropriate_change_axes_reports_not_having_out_of_beam_position( + self, change_axis_to_set + ): + IocDriver( + self.comp, + change_axis_to_set, + create_mock_axis("axis", 0, 1), + out_of_beam_positions=None, + ) for change_axis, component_axis in self.comp.beam_path_rbv.axis.items(): assert_that(component_axis.park_sequence_count, is_(0)) @@ -770,11 +1110,26 @@ def test_GIVEN_driver_on_component_has_no_out_of_beam_position_THEN_appropriate_ for change_axis, component_axis in self.comp.beam_path_set_point.axis.items(): assert_that(component_axis.park_sequence_count, is_(0)) - @parameterized.expand([(ChangeAxis.PHI,), (ChangeAxis.CHI,), (ChangeAxis.PSI,), (ChangeAxis.TRANS,), (ChangeAxis.HEIGHT,), (ChangeAxis.POSITION,)]) - def test_GIVEN_driver_on_component_has_out_of_beam_position_THEN_appropriate_change_axis_report_having_out_of_beam_position(self, change_axis_to_set): + @parameterized.expand( + [ + (ChangeAxis.PHI,), + (ChangeAxis.CHI,), + (ChangeAxis.PSI,), + (ChangeAxis.TRANS,), + (ChangeAxis.HEIGHT,), + (ChangeAxis.POSITION,), + ] + ) + def test_GIVEN_driver_on_component_has_out_of_beam_position_THEN_appropriate_change_axis_report_having_out_of_beam_position( + self, change_axis_to_set + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) - IocDriver(self.comp, change_axis_to_set, create_mock_axis("axis", 0, 1), - out_of_beam_positions=[out_of_beam_position]) + IocDriver( + self.comp, + change_axis_to_set, + create_mock_axis("axis", 0, 1), + out_of_beam_positions=[out_of_beam_position], + ) for change_axis, component_axis in self.comp.beam_path_rbv.axis.items(): if change_axis == change_axis_to_set: @@ -788,11 +1143,24 @@ def test_GIVEN_driver_on_component_has_out_of_beam_position_THEN_appropriate_cha else: assert_that(component_axis.park_sequence_count, is_(0)) - @parameterized.expand([(ChangeAxis.PHI,), (ChangeAxis.CHI,), (ChangeAxis.PSI,), (ChangeAxis.TRANS,), (ChangeAxis.HEIGHT,), (ChangeAxis.POSITION,)]) - def test_GIVEN_component_with_driver_with_out_of_beam_position_WHEN_motor_is_in_beam_THEN_component_axis_reports_in_beam(self, change_axis_to_set): + @parameterized.expand( + [ + (ChangeAxis.PHI,), + (ChangeAxis.CHI,), + (ChangeAxis.PSI,), + (ChangeAxis.TRANS,), + (ChangeAxis.HEIGHT,), + (ChangeAxis.POSITION,), + ] + ) + def test_GIVEN_component_with_driver_with_out_of_beam_position_WHEN_motor_is_in_beam_THEN_component_axis_reports_in_beam( + self, change_axis_to_set + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) motor_axis = create_mock_axis("axis", self.IN_BEAM_VALUE, 1) - IocDriver(self.comp, change_axis_to_set, motor_axis, out_of_beam_positions=[out_of_beam_position]) + IocDriver( + self.comp, change_axis_to_set, motor_axis, out_of_beam_positions=[out_of_beam_position] + ) expected = True motor_axis.trigger_rbv_change() @@ -800,11 +1168,24 @@ def test_GIVEN_component_with_driver_with_out_of_beam_position_WHEN_motor_is_in_ assert_that(actual, is_(expected)) - @parameterized.expand([(ChangeAxis.PHI,), (ChangeAxis.CHI,), (ChangeAxis.PSI,), (ChangeAxis.TRANS,), (ChangeAxis.HEIGHT,), (ChangeAxis.POSITION,)]) - def test_GIVEN_component_with_driver_with_out_of_beam_position_WHEN_motor_is_out_of_beam_THEN_component_axis_reports_out_of_beam(self, change_axis_to_set): + @parameterized.expand( + [ + (ChangeAxis.PHI,), + (ChangeAxis.CHI,), + (ChangeAxis.PSI,), + (ChangeAxis.TRANS,), + (ChangeAxis.HEIGHT,), + (ChangeAxis.POSITION,), + ] + ) + def test_GIVEN_component_with_driver_with_out_of_beam_position_WHEN_motor_is_out_of_beam_THEN_component_axis_reports_out_of_beam( + self, change_axis_to_set + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) motor_axis = create_mock_axis("axis", self.OUT_OF_BEAM_VALUE, 1) - IocDriver(self.comp, change_axis_to_set, motor_axis, out_of_beam_positions=[out_of_beam_position]) + IocDriver( + self.comp, change_axis_to_set, motor_axis, out_of_beam_positions=[out_of_beam_position] + ) expected = False motor_axis.trigger_rbv_change() @@ -812,10 +1193,23 @@ def test_GIVEN_component_with_driver_with_out_of_beam_position_WHEN_motor_is_out assert_that(actual, is_(expected)) - @parameterized.expand([(ChangeAxis.PHI,), (ChangeAxis.CHI,), (ChangeAxis.PSI,), (ChangeAxis.TRANS,), (ChangeAxis.HEIGHT,), (ChangeAxis.POSITION,)]) - def test_GIVEN_component_with_driver_with_out_of_beam_position_literal_WHEN_motor_is_out_of_beam_THEN_component_axis_reports_out_of_beam(self, change_axis_to_set): + @parameterized.expand( + [ + (ChangeAxis.PHI,), + (ChangeAxis.CHI,), + (ChangeAxis.PSI,), + (ChangeAxis.TRANS,), + (ChangeAxis.HEIGHT,), + (ChangeAxis.POSITION,), + ] + ) + def test_GIVEN_component_with_driver_with_out_of_beam_position_literal_WHEN_motor_is_out_of_beam_THEN_component_axis_reports_out_of_beam( + self, change_axis_to_set + ): motor_axis = create_mock_axis("axis", self.OUT_OF_BEAM_VALUE, 1) - IocDriver(self.comp, change_axis_to_set, motor_axis, out_of_beam_positions=self.OUT_OF_BEAM_VALUE) + IocDriver( + self.comp, change_axis_to_set, motor_axis, out_of_beam_positions=self.OUT_OF_BEAM_VALUE + ) expected = False motor_axis.trigger_rbv_change() @@ -823,10 +1217,23 @@ def test_GIVEN_component_with_driver_with_out_of_beam_position_literal_WHEN_moto assert_that(actual, is_(expected)) - @parameterized.expand([(ChangeAxis.PHI,), (ChangeAxis.CHI,), (ChangeAxis.PSI,), (ChangeAxis.TRANS,), (ChangeAxis.HEIGHT,), (ChangeAxis.POSITION,)]) - def test_GIVEN_component_with_driver_with_out_of_beam_position_literal_WHEN_motor_is_in_beam_THEN_component_axis_reports_in_beam(self, change_axis_to_set): + @parameterized.expand( + [ + (ChangeAxis.PHI,), + (ChangeAxis.CHI,), + (ChangeAxis.PSI,), + (ChangeAxis.TRANS,), + (ChangeAxis.HEIGHT,), + (ChangeAxis.POSITION,), + ] + ) + def test_GIVEN_component_with_driver_with_out_of_beam_position_literal_WHEN_motor_is_in_beam_THEN_component_axis_reports_in_beam( + self, change_axis_to_set + ): motor_axis = create_mock_axis("axis", self.IN_BEAM_VALUE, 1) - IocDriver(self.comp, change_axis_to_set, motor_axis, out_of_beam_positions=self.OUT_OF_BEAM_VALUE) + IocDriver( + self.comp, change_axis_to_set, motor_axis, out_of_beam_positions=self.OUT_OF_BEAM_VALUE + ) expected = True motor_axis.trigger_rbv_change() @@ -834,7 +1241,9 @@ def test_GIVEN_component_with_driver_with_out_of_beam_position_literal_WHEN_moto assert_that(actual, is_(expected)) - def test_GIVEN_component_with_one_driver_with_out_of_beam_position_WHEN_axis_is_not_in_beam_THEN_in_beam_rbv_is_false(self): + def test_GIVEN_component_with_one_driver_with_out_of_beam_position_WHEN_axis_is_not_in_beam_THEN_in_beam_rbv_is_false( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.OUT_OF_BEAM_VALUE, 1) IocDriver(self.comp, ChangeAxis.CHI, chi_axis, out_of_beam_positions=[out_of_beam_position]) @@ -845,7 +1254,9 @@ def test_GIVEN_component_with_one_driver_with_out_of_beam_position_WHEN_axis_is_ assert_that(actual, is_(expected)) - def test_GIVEN_component_with_one_driver_with_out_of_beam_position_WHEN_axis_is_in_beam_THEN_in_beam_rbv_is_true(self): + def test_GIVEN_component_with_one_driver_with_out_of_beam_position_WHEN_axis_is_in_beam_THEN_in_beam_rbv_is_true( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.IN_BEAM_VALUE, 1) IocDriver(self.comp, ChangeAxis.CHI, chi_axis, out_of_beam_positions=[out_of_beam_position]) @@ -856,7 +1267,9 @@ def test_GIVEN_component_with_one_driver_with_out_of_beam_position_WHEN_axis_is_ assert_that(actual, is_(expected)) - def test_GIVEN_component_with_multiple_drivers_with_out_of_beam_position_WHEN_all_are_in_beam_THEN_in_beam_rbv_is_true(self): + def test_GIVEN_component_with_multiple_drivers_with_out_of_beam_position_WHEN_all_are_in_beam_THEN_in_beam_rbv_is_true( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.IN_BEAM_VALUE, 1) psi_axis = create_mock_axis("psi", self.IN_BEAM_VALUE, 1) @@ -869,7 +1282,9 @@ def test_GIVEN_component_with_multiple_drivers_with_out_of_beam_position_WHEN_al assert_that(actual, is_(expected)) - def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_none_are_in_beam_THEN_in_beam_rbv_is_false(self): + def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_none_are_in_beam_THEN_in_beam_rbv_is_false( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.OUT_OF_BEAM_VALUE, 1) psi_axis = create_mock_axis("psi", self.OUT_OF_BEAM_VALUE, 1) @@ -883,7 +1298,9 @@ def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_none_ assert_that(actual, is_(expected)) - def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_one_is_in_beam_THEN_in_beam_rbv_is_true(self): + def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_one_is_in_beam_THEN_in_beam_rbv_is_true( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.OUT_OF_BEAM_VALUE, 1) psi_axis = create_mock_axis("psi", self.IN_BEAM_VALUE, 1) @@ -897,12 +1314,18 @@ def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_one_i assert_that(actual, is_(expected)) - def test_GIVEN_component_partially_in_beam_WHEN_setting_new_setpoint_for_axis_out_of_beam_THEN_driver_moves(self): + def test_GIVEN_component_partially_in_beam_WHEN_setting_new_setpoint_for_axis_out_of_beam_THEN_driver_moves( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.IN_BEAM_VALUE, 1) psi_axis = create_mock_axis("psi", self.OUT_OF_BEAM_VALUE, 1) - chi_driver = IocDriver(self.comp, ChangeAxis.CHI, chi_axis, out_of_beam_positions=[out_of_beam_position]) - psi_driver = IocDriver(self.comp, ChangeAxis.PSI, psi_axis, out_of_beam_positions=[out_of_beam_position]) + chi_driver = IocDriver( + self.comp, ChangeAxis.CHI, chi_axis, out_of_beam_positions=[out_of_beam_position] + ) + psi_driver = IocDriver( + self.comp, ChangeAxis.PSI, psi_axis, out_of_beam_positions=[out_of_beam_position] + ) chi_axis.sp = self.IN_BEAM_VALUE psi_axis.sp = self.OUT_OF_BEAM_VALUE chi_driver.initialise_setpoint() @@ -910,13 +1333,17 @@ def test_GIVEN_component_partially_in_beam_WHEN_setting_new_setpoint_for_axis_ou expected = self.IN_BEAM_VALUE - self.comp.beam_path_set_point.axis[ChangeAxis.PSI].set_displacement(CorrectedReadbackUpdate(self.IN_BEAM_VALUE, None, None)) + self.comp.beam_path_set_point.axis[ChangeAxis.PSI].set_displacement( + CorrectedReadbackUpdate(self.IN_BEAM_VALUE, None, None) + ) psi_driver.perform_move(1) actual = psi_axis.sp assert_that(actual, is_(expected)) - def test_GIVEN_component_with_axis_with_out_of_beam_position_WHEN_in_beam_sp_changes_THEN_only_axis_with_out_of_beam_position_marked_as_changed(self): + def test_GIVEN_component_with_axis_with_out_of_beam_position_WHEN_in_beam_sp_changes_THEN_only_axis_with_out_of_beam_position_marked_as_changed( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.IN_BEAM_VALUE, 1) psi_axis = create_mock_axis("psi", self.IN_BEAM_VALUE, 1) @@ -931,7 +1358,9 @@ def test_GIVEN_component_with_axis_with_out_of_beam_position_WHEN_in_beam_sp_cha assert_that(self.comp.beam_path_set_point.axis[ChangeAxis.PSI].is_changed, is_(True)) assert_that(self.comp.beam_path_set_point.axis[ChangeAxis.PHI].is_changed, is_(False)) - def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_setting_sp_THEN_in_beam_sp_moved_to_for_all_axes_with_out_of_beam_position(self): + def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_setting_sp_THEN_in_beam_sp_moved_to_for_all_axes_with_out_of_beam_position( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.IN_BEAM_VALUE, 1) psi_axis = create_mock_axis("psi", self.IN_BEAM_VALUE, 1) @@ -946,7 +1375,9 @@ def test_GIVEN_component_with_multiple_axes_with_out_of_beam_position_WHEN_setti assert_that(self.comp.beam_path_set_point.axis[ChangeAxis.PSI].is_in_beam, is_(False)) assert_that(self.comp.beam_path_set_point.axis[ChangeAxis.PHI].is_in_beam, is_(True)) - def test_GIVEN_component_WHEN_adding_driver_with_out_of_beam_position_THEN_inbeam_parameter_listens_to_relevant_axis(self): + def test_GIVEN_component_WHEN_adding_driver_with_out_of_beam_position_THEN_inbeam_parameter_listens_to_relevant_axis( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.OUT_OF_BEAM_VALUE, 1) in_beam_param = InBeamParameter("in_beam", self.comp) @@ -956,7 +1387,9 @@ def test_GIVEN_component_WHEN_adding_driver_with_out_of_beam_position_THEN_inbea assert_that(in_beam_param.rbv, is_(False)) - def test_GIVEN_inbeam_parameter_on_component_WHEN_adding_driver_with_out_of_beam_position_THEN_parameter_gets_init_value(self): + def test_GIVEN_inbeam_parameter_on_component_WHEN_adding_driver_with_out_of_beam_position_THEN_parameter_gets_init_value( + self, + ): out_of_beam_position = OutOfBeamPosition(self.OUT_OF_BEAM_VALUE) chi_axis = create_mock_axis("chi", self.OUT_OF_BEAM_VALUE, 1) in_beam_param = InBeamParameter("in_beam", self.comp) @@ -966,50 +1399,106 @@ def test_GIVEN_inbeam_parameter_on_component_WHEN_adding_driver_with_out_of_beam assert_that(in_beam_param.rbv, is_(False)) - def test_GIVEN_inbeam_parameter_on_detector_WHEN_init_with_angle_in_beam_THEN_theta_is_defined(self): + def test_GIVEN_inbeam_parameter_on_detector_WHEN_init_with_angle_in_beam_THEN_theta_is_defined( + self, + ): out_of_beam_pos = -5 det_in, theta = DataMother.beamline_theta_detector(out_of_beam_pos, out_of_beam_pos, -2, 0) assert_that(det_in.sp, is_(True)) assert_that(theta.sp, is_(0.0)) - def test_GIVEN_inbeam_parameter_on_detector_WHEN_init_with_pos_in_beam_THEN_theta_is_defined(self): + def test_GIVEN_inbeam_parameter_on_detector_WHEN_init_with_pos_in_beam_THEN_theta_is_defined( + self, + ): out_of_beam_pos = -5 det_in, theta = DataMother.beamline_theta_detector(-2, 0, out_of_beam_pos, out_of_beam_pos) assert_that(det_in.sp, is_(True)) assert_that(theta.sp, is_(0.0)) - def test_GIVEN_inbeam_parameter_on_detector_WHEN_init_with_both_out_of_beam_THEN_theta_sp_is_0(self): + def test_GIVEN_inbeam_parameter_on_detector_WHEN_init_with_both_out_of_beam_THEN_theta_sp_is_0( + self, + ): out_of_beam_pos = -5 out_of_beam_pos_z = -2 - det_in, theta = DataMother.beamline_theta_detector(out_of_beam_pos_z, out_of_beam_pos_z, out_of_beam_pos, out_of_beam_pos) + det_in, theta = DataMother.beamline_theta_detector( + out_of_beam_pos_z, out_of_beam_pos_z, out_of_beam_pos, out_of_beam_pos + ) assert_that(det_in.sp, is_(False)) assert_that(theta.sp, is_(0)) class TestComponentOutOfBeamFullBeamline(unittest.TestCase): - def setUp(self) -> None: ConfigHelper.reset() - @parameterized.expand([((-2, -3, -4), (-2, -3, -4), (-2, -3, -4), False), # all axes start out of beam, moving in moves to autosaved positions - ((-2, -3, -4), (0, -3, -4), (0, -3, -4), True), # first axes in beam others out of beam, moving in moves to autosaved positions - ((-2, -3, -4), (-2, 0, -4), (-2, 0, -4), True), # middle axes in beam others out of beam, moving in moves to autosaved positions - ((-2, -3, -4), (-2, -3, 0), (-2, -3, 0), True), # last axes in beam others out of beam, moving in moves to autosaved positions - ((-2, -3, -4), (0, -3, -4), (0, -3, -4), True), # first axes not in beam others in beam, moving in moves to autosaved positions - ((-2, -3, -4), (-2, 0, -4), (-2, 0, -4), True), # middle axes not in beam others in beam, moving in moves to autosaved positions - ((-2, -3, -4), (-2, -3, 0), (-2, -3, 0), True), # last axes not in beam others in beam, moving in moves to autosaved positions - ((-2, -3, -4), (1, 2, 3), (1, 2, 3), True), # all axes in the beam, move to current position - ]) + @parameterized.expand( + [ + ( + (-2, -3, -4), + (-2, -3, -4), + (-2, -3, -4), + False, + ), # all axes start out of beam, moving in moves to autosaved positions + ( + (-2, -3, -4), + (0, -3, -4), + (0, -3, -4), + True, + ), # first axes in beam others out of beam, moving in moves to autosaved positions + ( + (-2, -3, -4), + (-2, 0, -4), + (-2, 0, -4), + True, + ), # middle axes in beam others out of beam, moving in moves to autosaved positions + ( + (-2, -3, -4), + (-2, -3, 0), + (-2, -3, 0), + True, + ), # last axes in beam others out of beam, moving in moves to autosaved positions + ( + (-2, -3, -4), + (0, -3, -4), + (0, -3, -4), + True, + ), # first axes not in beam others in beam, moving in moves to autosaved positions + ( + (-2, -3, -4), + (-2, 0, -4), + (-2, 0, -4), + True, + ), # middle axes not in beam others in beam, moving in moves to autosaved positions + ( + (-2, -3, -4), + (-2, -3, 0), + (-2, -3, 0), + True, + ), # last axes not in beam others in beam, moving in moves to autosaved positions + ( + (-2, -3, -4), + (1, 2, 3), + (1, 2, 3), + True, + ), # all axes in the beam, move to current position + ] + ) @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_WHEN_init_and_move_in_THEN_component_moves_to_expected_palce(self, - out_of_beam_positions, init_positions, expected_positions, - expected_initial_inbeam, mock_autosave): - + def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_WHEN_init_and_move_in_THEN_component_moves_to_expected_palce( + self, + out_of_beam_positions, + init_positions, + expected_positions, + expected_initial_inbeam, + mock_autosave, + ): mock_autosave.read_parameter.side_effect = expected_positions - axis_ang, axis_phi, axis_pos, inbeam = self.setup_beamline(out_of_beam_positions, init_positions) + axis_ang, axis_phi, axis_pos, inbeam = self.setup_beamline( + out_of_beam_positions, init_positions + ) assert_that(inbeam.sp, is_(expected_initial_inbeam)) inbeam.sp = True @@ -1018,24 +1507,50 @@ def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_WHEN_init_ assert_that(axis_ang.rbv, is_(expected_positions[1])) assert_that(axis_phi.rbv, is_(expected_positions[2])) - def setup_beamline(self, out_of_beam_positions, init_positions, autosave_axes=True, autosave_inbeam=False): + def setup_beamline( + self, out_of_beam_positions, init_positions, autosave_axes=True, autosave_inbeam=False + ): # Setup is in or out of the beam and with autosaved positions, on move should move to autosaved positions mode = add_mode("MODE") comp = add_component(TiltingComponent("comp", PositionAndAngle(0, 0, 90))) - param_pos = add_parameter(AxisParameter("pos", comp, ChangeAxis.POSITION, autosave=autosave_axes), [mode]) - param_ang = add_parameter(AxisParameter("angle", comp, ChangeAxis.ANGLE, autosave=autosave_axes), [mode]) - param_phi = add_parameter(AxisParameter("phi", comp, ChangeAxis.PHI, autosave=autosave_axes), [mode]) + param_pos = add_parameter( + AxisParameter("pos", comp, ChangeAxis.POSITION, autosave=autosave_axes), [mode] + ) + param_ang = add_parameter( + AxisParameter("angle", comp, ChangeAxis.ANGLE, autosave=autosave_axes), [mode] + ) + param_phi = add_parameter( + AxisParameter("phi", comp, ChangeAxis.PHI, autosave=autosave_axes), [mode] + ) inbeam = add_parameter(InBeamParameter("in_beam", comp, autosave=autosave_inbeam), [mode]) axis_pos = create_mock_axis("pos_axis", init_positions[0], 1) axis_ang = create_mock_axis("ang_axis", init_positions[1], 1) axis_phi = create_mock_axis("phi_axis", init_positions[2], 1) - driver_pos = add_driver(IocDriver(comp, ChangeAxis.POSITION, axis_pos, - out_of_beam_positions=[OutOfBeamPosition(out_of_beam_positions[0])])) - driver_ang = add_driver(IocDriver(comp, ChangeAxis.ANGLE, axis_ang, - out_of_beam_positions=[OutOfBeamPosition(out_of_beam_positions[1])])) - driver_phi = add_driver(IocDriver(comp, ChangeAxis.PHI, axis_phi, - out_of_beam_positions=[OutOfBeamPosition(out_of_beam_positions[2])])) + driver_pos = add_driver( + IocDriver( + comp, + ChangeAxis.POSITION, + axis_pos, + out_of_beam_positions=[OutOfBeamPosition(out_of_beam_positions[0])], + ) + ) + driver_ang = add_driver( + IocDriver( + comp, + ChangeAxis.ANGLE, + axis_ang, + out_of_beam_positions=[OutOfBeamPosition(out_of_beam_positions[1])], + ) + ) + driver_phi = add_driver( + IocDriver( + comp, + ChangeAxis.PHI, + axis_phi, + out_of_beam_positions=[OutOfBeamPosition(out_of_beam_positions[2])], + ) + ) beamline = get_configured_beamline() assert_that(axis_ang.last_set_point_set, is_(None)) # set point wasn't set during init assert_that(axis_pos.last_set_point_set, is_(None)) # set point wasn't set during init @@ -1043,10 +1558,12 @@ def setup_beamline(self, out_of_beam_positions, init_positions, autosave_axes=Tr return axis_ang, axis_phi, axis_pos, inbeam @parameterized.expand(TEST_PARAMETER_POSITION_SETS) - def test_GIVEN_component_with_multiple_axes_out_of_beam_no_auto_save_WHEN_init_and_move_in_THEN_component_moves_to_expected_palce(self, - out_of_beam_positions, init_positions, expected_positions, expected_initial_inbeam): - - axis_ang, axis_phi, axis_pos, inbeam = self.setup_beamline(out_of_beam_positions, init_positions, False) + def test_GIVEN_component_with_multiple_axes_out_of_beam_no_auto_save_WHEN_init_and_move_in_THEN_component_moves_to_expected_palce( + self, out_of_beam_positions, init_positions, expected_positions, expected_initial_inbeam + ): + axis_ang, axis_phi, axis_pos, inbeam = self.setup_beamline( + out_of_beam_positions, init_positions, False + ) assert_that(inbeam.sp, is_(expected_initial_inbeam)) @@ -1058,11 +1575,18 @@ def test_GIVEN_component_with_multiple_axes_out_of_beam_no_auto_save_WHEN_init_a @parameterized.expand(TEST_PARAMETER_POSITION_SETS) @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_not_no_value_WHEN_init_and_move_in_THEN_component_moves_to_expected_palce(self, - out_of_beam_positions, init_positions, expected_positions, expected_initial_inbeam, mock_autosave): - + def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_not_no_value_WHEN_init_and_move_in_THEN_component_moves_to_expected_palce( + self, + out_of_beam_positions, + init_positions, + expected_positions, + expected_initial_inbeam, + mock_autosave, + ): mock_autosave.read_parameter.side_effect = [None, None, None] - axis_ang, axis_phi, axis_pos, inbeam = self.setup_beamline(out_of_beam_positions, init_positions, True) + axis_ang, axis_phi, axis_pos, inbeam = self.setup_beamline( + out_of_beam_positions, init_positions, True + ) assert_that(inbeam.sp, is_(expected_initial_inbeam)) @@ -1074,14 +1598,16 @@ def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_not_no_val @patch("ReflectometryServer.beam_path_calc.parking_index_autosave") @patch("ReflectometryServer.parameters.param_bool_autosave") - def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_only_on_in_beam_WHEN_init_and_move_in_THEN_component_moves_to_expected_place_and_do_not_move_on_init(self, - mock_autosave, mock_parking_index): - + def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_only_on_in_beam_WHEN_init_and_move_in_THEN_component_moves_to_expected_place_and_do_not_move_on_init( + self, mock_autosave, mock_parking_index + ): expected_position = 0 expected_initial_inbeam = False mock_autosave.read_parameter.side_effect = [False] mock_parking_index.read_parameter.return_value = None - axis_ang, axis_phi, axis_pos, inbeam = self.setup_beamline((-2, -3, -4), (-2, -3, -4), False, True) + axis_ang, axis_phi, axis_pos, inbeam = self.setup_beamline( + (-2, -3, -4), (-2, -3, -4), False, True + ) assert_that(inbeam.sp, is_(expected_initial_inbeam)) for axis in [axis_pos, axis_ang, axis_phi]: @@ -1096,16 +1622,26 @@ def test_GIVEN_component_with_multiple_axes_out_of_beam_with_autosave_only_on_in assert_that(axis_pos.rbv, is_(expected_position)) assert_that(axis_phi.rbv, is_(expected_position)) -class TestComponentParkingSequence(unittest.TestCase): - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - def test_GIVEN_component_with_one_axis_and_parking_sequence_WHEN_out_of_beam_THEN_component_moves_to_correct_places(self): +class TestComponentParkingSequence(unittest.TestCase): + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + def test_GIVEN_component_with_one_axis_and_parking_sequence_WHEN_out_of_beam_THEN_component_moves_to_correct_places( + self, + ): pos1 = -3 pos2 = -6 comp = Component("comp", PositionAndAngle(0, 0, 90)) parameter = InBeamParameter("val", comp) mock_axis = create_mock_axis("axis_motor", 0, 1) - driver = IocDriver(comp, ChangeAxis.HEIGHT, mock_axis, out_of_beam_positions=[OutOfBeamSequence([pos1, pos2])]) + driver = IocDriver( + comp, + ChangeAxis.HEIGHT, + mock_axis, + out_of_beam_positions=[OutOfBeamSequence([pos1, pos2])], + ) beamline = Beamline([comp], [parameter], [driver], [BeamlineMode("mode", [])]) parameter.sp = False @@ -1113,16 +1649,31 @@ def test_GIVEN_component_with_one_axis_and_parking_sequence_WHEN_out_of_beam_THE assert_that(mock_axis.sp, is_(pos2)) assert_that(parameter.rbv, is_(False)) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - def test_GIVEN_component_with_two_axes_and_parking_sequence_with_repeat_WHEN_out_of_beam_THEN_component_moves_to_correct_places(self): + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + def test_GIVEN_component_with_two_axes_and_parking_sequence_with_repeat_WHEN_out_of_beam_THEN_component_moves_to_correct_places( + self, + ): pos1 = -3 expected_sequence = [4, 6, 8] comp = Component("comp", PositionAndAngle(0, 0, 90)) parameter = InBeamParameter("val", comp) mock_axis1 = create_mock_axis("axis_motor1", 0, 1) mock_axis2 = create_mock_axis("axis_motor2", 0, 1) - driver1 = IocDriver(comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence([pos1, pos1, pos1])]) - driver2 = IocDriver(comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(expected_sequence)]) + driver1 = IocDriver( + comp, + ChangeAxis.HEIGHT, + mock_axis1, + out_of_beam_positions=[OutOfBeamSequence([pos1, pos1, pos1])], + ) + driver2 = IocDriver( + comp, + ChangeAxis.TRANS, + mock_axis2, + out_of_beam_positions=[OutOfBeamSequence(expected_sequence)], + ) beamline = Beamline([comp], [parameter], [driver1, driver2], [BeamlineMode("mode", [])]) parameter.sp = False @@ -1131,16 +1682,31 @@ def test_GIVEN_component_with_two_axes_and_parking_sequence_with_repeat_WHEN_out assert_that(mock_axis2.all_setpoints, is_(expected_sequence)) assert_that(parameter.rbv, is_(False)) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - def test_GIVEN_component_with_two_axes_and_parking_sequence_with_none_WHEN_out_of_beam_THEN_component_moves_to_correct_places(self): + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + def test_GIVEN_component_with_two_axes_and_parking_sequence_with_none_WHEN_out_of_beam_THEN_component_moves_to_correct_places( + self, + ): pos1 = -3 expected_sequence = [4, 6, 8] comp = Component("comp", PositionAndAngle(0, 0, 90)) parameter = InBeamParameter("val", comp) mock_axis1 = create_mock_axis("axis_motor1", 0, 1) mock_axis2 = create_mock_axis("axis_motor2", 0, 1) - driver1 = IocDriver(comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence([None, None, pos1])]) - driver2 = IocDriver(comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(expected_sequence)]) + driver1 = IocDriver( + comp, + ChangeAxis.HEIGHT, + mock_axis1, + out_of_beam_positions=[OutOfBeamSequence([None, None, pos1])], + ) + driver2 = IocDriver( + comp, + ChangeAxis.TRANS, + mock_axis2, + out_of_beam_positions=[OutOfBeamSequence(expected_sequence)], + ) beamline = Beamline([comp], [parameter], [driver1, driver2], [BeamlineMode("mode", [])]) mock_axis1.trigger_rbv_change() mock_axis2.trigger_rbv_change() @@ -1151,32 +1717,54 @@ def test_GIVEN_component_with_two_axes_and_parking_sequence_with_none_WHEN_out_o assert_that(mock_axis2.all_setpoints, is_(expected_sequence)) assert_that(parameter.rbv, is_(False)) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave') - def test_GIVEN_sequence_and_autosave_position_not_at_end_of_sequence_WHEN_init_THEN_init_error_and_autosave_overwriten(self, auto_save): + @patch("ReflectometryServer.beam_path_calc.parking_index_autosave") + def test_GIVEN_sequence_and_autosave_position_not_at_end_of_sequence_WHEN_init_THEN_init_error_and_autosave_overwriten( + self, auto_save + ): auto_save.read_parameter.return_value = 1 comp = Component("comp name", PositionAndAngle(0, 0, 90)) mock_axis1 = create_mock_axis("axis_motor1", 0, 1) with self.assertRaises(BeamlineConfigurationParkAutosaveInvalidException): - IocDriver(comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence([1, 2, 3])]) + IocDriver( + comp, + ChangeAxis.HEIGHT, + mock_axis1, + out_of_beam_positions=[OutOfBeamSequence([1, 2, 3])], + ) auto_save.write_parameter.assert_called() - @parameterized.expand([(None, [1]), # unparked - (0, [1]), # last value in length 1 sequence - (1, [1, 2]), # last value in length 2 sequence - (5, [1, 2]) # after the last value in a sequence (it may be a different sequence this parked in) - ]) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave') - def test_GIVEN_sequence_and_autosave_position_ok_WHEN_init_THEN_no_error(self, auto_save_value, sequence, auto_save): + @parameterized.expand( + [ + (None, [1]), # unparked + (0, [1]), # last value in length 1 sequence + (1, [1, 2]), # last value in length 2 sequence + ( + 5, + [1, 2], + ), # after the last value in a sequence (it may be a different sequence this parked in) + ] + ) + @patch("ReflectometryServer.beam_path_calc.parking_index_autosave") + def test_GIVEN_sequence_and_autosave_position_ok_WHEN_init_THEN_no_error( + self, auto_save_value, sequence, auto_save + ): auto_save.read_parameter.return_value = auto_save_value comp = Component("comp name", PositionAndAngle(0, 0, 90)) mock_axis1 = create_mock_axis("axis_motor1", 0, 1) - IocDriver(comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence(sequence)]) - - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_out_of_beam_and_then_in_beam_THEN_component_moves_to_correct_places(self): + IocDriver( + comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence(sequence)] + ) + + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_out_of_beam_and_then_in_beam_THEN_component_moves_to_correct_places( + self, + ): sequence1 = [4, 6, 8] expected_motor1_sps = [4, 6, 8, 6, 4, 0] # 0 is final in beam position sequence2 = [5, 7, 9] @@ -1185,8 +1773,15 @@ def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_out_of_beam_and parameter = InBeamParameter("val", comp) mock_axis1 = create_mock_axis("axis_motor1", 0, 1) mock_axis2 = create_mock_axis("axis_motor2", 0, 1) - driver1 = IocDriver(comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence(sequence1)]) - driver2 = IocDriver(comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(sequence2)]) + driver1 = IocDriver( + comp, + ChangeAxis.HEIGHT, + mock_axis1, + out_of_beam_positions=[OutOfBeamSequence(sequence1)], + ) + driver2 = IocDriver( + comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(sequence2)] + ) beamline = Beamline([comp], [parameter], [driver1, driver2], [BeamlineMode("mode", [])]) parameter.sp = False @@ -1195,8 +1790,13 @@ def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_out_of_beam_and assert_that(mock_axis1.all_setpoints, is_(expected_motor1_sps)) assert_that(mock_axis2.all_setpoints, is_(expected_motor2_sps)) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - def test_GIVEN_component_with_two_axes_and_parking_sequence_with_repeated_entry_at_end_WHEN_out_of_beam_and_then_in_beam_THEN_component_moves_to_correct_places(self): + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + def test_GIVEN_component_with_two_axes_and_parking_sequence_with_repeated_entry_at_end_WHEN_out_of_beam_and_then_in_beam_THEN_component_moves_to_correct_places( + self, + ): sequence1 = [4, 6, 6] expected_motor1_sps = [4, 6, 4, 0] sequence2 = [3, 5, 7] @@ -1205,8 +1805,15 @@ def test_GIVEN_component_with_two_axes_and_parking_sequence_with_repeated_entry_ parameter = InBeamParameter("val", comp) mock_axis1 = create_mock_axis("axis_motor1", 0, 1) mock_axis2 = create_mock_axis("axis_motor2", 0, 1) - driver1 = IocDriver(comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence(sequence1)]) - driver2 = IocDriver(comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(sequence2)]) + driver1 = IocDriver( + comp, + ChangeAxis.HEIGHT, + mock_axis1, + out_of_beam_positions=[OutOfBeamSequence(sequence1)], + ) + driver2 = IocDriver( + comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(sequence2)] + ) beamline = Beamline([comp], [parameter], [driver1, driver2], [BeamlineMode("mode", [])]) parameter.sp = False @@ -1215,8 +1822,13 @@ def test_GIVEN_component_with_two_axes_and_parking_sequence_with_repeated_entry_ assert_that(mock_axis1.all_setpoints, is_(expected_motor1_sps)) assert_that(mock_axis2.all_setpoints, is_(expected_motor2_sps)) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=2)) - def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_motors_out_of_beam_and_init_THEN_motors_do_not_move(self): + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=2), + ) + def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_motors_out_of_beam_and_init_THEN_motors_do_not_move( + self, + ): last_pos1 = 6 sequence1 = [4, 1, last_pos1] @@ -1227,19 +1839,33 @@ def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_motors_out_of_b parameter = InBeamParameter("val", comp) mock_axis1 = create_mock_axis("axis_motor1", last_pos1, 1) mock_axis2 = create_mock_axis("axis_motor2", last_pos2, 1) - driver1 = IocDriver(comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence(sequence1)]) - driver2 = IocDriver(comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(sequence2)]) - comp.beam_path_set_point.in_beam_manager.add_rbv_in_beam_manager(comp.beam_path_rbv.in_beam_manager) + driver1 = IocDriver( + comp, + ChangeAxis.HEIGHT, + mock_axis1, + out_of_beam_positions=[OutOfBeamSequence(sequence1)], + ) + driver2 = IocDriver( + comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(sequence2)] + ) + comp.beam_path_set_point.in_beam_manager.add_rbv_in_beam_manager( + comp.beam_path_rbv.in_beam_manager + ) driver1.initialise_setpoint() mock_axis1.trigger_rbv_change() mock_axis2.trigger_rbv_change() # this is not quite how it happens on start up where this is generated during - # the initalise but it is good enough + # the initalise but it is good enough assert_that(mock_axis1.all_setpoints, is_([])) assert_that(mock_axis2.all_setpoints, is_([])) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=2)) - def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_motors_out_of_beam_and_init_with_autosaved_bean_out_THEN_motors_do_not_move(self): + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=2), + ) + def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_motors_out_of_beam_and_init_with_autosaved_bean_out_THEN_motors_do_not_move( + self, + ): last_pos1 = 6 sequence1 = [4, 1, last_pos1] @@ -1247,22 +1873,33 @@ def test_GIVEN_component_with_two_axes_and_parking_sequence_WHEN_motors_out_of_b sequence2 = [3, 2, last_pos2] comp = Component("comp", PositionAndAngle(0, 0, 90)) - with patch('ReflectometryServer.parameters.param_bool_autosave.read_parameter') as param_autosave: + with patch( + "ReflectometryServer.parameters.param_bool_autosave.read_parameter" + ) as param_autosave: param_autosave.return_value = True parameter = InBeamParameter("val", comp, autosave=True) mock_axis1 = create_mock_axis("axis_motor1", last_pos1, 1) mock_axis2 = create_mock_axis("axis_motor2", last_pos2, 1) - driver1 = IocDriver(comp, ChangeAxis.HEIGHT, mock_axis1, out_of_beam_positions=[OutOfBeamSequence(sequence1)]) - driver2 = IocDriver(comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(sequence2)]) - comp.beam_path_set_point.in_beam_manager.add_rbv_in_beam_manager(comp.beam_path_rbv.in_beam_manager) + driver1 = IocDriver( + comp, + ChangeAxis.HEIGHT, + mock_axis1, + out_of_beam_positions=[OutOfBeamSequence(sequence1)], + ) + driver2 = IocDriver( + comp, ChangeAxis.TRANS, mock_axis2, out_of_beam_positions=[OutOfBeamSequence(sequence2)] + ) + comp.beam_path_set_point.in_beam_manager.add_rbv_in_beam_manager( + comp.beam_path_rbv.in_beam_manager + ) driver1.initialise_setpoint() mock_axis1.trigger_rbv_change() mock_axis2.trigger_rbv_change() # this is not quite how it happens on start up where this is generated during - # the initalise but it is good enough + # the initalise but it is good enough assert_that(mock_axis1.all_setpoints, is_([])) assert_that(mock_axis2.all_setpoints, is_([])) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_beamline_parameter.py b/ReflectometryServer/test_modules/test_beamline_parameter.py index 101ed909..0441be54 100644 --- a/ReflectometryServer/test_modules/test_beamline_parameter.py +++ b/ReflectometryServer/test_modules/test_beamline_parameter.py @@ -1,4 +1,3 @@ -import math import unittest from math import isnan from time import sleep @@ -14,49 +13,65 @@ from ReflectometryServer.parameters import ParameterReadbackUpdate from ReflectometryServer.pv_wrapper import ReadbackUpdate from ReflectometryServer.server_status_manager import STATUS_MANAGER - from ReflectometryServer.test_modules.data_mother import DataMother, create_mock_axis -from ReflectometryServer.test_modules.utils import position, DEFAULT_TEST_TOLERANCE, create_parameter_with_initial_value, setup_autosave +from ReflectometryServer.test_modules.utils import ( + DEFAULT_TEST_TOLERANCE, + create_parameter_with_initial_value, + position, + setup_autosave, +) class TestBeamlineParameter(unittest.TestCase): - def test_GIVEN_theta_WHEN_set_set_point_THEN_sample_hasnt_moved(self): theta_set = 10.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) mirror_pos = -100 - sample.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(mirror_pos, None, None)) + sample.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(mirror_pos, None, None) + ) theta = AxisParameter("theta", sample, ChangeAxis.ANGLE) theta.sp_no_move = theta_set assert_that(theta.sp, is_(theta_set)) - assert_that(sample.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), is_(mirror_pos)) - - def test_GIVEN_theta_WHEN_set_set_point_and_move_THEN_readback_is_as_set_and_sample_is_at_setpoint_postion(self): + assert_that( + sample.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), is_(mirror_pos) + ) + def test_GIVEN_theta_WHEN_set_set_point_and_move_THEN_readback_is_as_set_and_sample_is_at_setpoint_postion( + self, + ): theta_set = 10.0 expected_sample_angle = 10.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) sample.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) mirror_pos = -100 - sample.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(mirror_pos, None, None)) + sample.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(mirror_pos, None, None) + ) theta = AxisParameter("theta", sample, ChangeAxis.ANGLE) theta.sp_no_move = theta_set theta.move = 1 result = theta.sp_rbv assert_that(result, is_(theta_set)) - assert_that(sample.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), is_(expected_sample_angle)) - - def test_GIVEN_theta_set_WHEN_set_point_set_and_move_THEN_readback_is_as_original_value_but_setpoint_is_new_value(self): - + assert_that( + sample.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), + is_(expected_sample_angle), + ) + + def test_GIVEN_theta_set_WHEN_set_point_set_and_move_THEN_readback_is_as_original_value_but_setpoint_is_new_value( + self, + ): original_theta = 1.0 theta_set = 10.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) sample.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) mirror_pos = -100 - sample.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(mirror_pos, None, None)) + sample.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(mirror_pos, None, None) + ) theta = AxisParameter("theta", sample, ChangeAxis.ANGLE) theta.sp = original_theta @@ -77,7 +92,6 @@ def test_GIVEN_theta_and_a_set_but_no_move_WHEN_get_changed_THEN_changed_is_true assert_that(result, is_(True)) def test_GIVEN_theta_and_a_set_and_move_WHEN_get_changed_THEN_changed_is_false(self): - theta_set = 10.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) sample.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) @@ -89,14 +103,17 @@ def test_GIVEN_theta_and_a_set_and_move_WHEN_get_changed_THEN_changed_is_false(s assert_that(result, is_(False)) - def test_GIVEN_reflection_angle_WHEN_set_set_point_and_move_THEN_readback_is_as_set_and_sample_is_at_setpoint_postion(self): - + def test_GIVEN_reflection_angle_WHEN_set_set_point_and_move_THEN_readback_is_as_set_and_sample_is_at_setpoint_postion( + self, + ): angle_set = 10.0 expected_sample_angle = 10.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) sample.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) mirror_pos = -100 - sample.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(mirror_pos, None, None)) + sample.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(mirror_pos, None, None) + ) reflection_angle = AxisParameter("theta", sample, ChangeAxis.ANGLE) reflection_angle.sp_no_move = angle_set @@ -104,10 +121,14 @@ def test_GIVEN_reflection_angle_WHEN_set_set_point_and_move_THEN_readback_is_as_ result = reflection_angle.sp_rbv assert_that(result, is_(angle_set)) - assert_that(sample.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), is_(expected_sample_angle)) - - def test_GIVEN_jaw_height_WHEN_set_set_point_and_move_THEN_readback_is_as_set_and_jaws_are_at_setpoint_postion(self): - + assert_that( + sample.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), + is_(expected_sample_angle), + ) + + def test_GIVEN_jaw_height_WHEN_set_set_point_and_move_THEN_readback_is_as_set_and_jaws_are_at_setpoint_postion( + self, + ): height_set = 10.0 beam_height = 5 expected_height = beam_height + height_set @@ -121,10 +142,17 @@ def test_GIVEN_jaw_height_WHEN_set_set_point_and_move_THEN_readback_is_as_set_an result = tracking_height.sp_rbv assert_that(result, is_(height_set)) - assert_that(jaws.beam_path_set_point.position_in_mantid_coordinates().y, is_(expected_height)) - assert_that(jaws.beam_path_set_point.position_in_mantid_coordinates().z, is_(close_to(jaws_z, DEFAULT_TEST_TOLERANCE))) - - def test_GIVEN_component_parameter_in_beam_in_mode_WHEN_parameter_moved_to_THEN_component_is_in_beam(self): + assert_that( + jaws.beam_path_set_point.position_in_mantid_coordinates().y, is_(expected_height) + ) + assert_that( + jaws.beam_path_set_point.position_in_mantid_coordinates().z, + is_(close_to(jaws_z, DEFAULT_TEST_TOLERANCE)), + ) + + def test_GIVEN_component_parameter_in_beam_in_mode_WHEN_parameter_moved_to_THEN_component_is_in_beam( + self, + ): super_mirror = ReflectingComponent("super mirror", PositionAndAngle(z=10, y=0, angle=90)) super_mirror.beam_path_set_point.is_in_beam = False sm_in_beam = InBeamParameter("sminbeam", super_mirror) @@ -136,7 +164,9 @@ def test_GIVEN_component_parameter_in_beam_in_mode_WHEN_parameter_moved_to_THEN_ assert_that(sm_in_beam.sp_rbv, is_(in_beam_sp)) assert_that(super_mirror.beam_path_set_point.is_in_beam, is_(in_beam_sp)) - def test_GIVEN_component_in_beam_parameter_in_mode_WHEN_parameter_moved_to_THEN_component_is_not_in_beam(self): + def test_GIVEN_component_in_beam_parameter_in_mode_WHEN_parameter_moved_to_THEN_component_is_not_in_beam( + self, + ): super_mirror = ReflectingComponent("super mirror", PositionAndAngle(z=10, y=0, angle=90)) super_mirror.beam_path_set_point.axis[ChangeAxis.POSITION].park_sequence_count = 1 super_mirror.beam_path_set_point.axis[ChangeAxis.POSITION].is_in_beam = True @@ -149,7 +179,9 @@ def test_GIVEN_component_in_beam_parameter_in_mode_WHEN_parameter_moved_to_THEN_ assert_that(sm_in_beam.sp_rbv, is_(in_beam_sp)) assert_that(super_mirror.beam_path_set_point.is_in_beam, is_(in_beam_sp)) - def test_GIVEN_parameter_WHEN_setting_define_position_sp_THEN_define_as_changed_flag_is_false(self): + def test_GIVEN_parameter_WHEN_setting_define_position_sp_THEN_define_as_changed_flag_is_false( + self, + ): parameter = 6.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) theta = AxisParameter("theta", sample, ChangeAxis.ANGLE) @@ -159,7 +191,9 @@ def test_GIVEN_parameter_WHEN_setting_define_position_sp_THEN_define_as_changed_ assert_that(result, is_(False)) - def test_GIVEN_parameter_WHEN_setting_define_position_sp_no_action_THEN_define_as_sp_rbv_is_unchanged(self): + def test_GIVEN_parameter_WHEN_setting_define_position_sp_no_action_THEN_define_as_sp_rbv_is_unchanged( + self, + ): parameter = 6.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) theta = AxisParameter("theta", sample, ChangeAxis.ANGLE) @@ -171,7 +205,9 @@ def test_GIVEN_parameter_WHEN_setting_define_position_sp_no_action_THEN_define_a assert_that(result, is_(initial)) assert_that(result, is_not(parameter)) - def test_GIVEN_parameter_WHEN_setting_define_position_sp_no_action_THEN_define_as_changed_flag_is_true(self): + def test_GIVEN_parameter_WHEN_setting_define_position_sp_no_action_THEN_define_as_changed_flag_is_true( + self, + ): parameter = 6.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) theta = AxisParameter("theta", sample, ChangeAxis.ANGLE) @@ -181,7 +217,9 @@ def test_GIVEN_parameter_WHEN_setting_define_position_sp_no_action_THEN_define_a assert_that(result, is_(True)) - def test_GIVEN_define_as_sp_set_on_parameter_WHEN_triggering_define_as_action_THEN_define_as_sp_rbv_is_updated(self): + def test_GIVEN_define_as_sp_set_on_parameter_WHEN_triggering_define_as_action_THEN_define_as_sp_rbv_is_updated( + self, + ): parameter = 6.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) theta = AxisParameter("theta", sample, ChangeAxis.ANGLE) @@ -192,7 +230,9 @@ def test_GIVEN_define_as_sp_set_on_parameter_WHEN_triggering_define_as_action_TH assert_that(result, is_(parameter)) - def test_GIVEN_define_as_sp_set_on_parameter_WHEN_triggering_define_as_action_THEN_define_as_changed_flag_is_false(self): + def test_GIVEN_define_as_sp_set_on_parameter_WHEN_triggering_define_as_action_THEN_define_as_changed_flag_is_false( + self, + ): parameter = 6.0 sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 0, 90)) theta = AxisParameter("theta", sample, ChangeAxis.ANGLE) @@ -239,11 +279,13 @@ def test_GIVEN_read_only_parameter_WHEN_param_has_mode_inits_THEN_inits_ignored( assert_that(disp_angle.sp, is_(expected)) assert_that(disp_angle.sp_changed, is_(False)) - assert_that(sample_comp.beam_path_set_point.axis[ChangeAxis.DISPLACEMENT_ANGLE].get_displacement(), is_(expected)) + assert_that( + sample_comp.beam_path_set_point.axis[ChangeAxis.DISPLACEMENT_ANGLE].get_displacement(), + is_(expected), + ) class TestBeamlineParametersThoseRBVMirrosSP(unittest.TestCase): - def test_GIVEN_axis_parameter_has_sp_mirros_rbv_WHEN_move_THEN_sp_is_moved_to_rbv(self): expected_sp = 1.0 comp = Component("comp", PositionAndAngle(0, 0, 90)) @@ -252,7 +294,9 @@ def test_GIVEN_axis_parameter_has_sp_mirros_rbv_WHEN_move_THEN_sp_is_moved_to_rb param = AxisParameter("param", comp, axis=ChangeAxis.POSITION, sp_mirrors_rbv=True) param.sp = 0 - comp.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(expected_sp, None, None)) + comp.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(expected_sp, None, None) + ) param.move_to_sp_no_callback() # as beamline does assert_that(param.sp, is_(expected_sp)) @@ -271,10 +315,13 @@ def test_GIVEN_parameter_has_sp_mirros_rbv_WHEN_in_beam_THEN_parameter_is_disabl class TestBeamlineModes(unittest.TestCase): - - def test_GIVEN_unpolarised_mode_and_beamline_parameters_are_set_WHEN_move_THEN_components_move_onto_beam_line(self): + def test_GIVEN_unpolarised_mode_and_beamline_parameters_are_set_WHEN_move_THEN_components_move_onto_beam_line( + self, + ): slit2 = Component("s2", PositionAndAngle(0, z=10, angle=90)) - ideal_sample_point = ReflectingComponent("ideal_sample_point", PositionAndAngle(0, z=20, angle=90)) + ideal_sample_point = ReflectingComponent( + "ideal_sample_point", PositionAndAngle(0, z=20, angle=90) + ) detector = Component("detector", PositionAndAngle(0, z=30, angle=90)) components = [slit2, ideal_sample_point, detector] @@ -282,10 +329,13 @@ def test_GIVEN_unpolarised_mode_and_beamline_parameters_are_set_WHEN_move_THEN_c AxisParameter("slit2height", slit2, ChangeAxis.POSITION), AxisParameter("height", ideal_sample_point, ChangeAxis.POSITION), AxisParameter("theta", ideal_sample_point, ChangeAxis.ANGLE), - AxisParameter("detectorheight", detector, ChangeAxis.POSITION)] - #parameters["detectorAngle": TrackingAngle(detector) + AxisParameter("detectorheight", detector, ChangeAxis.POSITION), + ] + # parameters["detectorAngle": TrackingAngle(detector) beam = PositionAndAngle(0, 0, -45) - beamline = Beamline(components, parameters, [], [DataMother.BEAMLINE_MODE_NEUTRON_REFLECTION], beam) + beamline = Beamline( + components, parameters, [], [DataMother.BEAMLINE_MODE_NEUTRON_REFLECTION], beam + ) beamline.active_mode = DataMother.BEAMLINE_MODE_NEUTRON_REFLECTION.name beamline.parameter("theta").sp_no_move = 45 beamline.parameter("height").sp_no_move = 0 @@ -294,13 +344,26 @@ def test_GIVEN_unpolarised_mode_and_beamline_parameters_are_set_WHEN_move_THEN_c beamline.move = 1 - assert_that(slit2.beam_path_set_point.position_in_mantid_coordinates(), is_(position(Position(-10, 10)))) - assert_that(ideal_sample_point.beam_path_set_point.position_in_mantid_coordinates(), is_(position(Position(-20, 20)))) - assert_that(detector.beam_path_set_point.position_in_mantid_coordinates(), is_(position(Position(-10, 30)))) - - def test_GIVEN_a_mode_with_a_single_beamline_parameter_in_WHEN_move_THEN_beamline_parameter_is_calculated_on_move(self): + assert_that( + slit2.beam_path_set_point.position_in_mantid_coordinates(), + is_(position(Position(-10, 10))), + ) + assert_that( + ideal_sample_point.beam_path_set_point.position_in_mantid_coordinates(), + is_(position(Position(-20, 20))), + ) + assert_that( + detector.beam_path_set_point.position_in_mantid_coordinates(), + is_(position(Position(-10, 30))), + ) + + def test_GIVEN_a_mode_with_a_single_beamline_parameter_in_WHEN_move_THEN_beamline_parameter_is_calculated_on_move( + self, + ): angle_to_set = 45.0 - ideal_sample_point = ReflectingComponent("ideal_sample_point", PositionAndAngle(y=0, z=20, angle=90)) + ideal_sample_point = ReflectingComponent( + "ideal_sample_point", PositionAndAngle(y=0, z=20, angle=90) + ) theta = AxisParameter("theta", ideal_sample_point, ChangeAxis.ANGLE) beamline_mode = BeamlineMode("mode name", [theta.name]) beamline = Beamline([ideal_sample_point], [theta], [], [beamline_mode]) @@ -309,17 +372,26 @@ def test_GIVEN_a_mode_with_a_single_beamline_parameter_in_WHEN_move_THEN_beamlin beamline.active_mode = beamline_mode.name beamline.move = 1 - assert_that(ideal_sample_point.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), is_(angle_to_set)) + assert_that( + ideal_sample_point.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), + is_(angle_to_set), + ) - def test_GIVEN_a_mode_with_a_two_beamline_parameter_in_WHEN_move_first_THEN_second_beamline_parameter_is_calculated_and_moved_to(self): + def test_GIVEN_a_mode_with_a_two_beamline_parameter_in_WHEN_move_first_THEN_second_beamline_parameter_is_calculated_and_moved_to( + self, + ): angle_to_set = 45.0 - ideal_sample_point = ReflectingComponent("ideal_sample_point", PositionAndAngle(y=0, z=20, angle=90)) + ideal_sample_point = ReflectingComponent( + "ideal_sample_point", PositionAndAngle(y=0, z=20, angle=90) + ) theta = AxisParameter("theta", ideal_sample_point, ChangeAxis.ANGLE) super_mirror = ReflectingComponent("super mirror", PositionAndAngle(y=0, z=10, angle=90)) smangle = AxisParameter("smangle", super_mirror, ChangeAxis.ANGLE) beamline_mode = BeamlineMode("mode name", [theta.name, smangle.name]) - beamline = Beamline([super_mirror, ideal_sample_point], [smangle, theta], [], [beamline_mode]) + beamline = Beamline( + [super_mirror, ideal_sample_point], [smangle, theta], [], [beamline_mode] + ) theta.sp_no_move = angle_to_set smangle.sp_no_move = 0 beamline.active_mode = beamline_mode.name @@ -328,15 +400,20 @@ def test_GIVEN_a_mode_with_a_two_beamline_parameter_in_WHEN_move_first_THEN_seco smangle_to_set = -10 smangle.sp = smangle_to_set - assert_that(ideal_sample_point.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), is_(smangle_to_set * 2 + angle_to_set)) + assert_that( + ideal_sample_point.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), + is_(smangle_to_set * 2 + angle_to_set), + ) - def test_GIVEN_virtual_parmeter_without_component_WHEN_moved_THEN_sp_updated_and_rbv_updated(self): + def test_GIVEN_virtual_parmeter_without_component_WHEN_moved_THEN_sp_updated_and_rbv_updated( + self, + ): setpoint = 10 parameter = VirtualParameter("virtual", ChangeAxis.HEIGHT) super_mirror = ReflectingComponent("super mirror", PositionAndAngle(z=10, y=0, angle=90)) beamline_mode = BeamlineMode("mode name", [parameter.name]) - beamline = Beamline([super_mirror], [parameter],[],[beamline_mode]) + beamline = Beamline([super_mirror], [parameter], [], [beamline_mode]) parameter.sp_no_move = setpoint beamline.active_mode = beamline_mode.name beamline.move = 1 @@ -344,12 +421,15 @@ def test_GIVEN_virtual_parmeter_without_component_WHEN_moved_THEN_sp_updated_and self.assertEqual(parameter.sp_no_move, parameter.sp) self.assertEqual(parameter.sp_no_move, parameter.sp_rbv) - - def test_GIVEN_mode_has_initial_parameter_value_WHEN_setting_mode_THEN_component_sp_updated_but_rbv_unchanged(self): + def test_GIVEN_mode_has_initial_parameter_value_WHEN_setting_mode_THEN_component_sp_updated_but_rbv_unchanged( + self, + ): sm_angle = 0.0 sm_angle_to_set = 45.0 super_mirror = ReflectingComponent("super mirror", PositionAndAngle(z=10, y=0, angle=90)) - super_mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(sm_angle, None, None)) + super_mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(sm_angle, None, None) + ) smangle = AxisParameter("smangle", super_mirror, ChangeAxis.ANGLE) smangle.sp_no_move = sm_angle sp_inits = {smangle.name: sm_angle_to_set} @@ -360,12 +440,19 @@ def test_GIVEN_mode_has_initial_parameter_value_WHEN_setting_mode_THEN_component assert_that(smangle.sp, is_(sm_angle_to_set)) assert_that(smangle.sp_changed, is_(True)) - assert_that(super_mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), is_(sm_angle)) - - def test_GIVEN_mode_has_initial_value_for_param_not_in_beamline_WHEN_initialize_mode_THEN_keyerror_raised(self): + assert_that( + super_mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement(), + is_(sm_angle), + ) + + def test_GIVEN_mode_has_initial_value_for_param_not_in_beamline_WHEN_initialize_mode_THEN_keyerror_raised( + self, + ): sm_angle = 0.0 super_mirror = ReflectingComponent("super mirror", PositionAndAngle(z=10, y=0, angle=90)) - super_mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(sm_angle, None, None)) + super_mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(sm_angle, None, None) + ) smangle = AxisParameter("smangle", super_mirror, ChangeAxis.ANGLE) smangle.sp_no_move = sm_angle sp_inits = {"nonsense name": sm_angle} @@ -374,7 +461,9 @@ def test_GIVEN_mode_has_initial_value_for_param_not_in_beamline_WHEN_initialize_ with self.assertRaises(BeamlineConfigurationInvalidException): Beamline([super_mirror], [smangle], [], [beamline_mode]) - def test_GIVEN_parameter_not_in_mode_and_not_changed_and_no_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_unchanged(self): + def test_GIVEN_parameter_not_in_mode_and_not_changed_and_no_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_unchanged( + self, + ): initial_s2_height = 0.0 super_mirror = ReflectingComponent("sm", PositionAndAngle(0.0, 10, 90.0)) s2 = Component("s2", PositionAndAngle(initial_s2_height, 20, 90.0)) @@ -389,9 +478,13 @@ def test_GIVEN_parameter_not_in_mode_and_not_changed_and_no_previous_parameter_c beamline.move = 1 - assert_that(s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(initial_s2_height)) + assert_that( + s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(initial_s2_height) + ) - def test_GIVEN_parameter_not_in_mode_and_not_changed_and_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_unchanged(self): + def test_GIVEN_parameter_not_in_mode_and_not_changed_and_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_unchanged( + self, + ): initial_s2_height = 0.0 super_mirror = ReflectingComponent("sm", PositionAndAngle(0.0, 10, 90.0)) s2 = Component("s2", PositionAndAngle(initial_s2_height, 20, 90.0)) @@ -407,15 +500,23 @@ def test_GIVEN_parameter_not_in_mode_and_not_changed_and_previous_parameter_chan beamline.move = 1 - assert_that(s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(initial_s2_height)) + assert_that( + s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(initial_s2_height) + ) - def test_GIVEN_parameter_in_mode_and_not_changed_and_no_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_unchanged(self): + def test_GIVEN_parameter_in_mode_and_not_changed_and_no_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_unchanged( + self, + ): initial_s2_height = 0.0 super_mirror = ReflectingComponent("sm", PositionAndAngle(0.0, 10, 90.0)) s2 = Component("s2", PositionAndAngle(initial_s2_height, 20, 90.0)) - sm_angle = create_parameter_with_initial_value(0, AxisParameter, "smangle", super_mirror, ChangeAxis.ANGLE) - slit2_pos = create_parameter_with_initial_value(0, AxisParameter, "slit2pos", s2, ChangeAxis.POSITION) + sm_angle = create_parameter_with_initial_value( + 0, AxisParameter, "smangle", super_mirror, ChangeAxis.ANGLE + ) + slit2_pos = create_parameter_with_initial_value( + 0, AxisParameter, "slit2pos", s2, ChangeAxis.POSITION + ) mode = BeamlineMode("both_params", [sm_angle.name, slit2_pos.name]) @@ -424,9 +525,13 @@ def test_GIVEN_parameter_in_mode_and_not_changed_and_no_previous_parameter_chang beamline.move = 1 - assert_that(s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(initial_s2_height)) + assert_that( + s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(initial_s2_height) + ) - def test_GIVEN_parameter_changed_and_not_in_mode_and_no_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_moved_to_sp(self): + def test_GIVEN_parameter_changed_and_not_in_mode_and_no_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_moved_to_sp( + self, + ): initial_s2_height = 0.0 target_s2_height = 1.0 super_mirror = ReflectingComponent("sm", PositionAndAngle(0.0, 10, 90.0)) @@ -443,10 +548,13 @@ def test_GIVEN_parameter_changed_and_not_in_mode_and_no_previous_parameter_chang slit2_pos.sp_no_move = target_s2_height beamline.move = 1 - assert_that(s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(target_s2_height)) + assert_that( + s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(target_s2_height) + ) def test_GIVEN_parameter_changed_and_not_in_mode_and_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_moved_to_sp( - self): + self, + ): initial_s2_height = 0.0 target_s2_height = 11.0 super_mirror = ReflectingComponent("sm", PositionAndAngle(0.0, 10, 90.0)) @@ -464,17 +572,25 @@ def test_GIVEN_parameter_changed_and_not_in_mode_and_previous_parameter_changed_ slit2_pos.sp_no_move = 1.0 beamline.move = 1 - assert_that(s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(close_to(target_s2_height, DEFAULT_TEST_TOLERANCE))) + assert_that( + s2.beam_path_set_point.position_in_mantid_coordinates().y, + is_(close_to(target_s2_height, DEFAULT_TEST_TOLERANCE)), + ) def test_GIVEN_parameter_changed_and_in_mode_and_no_previous_parameter_changed_WHEN_moving_beamline_THEN_parameter_moved_to_sp( - self): + self, + ): initial_s2_height = 0.0 target_s2_height = 1.0 super_mirror = ReflectingComponent("sm", PositionAndAngle(0.0, 10, 90.0)) s2 = Component("s2", PositionAndAngle(initial_s2_height, 20, 90.0)) - sm_angle = create_parameter_with_initial_value(0, AxisParameter, "smangle", super_mirror, ChangeAxis.ANGLE) - slit2_pos = create_parameter_with_initial_value(0, AxisParameter, "slit2pos", s2, ChangeAxis.POSITION) + sm_angle = create_parameter_with_initial_value( + 0, AxisParameter, "smangle", super_mirror, ChangeAxis.ANGLE + ) + slit2_pos = create_parameter_with_initial_value( + 0, AxisParameter, "slit2pos", s2, ChangeAxis.POSITION + ) mode = BeamlineMode("both_params", [sm_angle.name, slit2_pos.name]) @@ -484,9 +600,13 @@ def test_GIVEN_parameter_changed_and_in_mode_and_no_previous_parameter_changed_W slit2_pos.sp_no_move = target_s2_height beamline.move = 1 - assert_that(s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(target_s2_height)) + assert_that( + s2.beam_path_set_point.position_in_mantid_coordinates().y, is_(target_s2_height) + ) - def test_GIVEN_two_changed_parameters_in_mode_WHEN_first_parameter_moved_to_SP_THEN_second_parameter_moved_to_SP_RBV(self): + def test_GIVEN_two_changed_parameters_in_mode_WHEN_first_parameter_moved_to_SP_THEN_second_parameter_moved_to_SP_RBV( + self, + ): beam_start = PositionAndAngle(0, 0, 0) s4_height_initial = 0.0 s4_height_sp = 1.0 @@ -495,8 +615,12 @@ def test_GIVEN_two_changed_parameters_in_mode_WHEN_first_parameter_moved_to_SP_T sample_to_s4_z = 10.0 sample_point = ReflectingComponent("sm", PositionAndAngle(0, sample_z, 90)) s4 = Component("s4", PositionAndAngle(s4_height_initial, sample_z + sample_to_s4_z, 90)) - theta = create_parameter_with_initial_value(True, AxisParameter, "theta", sample_point, ChangeAxis.ANGLE) - slit4_pos = create_parameter_with_initial_value(0, AxisParameter, "slit4pos", s4, ChangeAxis.POSITION) + theta = create_parameter_with_initial_value( + True, AxisParameter, "theta", sample_point, ChangeAxis.ANGLE + ) + slit4_pos = create_parameter_with_initial_value( + 0, AxisParameter, "slit4pos", s4, ChangeAxis.POSITION + ) mode = BeamlineMode("both_params", [theta.name, slit4_pos.name]) beamline = Beamline([sample_point, s4], [theta, slit4_pos], [], [mode]) beamline.active_mode = mode.name @@ -505,9 +629,14 @@ def test_GIVEN_two_changed_parameters_in_mode_WHEN_first_parameter_moved_to_SP_T slit4_pos.sp_no_move = s4_height_sp theta.move = 1 - assert_that(s4.beam_path_set_point.position_in_mantid_coordinates().y, is_(close_to(sample_to_s4_z + s4_height_initial, DEFAULT_TEST_TOLERANCE))) + assert_that( + s4.beam_path_set_point.position_in_mantid_coordinates().y, + is_(close_to(sample_to_s4_z + s4_height_initial, DEFAULT_TEST_TOLERANCE)), + ) - def test_GIVEN_two_changed_parameters_with_second_not_in_mode_WHEN_first_parameter_moved_to_SP_THEN_second_parameter_unchanged(self): + def test_GIVEN_two_changed_parameters_with_second_not_in_mode_WHEN_first_parameter_moved_to_SP_THEN_second_parameter_unchanged( + self, + ): beam_start = PositionAndAngle(0, 0, 0) s4_height_initial = 0.0 s4_height_sp = 1.0 @@ -526,10 +655,13 @@ def test_GIVEN_two_changed_parameters_with_second_not_in_mode_WHEN_first_paramet slit4_pos.sp_no_move = s4_height_sp theta.move = 1 - assert_that(s4.beam_path_set_point.position_in_mantid_coordinates().y, is_(s4_height_initial)) + assert_that( + s4.beam_path_set_point.position_in_mantid_coordinates().y, is_(s4_height_initial) + ) def test_GIVEN_two_changed_parameters_with_first_not_in_mode_WHEN_first_parameter_moved_to_SP_THEN_second_parameter_unchanged( - self): + self, + ): beam_start = PositionAndAngle(0, 0, 0) s4_height_initial = 0.0 s4_height_sp = 1.0 @@ -548,9 +680,13 @@ def test_GIVEN_two_changed_parameters_with_first_not_in_mode_WHEN_first_paramete slit4_pos.sp_no_move = s4_height_sp theta.move = 1 - assert_that(s4.beam_path_set_point.position_in_mantid_coordinates().y, is_(s4_height_initial)) + assert_that( + s4.beam_path_set_point.position_in_mantid_coordinates().y, is_(s4_height_initial) + ) - def test_GIVEN_component_in_beam_sp_rbv_is_out_for_setpoint_beam_path_THEN_axis_param_is_disabled(self): + def test_GIVEN_component_in_beam_sp_rbv_is_out_for_setpoint_beam_path_THEN_axis_param_is_disabled( + self, + ): in_beam = False expected = True component = Component("test_comp", setup=PositionAndAngle(0, 0, 90)) @@ -561,7 +697,9 @@ def test_GIVEN_component_in_beam_sp_rbv_is_out_for_setpoint_beam_path_THEN_axis_ assert_that(actual, is_(expected)) - def test_GIVEN_component_in_beam_sp_rbv_is_in_for_setpoint_beam_path_THEN_axis_param_is_not_disabled(self): + def test_GIVEN_component_in_beam_sp_rbv_is_in_for_setpoint_beam_path_THEN_axis_param_is_not_disabled( + self, + ): in_beam = True expected = False component = Component("test_comp", setup=PositionAndAngle(0, 0, 90)) @@ -572,7 +710,9 @@ def test_GIVEN_component_in_beam_sp_rbv_is_in_for_setpoint_beam_path_THEN_axis_p assert_that(actual, is_(expected)) - def test_GIVEN_component_in_beam_sp_rbv_is_out_for_readback_beam_path_THEN_axis_param_is_unaffected(self): + def test_GIVEN_component_in_beam_sp_rbv_is_out_for_readback_beam_path_THEN_axis_param_is_unaffected( + self, + ): in_beam = False expected = True component = Component("test_comp", setup=PositionAndAngle(0, 0, 90)) @@ -584,7 +724,9 @@ def test_GIVEN_component_in_beam_sp_rbv_is_out_for_readback_beam_path_THEN_axis_ assert_that(actual, is_(expected)) - def test_GIVEN_component_in_beam_sp_rbv_is_in_for_readback_beam_path_THEN_axis_param_is_unaffected(self): + def test_GIVEN_component_in_beam_sp_rbv_is_in_for_readback_beam_path_THEN_axis_param_is_unaffected( + self, + ): in_beam = True expected = False component = Component("test_comp", setup=PositionAndAngle(0, 0, 90)) @@ -596,12 +738,15 @@ def test_GIVEN_component_in_beam_sp_rbv_is_in_for_readback_beam_path_THEN_axis_p assert_that(actual, is_(expected)) + class TestBeamlineOnMove(unittest.TestCase): def test_GIVEN_three_beamline_parameters_WHEN_move_1st_THEN_all_move(self): beamline_parameters, _ = DataMother.beamline_with_3_empty_parameters() beamline_parameters[0].move = 1 - moves = [beamline_parameter.move_component_count for beamline_parameter in beamline_parameters] + moves = [ + beamline_parameter.move_component_count for beamline_parameter in beamline_parameters + ] assert_that(moves, contains_exactly(1, 1, 1), "beamline parameter move counts") @@ -609,7 +754,9 @@ def test_GIVEN_three_beamline_parameters_WHEN_move_2nd_THEN_2nd_and_3rd_move(sel beamline_parameters, _ = DataMother.beamline_with_3_empty_parameters() beamline_parameters[1].move = 1 - moves = [beamline_parameter.move_component_count for beamline_parameter in beamline_parameters] + moves = [ + beamline_parameter.move_component_count for beamline_parameter in beamline_parameters + ] assert_that(moves, contains_exactly(0, 1, 1), "beamline parameter move counts") @@ -617,39 +764,55 @@ def test_GIVEN_three_beamline_parameters_WHEN_move_3rd_THEN_3rd_moves(self): beamline_parameters, _ = DataMother.beamline_with_3_empty_parameters() beamline_parameters[2].move = 1 - moves = [beamline_parameter.move_component_count for beamline_parameter in beamline_parameters] + moves = [ + beamline_parameter.move_component_count for beamline_parameter in beamline_parameters + ] assert_that(moves, contains_exactly(0, 0, 1), "beamline parameter move counts") - def test_GIVEN_three_beamline_parameters_and_1_and_3_in_mode_WHEN_move_1st_THEN_parameters_in_the_mode_move(self): + def test_GIVEN_three_beamline_parameters_and_1_and_3_in_mode_WHEN_move_1st_THEN_parameters_in_the_mode_move( + self, + ): beamline_parameters, beamline = DataMother.beamline_with_3_empty_parameters() beamline.active_mode = "components1and3" beamline_parameters[0].move = 1 - moves = [beamline_parameter.move_component_count for beamline_parameter in beamline_parameters] + moves = [ + beamline_parameter.move_component_count for beamline_parameter in beamline_parameters + ] assert_that(moves, contains_exactly(1, 0, 1), "beamline parameter move counts") - def test_GIVEN_three_beamline_parameters_and_3_in_mode_WHEN_move_1st_THEN_only_2nd_parameter_moved(self): + def test_GIVEN_three_beamline_parameters_and_3_in_mode_WHEN_move_1st_THEN_only_2nd_parameter_moved( + self, + ): beamline_parameters, beamline = DataMother.beamline_with_3_empty_parameters() beamline.active_mode = "just2" beamline_parameters[0].move = 1 - moves = [beamline_parameter.move_component_count for beamline_parameter in beamline_parameters] + moves = [ + beamline_parameter.move_component_count for beamline_parameter in beamline_parameters + ] assert_that(moves, contains_exactly(1, 0, 0), "beamline parameter move counts") - def test_GIVEN_three_beamline_parameters_in_mode_WHEN_1st_changed_and_move_beamline_THEN_all_move(self): + def test_GIVEN_three_beamline_parameters_in_mode_WHEN_1st_changed_and_move_beamline_THEN_all_move( + self, + ): beamline_parameters, beamline = DataMother.beamline_with_3_empty_parameters() beamline_parameters[0].sp_no_move = 12.0 beamline.move = 1 - moves = [beamline_parameter.move_component_count for beamline_parameter in beamline_parameters] + moves = [ + beamline_parameter.move_component_count for beamline_parameter in beamline_parameters + ] assert_that(moves, contains_exactly(1, 1, 1), "beamline parameter move counts") @parameterized.expand([("s1", 12.132, "theta", 4.012), ("s3", 1.123, "theta", 2.342)]) - def test_GIVEN_parameter_with_new_sp_WHEN_theta_moved_independently_THEN_parameter_unchanged(self, param, param_sp, theta, theta_sp): + def test_GIVEN_parameter_with_new_sp_WHEN_theta_moved_independently_THEN_parameter_unchanged( + self, param, param_sp, theta, theta_sp + ): spacing = 2.0 bl, drives = DataMother.beamline_s1_s3_theta_detector(spacing) @@ -664,7 +827,9 @@ def test_GIVEN_parameter_with_new_sp_WHEN_theta_moved_independently_THEN_paramet assert_that(param_init_position, is_(close_to(param_final_position, delta=1e-6))) @parameterized.expand([("s3_gap", 2.452, "theta", 4.012), ("s3_gap", 4.223, "theta", 1.632)]) - def test_GIVEN_slit_gap_parameter_WHEN_theta_moved_independently_THEN_slit_gap_parameter_unchanged(self, param, param_sp, theta, theta_sp): + def test_GIVEN_slit_gap_parameter_WHEN_theta_moved_independently_THEN_slit_gap_parameter_unchanged( + self, param, param_sp, theta, theta_sp + ): spacing = 2.0 bl, drives = DataMother.beamline_s1_gap_theta_s3_gap_detector(spacing) @@ -678,22 +843,30 @@ def test_GIVEN_slit_gap_parameter_WHEN_theta_moved_independently_THEN_slit_gap_p assert_that(param_final_position, is_(close_to(param_init_position, delta=1e-6))) - def test_GIVEN_parameter_with_mode_init_and_beamline_set_not_to_reinit_on_move_WHEN_in_mode_but_not_at_init_THEN_move_all_param_does_not_reinit(self): + def test_GIVEN_parameter_with_mode_init_and_beamline_set_not_to_reinit_on_move_WHEN_in_mode_but_not_at_init_THEN_move_all_param_does_not_reinit( + self, + ): sm_angle = 0.0 init_sm_angle = 45.0 param_name = "smangle" - beamline = DataMother.beamline_with_one_mode_init_param_in_mode_and_at_off_init(init_sm_angle, sm_angle, param_name) + beamline = DataMother.beamline_with_one_mode_init_param_in_mode_and_at_off_init( + init_sm_angle, sm_angle, param_name + ) beamline.move = 1 assert_that(beamline.parameter(param_name).sp, is_(sm_angle)) assert_that(beamline.parameter(param_name).sp_changed, is_(False)) - def test_GIVEN_parameter_with_mode_init_and_beamline_set_to_reinit_on_move_WHEN_in_mode_but_not_at_init_THEN_move_all_param_does_reinit(self): + def test_GIVEN_parameter_with_mode_init_and_beamline_set_to_reinit_on_move_WHEN_in_mode_but_not_at_init_THEN_move_all_param_does_reinit( + self, + ): sm_angle = 0.0 init_sm_angle = 45.0 param_name = "smangle" - beamline = DataMother.beamline_with_one_mode_init_param_in_mode_and_at_off_init(init_sm_angle, sm_angle, param_name) + beamline = DataMother.beamline_with_one_mode_init_param_in_mode_and_at_off_init( + init_sm_angle, sm_angle, param_name + ) beamline.reinit_mode_on_move = True beamline.move = 1 @@ -703,22 +876,23 @@ def test_GIVEN_parameter_with_mode_init_and_beamline_set_to_reinit_on_move_WHEN_ class TestBeamlineParameterReadback(unittest.TestCase): - def test_GIVEN_tracking_parameter_WHEN_set_readback_on_component_THEN_readback_is_changed(self): - sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 10, 90)) displacement = 3.0 beam_height = 1.0 sample.beam_path_rbv.set_incoming_beam(PositionAndAngle(beam_height, 0, 0)) displacement_parameter = AxisParameter("param", sample, ChangeAxis.POSITION) - sample.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(displacement, None, None)) + sample.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(displacement, None, None) + ) result = displacement_parameter.rbv assert_that(result, is_(displacement - beam_height)) - def test_GIVEN_tracking_parameter_WHEN_set_readback_on_component_THEN_call_back_triggered_on_component_change(self): - + def test_GIVEN_tracking_parameter_WHEN_set_readback_on_component_THEN_call_back_triggered_on_component_change( + self, + ): sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 10, 90)) displacement = 3.0 beam_height = 1.0 @@ -726,13 +900,16 @@ def test_GIVEN_tracking_parameter_WHEN_set_readback_on_component_THEN_call_back_ displacement_parameter = AxisParameter("param", sample, ChangeAxis.POSITION) listener = Mock() displacement_parameter.add_listener(ParameterReadbackUpdate, listener) - sample.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(displacement, None, None)) + sample.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(displacement, None, None) + ) listener.assert_called_with(ParameterReadbackUpdate(displacement - beam_height, None, None)) assert_that(listener.call_count, is_(2)) # once for beam path and once for physcial move - def test_GIVEN_reflection_angle_WHEN_set_readback_on_component_THEN_call_back_triggered_on_component_change(self): - + def test_GIVEN_reflection_angle_WHEN_set_readback_on_component_THEN_call_back_triggered_on_component_change( + self, + ): sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 10, 90)) angle = 3.0 beam_angle = 1.0 @@ -740,12 +917,15 @@ def test_GIVEN_reflection_angle_WHEN_set_readback_on_component_THEN_call_back_tr angle_parameter = AxisParameter("param", sample, ChangeAxis.ANGLE) listener = Mock() angle_parameter.add_listener(ParameterReadbackUpdate, listener) - sample.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(angle, None, None)) - - listener.assert_called_with(ParameterReadbackUpdate(angle-beam_angle, None, None)) + sample.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(angle, None, None) + ) - def test_GIVEN_reflection_angle_on_tilting_component_WHEN_set_readback_on_component_THEN_call_back_triggered_on_component_change(self): + listener.assert_called_with(ParameterReadbackUpdate(angle - beam_angle, None, None)) + def test_GIVEN_reflection_angle_on_tilting_component_WHEN_set_readback_on_component_THEN_call_back_triggered_on_component_change( + self, + ): sample = TiltingComponent("sample", setup=PositionAndAngle(0, 10, 90)) angle = 3.0 beam_angle = 1.0 @@ -753,14 +933,19 @@ def test_GIVEN_reflection_angle_on_tilting_component_WHEN_set_readback_on_compon angle_parameter = AxisParameter("param", sample, ChangeAxis.ANGLE) listener = Mock() angle_parameter.add_listener(ParameterReadbackUpdate, listener) - sample.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(angle, None, None)) + sample.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(angle, None, None) + ) - listener.assert_called_with(ParameterReadbackUpdate(angle-beam_angle, None, None)) - - def test_GIVEN_component_in_beam_WHEN_set_readback_on_component_THEN_call_back_triggered_on_component_change(self): + listener.assert_called_with(ParameterReadbackUpdate(angle - beam_angle, None, None)) + def test_GIVEN_component_in_beam_WHEN_set_readback_on_component_THEN_call_back_triggered_on_component_change( + self, + ): sample = ReflectingComponent("sample", setup=PositionAndAngle(0, 10, 90)) - sample.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(0, None, None)) + sample.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(0, None, None) + ) state = True displacement_parameter = InBeamParameter("param", sample) @@ -771,7 +956,6 @@ def test_GIVEN_component_in_beam_WHEN_set_readback_on_component_THEN_call_back_t listener.assert_called_with(ParameterReadbackUpdate(state, None, None)) def test_GIVEN_theta_WHEN_no_next_component_THEN_value_is_nan(self): - sample = ThetaComponent("sample", setup=PositionAndAngle(0, 10, 90)) sample.beam_path_rbv.set_incoming_beam(PositionAndAngle(0, 0, 45)) theta = AxisParameter("param", sample, ChangeAxis.ANGLE) @@ -781,7 +965,6 @@ def test_GIVEN_theta_WHEN_no_next_component_THEN_value_is_nan(self): assert_that(isnan(result), is_(True), "Should be nan because there is no next component") def test_GIVEN_theta_with_45_deg_beam_WHEN_next_component_is_along_beam_THEN_value_is_0(self): - # Given beam_before_and_after_sample = PositionAndAngle(0, 0, 45) @@ -801,7 +984,6 @@ def test_GIVEN_theta_with_45_deg_beam_WHEN_next_component_is_along_beam_THEN_val assert_that(result, is_(0)) def test_GIVEN_theta_with_0_deg_beam_WHEN_next_component_is_at_45_THEN_value_is_22_5(self): - height_above_beam = 10 expected_theta = 22.5 s3 = Component("s3", setup=PositionAndAngle(height_above_beam, 10, 90)) @@ -818,7 +1000,9 @@ def test_GIVEN_theta_with_0_deg_beam_WHEN_next_component_is_at_45_THEN_value_is_ assert_that(result, is_(expected_theta)) - def test_GIVEN_position_parameter_WHEN_updating_displacement_with_alarms_on_component_THEN_parameter_is_in_alarm_and_propagates_alarm(self): + def test_GIVEN_position_parameter_WHEN_updating_displacement_with_alarms_on_component_THEN_parameter_is_in_alarm_and_propagates_alarm( + self, + ): component = Component("component", setup=PositionAndAngle(0, 10, 90)) new_displacement = 1.0 alarm_severity = 1 @@ -827,15 +1011,23 @@ def test_GIVEN_position_parameter_WHEN_updating_displacement_with_alarms_on_comp listener = Mock() displacement_parameter.add_listener(ParameterReadbackUpdate, listener) - component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(new_displacement, alarm_severity, alarm_status)) + component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(new_displacement, alarm_severity, alarm_status) + ) - listener.assert_called_with(ParameterReadbackUpdate(new_displacement, alarm_severity, alarm_status)) + listener.assert_called_with( + ParameterReadbackUpdate(new_displacement, alarm_severity, alarm_status) + ) self.assertEqual(displacement_parameter.alarm_severity, alarm_severity) self.assertEqual(displacement_parameter.alarm_status, alarm_status) - def test_GIVEN_position_parameter_WHEN_updating_angle_with_alarms_on_component_THEN_parameter_value_and_alarms_are_unchanged(self): + def test_GIVEN_position_parameter_WHEN_updating_angle_with_alarms_on_component_THEN_parameter_value_and_alarms_are_unchanged( + self, + ): component = ReflectingComponent("component", setup=PositionAndAngle(0, 10, 90)) - component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(0.0, None, None)) + component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(0.0, None, None) + ) new_displacement = 1.0 alarm_severity = 1 alarm_status = 2 @@ -843,13 +1035,17 @@ def test_GIVEN_position_parameter_WHEN_updating_angle_with_alarms_on_component_T listener = Mock() displacement_parameter.add_listener(ParameterReadbackUpdate, listener) - component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(new_displacement, alarm_severity, alarm_status)) + component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(new_displacement, alarm_severity, alarm_status) + ) listener.assert_called_once_with(ParameterReadbackUpdate(0.0, None, None)) self.assertEqual(displacement_parameter.alarm_severity, None) self.assertEqual(displacement_parameter.alarm_status, None) - def test_GIVEN_angle_parameter_WHEN_updating_angle_with_alarms_on_component_THEN_parameter_is_in_alarm_and_propagates_alarm(self): + def test_GIVEN_angle_parameter_WHEN_updating_angle_with_alarms_on_component_THEN_parameter_is_in_alarm_and_propagates_alarm( + self, + ): component = ReflectingComponent("component", setup=PositionAndAngle(0, 10, 90)) new_angle = 1.0 alarm_severity = 1 @@ -858,16 +1054,21 @@ def test_GIVEN_angle_parameter_WHEN_updating_angle_with_alarms_on_component_THEN listener = Mock() angle_parameter.add_listener(ParameterReadbackUpdate, listener) - component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(new_angle, alarm_severity, alarm_status)) + component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(new_angle, alarm_severity, alarm_status) + ) listener.assert_called_with(ParameterReadbackUpdate(True, alarm_severity, alarm_status)) self.assertEqual(angle_parameter.alarm_severity, alarm_severity) self.assertEqual(angle_parameter.alarm_status, alarm_status) - def test_GIVEN_angle_parameter_WHEN_updating_displacement_with_alarms_on_component_THEN_parameter_value_and_alarms_are_unchanged(self): + def test_GIVEN_angle_parameter_WHEN_updating_displacement_with_alarms_on_component_THEN_parameter_value_and_alarms_are_unchanged( + self, + ): component = ReflectingComponent("component", setup=PositionAndAngle(0, 10, 90)) component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement( - CorrectedReadbackUpdate(0, None, None)) + CorrectedReadbackUpdate(0, None, None) + ) new_angle = 1.0 alarm_severity = 1 alarm_status = 2 @@ -875,13 +1076,17 @@ def test_GIVEN_angle_parameter_WHEN_updating_displacement_with_alarms_on_compone listener = Mock() angle_parameter.add_listener(ParameterReadbackUpdate, listener) - component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(new_angle, alarm_severity, alarm_status)) + component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(new_angle, alarm_severity, alarm_status) + ) listener.assert_called_with(ParameterReadbackUpdate(0.0, None, None)) self.assertEqual(angle_parameter.alarm_severity, None) self.assertEqual(angle_parameter.alarm_status, None) - def test_GIVEN_inbeam_parameter_WHEN_updating_displacement_with_alarms_on_component_THEN_parameter_is_in_alarm_and_propagates_alarm(self): + def test_GIVEN_inbeam_parameter_WHEN_updating_displacement_with_alarms_on_component_THEN_parameter_is_in_alarm_and_propagates_alarm( + self, + ): component = ReflectingComponent("component", setup=PositionAndAngle(0, 10, 90)) new_value = 1.0 alarm_severity = 1 @@ -891,7 +1096,9 @@ def test_GIVEN_inbeam_parameter_WHEN_updating_displacement_with_alarms_on_compon in_beam_parameter.add_listener(ParameterReadbackUpdate, listener) component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 - component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(new_value, alarm_severity, alarm_status)) + component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(new_value, alarm_severity, alarm_status) + ) listener.assert_called_with(ParameterReadbackUpdate(True, alarm_severity, alarm_status)) # once for beam path update and once for physical move update @@ -899,9 +1106,13 @@ def test_GIVEN_inbeam_parameter_WHEN_updating_displacement_with_alarms_on_compon self.assertEqual(in_beam_parameter.alarm_severity, alarm_severity) self.assertEqual(in_beam_parameter.alarm_status, alarm_status) - def test_GIVEN_inbeam_parameter_WHEN_updating_angle_with_alarms_on_component_THEN_parameter_value_and_alarms_are_unchanged(self): + def test_GIVEN_inbeam_parameter_WHEN_updating_angle_with_alarms_on_component_THEN_parameter_value_and_alarms_are_unchanged( + self, + ): component = ReflectingComponent("component", setup=PositionAndAngle(0, 10, 90)) - component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(0, None, None)) + component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(0, None, None) + ) new_angle = 1.0 alarm_severity = 1 alarm_status = 2 @@ -909,13 +1120,17 @@ def test_GIVEN_inbeam_parameter_WHEN_updating_angle_with_alarms_on_component_THE listener = Mock() in_beam_parameter.add_listener(ParameterReadbackUpdate, listener) - component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(new_angle, alarm_severity, alarm_status)) + component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(new_angle, alarm_severity, alarm_status) + ) listener.assert_called_once_with(ParameterReadbackUpdate(True, None, None)) self.assertEqual(in_beam_parameter.alarm_severity, None) self.assertEqual(in_beam_parameter.alarm_status, None) - def test_GIVEN_direct_parameter_WHEN_updating_value_with_alarm_on_pv_wrapper_THEN_parameter_is_in_alarm_and_propagates_alarm(self): + def test_GIVEN_direct_parameter_WHEN_updating_value_with_alarm_on_pv_wrapper_THEN_parameter_is_in_alarm_and_propagates_alarm( + self, + ): pv_wrapper = create_mock_axis("s1vg", 0.0, 1) new_value = 1.0 alarm_severity = 1 @@ -926,14 +1141,17 @@ def test_GIVEN_direct_parameter_WHEN_updating_value_with_alarm_on_pv_wrapper_THE pv_wrapper.trigger_listeners(ReadbackUpdate(new_value, alarm_severity, alarm_status)) - listener.assert_called_once_with(ParameterReadbackUpdate(new_value, alarm_severity, alarm_status)) + listener.assert_called_once_with( + ParameterReadbackUpdate(new_value, alarm_severity, alarm_status) + ) self.assertEqual(parameter.alarm_severity, alarm_severity) self.assertEqual(parameter.alarm_status, alarm_status) class TestBeamlineThetaComponentWhenDisabled(unittest.TestCase): - - def test_GIVEN_theta_with_0_deg_beam_and_next_component_in_beam_but_disabled_WHEN_set_theta_to_45_THEN_component_sp_is_at_45_degrees(self): + def test_GIVEN_theta_with_0_deg_beam_and_next_component_in_beam_but_disabled_WHEN_set_theta_to_45_THEN_component_sp_is_at_45_degrees( + self, + ): sample = ThetaComponent("sample", setup=PositionAndAngle(0, 0, 90)) sample.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) @@ -947,9 +1165,13 @@ def test_GIVEN_theta_with_0_deg_beam_and_next_component_in_beam_but_disabled_WHE theta.sp = 22.5 result = detector.beam_path_set_point.axis[ChangeAxis.POSITION].get_relative_to_beam() - assert_that(result, is_(close_to(-10.0, 1e-6))) # the beam is now above the current position. The beam line parameter needs to be triggered to make is move + assert_that( + result, is_(close_to(-10.0, 1e-6)) + ) # the beam is now above the current position. The beam line parameter needs to be triggered to make is move - def test_GIVEN_theta_with_0_deg_beam_and_next_component_in_beam_is_not_disabled_WHEN_set_theta_to_45_THEN_component_sp_is_not_altered(self): + def test_GIVEN_theta_with_0_deg_beam_and_next_component_in_beam_is_not_disabled_WHEN_set_theta_to_45_THEN_component_sp_is_not_altered( + self, + ): # this calculation will be done via the beamline not the forced copy of output beam sample = ThetaComponent("sample", setup=PositionAndAngle(0, 0, 90)) sample.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) @@ -965,7 +1187,9 @@ def test_GIVEN_theta_with_0_deg_beam_and_next_component_in_beam_is_not_disabled_ assert_that(result, is_(close_to(0, 1e-6))) - def test_GIVEN_theta_with_0_deg_beam_and_next_two_component_in_beam_and_are_disabled_WHEN_set_theta_to_45_THEN_first_component_altered_second_one_not(self): + def test_GIVEN_theta_with_0_deg_beam_and_next_two_component_in_beam_and_are_disabled_WHEN_set_theta_to_45_THEN_first_component_altered_second_one_not( + self, + ): # this calculation will be done via the beamline not the forced copy of output beam sample = ThetaComponent("sample", setup=PositionAndAngle(0, 0, 90)) sample.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) @@ -986,7 +1210,9 @@ def test_GIVEN_theta_with_0_deg_beam_and_next_two_component_in_beam_and_are_disa assert_that(result1, is_(close_to(-10, 1e-6))) assert_that(result2, is_(close_to(0, 1e-6))) - def test_GIVEN_theta_with_0_deg_beam_and_next_first_component_out_of_beam_second_in_beam_and_are_disabled_WHEN_set_theta_to_45_THEN_first_component_not_altered_second_one_is(self): + def test_GIVEN_theta_with_0_deg_beam_and_next_first_component_out_of_beam_second_in_beam_and_are_disabled_WHEN_set_theta_to_45_THEN_first_component_not_altered_second_one_is( + self, + ): # this calculation will be done via the beamline not the forced copy of output beam sample = ThetaComponent("sample", setup=PositionAndAngle(0, 0, 90)) sample.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) @@ -1011,11 +1237,12 @@ def test_GIVEN_theta_with_0_deg_beam_and_next_first_component_out_of_beam_second class TestInitSetpoints(unittest.TestCase): - def setUp(self): setup_autosave({"param_float": 0.1}, {"param_bool": True}) self.component = Component("component", setup=PositionAndAngle(0, 1, 90)) - self.angle_component = TiltingComponent("angle_component", setup=PositionAndAngle(0, 10, 90)) + self.angle_component = TiltingComponent( + "angle_component", setup=PositionAndAngle(0, 10, 90) + ) self.jaws = create_mock_axis("s1vg", 0.2, 1) def test_GIVEN_autosave_is_not_set_WHEN_creating_param_THEN_defaults_to_false(self): @@ -1032,7 +1259,9 @@ def test_GIVEN_autosave_is_false_THEN_parameter_sp_is_none(self): self.assertIsNone(param.sp) self.assertIsNone(param.sp_rbv) - def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_tracking_displacement_parameter_THEN_sp_is_autosave_value(self): + def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_tracking_displacement_parameter_THEN_sp_is_autosave_value( + self, + ): expected = 0.1 param_name = "param_float" @@ -1041,7 +1270,9 @@ def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_tracking self.assertEqual(expected, param.sp) self.assertEqual(expected, param.sp_rbv) - def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_tracking_displacement_parameter_THEN_sp_is_none(self): + def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_tracking_displacement_parameter_THEN_sp_is_none( + self, + ): param_name = "param_not_in_file" param = AxisParameter(param_name, self.component, ChangeAxis.POSITION, autosave=True) @@ -1049,7 +1280,9 @@ def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_ self.assertIsNone(param.sp) self.assertIsNone(param.sp_rbv) - def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_tracking_displacement_parameter_THEN_sp_is_none(self): + def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_tracking_displacement_parameter_THEN_sp_is_none( + self, + ): param_name = "param_bool" param = AxisParameter(param_name, self.component, ChangeAxis.POSITION, autosave=True) @@ -1057,7 +1290,9 @@ def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_tracking_dis self.assertIsNone(param.sp) self.assertIsNone(param.sp_rbv) - def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_angle_parameter_THEN_sp_is_autosave_value(self): + def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_angle_parameter_THEN_sp_is_autosave_value( + self, + ): expected = 0.1 param_name = "param_float" @@ -1066,7 +1301,9 @@ def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_angle_pa self.assertEqual(expected, param.sp) self.assertEqual(expected, param.sp_rbv) - def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_angle_parameter_THEN_sp_is_none(self): + def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_angle_parameter_THEN_sp_is_none( + self, + ): param_name = "param_not_in_file" param = AxisParameter(param_name, self.angle_component, ChangeAxis.ANGLE, autosave=True) @@ -1074,7 +1311,9 @@ def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_ self.assertIsNone(param.sp) self.assertIsNone(param.sp_rbv) - def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_angle_parameter_THEN_sp_is_none(self): + def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_angle_parameter_THEN_sp_is_none( + self, + ): param_name = "param_bool" param = AxisParameter(param_name, self.angle_component, ChangeAxis.ANGLE, autosave=True) @@ -1082,7 +1321,9 @@ def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_angle_parame self.assertIsNone(param.sp) self.assertIsNone(param.sp_rbv) - def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_in_beam_parameter_THEN_sp_is_autosave_value(self): + def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_in_beam_parameter_THEN_sp_is_autosave_value( + self, + ): expected = True param_name = "param_bool" @@ -1091,7 +1332,9 @@ def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_in_beam_ self.assertEqual(expected, param.sp) self.assertEqual(expected, param.sp_rbv) - def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_in_beam_parameter_THEN_sp_is_none(self): + def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_in_beam_parameter_THEN_sp_is_none( + self, + ): param_name = "param_not_in_file" param = InBeamParameter(param_name, self.angle_component, autosave=True) @@ -1099,7 +1342,9 @@ def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_ self.assertIsNone(param.sp) self.assertIsNone(param.sp_rbv) - def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_in_beam_parameter_THEN_sp_is_none(self): + def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_in_beam_parameter_THEN_sp_is_none( + self, + ): param_name = "param_float" param = InBeamParameter(param_name, self.angle_component, autosave=True) @@ -1107,7 +1352,9 @@ def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_in_beam_para self.assertIsNone(param.sp) self.assertIsNone(param.sp_rbv) - def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_direct_parameter_THEN_sp_is_autosave_value(self): + def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_direct_parameter_THEN_sp_is_autosave_value( + self, + ): expected = 0.1 param_name = "param_float" @@ -1116,7 +1363,9 @@ def test_GIVEN_autosave_is_true_and_autosave_value_exists_WHEN_creating_direct_p self.assertEqual(expected, param.sp) self.assertEqual(expected, param.sp_rbv) - def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_direct_parameter_THEN_sp_is_taken_from_motor_instead(self): + def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_direct_parameter_THEN_sp_is_taken_from_motor_instead( + self, + ): expected = 0.2 param_name = "param_not_in_file" @@ -1125,7 +1374,9 @@ def test_GIVEN_autosave_is_true_and_autosave_value_does_not_exist_WHEN_creating_ self.assertEqual(expected, param.sp) self.assertEqual(expected, param.sp_rbv) - def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_direct_parameter_THEN_sp_is_taken_from_motor_instead(self): + def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_direct_parameter_THEN_sp_is_taken_from_motor_instead( + self, + ): expected = 0.2 param_name = "param_bool" @@ -1134,7 +1385,9 @@ def test_GIVEN_autosave_parameter_value_of_wrong_type_WHEN_creating_direct_param self.assertEqual(expected, param.sp) self.assertEqual(expected, param.sp_rbv) - def test_GIVEN_autosave_is_true_WHEN_initialising_tracking_position_THEN_beam_calc_caches_autosaved_offset(self): + def test_GIVEN_autosave_is_true_WHEN_initialising_tracking_position_THEN_beam_calc_caches_autosaved_offset( + self, + ): expected = 0.1 param_name = "param_float" @@ -1143,7 +1396,9 @@ def test_GIVEN_autosave_is_true_WHEN_initialising_tracking_position_THEN_beam_ca self.assertEqual(expected, actual) - def test_GIVEN_autosave_is_false_WHEN_initialising_tracking_position_THEN_beam_calc_has_no_autosaved_offset(self): + def test_GIVEN_autosave_is_false_WHEN_initialising_tracking_position_THEN_beam_calc_has_no_autosaved_offset( + self, + ): param_name = "param_float" param = AxisParameter(param_name, self.component, ChangeAxis.POSITION, autosave=False) @@ -1153,8 +1408,9 @@ def test_GIVEN_autosave_is_false_WHEN_initialising_tracking_position_THEN_beam_c class TestMultiChoiceParameter(unittest.TestCase): - def test_GIVEN_enum_parameter_WHEN_set_THEN_sp_readback_is_value_and_readback_is_triggered(self): - + def test_GIVEN_enum_parameter_WHEN_set_THEN_sp_readback_is_value_and_readback_is_triggered( + self, + ): opt1 = "opt1" opt2 = "opt2" param = EnumParameter("name", options=[opt1, opt2]) @@ -1170,7 +1426,6 @@ def test_GIVEN_enum_parameter_WHEN_set_THEN_sp_readback_is_value_and_readback_is assert_that(args[0][0].value, is_(opt1)) def test_GIVEN_enum_parameter_WHEN_set_THEN_readback_is_same_as_sp_rbv(self): - opt1 = "opt1" opt2 = "opt2" param = EnumParameter("name", options=[opt1, opt2]) @@ -1194,7 +1449,6 @@ def test_GIVEN_enum_parameter_WHEN_autosaved_THEN_autosaved_value_is_sp_rbv(self @patch("ReflectometryServer.parameters.param_string_autosave") def test_GIVEN_enum_parameter_with_no_options_WHEN_validate_THEN_error(self, autosave): - autosave.read_parameter.return_value = None param = EnumParameter("name", options=[]) @@ -1204,7 +1458,6 @@ def test_GIVEN_enum_parameter_with_no_options_WHEN_validate_THEN_error(self, aut @patch("ReflectometryServer.parameters.param_string_autosave") def test_GIVEN_enum_parameter_with_duplicate_options_WHEN_validate_THEN_error(self, autosave): - autosave.read_parameter.return_value = None param = EnumParameter("name", options=["dup", "dup"]) @@ -1213,7 +1466,6 @@ def test_GIVEN_enum_parameter_with_duplicate_options_WHEN_validate_THEN_error(se assert_that(errors, has_length(1)) def test_GIVEN_enum_parameter_WHEN_set_non_option_THEN_error(self): - opt1 = "opt1" opt2 = "opt2" param = EnumParameter("name", options=[opt1, opt2]) @@ -1226,15 +1478,19 @@ def test_GIVEN_enum_parameter_WHEN_set_non_option_THEN_error(self): class TestCustomFunctionCall(unittest.TestCase): - def setUp(self) -> None: STATUS_MANAGER.clear_all() - def test_GIVEN_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values(self): + def test_GIVEN_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values( + self, + ): mock_func = Mock() component = Component("comp", PositionAndAngle(0, 0, 0)) expected_original_sp = False - with patch('ReflectometryServer.parameters.param_bool_autosave.read_parameter', new=Mock(return_value=expected_original_sp)): + with patch( + "ReflectometryServer.parameters.param_bool_autosave.read_parameter", + new=Mock(return_value=expected_original_sp), + ): param = InBeamParameter("myname", component, custom_function=mock_func, autosave=True) expected_sp = True @@ -1245,8 +1501,9 @@ def test_GIVEN_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_a mock_func.assert_called_with(expected_sp, expected_original_sp) - def test_GIVEN_Parameter_with_no_function_WHEN_move_THEN_no_custom_function_is_called_and_there_is_no_error(self): - + def test_GIVEN_Parameter_with_no_function_WHEN_move_THEN_no_custom_function_is_called_and_there_is_no_error( + self, + ): component = Component("comp", PositionAndAngle(0, 0, 0)) param = InBeamParameter("myname", component, autosave=True) @@ -1257,11 +1514,15 @@ def test_GIVEN_Parameter_with_no_function_WHEN_move_THEN_no_custom_function_is_c assert_that(STATUS_MANAGER.error_log, is_("")) - def test_GIVEN_parameter_with_function_that_takes_time_WHEN_move_THEN_function_returns_immediately(self): - self.myval=None + def test_GIVEN_parameter_with_function_that_takes_time_WHEN_move_THEN_function_returns_immediately( + self, + ): + self.myval = None + def my_function(*args): sleep(1) - self.myval=1 + self.myval = 1 + component = Component("comp", PositionAndAngle(0, 0, 0)) param = InBeamParameter("myname", component, custom_function=my_function) @@ -1269,8 +1530,11 @@ def my_function(*args): assert_that(self.myval, is_(None)) - def test_GIVEN_parameter_with_function_that_causes_an_exception_WHEN_move_THEN_exception_is_put_in_error_log(self): + def test_GIVEN_parameter_with_function_that_causes_an_exception_WHEN_move_THEN_exception_is_put_in_error_log( + self, + ): expected_text = "Oh Dear" + def my_function(*args): raise ValueError(expected_text) @@ -1285,8 +1549,11 @@ def my_function(*args): assert_that(STATUS_MANAGER.error_log, contains_string(expected_text)) assert_that(STATUS_MANAGER.error_log, contains_string(param_name)) - def test_GIVEN_parameter_with_function_returns_a_string_WHEN_move_THEN_string_is_put_in_log(self): + def test_GIVEN_parameter_with_function_returns_a_string_WHEN_move_THEN_string_is_put_in_log( + self, + ): expected_text = "information!!" + def my_function(*args): return expected_text @@ -1300,12 +1567,19 @@ def my_function(*args): assert_that(STATUS_MANAGER.error_log, contains_string(expected_text)) - def test_GIVEN_Axis_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values(self): + def test_GIVEN_Axis_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values( + self, + ): mock_func = Mock() component = Component("comp", PositionAndAngle(0, 0, 0)) expected_original_sp = 0.1 - with patch('ReflectometryServer.parameters.param_float_autosave.read_parameter', new=Mock(return_value=expected_original_sp)): - param = AxisParameter("myname", component, ChangeAxis.HEIGHT, custom_function=mock_func, autosave=True) + with patch( + "ReflectometryServer.parameters.param_float_autosave.read_parameter", + new=Mock(return_value=expected_original_sp), + ): + param = AxisParameter( + "myname", component, ChangeAxis.HEIGHT, custom_function=mock_func, autosave=True + ) expected_sp = 0.3 @@ -1315,11 +1589,16 @@ def test_GIVEN_Axis_Parameter_WHEN_move_THEN_custom_function_is_called_with_move mock_func.assert_called_with(expected_sp, expected_original_sp) - def test_GIVEN_Direct_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values(self): + def test_GIVEN_Direct_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values( + self, + ): mock_func = Mock() expected_original_sp = 0.1 mock_axis = create_mock_axis("axis", expected_original_sp, 1) - with patch('ReflectometryServer.parameters.param_float_autosave.read_parameter', new=Mock(return_value=expected_original_sp)): + with patch( + "ReflectometryServer.parameters.param_float_autosave.read_parameter", + new=Mock(return_value=expected_original_sp), + ): param = DirectParameter("myname", mock_axis, custom_function=mock_func, autosave=True) mock_axis.sp = expected_original_sp @@ -1331,14 +1610,20 @@ def test_GIVEN_Direct_Parameter_WHEN_move_THEN_custom_function_is_called_with_mo mock_func.assert_called_with(expected_sp, expected_original_sp) - def test_GIVEN_Direct_Parameter_WHEN_move_THEN_engineering_correction_is_applied_to_axis_sp(self): + def test_GIVEN_Direct_Parameter_WHEN_move_THEN_engineering_correction_is_applied_to_axis_sp( + self, + ): offset = 2 correction = ConstantCorrection(2) expected_original_sp = 0.1 mock_axis = create_mock_axis("axis", expected_original_sp, 1) - with patch('ReflectometryServer.parameters.param_float_autosave.read_parameter', - new=Mock(return_value=expected_original_sp)): - param = DirectParameter("myname", mock_axis, engineering_correction=correction, autosave=True) + with patch( + "ReflectometryServer.parameters.param_float_autosave.read_parameter", + new=Mock(return_value=expected_original_sp), + ): + param = DirectParameter( + "myname", mock_axis, engineering_correction=correction, autosave=True + ) mock_axis.sp = expected_original_sp expected_sp = 0.3 @@ -1347,22 +1632,33 @@ def test_GIVEN_Direct_Parameter_WHEN_move_THEN_engineering_correction_is_applied sleep(0.1) # wait for thread to run assert_that(mock_axis.sp, is_(close_to(expected_sp + offset, DEFAULT_TEST_TOLERANCE))) - def test_GIVEN_Direct_Parameter_WHEN_constant_correction_applied_THEN_engineering_correction_is_applied_to_init_sp(self): + def test_GIVEN_Direct_Parameter_WHEN_constant_correction_applied_THEN_engineering_correction_is_applied_to_init_sp( + self, + ): offset = 2 correction = ConstantCorrection(2) initial_pos = 0.1 mock_axis = create_mock_axis("axis", initial_pos, 1) - with patch('ReflectometryServer.parameters.param_float_autosave.read_parameter', - new=Mock(return_value=initial_pos)): - param = DirectParameter("myname", mock_axis, engineering_correction=correction, autosave=True) + with patch( + "ReflectometryServer.parameters.param_float_autosave.read_parameter", + new=Mock(return_value=initial_pos), + ): + param = DirectParameter( + "myname", mock_axis, engineering_correction=correction, autosave=True + ) assert_that(mock_axis.rbv, is_(close_to(param.sp - offset, DEFAULT_TEST_TOLERANCE))) - def test_GIVEN_Slit_Gap_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values(self): + def test_GIVEN_Slit_Gap_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values( + self, + ): mock_func = Mock() expected_original_sp = 0.1 mock_axis = create_mock_axis("axis", expected_original_sp, 1) - with patch('ReflectometryServer.parameters.param_float_autosave.read_parameter', new=Mock(return_value=expected_original_sp)): + with patch( + "ReflectometryServer.parameters.param_float_autosave.read_parameter", + new=Mock(return_value=expected_original_sp), + ): param = SlitGapParameter("myname", mock_axis, custom_function=mock_func, autosave=True) mock_axis.sp = expected_original_sp @@ -1374,11 +1670,16 @@ def test_GIVEN_Slit_Gap_Parameter_WHEN_move_THEN_custom_function_is_called_with_ mock_func.assert_called_with(expected_sp, expected_original_sp) - def test_GIVEN_Enum_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values(self): + def test_GIVEN_Enum_Parameter_WHEN_move_THEN_custom_function_is_called_with_move_to_and_move_from_values( + self, + ): mock_func = Mock() expected_original_sp = "orig" mock_axis = create_mock_axis("axis", expected_original_sp, 1) - with patch('ReflectometryServer.parameters.param_string_autosave.read_parameter', new=Mock(return_value=expected_original_sp)): + with patch( + "ReflectometryServer.parameters.param_string_autosave.read_parameter", + new=Mock(return_value=expected_original_sp), + ): param = EnumParameter("myname", ["orig", "new"], custom_function=mock_func) mock_axis.sp = expected_original_sp @@ -1392,26 +1693,37 @@ def test_GIVEN_Enum_Parameter_WHEN_move_THEN_custom_function_is_called_with_move class TestReadonlyParameters(unittest.TestCase): - def setUp(self): # define expected values self.sm_z = 10 self.sm_to_sa_distance = 10 self.sm_angle_to_set = 22.5 self.updated_angle = 2 * self.sm_angle_to_set - self.updated_y = self.sm_to_sa_distance # height equal to dist as reflected angle at sm is 45 deg + self.updated_y = ( + self.sm_to_sa_distance + ) # height equal to dist as reflected angle at sm is 45 deg # Set up beamline - super_mirror = ReflectingComponent("super mirror", PositionAndAngle(z=self.sm_z, y=0, angle=90)) + super_mirror = ReflectingComponent( + "super mirror", PositionAndAngle(z=self.sm_z, y=0, angle=90) + ) self.sm_angle = AxisParameter("sm_angle", super_mirror, ChangeAxis.ANGLE) - sample = ReflectingComponent("sample", PositionAndAngle(z=self.sm_z + self.sm_to_sa_distance, y=0, angle=90)) - self.displacement_height = AxisParameter("displacement_height", sample, ChangeAxis.DISPLACEMENT_POSITION) - self.displacement_angle = AxisParameter("displacement_angle", sample, ChangeAxis.DISPLACEMENT_ANGLE) + sample = ReflectingComponent( + "sample", PositionAndAngle(z=self.sm_z + self.sm_to_sa_distance, y=0, angle=90) + ) + self.displacement_height = AxisParameter( + "displacement_height", sample, ChangeAxis.DISPLACEMENT_POSITION + ) + self.displacement_angle = AxisParameter( + "displacement_angle", sample, ChangeAxis.DISPLACEMENT_ANGLE + ) params = [self.sm_angle, self.displacement_height, self.displacement_angle] mode = BeamlineMode("mode name", [param.name for param in params]) self.beamline = Beamline([super_mirror, sample], params, [], [mode]) - def test_GIVEN_incoming_beam_has_changed_but_not_moved_THEN_outgoing_beam_param_sp_rbv_is_not_updated(self): + def test_GIVEN_incoming_beam_has_changed_but_not_moved_THEN_outgoing_beam_param_sp_rbv_is_not_updated( + self, + ): assert_that(self.displacement_angle.sp, is_(0)) assert_that(self.displacement_height.sp, is_(0)) expected_angle = 0 @@ -1424,7 +1736,9 @@ def test_GIVEN_incoming_beam_has_changed_but_not_moved_THEN_outgoing_beam_param_ assert_that(actual_y, is_(close_to(expected_y, DEFAULT_TEST_TOLERANCE))) assert_that(actual_angle, is_(expected_angle)) - def test_GIVEN_incoming_beam_has_changed_WHEN_moving_THEN_outgoing_beam_param_sp_is_updated(self): + def test_GIVEN_incoming_beam_has_changed_WHEN_moving_THEN_outgoing_beam_param_sp_is_updated( + self, + ): assert_that(self.displacement_angle.sp, is_(0)) assert_that(self.displacement_height.sp, is_(0)) expected_angle = self.updated_angle @@ -1437,7 +1751,9 @@ def test_GIVEN_incoming_beam_has_changed_WHEN_moving_THEN_outgoing_beam_param_sp assert_that(actual_y, is_(close_to(expected_y, DEFAULT_TEST_TOLERANCE))) assert_that(actual_angle, is_(expected_angle)) - def test_GIVEN_incoming_beam_has_changed_WHEN_moving_THEN_outgoing_beam_param_sp_rbv_is_updated(self): + def test_GIVEN_incoming_beam_has_changed_WHEN_moving_THEN_outgoing_beam_param_sp_rbv_is_updated( + self, + ): initial_value = 0 assert_that(self.displacement_angle.sp, is_(initial_value)) assert_that(self.displacement_height.sp, is_(initial_value)) @@ -1452,5 +1768,5 @@ def test_GIVEN_incoming_beam_has_changed_WHEN_moving_THEN_outgoing_beam_param_sp assert_that(actual_angle, is_(expected_angle)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_components.py b/ReflectometryServer/test_modules/test_components.py index 856e7dc2..f4b6bdc1 100644 --- a/ReflectometryServer/test_modules/test_components.py +++ b/ReflectometryServer/test_modules/test_components.py @@ -1,27 +1,41 @@ import unittest - -from math import tan, radians, isnan +from math import isnan, radians, tan from CaChannel._ca import AlarmSeverity from hamcrest import * -from mock import Mock, patch, call +from mock import Mock, call, patch from parameterized import parameterized, parameterized_class +from server_common.channel_access import AlarmStatus from ReflectometryServer import AxisParameter +from ReflectometryServer.axis import DefineValueAsEvent, PhysicalMoveUpdate from ReflectometryServer.beam_path_calc import BeamPathUpdate -from ReflectometryServer.axis import PhysicalMoveUpdate, DefineValueAsEvent -from ReflectometryServer.components import Component, ReflectingComponent, TiltingComponent, ThetaComponent -from ReflectometryServer.geometry import Position, PositionAndAngle, ChangeAxis +from ReflectometryServer.components import ( + Component, + ReflectingComponent, + ThetaComponent, + TiltingComponent, +) +from ReflectometryServer.geometry import ChangeAxis, Position, PositionAndAngle from ReflectometryServer.ioc_driver import CorrectedReadbackUpdate, IocDriver -from ReflectometryServer.test_modules.data_mother import create_mock_axis, get_standard_bench, ANGLE_OF_BENCH, \ - BENCH_MIN_ANGLE, BENCH_MAX_ANGLE -from ReflectometryServer.test_modules.utils import position_and_angle, position, DEFAULT_TEST_TOLERANCE -from server_common.channel_access import AlarmStatus +from ReflectometryServer.test_modules.data_mother import ( + ANGLE_OF_BENCH, + BENCH_MAX_ANGLE, + BENCH_MIN_ANGLE, + create_mock_axis, + get_standard_bench, +) +from ReflectometryServer.test_modules.utils import ( + DEFAULT_TEST_TOLERANCE, + position, + position_and_angle, +) class TestComponent(unittest.TestCase): - - def test_GIVEN_jaw_input_beam_is_at_0_deg_and_z0_y0_WHEN_get_beam_out_THEN_beam_output_is_same_as_beam_input(self): + def test_GIVEN_jaw_input_beam_is_at_0_deg_and_z0_y0_WHEN_get_beam_out_THEN_beam_output_is_same_as_beam_input( + self, + ): jaws_z_position = 10 beam_start = PositionAndAngle(y=0, z=0, angle=0) jaws = Component("component", setup=PositionAndAngle(0, jaws_z_position, 90)) @@ -31,7 +45,9 @@ def test_GIVEN_jaw_input_beam_is_at_0_deg_and_z0_y0_WHEN_get_beam_out_THEN_beam_ assert_that(result, is_(position_and_angle(beam_start))) - def test_GIVEN_jaw_at_10_input_beam_is_at_0_deg_and_z0_y0_WHEN_get_position_THEN_z_is_jaw_position_y_is_0(self): + def test_GIVEN_jaw_at_10_input_beam_is_at_0_deg_and_z0_y0_WHEN_get_position_THEN_z_is_jaw_position_y_is_0( + self, + ): jaws_z_position = 10 beam_start = PositionAndAngle(y=0, z=0, angle=0) expected_position = Position(y=0, z=jaws_z_position) @@ -42,11 +58,15 @@ def test_GIVEN_jaw_at_10_input_beam_is_at_0_deg_and_z0_y0_WHEN_get_position_THEN assert_that(result, is_(position(expected_position))) - def test_GIVEN_jaw_at_10_input_beam_is_at_60_deg_and_z0_y0_WHEN_get_position_THEN_z_is_jaw_position_y_is_at_tan_minus_60_times_10(self): + def test_GIVEN_jaw_at_10_input_beam_is_at_60_deg_and_z0_y0_WHEN_get_position_THEN_z_is_jaw_position_y_is_at_tan_minus_60_times_10( + self, + ): jaws_z_position = 10.0 beam_angle = 60.0 beam_start = PositionAndAngle(y=0, z=0, angle=beam_angle) - expected_position = Position(y=tan(radians(beam_angle)) * jaws_z_position, z=jaws_z_position) + expected_position = Position( + y=tan(radians(beam_angle)) * jaws_z_position, z=jaws_z_position + ) jaws = Component("component", setup=PositionAndAngle(0, jaws_z_position, 90)) jaws.beam_path_set_point.set_incoming_beam(beam_start) @@ -54,14 +74,18 @@ def test_GIVEN_jaw_at_10_input_beam_is_at_60_deg_and_z0_y0_WHEN_get_position_THE assert_that(result, is_(position(expected_position))) - def test_GIVEN_jaw_at_10_input_beam_is_at_60_deg_and_z5_y30_WHEN_get_position_THEN_z_is_jaw_position_y_is_at_tan_minus_60_times_distance_between_input_beam_and_component_plus_original_beam_y(self): + def test_GIVEN_jaw_at_10_input_beam_is_at_60_deg_and_z5_y30_WHEN_get_position_THEN_z_is_jaw_position_y_is_at_tan_minus_60_times_distance_between_input_beam_and_component_plus_original_beam_y( + self, + ): distance_between = 5.0 start_z = 5.0 start_y = 30 beam_angle = 60.0 jaws_z_position = distance_between + start_z beam_start = PositionAndAngle(y=start_y, z=start_z, angle=beam_angle) - expected_position = Position(y=tan(radians(beam_angle)) * distance_between + start_y, z=jaws_z_position) + expected_position = Position( + y=tan(radians(beam_angle)) * distance_between + start_y, z=jaws_z_position + ) jaws = Component("component", setup=PositionAndAngle(0, jaws_z_position, 90)) jaws.beam_path_set_point.set_incoming_beam(beam_start) @@ -69,7 +93,9 @@ def test_GIVEN_jaw_at_10_input_beam_is_at_60_deg_and_z5_y30_WHEN_get_position_TH assert_that(result, is_(position(expected_position))) - def test_GIVEN_component_has_offset_WHEN_requesting_intercept_in_mantid_coordinates_THEN_offset_is_ignored_in_result(self): + def test_GIVEN_component_has_offset_WHEN_requesting_intercept_in_mantid_coordinates_THEN_offset_is_ignored_in_result( + self, + ): beam_angle = 45.0 comp_z = 10.0 expected_y = comp_z @@ -83,8 +109,9 @@ def test_GIVEN_component_has_offset_WHEN_requesting_intercept_in_mantid_coordina assert_that(result, is_(position(expected_position))) - def test_GIVEN_component_WHEN_set_relative_position_of_position_THEN_position_axis_has_changed_set(self): - + def test_GIVEN_component_WHEN_set_relative_position_of_position_THEN_position_axis_has_changed_set( + self, + ): comp = Component("component", setup=PositionAndAngle(0, 0, 90)) comp.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) comp.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(1) @@ -95,19 +122,24 @@ def test_GIVEN_component_WHEN_set_relative_position_of_position_THEN_position_ax class TestTiltingJaws(unittest.TestCase): - def test_GIVEN_tilting_jaw_input_beam_is_at_60_deg_WHEN_set_angular_displacement_THEN_beam_no_altered(self): + def test_GIVEN_tilting_jaw_input_beam_is_at_60_deg_WHEN_set_angular_displacement_THEN_beam_no_altered( + self, + ): beam_angle = 60.0 beam_start = PositionAndAngle(y=0, z=0, angle=beam_angle) jaws = TiltingComponent("tilting jaws", setup=PositionAndAngle(0, 20, 90)) jaws.beam_path_set_point.set_incoming_beam(beam_start) - jaws.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(123, None, None)) + jaws.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(123, None, None) + ) result = jaws.beam_path_set_point.get_outgoing_beam() assert_that(result.angle, is_(beam_angle)) - def test_GIVEN_component_WHEN_set_relative_position_of_position_THEN_position_axis_has_changed_set_but_angle_does_not(self): - + def test_GIVEN_component_WHEN_set_relative_position_of_position_THEN_position_axis_has_changed_set_but_angle_does_not( + self, + ): comp = TiltingComponent("component", setup=PositionAndAngle(0, 0, 90)) comp.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) comp.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(1) @@ -118,8 +150,9 @@ def test_GIVEN_component_WHEN_set_relative_position_of_position_THEN_position_ax assert_that(result_pos, is_(True)) assert_that(result_angle, is_(False)) - def test_GIVEN_component_WHEN_set_relative_position_of_angle_THEN_angle_axis_has_changed_set_but_position_does_not(self): - + def test_GIVEN_component_WHEN_set_relative_position_of_angle_THEN_angle_axis_has_changed_set_but_position_does_not( + self, + ): comp = TiltingComponent("component", setup=PositionAndAngle(0, 0, 90)) comp.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) comp.beam_path_set_point.axis[ChangeAxis.ANGLE].set_relative_to_beam(1) @@ -132,15 +165,18 @@ def test_GIVEN_component_WHEN_set_relative_position_of_angle_THEN_angle_axis_has class TestActiveComponents(unittest.TestCase): - - def test_GIVEN_angled_mirror_is_not_in_beam_WHEN_get_beam_out_THEN_outgoing_beam_is_incoming_beam(self): + def test_GIVEN_angled_mirror_is_not_in_beam_WHEN_get_beam_out_THEN_outgoing_beam_is_incoming_beam( + self, + ): mirror_z_position = 10 mirror_angle = 15 beam_start = PositionAndAngle(y=0, z=0, angle=0) expected = beam_start mirror = ReflectingComponent("component", setup=PositionAndAngle(0, mirror_z_position, 90)) - mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(mirror_angle, None, None)) + mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(mirror_angle, None, None) + ) mirror.beam_path_set_point.set_incoming_beam(beam_start) mirror.beam_path_set_point.axis[ChangeAxis.POSITION].park_sequence_count = 1 mirror.beam_path_set_point.axis[ChangeAxis.POSITION].is_in_beam = False @@ -150,39 +186,43 @@ def test_GIVEN_angled_mirror_is_not_in_beam_WHEN_get_beam_out_THEN_outgoing_beam assert_that(result, is_(position_and_angle(expected))) def test_GIVEN_mirror_with_input_beam_at_0_deg_and_z0_y0_WHEN_get_beam_out_THEN_beam_output_z_is_zmirror_y_is_ymirror_angle_is_input_angle_plus_device_angle( - self): + self, + ): mirror_z_position = 10 mirror_angle = 15 beam_start = PositionAndAngle(y=0, z=0, angle=0) expected = PositionAndAngle(y=0, z=mirror_z_position, angle=2 * mirror_angle) mirror = ReflectingComponent("component", setup=PositionAndAngle(0, mirror_z_position, 90)) - mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(mirror_angle, None, None)) + mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(mirror_angle, None, None) + ) mirror.beam_path_set_point.set_incoming_beam(beam_start) result = mirror.beam_path_set_point.get_outgoing_beam() assert_that(result, is_(position_and_angle(expected))) - @parameterized.expand([(-30, 60, 150), - (0, 0, 0), - (30, 30, 30), - (0, 90, 180), - (-40, -30, -20)]) - def test_GIVEN_mirror_with_input_beam_at_WHEN_get_beam_out_THEN_beam_output_correct(self, beam_angle, - mirror_angle, - outgoing_angle): + @parameterized.expand([(-30, 60, 150), (0, 0, 0), (30, 30, 30), (0, 90, 180), (-40, -30, -20)]) + def test_GIVEN_mirror_with_input_beam_at_WHEN_get_beam_out_THEN_beam_output_correct( + self, beam_angle, mirror_angle, outgoing_angle + ): beam_start = PositionAndAngle(y=0, z=0, angle=beam_angle) expected = PositionAndAngle(y=0, z=0, angle=outgoing_angle) mirror = ReflectingComponent("component", setup=PositionAndAngle(0, 0, 90)) - mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(mirror_angle, None, None)) + mirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(mirror_angle, None, None) + ) mirror.beam_path_set_point.set_incoming_beam(beam_start) result = mirror.beam_path_set_point.get_outgoing_beam() - assert_that(result, is_(position_and_angle(expected)), - "beam_angle: {}, mirror_angle: {}".format(beam_angle, mirror_angle)) + assert_that( + result, + is_(position_and_angle(expected)), + "beam_angle: {}, mirror_angle: {}".format(beam_angle, mirror_angle), + ) class TestObservationOfComponentReadback(unittest.TestCase): @@ -207,7 +247,9 @@ def listen_for_value2(self, source): def test_GIVEN_listener_WHEN_readback_changes_THEN_listener_is_informed(self): expected_value = 10 self.component.beam_path_rbv.add_listener(BeamPathUpdate, self.listen_for_value) - self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(1, None, None)) + self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(1, None, None) + ) result = self.component.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement() @@ -218,7 +260,9 @@ def test_GIVEN_two_listeners_WHEN_readback_changes_THEN_listener_is_informed(sel expected_value = 10 self.component.beam_path_rbv.add_listener(BeamPathUpdate, self.listen_for_value) self.component.beam_path_rbv.add_listener(BeamPathUpdate, self.listen_for_value2) - self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(1, None, None)) + self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(1, None, None) + ) result = self.component.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement() @@ -227,7 +271,9 @@ def test_GIVEN_two_listeners_WHEN_readback_changes_THEN_listener_is_informed(sel assert_that(result, expected_value) def test_GIVEN_no_listener_WHEN_readback_changes_THEN_no_listeners_are_informed(self): - self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(1, None, None)) + self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(1, None, None) + ) assert_that(self._value, is_(0)) @@ -235,7 +281,9 @@ def test_GIVEN_listener_WHEN_beam_changes_THEN_listener_is_informed(self): expected_value = 10 self.component.beam_path_rbv.add_listener(BeamPathUpdate, self.listen_for_value) beam_y = 1 - self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(expected_value + beam_y, None, None)) + self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(expected_value + beam_y, None, None) + ) self.component.beam_path_rbv.set_incoming_beam(PositionAndAngle(beam_y, 0, 0)) result = self.component.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement() @@ -245,9 +293,7 @@ def test_GIVEN_listener_WHEN_beam_changes_THEN_listener_is_informed(self): class TestThetaComponent(unittest.TestCase): - def test_GIVEN_no_next_component_WHEN_get_read_back_THEN_nan_returned(self): - beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 10, 90)) theta.beam_path_rbv.set_incoming_beam(beam_start) @@ -257,7 +303,6 @@ def test_GIVEN_no_next_component_WHEN_get_read_back_THEN_nan_returned(self): assert_that(isnan(result), is_(True), "Is not a number") def test_GIVEN_next_component_is_not_in_beam_WHEN_get_read_back_THEN_nan_returned(self): - beam_start = PositionAndAngle(y=0, z=0, angle=0) next_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) @@ -271,58 +316,71 @@ def test_GIVEN_next_component_is_not_in_beam_WHEN_get_read_back_THEN_nan_returne assert_that(isnan(result), is_(True), "Is not a number") - def test_GIVEN_next_component_is_in_beam_WHEN_get_read_back_THEN_half_angle_to_component_is_readback(self): - + def test_GIVEN_next_component_is_in_beam_WHEN_get_read_back_THEN_half_angle_to_component_is_readback( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_to(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) result = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].get_relative_to_beam() - theta_calc_set_of_incoming_beam_next_comp = next_component.beam_path_rbv.substitute_incoming_beam_for_displacement + theta_calc_set_of_incoming_beam_next_comp = ( + next_component.beam_path_rbv.substitute_incoming_beam_for_displacement + ) assert_that(result, is_(0.0)) - assert_that(theta_calc_set_of_incoming_beam_next_comp, is_(position_and_angle(theta.beam_path_set_point.get_outgoing_beam())), "This component has defined theta rbv") - - def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_WHEN_get_read_back_THEN_half_angle_to_component_is_readback(self): - + assert_that( + theta_calc_set_of_incoming_beam_next_comp, + is_(position_and_angle(theta.beam_path_set_point.get_outgoing_beam())), + "This component has defined theta rbv", + ) + + def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_WHEN_get_read_back_THEN_half_angle_to_component_is_readback( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_to(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) result = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].get_relative_to_beam() - assert_that(result, is_(45.0/2.0)) - - def test_GIVEN_next_component_is_in_beam_and_at_90_degrees_WHEN_get_read_back_THEN_half_angle_to_component_is_readback(self): + assert_that(result, is_(45.0 / 2.0)) + def test_GIVEN_next_component_is_in_beam_and_at_90_degrees_WHEN_get_read_back_THEN_half_angle_to_component_is_readback( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = Component("comp", setup=PositionAndAngle(0, 5, 90)) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_to(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) result = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].get_relative_to_beam() - assert_that(result, is_(90/2.0)) - - def test_GIVEN_next_component_is_not_in_beam_and_next_component_but_one_is_in_beam_WHEN_get_read_back_THEN_half_angle_to_component_is_readback_and_theta_calc_set_of_incoming_beam_is_set(self): + assert_that(result, is_(90 / 2.0)) + def test_GIVEN_next_component_is_not_in_beam_and_next_component_but_one_is_in_beam_WHEN_get_read_back_THEN_half_angle_to_component_is_readback_and_theta_calc_set_of_incoming_beam_is_set( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) next_component = Component("comp1", setup=PositionAndAngle(0, 10, 90)) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 @@ -333,7 +391,8 @@ def test_GIVEN_next_component_is_not_in_beam_and_next_component_but_one_is_in_be next_but_one_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_but_one_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_but_one_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No) + ) next_component.beam_path_rbv.substitute_incoming_beam_for_displacement = "Not None" theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) @@ -342,15 +401,28 @@ def test_GIVEN_next_component_is_not_in_beam_and_next_component_but_one_is_in_be theta.beam_path_rbv.set_incoming_beam(beam_start) result = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].get_relative_to_beam() - theta_calc_set_of_incoming_beam_next_comp = next_component.beam_path_rbv.substitute_incoming_beam_for_displacement - theta_calc_set_of_incoming_beam_next_comp_but_one = next_but_one_component.beam_path_rbv.substitute_incoming_beam_for_displacement - - assert_that(result, is_(45.0/2.0)) - assert_that(theta_calc_set_of_incoming_beam_next_comp, is_(None), "This component does not define theta rbv") - assert_that(theta_calc_set_of_incoming_beam_next_comp_but_one, is_(position_and_angle(theta.beam_path_set_point.get_outgoing_beam())), "This component has defined theta rbv") - - def test_GIVEN_next_component_is_in_beam_and_next_component_but_one_is_also_in_beam_WHEN_get_read_back_THEN_half_angle_to_first_component_is_readback_and_theta_cal_set_only_on_first_component(self): - + theta_calc_set_of_incoming_beam_next_comp = ( + next_component.beam_path_rbv.substitute_incoming_beam_for_displacement + ) + theta_calc_set_of_incoming_beam_next_comp_but_one = ( + next_but_one_component.beam_path_rbv.substitute_incoming_beam_for_displacement + ) + + assert_that(result, is_(45.0 / 2.0)) + assert_that( + theta_calc_set_of_incoming_beam_next_comp, + is_(None), + "This component does not define theta rbv", + ) + assert_that( + theta_calc_set_of_incoming_beam_next_comp_but_one, + is_(position_and_angle(theta.beam_path_set_point.get_outgoing_beam())), + "This component has defined theta rbv", + ) + + def test_GIVEN_next_component_is_in_beam_and_next_component_but_one_is_also_in_beam_WHEN_get_read_back_THEN_half_angle_to_first_component_is_readback_and_theta_cal_set_only_on_first_component( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) @@ -360,28 +432,44 @@ def test_GIVEN_next_component_is_in_beam_and_next_component_but_one_is_also_in_b next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.substitute_incoming_beam_for_displacement = "Not None" next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_to(next_component) next_but_one_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) next_but_one_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_but_one_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True - next_but_one_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(0, None, None)) + next_but_one_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(0, None, None) + ) next_but_one_component.beam_path_rbv.substitute_incoming_beam_for_displacement = "Not None" theta.add_angle_to(next_but_one_component) theta.beam_path_rbv.set_incoming_beam(beam_start) result = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].get_relative_to_beam() - theta_calc_set_of_incoming_beam_next_comp = next_component.beam_path_rbv.substitute_incoming_beam_for_displacement - theta_calc_set_of_incoming_beam_next_comp_but_one = next_but_one_component.beam_path_rbv.substitute_incoming_beam_for_displacement - - assert_that(result, is_(45.0/2.0)) - assert_that(theta_calc_set_of_incoming_beam_next_comp, is_(position_and_angle(theta.beam_path_set_point.get_outgoing_beam())), "This component does not define theta rbv") - assert_that(theta_calc_set_of_incoming_beam_next_comp_but_one, is_(None), "This component has defined theta rbv") - - def test_GIVEN_next_component_is_in_beam_WHEN_set_next_component_displacement_THEN_change_in_beam_path_triggered_at_least_once(self): - + theta_calc_set_of_incoming_beam_next_comp = ( + next_component.beam_path_rbv.substitute_incoming_beam_for_displacement + ) + theta_calc_set_of_incoming_beam_next_comp_but_one = ( + next_but_one_component.beam_path_rbv.substitute_incoming_beam_for_displacement + ) + + assert_that(result, is_(45.0 / 2.0)) + assert_that( + theta_calc_set_of_incoming_beam_next_comp, + is_(position_and_angle(theta.beam_path_set_point.get_outgoing_beam())), + "This component does not define theta rbv", + ) + assert_that( + theta_calc_set_of_incoming_beam_next_comp_but_one, + is_(None), + "This component has defined theta rbv", + ) + + def test_GIVEN_next_component_is_in_beam_WHEN_set_next_component_displacement_THEN_change_in_beam_path_triggered_at_least_once( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) @@ -389,7 +477,8 @@ def test_GIVEN_next_component_is_in_beam_WHEN_set_next_component_displacement_TH next_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_to(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) @@ -397,19 +486,22 @@ def test_GIVEN_next_component_is_in_beam_WHEN_set_next_component_displacement_TH theta.beam_path_rbv.add_listener(BeamPathUpdate, listener) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(1, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(1, AlarmSeverity.No, AlarmStatus.No) + ) listener.assert_called_with(BeamPathUpdate(theta.beam_path_rbv)) - def test_GIVEN_next_component_is_in_beam_WHEN_set_next_component_incoming_beam_THEN_change_in_beam_path_is_not_triggered(self): - + def test_GIVEN_next_component_is_in_beam_WHEN_set_next_component_incoming_beam_THEN_change_in_beam_path_is_not_triggered( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_to(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) listener = Mock() @@ -419,39 +511,45 @@ def test_GIVEN_next_component_is_in_beam_WHEN_set_next_component_incoming_beam_T listener.assert_not_called() - def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_and_not_on_axis_WHEN_get_read_back_THEN_half_angle_to_component_is_readback(self): - + def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_and_not_on_axis_WHEN_get_read_back_THEN_half_angle_to_component_is_readback( + self, + ): beam_start = PositionAndAngle(y=10, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(15, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(15, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_to(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) result = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].get_relative_to_beam() - assert_that(result, is_(close_to(45.0/2.0, DEFAULT_TEST_TOLERANCE))) - - def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_and_incoming_angle_is_45_WHEN_get_read_back_THEN_half_angle_to_component_is_readback(self): + assert_that(result, is_(close_to(45.0 / 2.0, DEFAULT_TEST_TOLERANCE))) + def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_and_incoming_angle_is_45_WHEN_get_read_back_THEN_half_angle_to_component_is_readback( + self, + ): beam_start = PositionAndAngle(y=10, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = TiltingComponent("comp", setup=PositionAndAngle(0, 10, 90)) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement( - CorrectedReadbackUpdate(45, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(45, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_of(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) result = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].get_relative_to_beam() - assert_that(result, is_(close_to(45.0/2.0, DEFAULT_TEST_TOLERANCE))) + assert_that(result, is_(close_to(45.0 / 2.0, DEFAULT_TEST_TOLERANCE))) - def test_GIVEN_next_component_is_in_beam_theta_is_set_to_0_and_component_is_at_45_degrees_WHEN_get_read_back_from_component_THEN_component_readback_is_relative_to_setpoint_beam_not_readback_beam_and_is_not_0_and_outgoing_beam_is_readback_outgoing_beam(self): + def test_GIVEN_next_component_is_in_beam_theta_is_set_to_0_and_component_is_at_45_degrees_WHEN_get_read_back_from_component_THEN_component_readback_is_relative_to_setpoint_beam_not_readback_beam_and_is_not_0_and_outgoing_beam_is_readback_outgoing_beam( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) @@ -460,21 +558,27 @@ def test_GIVEN_next_component_is_in_beam_theta_is_set_to_0_and_component_is_at_4 theta.add_angle_to(next_component) expected_position = 5 next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(expected_position, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(expected_position, AlarmSeverity.No, AlarmStatus.No) + ) theta.beam_path_set_point.set_incoming_beam(beam_start) theta.beam_path_set_point.axis[ChangeAxis.ANGLE].set_relative_to_beam(0) theta.beam_path_rbv.set_incoming_beam(beam_start) next_component.beam_path_rbv.set_incoming_beam(theta.beam_path_rbv.get_outgoing_beam()) - result_position = next_component.beam_path_rbv.axis[ChangeAxis.POSITION].get_relative_to_beam() + result_position = next_component.beam_path_rbv.axis[ + ChangeAxis.POSITION + ].get_relative_to_beam() result_outgoing_beam = next_component.beam_path_rbv.get_outgoing_beam() assert_that(result_position, is_(expected_position)) - assert_that(result_outgoing_beam, is_(position_and_angle(theta.beam_path_rbv.get_outgoing_beam()))) - - def test_GIVEN_next_component_is_in_beam_and_disabled_WHEN_theta_rbv_changed_THEN_beampath_on_rbv_is_updated(self): + assert_that( + result_outgoing_beam, is_(position_and_angle(theta.beam_path_rbv.get_outgoing_beam())) + ) + def test_GIVEN_next_component_is_in_beam_and_disabled_WHEN_theta_rbv_changed_THEN_beampath_on_rbv_is_updated( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = TiltingComponent("comp", setup=PositionAndAngle(0, 10, 90)) @@ -482,9 +586,11 @@ def test_GIVEN_next_component_is_in_beam_and_disabled_WHEN_theta_rbv_changed_THE next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True next_component.beam_path_rbv.incoming_beam_can_change = False next_component.beam_path_rbv.axis[ChangeAxis.ANGLE].set_displacement( - CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.No) + ) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No)) + CorrectedReadbackUpdate(5, AlarmSeverity.No, AlarmStatus.No) + ) theta.add_angle_to(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) @@ -492,8 +598,9 @@ def test_GIVEN_next_component_is_in_beam_and_disabled_WHEN_theta_rbv_changed_THE assert_that(result, is_(position_and_angle(theta.beam_path_rbv.get_outgoing_beam()))) - def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_and_incoming_angle_is_45_WHEN_get_init_sp_THEN_half_angle_to_component_is_readback(self): - + def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_and_incoming_angle_is_45_WHEN_get_init_sp_THEN_half_angle_to_component_is_readback( + self, + ): beam_start = PositionAndAngle(y=10, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = TiltingComponent("comp", setup=PositionAndAngle(0, 10, 90)) @@ -505,9 +612,11 @@ def test_GIVEN_next_component_is_in_beam_and_at_45_degrees_and_incoming_angle_is result = theta.beam_path_set_point.axis[ChangeAxis.ANGLE].get_relative_to_beam() - assert_that(result, is_(close_to(45.0/2.0, DEFAULT_TEST_TOLERANCE))) + assert_that(result, is_(close_to(45.0 / 2.0, DEFAULT_TEST_TOLERANCE))) - def test_GIVEN_next_component_is_in_beam_and_at_47_degrees_with_an_autosaved_offset_of_2_and_incoming_angle_is_45_WHEN_get_init_sp_THEN_half_angle_to_component_minus_offset_is_readback(self): + def test_GIVEN_next_component_is_in_beam_and_at_47_degrees_with_an_autosaved_offset_of_2_and_incoming_angle_is_45_WHEN_get_init_sp_THEN_half_angle_to_component_minus_offset_is_readback( + self, + ): offset = 2 beam_start = PositionAndAngle(y=10, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) @@ -517,13 +626,17 @@ def test_GIVEN_next_component_is_in_beam_and_at_47_degrees_with_an_autosaved_off next_component.beam_path_set_point.axis[ChangeAxis.ANGLE].autosaved_value = offset theta.add_angle_of(next_component) theta.beam_path_rbv.set_incoming_beam(beam_start) - next_component.beam_path_set_point.axis[ChangeAxis.ANGLE].init_displacement_from_motor(45 + offset) + next_component.beam_path_set_point.axis[ChangeAxis.ANGLE].init_displacement_from_motor( + 45 + offset + ) result = theta.beam_path_set_point.axis[ChangeAxis.ANGLE].get_relative_to_beam() - assert_that(result, is_(close_to(45.0/2.0, DEFAULT_TEST_TOLERANCE))) + assert_that(result, is_(close_to(45.0 / 2.0, DEFAULT_TEST_TOLERANCE))) - def test_GIVEN_theta_dependent_on_multiple_axis_WHEN_one_axis_major_and_other_axis_no_THEN_theta_is_major(self): + def test_GIVEN_theta_dependent_on_multiple_axis_WHEN_one_axis_major_and_other_axis_no_THEN_theta_is_major( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) @@ -531,16 +644,20 @@ def test_GIVEN_theta_dependent_on_multiple_axis_WHEN_one_axis_major_and_other_ax next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True theta.add_angle_to(next_component) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(0, AlarmSeverity.Major, AlarmStatus.HiHi)) + CorrectedReadbackUpdate(0, AlarmSeverity.Major, AlarmStatus.HiHi) + ) next_component.beam_path_rbv.axis[ChangeAxis.LONG_AXIS].set_displacement( - CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.Low)) + CorrectedReadbackUpdate(0, AlarmSeverity.No, AlarmStatus.Low) + ) theta.beam_path_rbv.set_incoming_beam(beam_start) theta_alarm = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].alarm assert_that(theta_alarm, is_((AlarmSeverity.Major, AlarmStatus.HiHi))) - def test_GIVEN_theta_dependent_on_multiple_axis_WHEN_one_axis_major_and_other_axis_minor_THEN_theta_is_major(self): + def test_GIVEN_theta_dependent_on_multiple_axis_WHEN_one_axis_major_and_other_axis_minor_THEN_theta_is_major( + self, + ): beam_start = PositionAndAngle(y=0, z=0, angle=0) theta = ThetaComponent("theta", setup=PositionAndAngle(0, 5, 90)) next_component = Component("comp", setup=PositionAndAngle(0, 10, 90)) @@ -548,9 +665,11 @@ def test_GIVEN_theta_dependent_on_multiple_axis_WHEN_one_axis_major_and_other_ax next_component.beam_path_rbv.axis[ChangeAxis.POSITION].is_in_beam = True theta.add_angle_to(next_component) next_component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement( - CorrectedReadbackUpdate(0, AlarmSeverity.Minor, AlarmStatus.HiHi)) + CorrectedReadbackUpdate(0, AlarmSeverity.Minor, AlarmStatus.HiHi) + ) next_component.beam_path_rbv.axis[ChangeAxis.LONG_AXIS].set_displacement( - CorrectedReadbackUpdate(0, AlarmSeverity.Major, AlarmStatus.Low)) + CorrectedReadbackUpdate(0, AlarmSeverity.Major, AlarmStatus.Low) + ) theta.beam_path_rbv.set_incoming_beam(beam_start) theta_alarm = theta.beam_path_rbv.axis[ChangeAxis.ANGLE].alarm @@ -559,7 +678,6 @@ def test_GIVEN_theta_dependent_on_multiple_axis_WHEN_one_axis_major_and_other_ax class TestComponentInitialisation(unittest.TestCase): - def setUp(self): self.Z_COMPONENT = 10 self.REFLECTION_ANGLE = 45 @@ -568,13 +686,19 @@ def setUp(self): self.EXPECTED_INTERCEPT = self.Z_COMPONENT self.EXPECTED_ANGLE = self.REFLECTION_ANGLE - self.component = TiltingComponent("component", setup=PositionAndAngle(0, self.Z_COMPONENT, 90)) + self.component = TiltingComponent( + "component", setup=PositionAndAngle(0, self.Z_COMPONENT, 90) + ) self.component.beam_path_set_point.set_incoming_beam(PositionAndAngle(y=0, z=0, angle=0)) # tests that changing beam on init does the right thing - def test_GIVEN_component_has_autosaved_offset_WHEN_incoming_beam_changes_on_init_THEN_displacement_is_beam_intercept_plus_offset(self): + def test_GIVEN_component_has_autosaved_offset_WHEN_incoming_beam_changes_on_init_THEN_displacement_is_beam_intercept_plus_offset( + self, + ): autosaved_offset = 1 - self.component.beam_path_set_point.axis[ChangeAxis.POSITION].autosaved_value = autosaved_offset + self.component.beam_path_set_point.axis[ + ChangeAxis.POSITION + ].autosaved_value = autosaved_offset expected = self.EXPECTED_INTERCEPT + autosaved_offset self.component.beam_path_set_point.set_incoming_beam(self.BOUNCED_BEAM, on_init=True) @@ -582,7 +706,9 @@ def test_GIVEN_component_has_autosaved_offset_WHEN_incoming_beam_changes_on_init assert_that(actual, is_(close_to(expected, DEFAULT_TEST_TOLERANCE))) - def test_GIVEN_component_has_autosaved_angle_WHEN_incoming_beam_changes_on_init_THEN_angle_is_beam_angle_plus_offset(self): + def test_GIVEN_component_has_autosaved_angle_WHEN_incoming_beam_changes_on_init_THEN_angle_is_beam_angle_plus_offset( + self, + ): autosaved_offset = 1 self.component.beam_path_set_point.axis[ChangeAxis.ANGLE].autosaved_value = autosaved_offset expected = self.EXPECTED_ANGLE + autosaved_offset @@ -592,17 +718,22 @@ def test_GIVEN_component_has_autosaved_angle_WHEN_incoming_beam_changes_on_init_ assert_that(actual, is_(close_to(expected, DEFAULT_TEST_TOLERANCE))) - - def test_GIVEN_component_has_autosave_position_WHEN_incoming_beam_changes_on_init_THEN_pos_relative_to_beam_is_autosaved_offset(self): + def test_GIVEN_component_has_autosave_position_WHEN_incoming_beam_changes_on_init_THEN_pos_relative_to_beam_is_autosaved_offset( + self, + ): autosaved_offset = 1 - self.component.beam_path_set_point.axis[ChangeAxis.POSITION].autosaved_value = autosaved_offset + self.component.beam_path_set_point.axis[ + ChangeAxis.POSITION + ].autosaved_value = autosaved_offset self.component.beam_path_set_point.set_incoming_beam(self.BOUNCED_BEAM, on_init=True) actual = self.component.beam_path_set_point.axis[ChangeAxis.POSITION].get_relative_to_beam() self.assertEqual(autosaved_offset, actual) - def test_GIVEN_component_has_autosave_angle_WHEN_incoming_beam_changes_on_init_THEN_angle_relative_to_beam_is_autosaved_offset(self): + def test_GIVEN_component_has_autosave_angle_WHEN_incoming_beam_changes_on_init_THEN_angle_relative_to_beam_is_autosaved_offset( + self, + ): autosaved_offset = 1 self.component.beam_path_set_point.axis[ChangeAxis.ANGLE].autosaved_value = autosaved_offset @@ -611,7 +742,9 @@ def test_GIVEN_component_has_autosave_angle_WHEN_incoming_beam_changes_on_init_T self.assertEqual(autosaved_offset, actual) - def test_GIVEN_component_has_no_autosaved_offset_WHEN_incoming_beam_changes_on_init_THEN_displacement_is_unchanged(self): + def test_GIVEN_component_has_no_autosaved_offset_WHEN_incoming_beam_changes_on_init_THEN_displacement_is_unchanged( + self, + ): expected = self.component.beam_path_set_point.axis[ChangeAxis.POSITION].get_displacement() self.component.beam_path_set_point.set_incoming_beam(self.BOUNCED_BEAM, on_init=True) @@ -619,7 +752,9 @@ def test_GIVEN_component_has_no_autosaved_offset_WHEN_incoming_beam_changes_on_i self.assertEqual(expected, actual) - def test_GIVEN_component_has_no_autosaved_angle_WHEN_incoming_beam_changes_on_init_THEN_angle_is_unchanged(self): + def test_GIVEN_component_has_no_autosaved_angle_WHEN_incoming_beam_changes_on_init_THEN_angle_is_unchanged( + self, + ): expected = self.component.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement() self.component.beam_path_set_point.set_incoming_beam(self.BOUNCED_BEAM, on_init=True) @@ -627,9 +762,13 @@ def test_GIVEN_component_has_no_autosaved_angle_WHEN_incoming_beam_changes_on_in self.assertEqual(expected, actual) - def test_GIVEN_component_has_no_autosave_position_WHEN_incoming_beam_changes_on_init_THEN_pos_relative_to_beam_is_displacement_minus_beam_intercept(self): + def test_GIVEN_component_has_no_autosave_position_WHEN_incoming_beam_changes_on_init_THEN_pos_relative_to_beam_is_displacement_minus_beam_intercept( + self, + ): displacement = 5 - self.component.beam_path_set_point.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(displacement, None, None)) + self.component.beam_path_set_point.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(displacement, None, None) + ) expected = displacement - self.EXPECTED_INTERCEPT self.component.beam_path_set_point.set_incoming_beam(self.BOUNCED_BEAM, on_init=True) @@ -637,7 +776,9 @@ def test_GIVEN_component_has_no_autosave_position_WHEN_incoming_beam_changes_on_ assert_that(actual, is_(close_to(expected, DEFAULT_TEST_TOLERANCE))) - def test_GIVEN_theta_angled_to_autosaved_comp_WHEN_initialising_comp_THEN_theta_is_init_with_regards_to_beam_intercept(self): + def test_GIVEN_theta_angled_to_autosaved_comp_WHEN_initialising_comp_THEN_theta_is_init_with_regards_to_beam_intercept( + self, + ): z_theta = self.Z_COMPONENT / 2 offset_comp = 3 self.theta = ThetaComponent("theta", PositionAndAngle(0, z_theta, 90)) @@ -646,7 +787,9 @@ def test_GIVEN_theta_angled_to_autosaved_comp_WHEN_initialising_comp_THEN_theta_ self.theta.beam_path_set_point.set_incoming_beam(self.STRAIGHT_BEAM) expected = self.REFLECTION_ANGLE / 2.0 - self.component.beam_path_set_point.axis[ChangeAxis.POSITION].init_displacement_from_motor(z_theta + offset_comp) + self.component.beam_path_set_point.axis[ChangeAxis.POSITION].init_displacement_from_motor( + z_theta + offset_comp + ) actual = self.theta.beam_path_set_point.axis[ChangeAxis.ANGLE].get_relative_to_beam() assert_that(actual, is_(close_to(expected, DEFAULT_TEST_TOLERANCE))) @@ -662,14 +805,20 @@ def setUp(self): self.component = ReflectingComponent("component", setup=PositionAndAngle(0, 2, 90)) def test_WHEN_init_THEN_component_alarms_are_undefined(self): - self.assertEqual(self.component.beam_path_rbv.axis[ChangeAxis.POSITION].alarm, self.UNDEFINED) + self.assertEqual( + self.component.beam_path_rbv.axis[ChangeAxis.POSITION].alarm, self.UNDEFINED + ) self.assertEqual(self.component.beam_path_rbv.axis[ChangeAxis.ANGLE].alarm, self.UNDEFINED) - self.assertEqual(self.component.beam_path_set_point.axis[ChangeAxis.POSITION].alarm, self.UNDEFINED) - self.assertEqual(self.component.beam_path_set_point.axis[ChangeAxis.ANGLE].alarm, self.UNDEFINED) + self.assertEqual( + self.component.beam_path_set_point.axis[ChangeAxis.POSITION].alarm, self.UNDEFINED + ) + self.assertEqual( + self.component.beam_path_set_point.axis[ChangeAxis.ANGLE].alarm, self.UNDEFINED + ) def test_GIVEN_alarms_WHEN_updating_displacement_THEN_component_displacement_alarm_is_set(self): update = CorrectedReadbackUpdate(0, self.ALARM_SEVERITY, self.ALARM_STATUS) - + self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(update) actual_alarm_info = self.component.beam_path_rbv.axis[ChangeAxis.POSITION].alarm @@ -699,18 +848,24 @@ def test_GIVEN_alarms_WHEN_updating_angle_THEN_component_displacement_alarm_is_u self.assertEqual(self.UNDEFINED, actual_alarm_info) - def test_GIVEN_theta_angled_to_component_WHEN_updating_displacement_with_alarms_on_component_THEN_theta_angle_alarm_set(self): + def test_GIVEN_theta_angled_to_component_WHEN_updating_displacement_with_alarms_on_component_THEN_theta_angle_alarm_set( + self, + ): self.theta = ThetaComponent("theta", setup=PositionAndAngle(0, 1, 90)) self.theta.add_angle_to(self.component) update = CorrectedReadbackUpdate(0, self.ALARM_SEVERITY, self.ALARM_STATUS) - self.component.beam_path_rbv.axis[ChangeAxis.LONG_AXIS].set_alarm(AlarmSeverity.No, AlarmStatus.No) + self.component.beam_path_rbv.axis[ChangeAxis.LONG_AXIS].set_alarm( + AlarmSeverity.No, AlarmStatus.No + ) self.component.beam_path_rbv.axis[ChangeAxis.POSITION].set_displacement(update) actual_alarm_info = self.theta.beam_path_rbv.axis[ChangeAxis.ANGLE].alarm self.assertEqual(self.ALARM, actual_alarm_info) - def test_GIVEN_theta_angled_of_component_WHEN_updating_displacement_with_alarms_on_component_THEN_theta_angle_alarm_set(self): + def test_GIVEN_theta_angled_of_component_WHEN_updating_displacement_with_alarms_on_component_THEN_theta_angle_alarm_set( + self, + ): self.theta = ThetaComponent("theta", setup=PositionAndAngle(0, 1, 90)) self.theta.add_angle_of(self.component) update = CorrectedReadbackUpdate(0, self.ALARM_SEVERITY, self.ALARM_STATUS) @@ -720,7 +875,9 @@ def test_GIVEN_theta_angled_of_component_WHEN_updating_displacement_with_alarms_ self.assertEqual(self.ALARM, actual_alarm_info) - def test_GIVEN_theta_angled_to_component_WHEN_updating_angle_with_alarms_on_component_THEN_theta_angle_is_unchanged(self): + def test_GIVEN_theta_angled_to_component_WHEN_updating_angle_with_alarms_on_component_THEN_theta_angle_is_unchanged( + self, + ): self.theta = ThetaComponent("theta", setup=PositionAndAngle(0, 1, 90)) self.theta.add_angle_to(self.component) update = CorrectedReadbackUpdate(0, self.ALARM_SEVERITY, self.ALARM_STATUS) @@ -730,7 +887,9 @@ def test_GIVEN_theta_angled_to_component_WHEN_updating_angle_with_alarms_on_comp self.assertEqual(self.UNDEFINED, actual_alarm_info) - def test_GIVEN_theta_angled_to_two_components_one_not_in_beam_WHEN_updating_displacement_of_out_of_beam_coomponent_with_alarms_on_component_THEN_theta_angle_alarm_not_set(self): + def test_GIVEN_theta_angled_to_two_components_one_not_in_beam_WHEN_updating_displacement_of_out_of_beam_coomponent_with_alarms_on_component_THEN_theta_angle_alarm_not_set( + self, + ): self.theta = ThetaComponent("theta", setup=PositionAndAngle(0, 1, 90)) component2 = ReflectingComponent("component2", setup=PositionAndAngle(0, 2, 90)) self.theta.add_angle_to(self.component) @@ -745,7 +904,9 @@ def test_GIVEN_theta_angled_to_two_components_one_not_in_beam_WHEN_updating_disp assert_that(actual_alarm_info, is_(self.UNDEFINED)) - def test_GIVEN_theta_angled_of_two_components_one_not_in_beam_WHEN_updating_displacement_of_out_of_beam_component_with_alarms_on_component_THEN_theta_angle_alarm_not_set(self): + def test_GIVEN_theta_angled_of_two_components_one_not_in_beam_WHEN_updating_displacement_of_out_of_beam_component_with_alarms_on_component_THEN_theta_angle_alarm_not_set( + self, + ): self.theta = ThetaComponent("theta", setup=PositionAndAngle(0, 1, 90)) component2 = ReflectingComponent("component2", setup=PositionAndAngle(0, 2, 90)) self.theta.add_angle_of(self.component) @@ -762,13 +923,14 @@ def test_GIVEN_theta_angled_of_two_components_one_not_in_beam_WHEN_updating_disp class TestThetaChange(unittest.TestCase): - def setUp(self): self.component = ReflectingComponent("comp", setup=PositionAndAngle(0, 0, 0)) self.theta = ThetaComponent("theta", setup=PositionAndAngle(0, 1, 90)) @parameterized.expand([(True,), (False,)]) - def test_GIVEN_component_pointed_at_by_theta_changes_WHEN_get_theta_change_THEN_theta_is_changing(self, is_changing): + def test_GIVEN_component_pointed_at_by_theta_changes_WHEN_get_theta_change_THEN_theta_is_changing( + self, is_changing + ): self.theta.add_angle_to(self.component) self.component.beam_path_rbv.axis[ChangeAxis.POSITION].is_changing = is_changing @@ -777,7 +939,9 @@ def test_GIVEN_component_pointed_at_by_theta_changes_WHEN_get_theta_change_THEN_ assert_that(result, is_(is_changing), "Theta is changing") @parameterized.expand([(True,), (False,)]) - def test_GIVEN_2_components_pointed_at_by_theta_changes_when_first_out_of_beam_WHEN_get_theta_change_THEN_theta_is_changing_as_second_component(self, is_changing): + def test_GIVEN_2_components_pointed_at_by_theta_changes_when_first_out_of_beam_WHEN_get_theta_change_THEN_theta_is_changing_as_second_component( + self, is_changing + ): self.theta.add_angle_to(self.component) self.component.beam_path_rbv.axis[ChangeAxis.POSITION].is_changing = not is_changing self.component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 @@ -791,7 +955,9 @@ def test_GIVEN_2_components_pointed_at_by_theta_changes_when_first_out_of_beam_W assert_that(result, is_(is_changing), "Theta is changing") @parameterized.expand([(True,), (False,)]) - def test_GIVEN_component_pointed_at_by_theta_changes_angle_of_WHEN_get_theta_change_THEN_theta_is_changing(self, is_changing): + def test_GIVEN_component_pointed_at_by_theta_changes_angle_of_WHEN_get_theta_change_THEN_theta_is_changing( + self, is_changing + ): self.theta.add_angle_of(self.component) self.component.beam_path_rbv.axis[ChangeAxis.ANGLE].is_changing = is_changing @@ -800,7 +966,9 @@ def test_GIVEN_component_pointed_at_by_theta_changes_angle_of_WHEN_get_theta_cha assert_that(result, is_(is_changing), "Theta is changing") @parameterized.expand([(True, True), (False, True), (True, False), (False, False)]) - def test_GIVEN_2_components_pointed_at_by_theta_changes_when_neither_in_WHEN_get_theta_change_THEN_theta_is_changing_as_first_or_second_component(self, is_changing_1, is_changing_2): + def test_GIVEN_2_components_pointed_at_by_theta_changes_when_neither_in_WHEN_get_theta_change_THEN_theta_is_changing_as_first_or_second_component( + self, is_changing_1, is_changing_2 + ): # on the hope that one is moving to define theta self.theta.add_angle_to(self.component) self.component.beam_path_rbv.axis[ChangeAxis.POSITION].park_sequence_count = 1 @@ -817,7 +985,9 @@ def test_GIVEN_2_components_pointed_at_by_theta_changes_when_neither_in_WHEN_get assert_that(result, is_(is_changing_1 or is_changing_2), "Theta is changing") @parameterized.expand([(True,), (False,)]) - def test_GIVEN_long_axis_of_component_pointed_at_by_theta_changes_WHEN_get_theta_change_THEN_theta_is_changing(self, is_changing): + def test_GIVEN_long_axis_of_component_pointed_at_by_theta_changes_WHEN_get_theta_change_THEN_theta_is_changing( + self, is_changing + ): self.theta.add_angle_to(self.component) self.component.beam_path_rbv.axis[ChangeAxis.LONG_AXIS].is_changing = is_changing @@ -827,9 +997,10 @@ def test_GIVEN_long_axis_of_component_pointed_at_by_theta_changes_WHEN_get_theta class TestComponentDisablingAndAutosaveInit(unittest.TestCase): - - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave') - def test_GIVEN_component_WHEN_disabled_THEN_incoming_beam_for_rbv_and_sp_are_saved(self, mock_auto_save): + @patch("ReflectometryServer.beam_path_calc.disable_mode_autosave") + def test_GIVEN_component_WHEN_disabled_THEN_incoming_beam_for_rbv_and_sp_are_saved( + self, mock_auto_save + ): expected_incoming_beam_sp = PositionAndAngle(1, 2, 3) expected_incoming_beam_rbv = PositionAndAngle(1, 2, 3) expected_name = "comp" @@ -839,10 +1010,15 @@ def test_GIVEN_component_WHEN_disabled_THEN_incoming_beam_for_rbv_and_sp_are_sav component.set_incoming_beam_can_change(False) - mock_auto_save.write_parameter.assert_has_calls([call(expected_name + "_rbv", expected_incoming_beam_rbv), - call(expected_name + "_sp", expected_incoming_beam_sp)], any_order=True) + mock_auto_save.write_parameter.assert_has_calls( + [ + call(expected_name + "_rbv", expected_incoming_beam_rbv), + call(expected_name + "_sp", expected_incoming_beam_sp), + ], + any_order=True, + ) - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave') + @patch("ReflectometryServer.beam_path_calc.disable_mode_autosave") def test_GIVEN_component_WHEN_enabled_THEN_beamline_is_not_saved(self, mock_auto_save): component = Component("comp", PositionAndAngle(0, 0, 0)) component.beam_path_set_point.set_incoming_beam(PositionAndAngle(1, 2, 3)) @@ -860,75 +1036,105 @@ def test_GIVEN_position_and_angle_WHEN_convert_for_autosave_and_back_THEN_is_sam assert_that(result, is_(position_and_angle(expected_position_and_angle))) def test_GIVEN_position_and_angle_WHEN_convert_from_none_THEN_value_error(self): - assert_that(calling(PositionAndAngle.autosave_convert_for_read).with_args(None), - raises(ValueError)) + assert_that( + calling(PositionAndAngle.autosave_convert_for_read).with_args(None), raises(ValueError) + ) def test_GIVEN_position_and_angle_WHEN_convert_from_nonsense_THEN_raises_value_error(self): - - assert_that(calling(PositionAndAngle.autosave_convert_for_read).with_args("blah"), - raises(ValueError)) - - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave') - def test_GIVEN_component_WHEN_init_disabled_with_valid_beam_THEN_incoming_beam_is_restored(self, mock_auto_save): + assert_that( + calling(PositionAndAngle.autosave_convert_for_read).with_args("blah"), + raises(ValueError), + ) + + @patch("ReflectometryServer.beam_path_calc.disable_mode_autosave") + def test_GIVEN_component_WHEN_init_disabled_with_valid_beam_THEN_incoming_beam_is_restored( + self, mock_auto_save + ): expected_incoming_beam = PositionAndAngle(1, 2, 3) mock_auto_save.read_parameter.return_value = expected_incoming_beam component = Component("comp", PositionAndAngle(0, 0, 0)) component.set_incoming_beam_can_change(False, on_init=True) - assert_that(component.beam_path_set_point.get_outgoing_beam(), position_and_angle(expected_incoming_beam)) + assert_that( + component.beam_path_set_point.get_outgoing_beam(), + position_and_angle(expected_incoming_beam), + ) - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave') - def test_GIVEN_component_WHEN_init_disabled_with_invalid_beam_THEN_incoming_beam_is_0_0_0(self, mock_auto_save): + @patch("ReflectometryServer.beam_path_calc.disable_mode_autosave") + def test_GIVEN_component_WHEN_init_disabled_with_invalid_beam_THEN_incoming_beam_is_0_0_0( + self, mock_auto_save + ): expected_incoming_beam = PositionAndAngle(0, 0, 0) mock_auto_save.read_parameter.return_value = None component = Component("comp", PositionAndAngle(0, 10, 90)) component.set_incoming_beam_can_change(False, on_init=True) - assert_that(component.beam_path_set_point.get_outgoing_beam(), position_and_angle(expected_incoming_beam)) + assert_that( + component.beam_path_set_point.get_outgoing_beam(), + position_and_angle(expected_incoming_beam), + ) - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave') - def test_GIVEN_component_WHEN_init_enabled_with_valid_beam_THEN_incoming_beam_is_not_restored(self, mock_auto_save): + @patch("ReflectometryServer.beam_path_calc.disable_mode_autosave") + def test_GIVEN_component_WHEN_init_enabled_with_valid_beam_THEN_incoming_beam_is_not_restored( + self, mock_auto_save + ): expected_incoming_beam = PositionAndAngle(0, 0, 0) mock_auto_save.read_parameter.return_value = PositionAndAngle(12, 24, 63) component = Component("comp", PositionAndAngle(0, 0, 0)) component.set_incoming_beam_can_change(True, on_init=True) - assert_that(component.beam_path_set_point.get_outgoing_beam(), position_and_angle(expected_incoming_beam)) + assert_that( + component.beam_path_set_point.get_outgoing_beam(), + position_and_angle(expected_incoming_beam), + ) - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave') - def test_GIVEN_component_WHEN_disabled_and_incoming_beam_change_forced_THEN_incoming_beam_for_rbv_and_sp_are_saved(self, mock_auto_save): + @patch("ReflectometryServer.beam_path_calc.disable_mode_autosave") + def test_GIVEN_component_WHEN_disabled_and_incoming_beam_change_forced_THEN_incoming_beam_for_rbv_and_sp_are_saved( + self, mock_auto_save + ): expected_incoming_beam_sp = PositionAndAngle(1, 2, 3) expected_incoming_beam_rbv = PositionAndAngle(1, 2, 3) expected_name = "comp" component = Component(expected_name, PositionAndAngle(0, 10, 90)) - component.beam_path_set_point.set_incoming_beam(PositionAndAngle(0,0,0)) - component.beam_path_rbv.set_incoming_beam(PositionAndAngle(0,0,0)) + component.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) + component.beam_path_rbv.set_incoming_beam(PositionAndAngle(0, 0, 0)) component.set_incoming_beam_can_change(False) component.beam_path_set_point.set_incoming_beam(expected_incoming_beam_sp, force=True) component.beam_path_rbv.set_incoming_beam(expected_incoming_beam_rbv, force=True) - mock_auto_save.write_parameter.assert_has_calls([call(expected_name + "_rbv", expected_incoming_beam_rbv), - call(expected_name + "_sp", expected_incoming_beam_sp)], any_order=True) - - -@parameterized_class(('axis'), [(ChangeAxis.SEESAW,), - (ChangeAxis.CHI,), - (ChangeAxis.TRANS, ), - (ChangeAxis.PSI,), - (ChangeAxis.PHI,), - (ChangeAxis.HEIGHT,)]) + mock_auto_save.write_parameter.assert_has_calls( + [ + call(expected_name + "_rbv", expected_incoming_beam_rbv), + call(expected_name + "_sp", expected_incoming_beam_sp), + ], + any_order=True, + ) + + +@parameterized_class( + ("axis"), + [ + (ChangeAxis.SEESAW,), + (ChangeAxis.CHI,), + (ChangeAxis.TRANS,), + (ChangeAxis.PSI,), + (ChangeAxis.PHI,), + (ChangeAxis.HEIGHT,), + ], +) class TestDirectAxisWithBenchComponent(unittest.TestCase): - def test_GIVEN_axis_updated_WHEN_get_axis_THEN_updated_value_is_read(self): expected_result = 10 bench = get_standard_bench() param = AxisParameter("PARAM", bench, self.axis) - bench.beam_path_rbv.axis[self.axis].set_displacement(CorrectedReadbackUpdate(expected_result, None, None)) + bench.beam_path_rbv.axis[self.axis].set_displacement( + CorrectedReadbackUpdate(expected_result, None, None) + ) result = bench.beam_path_rbv.axis[self.axis].get_relative_to_beam() assert_that(result, is_(expected_result)) @@ -938,7 +1144,9 @@ def test_GIVEN_axis_updated_with_alarm_WHEN_get_see_saw_THEN_alarm_updated(self) bench = get_standard_bench() param = AxisParameter("PARAM", bench, self.axis) - bench.beam_path_rbv.axis[self.axis].set_displacement(CorrectedReadbackUpdate(expected_result, *expected_result)) + bench.beam_path_rbv.axis[self.axis].set_displacement( + CorrectedReadbackUpdate(expected_result, *expected_result) + ) result = bench.beam_path_rbv.axis[self.axis].alarm assert_that(result, is_(expected_result)) @@ -954,7 +1162,9 @@ def mylistener(pyhsical_move): param = AxisParameter("PARAM", bench, self.axis) bench.beam_path_rbv.axis[self.axis].add_listener(PhysicalMoveUpdate, mylistener) - bench.beam_path_rbv.axis[self.axis].set_displacement(CorrectedReadbackUpdate(expected_result, *expected_result)) + bench.beam_path_rbv.axis[self.axis].set_displacement( + CorrectedReadbackUpdate(expected_result, *expected_result) + ) result = self.physical_move.source assert_that(result, is_(bench.beam_path_rbv.axis[self.axis])) @@ -973,8 +1183,10 @@ def test_GIVEN_set_axis_WHEN_get_axis_value_THEN_value_returned_and_axis_changed def test_GIVEN_axis_WHEN_define_position_THEN_position_define_event_occurs(self): self.define_event = None + def mylistener(define_value): self.define_event = define_value + expected_result = 10 bench = get_standard_bench() bench.beam_path_rbv.axis[self.axis].add_listener(DefineValueAsEvent, mylistener) @@ -999,9 +1211,7 @@ def test_GIVEN_axis_parameter_WHEN_init_from_motor_on_component_THEN_parameter_s class TestBenchComponent(unittest.TestCase): - def _setup_bench(self, initial_position): - initial_angle, initial_height, initial_seesaw = initial_position bench = get_standard_bench() bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) @@ -1022,14 +1232,18 @@ def _setup_bench(self, initial_position): slide_driver.perform_move(1, False) return position, angle, seesaw, j1_axis, j2_axis, slide_axis - @parameterized.expand([ - (ChangeAxis.JACK_FRONT, 1, 1), - (ChangeAxis.JACK_REAR, 1, 1), - (ChangeAxis.JACK_FRONT, -4, -4), - (ChangeAxis.JACK_REAR, 2, 2), - (ChangeAxis.SLIDE, 1, 0) - ]) - def test_GIVEN_set_height_axis_with_0_angle_WHEN_get_axis_value_THEN_j1_value_returned_and_axis_changed(self, axis, position, expected_result): + @parameterized.expand( + [ + (ChangeAxis.JACK_FRONT, 1, 1), + (ChangeAxis.JACK_REAR, 1, 1), + (ChangeAxis.JACK_FRONT, -4, -4), + (ChangeAxis.JACK_REAR, 2, 2), + (ChangeAxis.SLIDE, 1, 0), + ] + ) + def test_GIVEN_set_height_axis_with_0_angle_WHEN_get_axis_value_THEN_j1_value_returned_and_axis_changed( + self, axis, position, expected_result + ): bench = get_standard_bench(with_angle=0) bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) param = AxisParameter("PARAM", bench, ChangeAxis.POSITION) @@ -1041,19 +1255,43 @@ def test_GIVEN_set_height_axis_with_0_angle_WHEN_get_axis_value_THEN_j1_value_re assert_that(result, is_(close_to(expected_result, 1e-6))) assert_that(changed, is_(True), "axis is changed") - @parameterized.expand([ - (ChangeAxis.JACK_FRONT, 12.3, 202.2279727), # expected values from spreadsheet + height - (ChangeAxis.JACK_REAR, 12.3, 476.9454087), # expected values from spreadsheet + height - (ChangeAxis.JACK_FRONT, -0.1, -50.88767676), # expected values from spreadsheet + height - (ChangeAxis.JACK_REAR, 0.0, -111.3188069), # expected values from spreadsheet + height - (ChangeAxis.SLIDE, 3.3, 10.54157171), # expected values from spreadsheet + height - (ChangeAxis.SLIDE, 0.1, -26.15894011), # expected values from spreadsheet + height - (ChangeAxis.SLIDE, BENCH_MIN_ANGLE, -27.44574943), # expected values from spreadsheet + height at min angle - (ChangeAxis.SLIDE, BENCH_MIN_ANGLE - 0.1, -27.44574943), # expected values from spreadsheet + height at 0 deg because of cut off - (ChangeAxis.SLIDE, BENCH_MAX_ANGLE, 24.79311549), # expected values from spreadsheet + height at max angle - (ChangeAxis.SLIDE, BENCH_MAX_ANGLE + 1.0, 24.79311549), # expected values from spreadsheet + height at 4.8 deg because of cut off - ]) - def test_GIVEN_set_height_axis_WHEN_get_axis_value_THEN_j1_value_returned_and_axis_changed(self, axis, angle, expected_result): + @parameterized.expand( + [ + (ChangeAxis.JACK_FRONT, 12.3, 202.2279727), # expected values from spreadsheet + height + (ChangeAxis.JACK_REAR, 12.3, 476.9454087), # expected values from spreadsheet + height + ( + ChangeAxis.JACK_FRONT, + -0.1, + -50.88767676, + ), # expected values from spreadsheet + height + (ChangeAxis.JACK_REAR, 0.0, -111.3188069), # expected values from spreadsheet + height + (ChangeAxis.SLIDE, 3.3, 10.54157171), # expected values from spreadsheet + height + (ChangeAxis.SLIDE, 0.1, -26.15894011), # expected values from spreadsheet + height + ( + ChangeAxis.SLIDE, + BENCH_MIN_ANGLE, + -27.44574943, + ), # expected values from spreadsheet + height at min angle + ( + ChangeAxis.SLIDE, + BENCH_MIN_ANGLE - 0.1, + -27.44574943, + ), # expected values from spreadsheet + height at 0 deg because of cut off + ( + ChangeAxis.SLIDE, + BENCH_MAX_ANGLE, + 24.79311549, + ), # expected values from spreadsheet + height at max angle + ( + ChangeAxis.SLIDE, + BENCH_MAX_ANGLE + 1.0, + 24.79311549, + ), # expected values from spreadsheet + height at 4.8 deg because of cut off + ] + ) + def test_GIVEN_set_height_axis_WHEN_get_axis_value_THEN_j1_value_returned_and_axis_changed( + self, axis, angle, expected_result + ): bench = get_standard_bench() bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) param = AxisParameter("PARAM", bench, ChangeAxis.ANGLE) @@ -1062,18 +1300,22 @@ def test_GIVEN_set_height_axis_WHEN_get_axis_value_THEN_j1_value_returned_and_ax result = bench.beam_path_set_point.axis[axis].get_displacement() changed = bench.beam_path_set_point.axis[axis].is_changed - assert_that(result, is_(close_to(expected_result, 1E-6))) + assert_that(result, is_(close_to(expected_result, 1e-6))) assert_that(changed, is_(True), "axis is changed") - @parameterized.expand([ - (ChangeAxis.JACK_FRONT, 1, 1), - (ChangeAxis.JACK_REAR, 1, -1), - (ChangeAxis.JACK_FRONT, -4, -4), - (ChangeAxis.JACK_REAR, -2, 2), - (ChangeAxis.JACK_FRONT, 0, 0), - (ChangeAxis.JACK_REAR, 0, 0) - ]) - def test_GIVEN_set_seesaw_axis_with_0_angle_and_height_WHEN_get_axis_value_THEN_j1_value_returned_and_axis_changed(self, axis, see_saw, expected_result): + @parameterized.expand( + [ + (ChangeAxis.JACK_FRONT, 1, 1), + (ChangeAxis.JACK_REAR, 1, -1), + (ChangeAxis.JACK_FRONT, -4, -4), + (ChangeAxis.JACK_REAR, -2, 2), + (ChangeAxis.JACK_FRONT, 0, 0), + (ChangeAxis.JACK_REAR, 0, 0), + ] + ) + def test_GIVEN_set_seesaw_axis_with_0_angle_and_height_WHEN_get_axis_value_THEN_j1_value_returned_and_axis_changed( + self, axis, see_saw, expected_result + ): bench = get_standard_bench(with_angle=0) bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) param = AxisParameter("PARAM", bench, ChangeAxis.SEESAW) @@ -1085,14 +1327,53 @@ def test_GIVEN_set_seesaw_axis_with_0_angle_and_height_WHEN_get_axis_value_THEN_ assert_that(result, is_(close_to(expected_result, 1e-6))) assert_that(changed, is_(True), "axis is changed") - @parameterized.expand([ - ((ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), (), ( ), (ChangeAxis.JACK_FRONT,), True), - ((), (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), (ChangeAxis.JACK_FRONT,), (), False), - ((), (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), (ChangeAxis.JACK_REAR,), (), False), - ((), (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), (ChangeAxis.JACK_REAR,), (ChangeAxis.JACK_FRONT,), True), - ((), (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), (ChangeAxis.JACK_FRONT,), (ChangeAxis.SLIDE,), True), - ]) - def test_GIVEN_changing_true_on_some_axis_WHEN_changing_set_on_an_axis_THEN_bench_angle_seesaw_and_height_on_changing_to_expected_answer(self, inital_setup_false, inital_setup_true, axes_to_set_false, axes_to_set_true, expected_result): + @parameterized.expand( + [ + ( + (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), + (), + (), + (ChangeAxis.JACK_FRONT,), + True, + ), + ( + (), + (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), + (ChangeAxis.JACK_FRONT,), + (), + False, + ), + ( + (), + (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), + (ChangeAxis.JACK_REAR,), + (), + False, + ), + ( + (), + (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), + (ChangeAxis.JACK_REAR,), + (ChangeAxis.JACK_FRONT,), + True, + ), + ( + (), + (ChangeAxis.POSITION, ChangeAxis.ANGLE, ChangeAxis.SEESAW), + (ChangeAxis.JACK_FRONT,), + (ChangeAxis.SLIDE,), + True, + ), + ] + ) + def test_GIVEN_changing_true_on_some_axis_WHEN_changing_set_on_an_axis_THEN_bench_angle_seesaw_and_height_on_changing_to_expected_answer( + self, + inital_setup_false, + inital_setup_true, + axes_to_set_false, + axes_to_set_true, + expected_result, + ): bench = get_standard_bench(with_angle=0) bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) for axis in inital_setup_true: @@ -1110,12 +1391,10 @@ def test_GIVEN_changing_true_on_some_axis_WHEN_changing_set_on_an_axis_THEN_benc assert_that(bench.beam_path_rbv.axis[ChangeAxis.ANGLE].is_changing, is_(expected_result)) assert_that(bench.beam_path_rbv.axis[ChangeAxis.SEESAW].is_changing, is_(expected_result)) - @parameterized.expand([ - (1, 1, 1), - (-4, -4, -4), - (2, 2, 2) - ]) - def test_GIVEN_set_jacks_with_height_with_0_angle_at_seesaw_setpoint_0_WHEN_get_control_axis_value_THEN_values_correct(self, jack_front_position, jack_rear_position, expected_position): + @parameterized.expand([(1, 1, 1), (-4, -4, -4), (2, 2, 2)]) + def test_GIVEN_set_jacks_with_height_with_0_angle_at_seesaw_setpoint_0_WHEN_get_control_axis_value_THEN_values_correct( + self, jack_front_position, jack_rear_position, expected_position + ): bench = get_standard_bench(with_angle=0) bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) position = AxisParameter("PARAM", bench, ChangeAxis.POSITION) @@ -1123,19 +1402,27 @@ def test_GIVEN_set_jacks_with_height_with_0_angle_at_seesaw_setpoint_0_WHEN_get_ angle = AxisParameter("PARAM", bench, ChangeAxis.ANGLE) seesaw.sp = 0 - bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement(CorrectedReadbackUpdate(jack_front_position, AlarmSeverity.No, AlarmStatus.No)) - bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement(CorrectedReadbackUpdate(jack_rear_position, AlarmSeverity.No, AlarmStatus.No)) + bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement( + CorrectedReadbackUpdate(jack_front_position, AlarmSeverity.No, AlarmStatus.No) + ) + bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement( + CorrectedReadbackUpdate(jack_rear_position, AlarmSeverity.No, AlarmStatus.No) + ) assert_that(position.rbv, is_(close_to(expected_position, 1e-6))) assert_that(angle.rbv, is_(close_to(0, 1e-6))) assert_that(seesaw.rbv, is_(close_to(0, 1e-6))) - @parameterized.expand([ - (202.2279727, 476.9454087, 0, 12.3), - (-46.6006546, -106.4529773, 0, 0.1), - (-70.29056346, -160.1242782, 0, -1) - ]) - def test_GIVEN_set_jacks_with_height_seesaw_setpoint_0_WHEN_get_control_axis_value_THEN_values_correct(self, jack_front_position, jack_rear_position, expected_position, expected_angle): + @parameterized.expand( + [ + (202.2279727, 476.9454087, 0, 12.3), + (-46.6006546, -106.4529773, 0, 0.1), + (-70.29056346, -160.1242782, 0, -1), + ] + ) + def test_GIVEN_set_jacks_with_height_seesaw_setpoint_0_WHEN_get_control_axis_value_THEN_values_correct( + self, jack_front_position, jack_rear_position, expected_position, expected_angle + ): bench = get_standard_bench() bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) position = AxisParameter("PARAM", bench, ChangeAxis.POSITION) @@ -1143,20 +1430,28 @@ def test_GIVEN_set_jacks_with_height_seesaw_setpoint_0_WHEN_get_control_axis_val angle = AxisParameter("PARAM", bench, ChangeAxis.ANGLE) seesaw.sp = 0 - bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement(CorrectedReadbackUpdate(jack_front_position, AlarmSeverity.No, AlarmStatus.No)) - bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement(CorrectedReadbackUpdate(jack_rear_position, AlarmSeverity.No, AlarmStatus.No)) + bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement( + CorrectedReadbackUpdate(jack_front_position, AlarmSeverity.No, AlarmStatus.No) + ) + bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement( + CorrectedReadbackUpdate(jack_rear_position, AlarmSeverity.No, AlarmStatus.No) + ) assert_that(position.rbv, is_(close_to(expected_position, 1e-6))) assert_that(angle.rbv, is_(close_to(expected_angle, 1e-6))) assert_that(seesaw.rbv, is_(close_to(0, 1e-6))) - @parameterized.expand([ - (1, -1, ANGLE_OF_BENCH, 0, 1), - (-1, 1, ANGLE_OF_BENCH, 0, -1), - (-46.6006546 + 0.3, -106.4529773-0.3, 0.1, 0, 0.3), - (-46.6006546 + 0.3 + 0.5, -106.4529773 - 0.3 + 0.5, 0.1, + 0.5, 0.3), - ]) - def test_GIVEN_set_jacks_with_height_seesaw_setpoint_non_zero_WHEN_get_control_axis_value_THEN_values_correct(self, jack_front_position, jack_rear_position, angle_sp, expected_position, expected_seesaw): + @parameterized.expand( + [ + (1, -1, ANGLE_OF_BENCH, 0, 1), + (-1, 1, ANGLE_OF_BENCH, 0, -1), + (-46.6006546 + 0.3, -106.4529773 - 0.3, 0.1, 0, 0.3), + (-46.6006546 + 0.3 + 0.5, -106.4529773 - 0.3 + 0.5, 0.1, +0.5, 0.3), + ] + ) + def test_GIVEN_set_jacks_with_height_seesaw_setpoint_non_zero_WHEN_get_control_axis_value_THEN_values_correct( + self, jack_front_position, jack_rear_position, angle_sp, expected_position, expected_seesaw + ): bench = get_standard_bench() bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) position = AxisParameter("PARAM", bench, ChangeAxis.POSITION) @@ -1165,8 +1460,12 @@ def test_GIVEN_set_jacks_with_height_seesaw_setpoint_non_zero_WHEN_get_control_a angle.sp = angle_sp seesaw.sp = expected_seesaw - bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement(CorrectedReadbackUpdate(jack_front_position, AlarmSeverity.No, AlarmStatus.No)) - bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement(CorrectedReadbackUpdate(jack_rear_position, AlarmSeverity.No, AlarmStatus.No)) + bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement( + CorrectedReadbackUpdate(jack_front_position, AlarmSeverity.No, AlarmStatus.No) + ) + bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement( + CorrectedReadbackUpdate(jack_rear_position, AlarmSeverity.No, AlarmStatus.No) + ) assert_that(position.rbv, is_(close_to(expected_position, 1e-6))) assert_that(angle.rbv, is_(close_to(angle_sp, 1e-6))) @@ -1176,13 +1475,17 @@ def test_GIVEN_set_jacks_with_height_seesaw_setpoint_non_zero_WHEN_get_control_a MAJOR_ALARM = (AlarmSeverity.Major, AlarmStatus.HiHi) INVALID_ALARM = (AlarmSeverity.Invalid, AlarmStatus.Timeout) - @parameterized.expand([ - (MAJOR_ALARM, NO_ALARM, NO_ALARM, MAJOR_ALARM), - (NO_ALARM, MAJOR_ALARM, NO_ALARM, MAJOR_ALARM), - (NO_ALARM, NO_ALARM, MAJOR_ALARM, MAJOR_ALARM), - (INVALID_ALARM, NO_ALARM, MAJOR_ALARM, INVALID_ALARM) - ]) - def test_GIVEN_set_alarms_on_jacks_and_slide_WHEN_get_control_axis_alarm_THEN_alarm_is_most_sever(self, front_alarm, rear_alarm, slide_alarm, expected_alarm): + @parameterized.expand( + [ + (MAJOR_ALARM, NO_ALARM, NO_ALARM, MAJOR_ALARM), + (NO_ALARM, MAJOR_ALARM, NO_ALARM, MAJOR_ALARM), + (NO_ALARM, NO_ALARM, MAJOR_ALARM, MAJOR_ALARM), + (INVALID_ALARM, NO_ALARM, MAJOR_ALARM, INVALID_ALARM), + ] + ) + def test_GIVEN_set_alarms_on_jacks_and_slide_WHEN_get_control_axis_alarm_THEN_alarm_is_most_sever( + self, front_alarm, rear_alarm, slide_alarm, expected_alarm + ): bench = get_standard_bench() bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) position = AxisParameter("PARAM", bench, ChangeAxis.POSITION) @@ -1191,19 +1494,33 @@ def test_GIVEN_set_alarms_on_jacks_and_slide_WHEN_get_control_axis_alarm_THEN_al angle.sp = 0 seesaw.sp = 0 - bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement(CorrectedReadbackUpdate(0, *front_alarm)) - bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement(CorrectedReadbackUpdate(0, *rear_alarm)) - bench.beam_path_rbv.axis[ChangeAxis.SLIDE].set_displacement(CorrectedReadbackUpdate(0, *slide_alarm)) + bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement( + CorrectedReadbackUpdate(0, *front_alarm) + ) + bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement( + CorrectedReadbackUpdate(0, *rear_alarm) + ) + bench.beam_path_rbv.axis[ChangeAxis.SLIDE].set_displacement( + CorrectedReadbackUpdate(0, *slide_alarm) + ) assert_that(position.alarm_severity, is_(expected_alarm[0])) assert_that(position.alarm_status, is_(expected_alarm[1])) - @parameterized.expand([((ANGLE_OF_BENCH, 1, 0), 2, (2, 2, None)), - ((0, 1, 0), 0, (-48.74306278, -111.3188069, None)), - ((0.1, 1, 0), 0, (-46.6006546, -106.4529773, None)), - ((0, 1, 2), 0, (-48.74306278+2, -111.3188069-2, None)),]) - def test_GIVEN_bench_in_location_WHEN_define_position_THEN_new_height_defined(self, initial_position, define_height_as, expected_positions): - expected_jack_front_height, expected_jack_rear_height, expected_slide_position = expected_positions + @parameterized.expand( + [ + ((ANGLE_OF_BENCH, 1, 0), 2, (2, 2, None)), + ((0, 1, 0), 0, (-48.74306278, -111.3188069, None)), + ((0.1, 1, 0), 0, (-46.6006546, -106.4529773, None)), + ((0, 1, 2), 0, (-48.74306278 + 2, -111.3188069 - 2, None)), + ] + ) + def test_GIVEN_bench_in_location_WHEN_define_position_THEN_new_height_defined( + self, initial_position, define_height_as, expected_positions + ): + expected_jack_front_height, expected_jack_rear_height, expected_slide_position = ( + expected_positions + ) position, angle, seesaw, j1_axis, j2_axis, slide_axis = self._setup_bench(initial_position) position.define_current_value_as.new_value_sp_rbv = define_height_as @@ -1212,12 +1529,28 @@ def test_GIVEN_bench_in_location_WHEN_define_position_THEN_new_height_defined(se assert_that(j2_axis.set_position_as_value, is_(close_to(expected_jack_rear_height, 1e-6))) assert_that(slide_axis.set_position_as_value, is_(None)) - @parameterized.expand([((0.1, 0, 0), 0, (-48.74306278, -111.3188069, -27.44574943)), # no height or seesaw set to 0 - ((2, 0, 0), 0.1, (-46.6006546, -106.4529773, -26.15894011)), # no height or seesaw set to non-zero - ((0, 0, 2), 0, (-48.74306278+2, -111.3188069-2, -27.44574943)), # with seesaw - ((0, 1, 2), 0, (-48.74306278+1+2, -111.3188069+1-2, -27.44574943))]) # with height and seesaw - def test_GIVEN_bench_in_location_WHEN_define_angle_THEN_new_height_defined(self, initial_position, define_height_as, expected_positions): - expected_jack_front_height, expected_jack_rear_height, expected_slide_position = expected_positions + @parameterized.expand( + [ + ( + (0.1, 0, 0), + 0, + (-48.74306278, -111.3188069, -27.44574943), + ), # no height or seesaw set to 0 + ( + (2, 0, 0), + 0.1, + (-46.6006546, -106.4529773, -26.15894011), + ), # no height or seesaw set to non-zero + ((0, 0, 2), 0, (-48.74306278 + 2, -111.3188069 - 2, -27.44574943)), # with seesaw + ((0, 1, 2), 0, (-48.74306278 + 1 + 2, -111.3188069 + 1 - 2, -27.44574943)), + ] + ) # with height and seesaw + def test_GIVEN_bench_in_location_WHEN_define_angle_THEN_new_height_defined( + self, initial_position, define_height_as, expected_positions + ): + expected_jack_front_height, expected_jack_rear_height, expected_slide_position = ( + expected_positions + ) position, angle, seesaw, j1_axis, j2_axis, slide_axis = self._setup_bench(initial_position) angle.define_current_value_as.new_value_sp_rbv = define_height_as @@ -1226,12 +1559,28 @@ def test_GIVEN_bench_in_location_WHEN_define_angle_THEN_new_height_defined(self, assert_that(j2_axis.set_position_as_value, is_(close_to(expected_jack_rear_height, 1e-6))) assert_that(slide_axis.set_position_as_value, is_(close_to(expected_slide_position, 1e-6))) - @parameterized.expand([((0, 0, 2), 0, (-48.74306278, -111.3188069, -27.44574943)), # no height or angle set to 0 - ((0, 0, 0), 3, (-48.74306278 + 3.0, -111.3188069 - 3.0, -27.44574943)), # no height or angle set to non-zero - ((0.1, 0, 2), 0, (-46.6006546, -106.4529773, -26.15894011)), # no height but angle set - ((0.1, 1, 2), 0, (-46.6006546 + 1, -106.4529773 + 1, -26.15894011))]) # height, and angle - def test_GIVEN_bench_in_location_WHEN_define_seesaw_THEN_new_height_defined(self, initial_position, define_height_as, expected_positions): - expected_jack_front_height, expected_jack_rear_height, expected_slide_position = expected_positions + @parameterized.expand( + [ + ( + (0, 0, 2), + 0, + (-48.74306278, -111.3188069, -27.44574943), + ), # no height or angle set to 0 + ( + (0, 0, 0), + 3, + (-48.74306278 + 3.0, -111.3188069 - 3.0, -27.44574943), + ), # no height or angle set to non-zero + ((0.1, 0, 2), 0, (-46.6006546, -106.4529773, -26.15894011)), # no height but angle set + ((0.1, 1, 2), 0, (-46.6006546 + 1, -106.4529773 + 1, -26.15894011)), + ] + ) # height, and angle + def test_GIVEN_bench_in_location_WHEN_define_seesaw_THEN_new_height_defined( + self, initial_position, define_height_as, expected_positions + ): + expected_jack_front_height, expected_jack_rear_height, expected_slide_position = ( + expected_positions + ) position, angle, seesaw, j1_axis, j2_axis, slide_axis = self._setup_bench(initial_position) seesaw.define_current_value_as.new_value_sp_rbv = define_height_as @@ -1240,19 +1589,36 @@ def test_GIVEN_bench_in_location_WHEN_define_seesaw_THEN_new_height_defined(self assert_that(j2_axis.set_position_as_value, is_(close_to(expected_jack_rear_height, 1e-6))) assert_that(slide_axis.set_position_as_value, is_(close_to(expected_slide_position, 1e-6))) - @parameterized.expand([ - (0, 0, 0, ANGLE_OF_BENCH, 0, 0), # seesaw zero - (1, -1, 1, ANGLE_OF_BENCH, 0, 1), # seesaw non-zero - (0, 0, None, ANGLE_OF_BENCH, 0, 0), # autosave not set - (-46.6006546, -106.4529773, 0.0, 0.1, 0, 0.0), # non-zero angle and zero seesaw - (-46.6006546 + 0.3, -106.4529773-0.3, 0.3, 0.1, 0, 0.3), # non-zero angle and seesaw - (-46.6006546 + 0.3 + 0.5, -106.4529773 - 0.3 + 0.5, 0.3, 0.1, + 0.5, 0.3), # non-zero angle and height and seesaw - ]) - def test_GIVEN_jacks_init_with_height_seesaw_setpoint_set_WHEN_get_parameter_sp_THEN_values_correct(self, jack_front_position, jack_rear_position, seesaw_autosave, expected_angle, expected_position, expected_seesaw): + @parameterized.expand( + [ + (0, 0, 0, ANGLE_OF_BENCH, 0, 0), # seesaw zero + (1, -1, 1, ANGLE_OF_BENCH, 0, 1), # seesaw non-zero + (0, 0, None, ANGLE_OF_BENCH, 0, 0), # autosave not set + (-46.6006546, -106.4529773, 0.0, 0.1, 0, 0.0), # non-zero angle and zero seesaw + (-46.6006546 + 0.3, -106.4529773 - 0.3, 0.3, 0.1, 0, 0.3), # non-zero angle and seesaw + ( + -46.6006546 + 0.3 + 0.5, + -106.4529773 - 0.3 + 0.5, + 0.3, + 0.1, + +0.5, + 0.3, + ), # non-zero angle and height and seesaw + ] + ) + def test_GIVEN_jacks_init_with_height_seesaw_setpoint_set_WHEN_get_parameter_sp_THEN_values_correct( + self, + jack_front_position, + jack_rear_position, + seesaw_autosave, + expected_angle, + expected_position, + expected_seesaw, + ): bench = get_standard_bench() bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) position = AxisParameter("PARAM", bench, ChangeAxis.POSITION) - with patch('ReflectometryServer.parameters.param_float_autosave') as bench_autosave: + with patch("ReflectometryServer.parameters.param_float_autosave") as bench_autosave: bench_autosave.read_parameter.return_value = seesaw_autosave seesaw = AxisParameter("PARAM", bench, ChangeAxis.SEESAW, autosave=True) angle = AxisParameter("PARAM", bench, ChangeAxis.ANGLE) @@ -1281,12 +1647,12 @@ def setUp(self): self.param_trans = AxisParameter("TRANS", self.bench, ChangeAxis.TRANS) self.param_seesaw = AxisParameter("SEESAW", self.bench, ChangeAxis.SEESAW) self.param_chi = AxisParameter("CHI", self.bench, ChangeAxis.CHI) - + self.j1_axis = create_mock_axis("j1", 0, 1) self.j2_axis = create_mock_axis("j2", 0, 1) self.slide_axis = create_mock_axis("axis", 0, 1) self.arc_axis = create_mock_axis("arc", 0, 1) - + j1_driver = IocDriver(self.bench, ChangeAxis.JACK_FRONT, self.j1_axis) j2_driver = IocDriver(self.bench, ChangeAxis.JACK_REAR, self.j2_axis) slide_driver = IocDriver(self.bench, ChangeAxis.SLIDE, self.slide_axis) @@ -1336,7 +1702,9 @@ def test_GIVEN_vertical_bench_WHEN_moving_seesaw_THEN_jacks_move_to_correct_posi assert_that(self.slide_axis.rbv, is_(self.initial_motor_position)) assert_that(self.arc_axis.rbv, is_(self.initial_motor_position)) - def test_GIVEN_vertical_bench_WHEN_moving_chi_THEN_jacks_and_slide_move_to_correct_position(self): + def test_GIVEN_vertical_bench_WHEN_moving_chi_THEN_jacks_and_slide_move_to_correct_position( + self, + ): bench_angle = 0 # relative to natural beam of -2.3 deg # Expected values taken from bench calc spreadsheet expected_j1 = -48.74306278 @@ -1356,13 +1724,17 @@ def test_GIVEN_vertical_bench_WHEN_moving_chi_THEN_jacks_and_slide_move_to_corre MAJOR_ALARM = (AlarmSeverity.Major, AlarmStatus.HiHi) INVALID_ALARM = (AlarmSeverity.Invalid, AlarmStatus.Timeout) - @parameterized.expand([ - (MAJOR_ALARM, NO_ALARM, NO_ALARM, MAJOR_ALARM), - (NO_ALARM, MAJOR_ALARM, NO_ALARM, MAJOR_ALARM), - (NO_ALARM, NO_ALARM, MAJOR_ALARM, MAJOR_ALARM), - (INVALID_ALARM, NO_ALARM, MAJOR_ALARM, INVALID_ALARM) - ]) - def test_GIVEN_set_alarms_on_jacks_and_slide_for_vertical_bench_WHEN_get_control_axis_alarm_THEN_alarm_is_most_sever(self, front_alarm, rear_alarm, slide_alarm, expected_alarm): + @parameterized.expand( + [ + (MAJOR_ALARM, NO_ALARM, NO_ALARM, MAJOR_ALARM), + (NO_ALARM, MAJOR_ALARM, NO_ALARM, MAJOR_ALARM), + (NO_ALARM, NO_ALARM, MAJOR_ALARM, MAJOR_ALARM), + (INVALID_ALARM, NO_ALARM, MAJOR_ALARM, INVALID_ALARM), + ] + ) + def test_GIVEN_set_alarms_on_jacks_and_slide_for_vertical_bench_WHEN_get_control_axis_alarm_THEN_alarm_is_most_sever( + self, front_alarm, rear_alarm, slide_alarm, expected_alarm + ): bench = get_standard_bench(vertical_mode=True) bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) chi = AxisParameter("PARAM", bench, ChangeAxis.CHI) @@ -1374,9 +1746,15 @@ def test_GIVEN_set_alarms_on_jacks_and_slide_for_vertical_bench_WHEN_get_control angle.sp = 0 seesaw.sp = 0 - bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement(CorrectedReadbackUpdate(0, *front_alarm)) - bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement(CorrectedReadbackUpdate(0, *rear_alarm)) - bench.beam_path_rbv.axis[ChangeAxis.SLIDE].set_displacement(CorrectedReadbackUpdate(0, *slide_alarm)) + bench.beam_path_rbv.axis[ChangeAxis.JACK_FRONT].set_displacement( + CorrectedReadbackUpdate(0, *front_alarm) + ) + bench.beam_path_rbv.axis[ChangeAxis.JACK_REAR].set_displacement( + CorrectedReadbackUpdate(0, *rear_alarm) + ) + bench.beam_path_rbv.axis[ChangeAxis.SLIDE].set_displacement( + CorrectedReadbackUpdate(0, *slide_alarm) + ) for param in [chi, trans, seesaw]: assert_that(param.alarm_severity, is_(expected_alarm[0])) @@ -1385,15 +1763,53 @@ def test_GIVEN_set_alarms_on_jacks_and_slide_for_vertical_bench_WHEN_get_control assert_that(angle.alarm_severity, is_(AlarmSeverity.No)) assert_that(angle.alarm_status, is_(AlarmStatus.No)) - - @parameterized.expand([ - ((ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), (), ( ), (ChangeAxis.JACK_FRONT,), True), - ((), (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), (ChangeAxis.JACK_FRONT,), (), False), - ((), (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), (ChangeAxis.JACK_REAR,), (), False), - ((), (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), (ChangeAxis.JACK_REAR,), (ChangeAxis.JACK_FRONT,), True), - ((), (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), (ChangeAxis.JACK_FRONT,), (ChangeAxis.SLIDE,), True), - ]) - def test_GIVEN_changing_true_on_some_axis_WHEN_changing_set_on_an_axis_THEN_bench_chi_seesaw_and_trans_on_changing_to_expected_answer(self, inital_setup_false, inital_setup_true, axes_to_set_false, axes_to_set_true, expected_result): + @parameterized.expand( + [ + ( + (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), + (), + (), + (ChangeAxis.JACK_FRONT,), + True, + ), + ( + (), + (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), + (ChangeAxis.JACK_FRONT,), + (), + False, + ), + ( + (), + (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), + (ChangeAxis.JACK_REAR,), + (), + False, + ), + ( + (), + (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), + (ChangeAxis.JACK_REAR,), + (ChangeAxis.JACK_FRONT,), + True, + ), + ( + (), + (ChangeAxis.TRANS, ChangeAxis.CHI, ChangeAxis.SEESAW), + (ChangeAxis.JACK_FRONT,), + (ChangeAxis.SLIDE,), + True, + ), + ] + ) + def test_GIVEN_changing_true_on_some_axis_WHEN_changing_set_on_an_axis_THEN_bench_chi_seesaw_and_trans_on_changing_to_expected_answer( + self, + inital_setup_false, + inital_setup_true, + axes_to_set_false, + axes_to_set_true, + expected_result, + ): bench = get_standard_bench(with_angle=0, vertical_mode=True) bench.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, 0)) for axis in inital_setup_true: @@ -1414,5 +1830,5 @@ def test_GIVEN_changing_true_on_some_axis_WHEN_changing_set_on_an_axis_THEN_benc assert_that(bench.beam_path_rbv.axis[ChangeAxis.ANGLE].is_changing, is_(False)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_config/refl/config.py b/ReflectometryServer/test_modules/test_config/refl/config.py index b6bed9de..9e973519 100644 --- a/ReflectometryServer/test_modules/test_config/refl/config.py +++ b/ReflectometryServer/test_modules/test_config/refl/config.py @@ -19,10 +19,24 @@ def get_beamline(macros): add_component(Component("s1", PositionAndAngle(0.0, 1, perp_to_floor))) super_mirror = add_component(ReflectingComponent("sm", PositionAndAngle(0.0, 5, perp_to_floor))) - add_parameter(InBeamParameter("smenabled", super_mirror), modes=[pnr], mode_inits=[(nr, False), (pnr, True)]) - add_parameter(AxisParameter("smangle", super_mirror, ChangeAxis.ANGLE), modes=[pnr], mode_inits=[(nr, 0.0), (pnr, 0.5)]) - add_driver(IocDriver(super_mirror, ChangeAxis.POSITION, create_mock_axis("MOT:MTR0101", 0, 1), - out_of_beam_positions=[OutOfBeamPosition(position=-10)])) + add_parameter( + InBeamParameter("smenabled", super_mirror), + modes=[pnr], + mode_inits=[(nr, False), (pnr, True)], + ) + add_parameter( + AxisParameter("smangle", super_mirror, ChangeAxis.ANGLE), + modes=[pnr], + mode_inits=[(nr, 0.0), (pnr, 0.5)], + ) + add_driver( + IocDriver( + super_mirror, + ChangeAxis.POSITION, + create_mock_axis("MOT:MTR0101", 0, 1), + out_of_beam_positions=[OutOfBeamPosition(position=-10)], + ) + ) s2 = add_component(Component("s2", PositionAndAngle(0.0, 9, perp_to_floor))) add_parameter(AxisParameter("slit2pos", s2, ChangeAxis.POSITION), modes=[nr, pnr]) @@ -40,7 +54,9 @@ def get_beamline(macros): add_parameter(AxisParameter("slit4pos", s4, ChangeAxis.POSITION), modes=[nr, pnr]) point_det = add_component(TiltingComponent("det", PositionAndAngle(0.0, 20, perp_to_floor))) - add_parameter(AxisParameter("detpos", point_det, ChangeAxis.POSITION), modes=[nr, pnr, disabled]) + add_parameter( + AxisParameter("detpos", point_det, ChangeAxis.POSITION), modes=[nr, pnr, disabled] + ) theta.add_angle_to(point_det) if optional_is_set(1, macros): diff --git a/ReflectometryServer/test_modules/test_config/refl/invalid_config.py b/ReflectometryServer/test_modules/test_config/refl/invalid_config.py index 19db0c14..8534d919 100644 --- a/ReflectometryServer/test_modules/test_config/refl/invalid_config.py +++ b/ReflectometryServer/test_modules/test_config/refl/invalid_config.py @@ -1,3 +1,4 @@ +# ruff: noqa # FOR TESTING # Configuration script for a refelctometry beamline which contains errors diff --git a/ReflectometryServer/test_modules/test_config/refl/other_config.py b/ReflectometryServer/test_modules/test_config/refl/other_config.py index b76cd7f5..144181ee 100644 --- a/ReflectometryServer/test_modules/test_config/refl/other_config.py +++ b/ReflectometryServer/test_modules/test_config/refl/other_config.py @@ -2,7 +2,6 @@ # Valid configuration script for a reflectometry beamline from ReflectometryServer import * -from ReflectometryServer.test_modules.data_mother import create_mock_axis OTHER_CONFIG_PARAM = "other_config_param" @@ -14,8 +13,12 @@ def get_beamline(macros): # MODES nr = add_mode("nr") - other_config_comp = add_component(Component("other_config_comp", PositionAndAngle(0.0, 1, perp_to_floor))) - add_parameter(AxisParameter(OTHER_CONFIG_PARAM, other_config_comp, ChangeAxis.POSITION), modes=[nr]) + other_config_comp = add_component( + Component("other_config_comp", PositionAndAngle(0.0, 1, perp_to_floor)) + ) + add_parameter( + AxisParameter(OTHER_CONFIG_PARAM, other_config_comp, ChangeAxis.POSITION), modes=[nr] + ) add_beam_start(PositionAndAngle(0.0, 0.0, beam_angle_natural)) diff --git a/ReflectometryServer/test_modules/test_config_helper.py b/ReflectometryServer/test_modules/test_config_helper.py index 15de8e59..40d869b6 100644 --- a/ReflectometryServer/test_modules/test_config_helper.py +++ b/ReflectometryServer/test_modules/test_config_helper.py @@ -14,15 +14,15 @@ class TestConfigHelper(unittest.TestCase): def setUp(self): ConfigHelper.reset() - def test_GIVEN_no_beam_line_constants_added_WHEN_get_beamline_and_config_helper_THEN_constants_are_empty(self): - + def test_GIVEN_no_beam_line_constants_added_WHEN_get_beamline_and_config_helper_THEN_constants_are_empty( + self, + ): result = get_configured_beamline() assert_that(result.beamline_constants, is_([])) assert_that(ConfigHelper.constants, is_([])) def test_GIVEN_no_components_added_WHEN_get_beamline_THEN_components_are_empty(self): - result = get_configured_beamline() assert_that(ConfigHelper.components, is_([])) @@ -38,7 +38,10 @@ def test_GIVEN_beam_line_constants_added_WHEN_get_beamline_THEN_beamline_has_con result = get_configured_beamline() - assert_that(result.beamline_constants, only_contains(expected_constant1, expected_constant2, expected_constant3)) + assert_that( + result.beamline_constants, + only_contains(expected_constant1, expected_constant2, expected_constant3), + ) def test_GIVEN_beam_line_components_added_WHEN_get_beamline_THEN_beam_line_has_components(self): expected1 = Component("1", PositionAndAngle(0, 10, 90)) @@ -54,7 +57,6 @@ def test_GIVEN_beam_line_components_added_WHEN_get_beamline_THEN_beam_line_has_c assert_that(ConfigHelper.components, only_contains(expected1, expected2, expected3)) def test_GIVEN_no_parameters_added_WHEN_get_beamline_THEN_parameters_are_empty(self): - result = get_configured_beamline() assert_that(ConfigHelper.parameters, is_([])) @@ -77,7 +79,6 @@ def test_GIVEN_beam_line_parameter_added_WHEN_get_beamline_THEN_beam_line_has_pa assert_that(result.parameters.values(), only_contains(expected1, expected2)) def test_GIVEN_no_mode_added_WHEN_get_beamline_THEN_modes_are_empty(self): - result = get_configured_beamline() assert_that(ConfigHelper.modes, is_([])) @@ -110,7 +111,9 @@ def test_GIVEN_parameter_has_mode_WHEN_get_beamline_THEN_parameter_in_mode(self) assert_that(result.get_param_names_in_mode(), only_contains(expected_name1)) - def test_GIVEN_multiple_parameter_with_modes_WHEN_get_beamline_THEN_parameter_in_given_mode(self): + def test_GIVEN_multiple_parameter_with_modes_WHEN_get_beamline_THEN_parameter_in_given_mode( + self, + ): param_name1 = "param1" param_name2 = "param2" comp1 = Component("1", PositionAndAngle(0, 0, 1)) @@ -131,7 +134,9 @@ def test_GIVEN_multiple_parameter_with_modes_WHEN_get_beamline_THEN_parameter_in result.active_mode = mode2 assert_that(result.get_param_names_in_mode(), only_contains(param_name1, param_name2)) - def test_GIVEN_multiple_parameter_with_modes_and_inits_WHEN_get_beamline_THEN_parameter_in_given_mode(self): + def test_GIVEN_multiple_parameter_with_modes_and_inits_WHEN_get_beamline_THEN_parameter_in_given_mode( + self, + ): param_name1 = "param1" param_name2 = "param2" comp1 = Component("1", PositionAndAngle(0, 0, 1)) @@ -167,7 +172,6 @@ def test_GIVEN_parameter_has_initial_value_WHEN_add_THEN_initial_values_added_to assert_that(result, has_entry(expected_name1, expected_init)) def test_GIVEN_no_driver_added_WHEN_get_beamline_THEN_drivers_are_empty(self): - result = get_configured_beamline() assert_that(ConfigHelper.drivers, is_([])) @@ -181,40 +185,63 @@ def test_GIVEN_add_driver_WHEN_get_beamline_THEN_driver_added(self): assert_that(result.drivers, only_contains(driver)) - def test_GIVEN_add_slits_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist(self): - + def test_GIVEN_add_slits_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist( + self, + ): with patch("ReflectometryServer.config_helper.create_jaws_pv_driver") as jaws_wrapper_mock: - add_slit_parameters(1) result = ConfigHelper.parameters - assert_that([parameter.name for parameter in result], contains_inanyorder("S1VG", "S1HG")) - - assert_that([call[0][0] for call in jaws_wrapper_mock.call_args_list], only_contains("MOT:JAWS1")) - assert_that([call[0][1] for call in jaws_wrapper_mock.call_args_list], contains_exactly(True, False)) - assert_that([call[0][2] for call in jaws_wrapper_mock.call_args_list], contains_exactly(True, True)) - - def test_GIVEN_add_slits_and_gaps_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_and_centres_exist(self): - + assert_that( + [parameter.name for parameter in result], contains_inanyorder("S1VG", "S1HG") + ) + + assert_that( + [call[0][0] for call in jaws_wrapper_mock.call_args_list], + only_contains("MOT:JAWS1"), + ) + assert_that( + [call[0][1] for call in jaws_wrapper_mock.call_args_list], + contains_exactly(True, False), + ) + assert_that( + [call[0][2] for call in jaws_wrapper_mock.call_args_list], + contains_exactly(True, True), + ) + + def test_GIVEN_add_slits_and_gaps_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_and_centres_exist( + self, + ): with patch("ReflectometryServer.config_helper.create_jaws_pv_driver") as jaws_wrapper_mock: - add_slit_parameters(1, include_centres=True) result = ConfigHelper.parameters - assert_that([parameter.name for parameter in result], contains_inanyorder("S1VG", "S1VC", "S1HG", "S1HC")) - - assert_that([call[0][0] for call in jaws_wrapper_mock.call_args_list], only_contains("MOT:JAWS1")) - assert_that([call[0][1] for call in jaws_wrapper_mock.call_args_list], contains(True, True, False, False)) - assert_that([call[0][2] for call in jaws_wrapper_mock.call_args_list], contains(True, False, True, False)) - - - def test_GIVEN_add_slits_and_gaps_into_modes_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist(self): + assert_that( + [parameter.name for parameter in result], + contains_inanyorder("S1VG", "S1VC", "S1HG", "S1HC"), + ) + + assert_that( + [call[0][0] for call in jaws_wrapper_mock.call_args_list], + only_contains("MOT:JAWS1"), + ) + assert_that( + [call[0][1] for call in jaws_wrapper_mock.call_args_list], + contains(True, True, False, False), + ) + assert_that( + [call[0][2] for call in jaws_wrapper_mock.call_args_list], + contains(True, False, True, False), + ) + + def test_GIVEN_add_slits_and_gaps_into_modes_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist( + self, + ): mode = add_mode("mode") mode1 = add_mode("mode1") with patch("ReflectometryServer.config_helper.create_jaws_pv_driver"): - add_slit_parameters(1, modes=[mode, mode1], include_centres=True) result = ConfigHelper.mode_params @@ -222,106 +249,156 @@ def test_GIVEN_add_slits_and_gaps_into_modes_WHEN_get_parameters_in_config_THEN_ assert_that(result[mode], contains_inanyorder("S1VG", "S1VC", "S1HG", "S1HC")) assert_that(result[mode1], contains_inanyorder("S1VG", "S1VC", "S1HG", "S1HC")) - def test_GIVEN_add_slits_and_gaps_excluding_VC_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist_except_VC(self): - + def test_GIVEN_add_slits_and_gaps_excluding_VC_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist_except_VC( + self, + ): with patch("ReflectometryServer.config_helper.create_jaws_pv_driver"): - add_slit_parameters(1, exclude="VC", include_centres=True) result = ConfigHelper.parameters - assert_that([parameter.name for parameter in result], contains_inanyorder("S1VG", "S1HG", "S1HC")) - - def test_GIVEN_add_slits_and_gaps_excluding_VC_using_helper_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist_except_VC(self): + assert_that( + [parameter.name for parameter in result], contains_inanyorder("S1VG", "S1HG", "S1HC") + ) + def test_GIVEN_add_slits_and_gaps_excluding_VC_using_helper_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist_except_VC( + self, + ): with patch("ReflectometryServer.config_helper.create_jaws_pv_driver"): - add_slit_parameters(1, exclude=SlitAxes.VertCent, include_centres=True) result = ConfigHelper.parameters - assert_that([parameter.name for parameter in result], contains_inanyorder("S1VG", "S1HG", "S1HC")) - - def test_GIVEN_add_slits_with_beam_blocker_N_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist(self): + assert_that( + [parameter.name for parameter in result], contains_inanyorder("S1VG", "S1HG", "S1HC") + ) + def test_GIVEN_add_slits_with_beam_blocker_N_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist( + self, + ): with patch("ReflectometryServer.config_helper.create_blade_pv_driver") as blade_drivers: with patch("ReflectometryServer.config_helper.create_jaws_pv_driver") as jaws_driver: - add_slit_parameters(1, beam_blocker="N") result = ConfigHelper.parameters - assert_that([parameter.name for parameter in result], contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1N", "S1S")) - - assert_that([call[0][0] for call in blade_drivers.call_args_list], only_contains("MOT:JAWS1")) - assert_that([call[0][1] for call in blade_drivers.call_args_list], contains_exactly("N", "S")) - - def test_GIVEN_add_slits_with_beam_blocker_S_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist(self): - + assert_that( + [parameter.name for parameter in result], + contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1N", "S1S"), + ) + + assert_that( + [call[0][0] for call in blade_drivers.call_args_list], + only_contains("MOT:JAWS1"), + ) + assert_that( + [call[0][1] for call in blade_drivers.call_args_list], + contains_exactly("N", "S"), + ) + + def test_GIVEN_add_slits_with_beam_blocker_S_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist( + self, + ): with patch("ReflectometryServer.config_helper.create_blade_pv_driver") as blade_drivers: with patch("ReflectometryServer.config_helper.create_jaws_pv_driver") as jaws_driver: - add_slit_parameters(1, beam_blocker="S") result = ConfigHelper.parameters parameters = {parameter.name: parameter for parameter in result} - assert_that(parameters.keys(), contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1N", "S1S")) + assert_that( + parameters.keys(), contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1N", "S1S") + ) assert_that(parameters["S1BLOCK"].options, contains_inanyorder("No", "South")) - assert_that([call[0][0] for call in blade_drivers.call_args_list], only_contains("MOT:JAWS1")) - assert_that([call[0][1] for call in blade_drivers.call_args_list], contains_exactly("N", "S")) - - def test_GIVEN_add_slits_with_beam_blocker_E_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist(self): - + assert_that( + [call[0][0] for call in blade_drivers.call_args_list], + only_contains("MOT:JAWS1"), + ) + assert_that( + [call[0][1] for call in blade_drivers.call_args_list], + contains_exactly("N", "S"), + ) + + def test_GIVEN_add_slits_with_beam_blocker_E_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist( + self, + ): with patch("ReflectometryServer.config_helper.create_blade_pv_driver") as blade_drivers: with patch("ReflectometryServer.config_helper.create_jaws_pv_driver") as jaws_driver: - add_slit_parameters(1, beam_blocker="E") result = ConfigHelper.parameters parameters = {parameter.name: parameter for parameter in result} - assert_that(parameters.keys(), contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1E", "S1W")) + assert_that( + parameters.keys(), contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1E", "S1W") + ) assert_that(parameters["S1BLOCK"].options, contains_inanyorder("No", "East")) - assert_that([call[0][0] for call in blade_drivers.call_args_list], only_contains("MOT:JAWS1")) - assert_that([call[0][1] for call in blade_drivers.call_args_list], contains_exactly("E", "W")) - - def test_GIVEN_add_slits_with_beam_blocker_EN_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist(self): - + assert_that( + [call[0][0] for call in blade_drivers.call_args_list], + only_contains("MOT:JAWS1"), + ) + assert_that( + [call[0][1] for call in blade_drivers.call_args_list], + contains_exactly("E", "W"), + ) + + def test_GIVEN_add_slits_with_beam_blocker_EN_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist( + self, + ): with patch("ReflectometryServer.config_helper.create_blade_pv_driver") as blade_drivers: with patch("ReflectometryServer.config_helper.create_jaws_pv_driver") as jaws_driver: - add_slit_parameters(1, beam_blocker="EN") result = ConfigHelper.parameters parameters = {parameter.name: parameter for parameter in result} - assert_that(parameters.keys(), contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1E", "S1W", "S1S", "S1N")) - assert_that(parameters["S1BLOCK"].options, contains_inanyorder("No", "East", "North")) - - assert_that([call[0][0] for call in blade_drivers.call_args_list], only_contains("MOT:JAWS1")) - assert_that([call[0][1] for call in blade_drivers.call_args_list], contains_inanyorder("N", "S", "E", "W")) - - def test_GIVEN_add_slits_with_beam_blocker_EN_and_exclude_W_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist(self): - + assert_that( + parameters.keys(), + contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1E", "S1W", "S1S", "S1N"), + ) + assert_that( + parameters["S1BLOCK"].options, contains_inanyorder("No", "East", "North") + ) + + assert_that( + [call[0][0] for call in blade_drivers.call_args_list], + only_contains("MOT:JAWS1"), + ) + assert_that( + [call[0][1] for call in blade_drivers.call_args_list], + contains_inanyorder("N", "S", "E", "W"), + ) + + def test_GIVEN_add_slits_with_beam_blocker_EN_and_exclude_W_WHEN_get_parameters_in_config_THEN_parameters_for_all_slit_gaps_exist( + self, + ): with patch("ReflectometryServer.config_helper.create_blade_pv_driver") as blade_drivers: with patch("ReflectometryServer.config_helper.create_jaws_pv_driver") as jaws_driver: - add_slit_parameters(1, beam_blocker="EN", exclude=["W"]) result = ConfigHelper.parameters parameters = {parameter.name: parameter for parameter in result} - assert_that(parameters.keys(), contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1E", "S1S", "S1N")) - assert_that(parameters["S1BLOCK"].options, contains_inanyorder("No", "East", "North")) - - assert_that([call[0][0] for call in blade_drivers.call_args_list], only_contains("MOT:JAWS1")) - assert_that([call[0][1] for call in blade_drivers.call_args_list], contains_inanyorder("N", "S", "E")) + assert_that( + parameters.keys(), + contains_inanyorder("S1VG", "S1HG", "S1BLOCK", "S1E", "S1S", "S1N"), + ) + assert_that( + parameters["S1BLOCK"].options, contains_inanyorder("No", "East", "North") + ) + + assert_that( + [call[0][0] for call in blade_drivers.call_args_list], + only_contains("MOT:JAWS1"), + ) + assert_that( + [call[0][1] for call in blade_drivers.call_args_list], + contains_inanyorder("N", "S", "E"), + ) def test_GIVEN_no_beam_start_added_WHEN_get_beamline_THEN_beamstart_is_none(self): - result = get_configured_beamline() assert_that(ConfigHelper.beam_start, is_(None)) @@ -337,7 +414,6 @@ def test_GIVEN_add_beam_start_WHEN_get_beamline_THEN_beam_start_is_correct(self) assert_that(comp.beam_path_set_point.get_outgoing_beam(), is_(expected_beam_start)) def test_GIVEN_no_footprint_added_WHEN_get_beamline_THEN_footprint_is_none(self): - result = get_configured_beamline() assert_that(ConfigHelper.footprint_setup, is_(None)) @@ -348,13 +424,17 @@ def test_GIVEN_add_footprint_setup_WHEN_get_beamline_THEN_beam_start_is_correct( param = AxisParameter("name", comp, ChangeAxis.POSITION) expected_sample_length = 10 - footprint = add_footprint_setup(FootprintSetup(1, 2, 3, 4, 5, param, param, param, param, param, -1, 1)) + footprint = add_footprint_setup( + FootprintSetup(1, 2, 3, 4, 5, param, param, param, param, param, -1, 1) + ) footprint.sample_length = expected_sample_length result = get_configured_beamline() assert_that(result.footprint_manager.get_sample_length(), is_(expected_sample_length)) - def test_GIVEN_beam_line_parameter_driver_and_component_added_at_marker_WHEN_get_parameters_THEN_inserted_at_right_place(self): + def test_GIVEN_beam_line_parameter_driver_and_component_added_at_marker_WHEN_get_parameters_THEN_inserted_at_right_place( + self, + ): comp1 = Component("1", PositionAndAngle(0, 0, 1)) expected1 = AxisParameter("param1", comp1, ChangeAxis.POSITION) driver1 = IocDriver(comp1, ChangeAxis.POSITION, create_mock_axis("MOT0101", 1, 1)) @@ -397,7 +477,9 @@ def test_GIVEN_optional_is_not_set_WHEN_checking_optional_is_set_THEN_return_fal assert_that(actual, is_(expected)) @parameterized.expand([("True", True), ("false", False), ("Nonesense", False)]) - def test_GIVEN_optional_is_set_to_string_WHEN_checking_optional_is_set_THEN_return_expected_bool(self, string, expected): + def test_GIVEN_optional_is_set_to_string_WHEN_checking_optional_is_set_THEN_return_expected_bool( + self, string, expected + ): macros = {"OPTIONAL_1": string} optional_id = 1 @@ -405,7 +487,9 @@ def test_GIVEN_optional_is_set_to_string_WHEN_checking_optional_is_set_THEN_retu assert_that(actual, is_(expected)) - def test_WHEN_creating_mode_correction_with_helper_method_THEN_mode_select_correction_returned(self): + def test_WHEN_creating_mode_correction_with_helper_method_THEN_mode_select_correction_returned( + self, + ): correction = 1 mode_name = "nr" @@ -413,7 +497,9 @@ def test_WHEN_creating_mode_correction_with_helper_method_THEN_mode_select_corre assert_that(result, instance_of(ModeSelectCorrection)) - def test_GIVEN_value_and_mode_WHEN_creating_mode_correction_with_helper_method_THEN_default_correction_is_0(self): + def test_GIVEN_value_and_mode_WHEN_creating_mode_correction_with_helper_method_THEN_default_correction_is_0( + self, + ): correction = 1 mode_name = "nr" mode_correction = as_mode_correction(correction, [mode_name]) @@ -422,7 +508,9 @@ def test_GIVEN_value_and_mode_WHEN_creating_mode_correction_with_helper_method_T assert_that(result, is_(0)) - def test_GIVEN_value_and_mode_WHEN_creating_mode_correction_with_helper_method_THEN_correction_in_mode_behaves_correctly(self): + def test_GIVEN_value_and_mode_WHEN_creating_mode_correction_with_helper_method_THEN_correction_in_mode_behaves_correctly( + self, + ): correction = 1 mode_name = "nr" mock_beamline = MockBeamline() @@ -435,5 +523,5 @@ def test_GIVEN_value_and_mode_WHEN_creating_mode_correction_with_helper_method_T assert_that(result, is_(correction * (-1))) # correction is subtracted -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_configuration.py b/ReflectometryServer/test_modules/test_configuration.py index 12035024..5f587d6f 100644 --- a/ReflectometryServer/test_modules/test_configuration.py +++ b/ReflectometryServer/test_modules/test_configuration.py @@ -1,18 +1,17 @@ import os -import sys import unittest from unittest.mock import patch -from ReflectometryServer import beamline_configuration, ConfigHelper -from ReflectometryServer.server_status_manager import STATUS import ReflectometryServer.server_status_manager +from ReflectometryServer import ConfigHelper, beamline_configuration +from ReflectometryServer.server_status_manager import STATUS from ReflectometryServer.test_modules.test_config.refl.config import OPTIONAL_PARAM_1 from ReflectometryServer.test_modules.test_config.refl.other_config import OTHER_CONFIG_PARAM PATH_TO_CONFGIS = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_config", "refl")) -class TestConfiguration(unittest.TestCase): +class TestConfiguration(unittest.TestCase): def setUp(self): ReflectometryServer.server_status_manager.STATUS_MANAGER._reset() ConfigHelper.reset() @@ -21,38 +20,66 @@ def test_WHEN_loading_valid_beamline_configuration_file_THEN_correct_PVs_are_cre beamline_configuration.REFL_CONFIG_PATH = PATH_TO_CONFGIS beamline = beamline_configuration.create_beamline_from_configuration({}) # Check Status PV - self.assertEqual(STATUS.OKAY, ReflectometryServer.server_status_manager.STATUS_MANAGER.status) + self.assertEqual( + STATUS.OKAY, ReflectometryServer.server_status_manager.STATUS_MANAGER.status + ) def test_WHEN_loading_invalid_beamline_configuration_file_THEN_status_PV_shows_error(self): beamline_configuration.REFL_CONFIG_PATH = PATH_TO_CONFGIS - beamline = beamline_configuration.create_beamline_from_configuration({"CONFIG_FILE": "invalid_config.py"}) + beamline = beamline_configuration.create_beamline_from_configuration( + {"CONFIG_FILE": "invalid_config.py"} + ) # Check Status PV - self.assertEqual(STATUS.ERROR, ReflectometryServer.server_status_manager.STATUS_MANAGER.status) + self.assertEqual( + STATUS.ERROR, ReflectometryServer.server_status_manager.STATUS_MANAGER.status + ) - def test_WHEN_loading_beamline_configuration_file_that_doesnt_exist_THEN_status_PV_shows_import_error(self): - beamline_configuration.REFL_CONFIG_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "refl")) - with patch("ReflectometryServer.beamline_configuration.import_module", side_effect=ImportError("")) as import_fn: + def test_WHEN_loading_beamline_configuration_file_that_doesnt_exist_THEN_status_PV_shows_import_error( + self, + ): + beamline_configuration.REFL_CONFIG_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), "refl") + ) + with patch( + "ReflectometryServer.beamline_configuration.import_module", side_effect=ImportError("") + ) as import_fn: beamline = beamline_configuration.create_beamline_from_configuration({}) # Check Status PV - self.assertEqual(STATUS.ERROR, ReflectometryServer.server_status_manager.STATUS_MANAGER.status) + self.assertEqual( + STATUS.ERROR, ReflectometryServer.server_status_manager.STATUS_MANAGER.status + ) - def test_GIVEN_valid_other_config_file_in_macro_WHEN_loading_config_THEN_other_config_is_loaded(self): + def test_GIVEN_valid_other_config_file_in_macro_WHEN_loading_config_THEN_other_config_is_loaded( + self, + ): beamline_configuration.REFL_CONFIG_PATH = PATH_TO_CONFGIS - beamline = beamline_configuration.create_beamline_from_configuration({"CONFIG_FILE": "other_config.py"}) + beamline = beamline_configuration.create_beamline_from_configuration( + {"CONFIG_FILE": "other_config.py"} + ) # Check Status PV - self.assertEqual(STATUS.OKAY, ReflectometryServer.server_status_manager.STATUS_MANAGER.status) + self.assertEqual( + STATUS.OKAY, ReflectometryServer.server_status_manager.STATUS_MANAGER.status + ) self.assertTrue(OTHER_CONFIG_PARAM in beamline.parameters.keys()) def test_GIVEN_optional_macro_set_WHEN_loading_config_THEN_optional_items_part_of_config(self): beamline_configuration.REFL_CONFIG_PATH = PATH_TO_CONFGIS beamline = beamline_configuration.create_beamline_from_configuration({"OPTIONAL_1": "True"}) # Check Status PV - self.assertEqual(STATUS.OKAY, ReflectometryServer.server_status_manager.STATUS_MANAGER.status) + self.assertEqual( + STATUS.OKAY, ReflectometryServer.server_status_manager.STATUS_MANAGER.status + ) self.assertTrue(OPTIONAL_PARAM_1 in beamline.parameters.keys()) - def test_GIVEN_optional_macro_not_set_WHEN_loading_config_THEN_optional_items_not_part_of_config(self): + def test_GIVEN_optional_macro_not_set_WHEN_loading_config_THEN_optional_items_not_part_of_config( + self, + ): beamline_configuration.REFL_CONFIG_PATH = PATH_TO_CONFGIS - beamline = beamline_configuration.create_beamline_from_configuration({"OPTIONAL_1": "False"}) + beamline = beamline_configuration.create_beamline_from_configuration( + {"OPTIONAL_1": "False"} + ) # Check Status PV - self.assertEqual(STATUS.OKAY, ReflectometryServer.server_status_manager.STATUS_MANAGER.status) + self.assertEqual( + STATUS.OKAY, ReflectometryServer.server_status_manager.STATUS_MANAGER.status + ) self.assertTrue(OPTIONAL_PARAM_1 not in beamline.parameters.keys()) diff --git a/ReflectometryServer/test_modules/test_driver_utils.py b/ReflectometryServer/test_modules/test_driver_utils.py index 75c3579e..3feb5223 100644 --- a/ReflectometryServer/test_modules/test_driver_utils.py +++ b/ReflectometryServer/test_modules/test_driver_utils.py @@ -1,19 +1,15 @@ import unittest from hamcrest import * +from server_common.channel_access import AlarmSeverity, AlarmStatus +from ReflectometryServer.beamline import Beamline, BeamlineMode from ReflectometryServer.ChannelAccess.driver_utils import DriverParamHelper from ReflectometryServer.ChannelAccess.pv_manager import PVManager -from ReflectometryServer.components import ReflectingComponent, Component -from ReflectometryServer.geometry import PositionAndAngle, PositionAndAngle, ChangeAxis -from ReflectometryServer.beamline import Beamline, BeamlineMode -from ReflectometryServer.parameters import AxisParameter, EnumParameter, ParameterUpdateBase -from ReflectometryServer.test_modules.utils import create_parameter_with_initial_value -from server_common.channel_access import AlarmSeverity, AlarmStatus +from ReflectometryServer.parameters import EnumParameter, ParameterUpdateBase class TestDriverUtils(unittest.TestCase): - def setUp(self) -> None: self.param_name = "ENUM" self.pvname = "PARAM:{}".format(self.param_name) @@ -29,79 +25,107 @@ def setUp(self) -> None: self.driver_helper = DriverParamHelper(pvmanager, bl) def test_GIVEN_enum_param_WHEN_set_from_pv_THEN_parameter_is_set(self): - self.driver_helper.param_write("{}:SP".format(self.pvname), self.index_of_opt1) assert_that(self.param.sp, is_(self.opt1)) def test_GIVEN_enum_param_WHEN_get_monitor_updates_THEN_all_pvs_and_values_returned(self): - result = list(self.driver_helper.get_param_monitor_updates()) pvname = self.pvname index_of_opt1 = self.index_of_opt1 - assert_that(result, contains_inanyorder(("{}".format(pvname), index_of_opt1, None, None), - ("{}:SP".format(pvname), index_of_opt1, AlarmSeverity.No, AlarmSeverity.No), - ("{}:SP:RBV".format(pvname), index_of_opt1, AlarmSeverity.No, AlarmSeverity.No), - ("{}:SP_NO_ACTION".format(pvname), index_of_opt1, AlarmSeverity.No, AlarmSeverity.No), - ("{}:CHANGED".format(pvname), False, AlarmSeverity.No, AlarmSeverity.No), - ("{}:ACTION".format(pvname), 0, AlarmSeverity.No, AlarmSeverity.No), - ("{}:RBV:AT_SP".format(pvname), True, AlarmSeverity.No, AlarmSeverity.No), - ("{}:LOCKED".format(pvname), False, AlarmSeverity.No, AlarmSeverity.No), - ("{}:READ_ONLY".format(pvname), False, AlarmSeverity.No, AlarmSeverity.No), - )) - - def test_GIVEN_enum_param_WHEN_update_event_processed_using_get_param_update_from_event_THEN_fields_(self): + assert_that( + result, + contains_inanyorder( + ("{}".format(pvname), index_of_opt1, None, None), + ("{}:SP".format(pvname), index_of_opt1, AlarmSeverity.No, AlarmSeverity.No), + ("{}:SP:RBV".format(pvname), index_of_opt1, AlarmSeverity.No, AlarmSeverity.No), + ( + "{}:SP_NO_ACTION".format(pvname), + index_of_opt1, + AlarmSeverity.No, + AlarmSeverity.No, + ), + ("{}:CHANGED".format(pvname), False, AlarmSeverity.No, AlarmSeverity.No), + ("{}:ACTION".format(pvname), 0, AlarmSeverity.No, AlarmSeverity.No), + ("{}:RBV:AT_SP".format(pvname), True, AlarmSeverity.No, AlarmSeverity.No), + ("{}:LOCKED".format(pvname), False, AlarmSeverity.No, AlarmSeverity.No), + ("{}:READ_ONLY".format(pvname), False, AlarmSeverity.No, AlarmSeverity.No), + ), + ) + + def test_GIVEN_enum_param_WHEN_update_event_processed_using_get_param_update_from_event_THEN_fields_( + self, + ): expected_severity = AlarmSeverity.Major expected_status = AlarmStatus.HiHi update = ParameterUpdateBase(self.opt1, expected_severity, expected_status) - result_name, result_value, result_severity, result_status = \ - self.driver_helper.get_param_update_from_event(self.pvname, self.param.parameter_type, update) + result_name, result_value, result_severity, result_status = ( + self.driver_helper.get_param_update_from_event( + self.pvname, self.param.parameter_type, update + ) + ) assert_that(result_name, is_(self.pvname)) assert_that(result_value, is_(self.index_of_opt1)) assert_that(result_severity, is_(expected_severity)) assert_that(result_status, is_(expected_status)) - def test_GIVEN_enum_param_WHEN_update_event_processed_using_get_param_update_from_event_with_to_large_status_indexTHEN_index_capped_at_15(self): + def test_GIVEN_enum_param_WHEN_update_event_processed_using_get_param_update_from_event_with_to_large_status_indexTHEN_index_capped_at_15( + self, + ): expected_severity = AlarmSeverity.Major expected_status = 32 update = ParameterUpdateBase(self.opt1, expected_severity, expected_status) - result_name, result_value, result_severity, result_status = \ - self.driver_helper.get_param_update_from_event(self.pvname, self.param.parameter_type, update) + result_name, result_value, result_severity, result_status = ( + self.driver_helper.get_param_update_from_event( + self.pvname, self.param.parameter_type, update + ) + ) assert_that(result_name, is_(self.pvname)) assert_that(result_value, is_(self.index_of_opt1)) assert_that(result_severity, is_(expected_severity)) assert_that(int(result_status), is_(15)) - def test_GIVEN_enum_param_WHEN_update_event_processed_using_get_param_update_from_event_and_value_is_not_in_enum_THEN_value_is_set_to_minus_one_and_error_in_alarms(self): + def test_GIVEN_enum_param_WHEN_update_event_processed_using_get_param_update_from_event_and_value_is_not_in_enum_THEN_value_is_set_to_minus_one_and_error_in_alarms( + self, + ): expected_severity = AlarmSeverity.Invalid expected_status = AlarmStatus.State update = ParameterUpdateBase("not an option", expected_severity, expected_status) - result_name, result_value, result_severity, result_status = \ - self.driver_helper.get_param_update_from_event(self.pvname, self.param.parameter_type, update) + result_name, result_value, result_severity, result_status = ( + self.driver_helper.get_param_update_from_event( + self.pvname, self.param.parameter_type, update + ) + ) assert_that(result_name, is_(self.pvname)) assert_that(result_value, is_(-1)) assert_that(result_severity, is_(expected_severity)) assert_that(result_status, is_(expected_status)) - def test_GIVEN_enum_param_WHEN_update_event_processed_using_get_param_update_from_event_and_value_is_None_THEN_value_is_set_to_minus_one_and_error_in_alarms(self): + def test_GIVEN_enum_param_WHEN_update_event_processed_using_get_param_update_from_event_and_value_is_None_THEN_value_is_set_to_minus_one_and_error_in_alarms( + self, + ): expected_severity = AlarmSeverity.Invalid expected_status = AlarmStatus.State update = ParameterUpdateBase(None, expected_severity, expected_status) - result_name, result_value, result_severity, result_status = \ - self.driver_helper.get_param_update_from_event(self.pvname, self.param.parameter_type, update) + result_name, result_value, result_severity, result_status = ( + self.driver_helper.get_param_update_from_event( + self.pvname, self.param.parameter_type, update + ) + ) assert_that(result_name, is_(self.pvname)) assert_that(result_value, is_(-1)) assert_that(result_severity, is_(expected_severity)) assert_that(result_status, is_(expected_status)) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_engineering_corrections.py b/ReflectometryServer/test_modules/test_engineering_corrections.py index 05327e29..d0237b5c 100644 --- a/ReflectometryServer/test_modules/test_engineering_corrections.py +++ b/ReflectometryServer/test_modules/test_engineering_corrections.py @@ -1,41 +1,53 @@ import io -import numpy as np import os +import unittest +import numpy as np from hamcrest import * -from mock import patch, Mock +from mock import Mock, patch from parameterized import parameterized +from server_common.observable import observable -import unittest - - -from ReflectometryServer import beamline_configuration, Component, TiltingComponent, AxisParameter -from ReflectometryServer.beamline import ActiveModeUpdate, BeamlineMode, Beamline +from ReflectometryServer import AxisParameter, Component, TiltingComponent, beamline_configuration +from ReflectometryServer.beamline import ActiveModeUpdate, Beamline, BeamlineMode +from ReflectometryServer.engineering_corrections import ( + ConstantCorrection, + CorrectionRecalculate, + CorrectionUpdate, + GridDataFileReader, + InterpolateGridDataCorrectionFromProvider, + ModeSelectCorrection, + UserFunctionCorrection, +) from ReflectometryServer.geometry import ChangeAxis, PositionAndAngle -from ReflectometryServer.engineering_corrections import InterpolateGridDataCorrectionFromProvider, CorrectionUpdate, \ - ModeSelectCorrection, CorrectionRecalculate, ConstantCorrection, UserFunctionCorrection, GridDataFileReader - from ReflectometryServer.ioc_driver import CorrectedReadbackUpdate, IocDriver from ReflectometryServer.out_of_beam import OutOfBeamPosition -from server_common.observable import observable -from ReflectometryServer.test_modules.data_mother import create_mock_axis, DataMother +from ReflectometryServer.test_modules.data_mother import DataMother, create_mock_axis FLOAT_TOLERANCE = 1e-9 OUT_OF_BEAM_POSITION = OutOfBeamPosition(10) class TestEngineeringCorrections(unittest.TestCase): - def _setup_driver_axis_and_correction(self, correction): comp = Component("comp", PositionAndAngle(0.0, 0.0, 90.0)) mock_axis = create_mock_axis("MOT:MTR0101", 0, 1) - driver = IocDriver(comp, ChangeAxis.POSITION, mock_axis, out_of_beam_positions=[OUT_OF_BEAM_POSITION], - engineering_correction=ConstantCorrection(correction)) - comp.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = lambda: True # simulate that the component has requested a change + driver = IocDriver( + comp, + ChangeAxis.POSITION, + mock_axis, + out_of_beam_positions=[OUT_OF_BEAM_POSITION], + engineering_correction=ConstantCorrection(correction), + ) + comp.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = ( + lambda: True + ) # simulate that the component has requested a change comp.beam_path_set_point.in_beam_manager.parking_index = 0 # The parking index is normally set initialised either through autosave or through initialise on the motor return driver, mock_axis, comp - def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_told_to_go_to_0_THEN_pv_sent_to_1(self): + def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_told_to_go_to_0_THEN_pv_sent_to_1( + self, + ): expected_correction = 1 driver, mock_axis, comp = self._setup_driver_axis_and_correction(expected_correction) comp.beam_path_set_point.in_beam_manager.set_is_in_beam(True) @@ -45,20 +57,30 @@ def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_told_to_go_to_0_TH assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - def test_GIVEN_engineering_correction_offset_of_1_on_angle_driver_WHEN_driver_told_to_go_to_0_THEN_pv_sent_to_1(self): + def test_GIVEN_engineering_correction_offset_of_1_on_angle_driver_WHEN_driver_told_to_go_to_0_THEN_pv_sent_to_1( + self, + ): expected_correction = 1 comp = TiltingComponent("comp", PositionAndAngle(0.0, 0.0, 0.0)) mock_axis = create_mock_axis("MOT:MTR0101", 0, 1) - driver = IocDriver(comp, ChangeAxis.ANGLE, mock_axis, - engineering_correction=ConstantCorrection(expected_correction)) - comp.beam_path_set_point.axis[ChangeAxis.ANGLE].is_changed = lambda: True # simulate that the component has requested a change + driver = IocDriver( + comp, + ChangeAxis.ANGLE, + mock_axis, + engineering_correction=ConstantCorrection(expected_correction), + ) + comp.beam_path_set_point.axis[ChangeAxis.ANGLE].is_changed = ( + lambda: True + ) # simulate that the component has requested a change driver.perform_move(1) result = mock_axis.sp assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_is_at_2_THEN_read_back_is_at_1(self): + def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_is_at_2_THEN_read_back_is_at_1( + self, + ): expected_correct_value = 1 correction = 1 move_to = expected_correct_value + correction @@ -69,10 +91,14 @@ def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_is_at_2_THEN_read_ assert_that(result, is_(close_to(expected_correct_value, FLOAT_TOLERANCE))) - def test_GIVEN_engineering_correction_offset_of_1_WHEN_at_set_point_THEN_at_target_setpoint_is_true(self): + def test_GIVEN_engineering_correction_offset_of_1_WHEN_at_set_point_THEN_at_target_setpoint_is_true( + self, + ): correction = 4 driver, mock_axis, comp = self._setup_driver_axis_and_correction(correction) - comp.beam_path_set_point.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(2, None, None)) + comp.beam_path_set_point.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(2, None, None) + ) driver.perform_move(1) result = driver.at_target_setpoint() @@ -96,7 +122,9 @@ def test_GIVEN_engineering_correction_offset_of_1_WHEN_initialise_THEN_rbv_set_c assert_that(result, is_(-1 * correction)) - def test_GIVEN_engineering_correction_offset_of_1_and_out_of_beam_WHEN_initialise_THEN_sp_set_correctly(self): + def test_GIVEN_engineering_correction_offset_of_1_and_out_of_beam_WHEN_initialise_THEN_sp_set_correctly( + self, + ): correction = 4 driver, mock_axis, comp = self._setup_driver_axis_and_correction(correction) mock_axis.sp = OUT_OF_BEAM_POSITION.get_final_position() @@ -106,11 +134,15 @@ def test_GIVEN_engineering_correction_offset_of_1_and_out_of_beam_WHEN_initialis assert_that(result, is_(OUT_OF_BEAM_POSITION.get_final_position())) - def test_GIVEN_engineering_correction_offset_of_1_on_angle_driver_WHEN_initialise_THEN_rbv_set_correctly(self): + def test_GIVEN_engineering_correction_offset_of_1_on_angle_driver_WHEN_initialise_THEN_rbv_set_correctly( + self, + ): correction = 1 comp = TiltingComponent("comp", PositionAndAngle(0.0, 0.0, 0.0)) mock_axis = create_mock_axis("MOT:MTR0101", 0, 1) - driver = IocDriver(comp, ChangeAxis.ANGLE, mock_axis, engineering_correction=ConstantCorrection(correction)) + driver = IocDriver( + comp, ChangeAxis.ANGLE, mock_axis, engineering_correction=ConstantCorrection(correction) + ) driver.initialise() result = comp.beam_path_set_point.axis[ChangeAxis.ANGLE].get_displacement() @@ -119,8 +151,9 @@ def test_GIVEN_engineering_correction_offset_of_1_on_angle_driver_WHEN_initialis class TestEngineeringCorrectionsPureFunction(unittest.TestCase): - - def test_GIVEN_user_function_engineering_correction_which_adds_no_correction_WHEN_set_value_on_axis_THEN_value_is_same_as_set(self): + def test_GIVEN_user_function_engineering_correction_which_adds_no_correction_WHEN_set_value_on_axis_THEN_value_is_same_as_set( + self, + ): def _test_correction(setpoint): return 0 @@ -133,13 +166,15 @@ def _test_correction(setpoint): assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - def test_GIVEN_user_function_engineering_correction_which_adds_a_correction_of_the_setpoint_on_WHEN_set_value_on_axis_THEN_value_is_double_what_was_set(self): + def test_GIVEN_user_function_engineering_correction_which_adds_a_correction_of_the_setpoint_on_WHEN_set_value_on_axis_THEN_value_is_double_what_was_set( + self, + ): def _test_correction(setpoint): """Correction is the same as the setpoint so it doubles the correction""" return setpoint value = 1 - expected_correction = value*2 + expected_correction = value * 2 engineering_correction = UserFunctionCorrection(_test_correction) @@ -147,7 +182,9 @@ def _test_correction(setpoint): assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - def test_GIVEN_user_function_engineering_correction_which_adds_a_correction_of_the_setpoint_on_WHEN_get_value_from_axis_THEN_value_is_that_value_minus_the_setpoint(self): + def test_GIVEN_user_function_engineering_correction_which_adds_a_correction_of_the_setpoint_on_WHEN_get_value_from_axis_THEN_value_is_that_value_minus_the_setpoint( + self, + ): def _test_correction(setpoint): """Correction is the same as the setpoint so it doubles the correction""" return setpoint @@ -162,7 +199,9 @@ def _test_correction(setpoint): assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - def test_GIVEN_user_function_engineering_correction_which_adds_setpoint_and_beamline_param_WHEN_set_value_on_axis_THEN_value_is_twice_value_plus_beamline_parameter_value(self): + def test_GIVEN_user_function_engineering_correction_which_adds_setpoint_and_beamline_param_WHEN_set_value_on_axis_THEN_value_is_twice_value_plus_beamline_parameter_value( + self, + ): def _test_correction(setpoint, beamline_parameter): return setpoint + beamline_parameter @@ -180,7 +219,9 @@ def _test_correction(setpoint, beamline_parameter): assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - def test_GIVEN_user_function_engineering_correction_which_throws_WHEN_set_value_on_axis_THEN_0_correction(self): + def test_GIVEN_user_function_engineering_correction_which_throws_WHEN_set_value_on_axis_THEN_0_correction( + self, + ): def _test_correction(setpoint): raise TypeError() @@ -190,22 +231,39 @@ def _test_correction(setpoint): assert_that(result, is_(close_to(0, FLOAT_TOLERANCE))) -class TestEngineeringCorrectionsLinear(unittest.TestCase): +class TestEngineeringCorrectionsLinear(unittest.TestCase): def setUp(self): self._file_contents = "" - @parameterized.expand([(1.0, 1.234), # first point in grid - (2.0, 4.0), # second point in grid - (1.5, (4.0 - 1.234)/2 + 1.234), # halfway between 1st and 2nd point - (1.25, (4.0 - 1.234)/4 + 1.234), # quarter of the way between 1st and 2nd point - (0.9, 0), # outside the grid before first point - (3.1, 0) # outside the grid after last point - ]) - def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_axis_WHEN_request_a_point_THEN_correction_returned_for_that_point_with_interpolation_if_needed(self, value, expected_correction): + @parameterized.expand( + [ + (1.0, 1.234), # first point in grid + (2.0, 4.0), # second point in grid + (1.5, (4.0 - 1.234) / 2 + 1.234), # halfway between 1st and 2nd point + (1.25, (4.0 - 1.234) / 4 + 1.234), # quarter of the way between 1st and 2nd point + (0.9, 0), # outside the grid before first point + (3.1, 0), # outside the grid after last point + ] + ) + def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_axis_WHEN_request_a_point_THEN_correction_returned_for_that_point_with_interpolation_if_needed( + self, value, expected_correction + ): grid_data_provider = GridDataFileReader("Test") grid_data_provider.variables = ["driver"] - grid_data_provider.points = np.array([[1.0, ], [2.0, ], [3.0, ]]) + grid_data_provider.points = np.array( + [ + [ + 1.0, + ], + [ + 2.0, + ], + [ + 3.0, + ], + ] + ) grid_data_provider.corrections = np.array([1.234, 4.0, 6.0]) grid_data_provider.read = lambda: None @@ -215,17 +273,34 @@ def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_axis_WHEN_request_a_po assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - @parameterized.expand([(1.0, 1.234), # first point in grid - (2.0, 4.0), # second point in grid - (1.5, (4.0 - 1.234)/2 + 1.234), # halfway between 1st and 2nd point - (1.25, (4.0 - 1.234)/4 + 1.234), # quarter of the way between 1st and 2nd point - (0.9, 0), # outside the grid before first point - (3.1, 0) # outside the grid after last point - ]) - def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_WHEN_request_a_point_THEN_correction_returned_for_that_point_with_interpolation_if_needed(self, value, expected_correction): + @parameterized.expand( + [ + (1.0, 1.234), # first point in grid + (2.0, 4.0), # second point in grid + (1.5, (4.0 - 1.234) / 2 + 1.234), # halfway between 1st and 2nd point + (1.25, (4.0 - 1.234) / 4 + 1.234), # quarter of the way between 1st and 2nd point + (0.9, 0), # outside the grid before first point + (3.1, 0), # outside the grid after last point + ] + ) + def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_WHEN_request_a_point_THEN_correction_returned_for_that_point_with_interpolation_if_needed( + self, value, expected_correction + ): grid_data_provider = GridDataFileReader("Test") grid_data_provider.variables = ["Theta"] - grid_data_provider.points = np.array([[1.0, ], [2.0, ], [3.0, ]]) + grid_data_provider.points = np.array( + [ + [ + 1.0, + ], + [ + 2.0, + ], + [ + 3.0, + ], + ] + ) grid_data_provider.corrections = np.array([1.234, 4.0, 6.0]) grid_data_provider.read = lambda: None @@ -240,18 +315,37 @@ def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_WHE assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - @parameterized.expand([(1.0, 1.0, 1.234), # first point in grid - (2.0, 6.0, 5.0), # second point in grid - (1.5, 1.0, (4.0 - 1.234)/2 + 1.234), # value (2.617) halfway between 1st and 2nd point - (1.0, 3.5, (2.234 - 1.234) / 2 + 1.234), # value (1.734) halfway between 1st and 4th point - (1.5, 3.5, (1.234 + 2.234 + 4.0 + 5.0) / 4), # halfway between 1st, 2nd, 3rd and 4th point - (0.9, 1.1, 0), # outside the grid in value - (2.1, 6.1, 0) # outside the grid in theta - ]) - def test_GIVEN_interp_with_2D_points_based_on_setpoint_of_beamline_parameter_WHEN_request_a_point_THEN_correction_returned_for_that_point_with_interpolation_if_needed(self, value, theta, expected_correction): + @parameterized.expand( + [ + (1.0, 1.0, 1.234), # first point in grid + (2.0, 6.0, 5.0), # second point in grid + ( + 1.5, + 1.0, + (4.0 - 1.234) / 2 + 1.234, + ), # value (2.617) halfway between 1st and 2nd point + ( + 1.0, + 3.5, + (2.234 - 1.234) / 2 + 1.234, + ), # value (1.734) halfway between 1st and 4th point + ( + 1.5, + 3.5, + (1.234 + 2.234 + 4.0 + 5.0) / 4, + ), # halfway between 1st, 2nd, 3rd and 4th point + (0.9, 1.1, 0), # outside the grid in value + (2.1, 6.1, 0), # outside the grid in theta + ] + ) + def test_GIVEN_interp_with_2D_points_based_on_setpoint_of_beamline_parameter_WHEN_request_a_point_THEN_correction_returned_for_that_point_with_interpolation_if_needed( + self, value, theta, expected_correction + ): grid_data_provider = GridDataFileReader("Test") grid_data_provider.variables = ["driver", "Theta"] - grid_data_provider.points = np.array([[1.0, 1.0], [2.0, 1.0], [3.0, 1.0], [1.0, 6.0], [2.0, 6.0], [3.0, 6.0]]) + grid_data_provider.points = np.array( + [[1.0, 1.0], [2.0, 1.0], [3.0, 1.0], [1.0, 6.0], [2.0, 6.0], [3.0, 6.0]] + ) grid_data_provider.corrections = np.array([1.234, 4.0, 6.0, 2.234, 5.0, 7.0]) grid_data_provider.read = lambda: None @@ -265,10 +359,24 @@ def test_GIVEN_interp_with_2D_points_based_on_setpoint_of_beamline_parameter_WHE assert_that(result, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_WHEN_column_name_not_beamline_name_THEN_error_in_initialise(self): + def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_WHEN_column_name_not_beamline_name_THEN_error_in_initialise( + self, + ): grid_data_provider = GridDataFileReader("Test") grid_data_provider.variables = ["Theta"] - grid_data_provider.points = np.array([[1.0, ], [2.0, ], [3.0, ]]) + grid_data_provider.points = np.array( + [ + [ + 1.0, + ], + [ + 2.0, + ], + [ + 3.0, + ], + ] + ) grid_data_provider.corrections = np.array([1.234, 4.0, 6.0]) grid_data_provider.read = lambda: None @@ -276,13 +384,30 @@ def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_WHE beamline_parameter = AxisParameter("not theta", comp, ChangeAxis.POSITION) assert_that( - calling(InterpolateGridDataCorrectionFromProvider).with_args(grid_data_provider, beamline_parameter), - raises(ValueError)) - - def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_which_has_not_been_initialised_WHEN_request_a_point_THEN_correction_0_returned(self): + calling(InterpolateGridDataCorrectionFromProvider).with_args( + grid_data_provider, beamline_parameter + ), + raises(ValueError), + ) + + def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_which_has_not_been_initialised_WHEN_request_a_point_THEN_correction_0_returned( + self, + ): grid_data_provider = GridDataFileReader("Test") grid_data_provider.variables = ["Theta"] - grid_data_provider.points = np.array([[1.0, ], [2.0, ], [3.0, ]]) + grid_data_provider.points = np.array( + [ + [ + 1.0, + ], + [ + 2.0, + ], + [ + 3.0, + ], + ] + ) grid_data_provider.corrections = np.array([1.234, 4.0, 6.0]) grid_data_provider.read = lambda: None @@ -296,10 +421,24 @@ def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_beamline_parameter_whi assert_that(result, is_(close_to(0, FLOAT_TOLERANCE))) - def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_driver_which_has_not_been_initialised_WHEN_request_a_point_THEN_correction_0_returned(self): + def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_driver_which_has_not_been_initialised_WHEN_request_a_point_THEN_correction_0_returned( + self, + ): grid_data_provider = GridDataFileReader("Test") grid_data_provider.variables = ["DRIVER"] - grid_data_provider.points = np.array([[1.0, ], [2.0, ], [3.0, ]]) + grid_data_provider.points = np.array( + [ + [ + 1.0, + ], + [ + 2.0, + ], + [ + 3.0, + ], + ] + ) grid_data_provider.corrections = np.array([1.234, 4.0, 6.0]) grid_data_provider.read = lambda: None @@ -314,24 +453,25 @@ def test_GIVEN_interp_with_1D_points_based_on_setpoint_of_driver_which_has_not_b class Test1DInterpolationFileReader(unittest.TestCase): - def test_GIVEN_file_reader_WHEN_file_does_not_exist_THEN_error(self): reader = GridDataFileReader("non_existant_filename") assert_that(calling(reader.read), raises(IOError, ".*No such file.*")) def test_GIVEN_1d_interp_WHEN_file_does_exist_but_is_empty_THEN_error(self): - beamline_configuration.REFL_CONFIG_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "test_config", "refl")) + beamline_configuration.REFL_CONFIG_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), "test_config", "refl") + ) reader = GridDataFileReader("blankfile.dat") assert_that(calling(reader.read), raises(IOError, "No data found.*")) @patch("ReflectometryServer.engineering_corrections.GridDataFileReader._open_file") - def test_GIVEN_1d_interp_with_a_good_file_WHEN_get_correction_details_THEN_description_returned(self, open_file_mock): + def test_GIVEN_1d_interp_with_a_good_file_WHEN_get_correction_details_THEN_description_returned( + self, open_file_mock + ): expected_variable_name = "name" - _file_contents = [u" {} , correction".format(expected_variable_name), - u"1, 2", - u"2, 3"] + _file_contents = [" {} , correction".format(expected_variable_name), "1, 2", "2, 3"] open_file_mock.configure_mock(return_value=io.StringIO("\n".join(_file_contents))) correction = GridDataFileReader("") @@ -341,21 +481,21 @@ def test_GIVEN_1d_interp_with_a_good_file_WHEN_get_correction_details_THEN_descr assert_that(result, is_([expected_variable_name])) @patch("ReflectometryServer.engineering_corrections.GridDataFileReader._open_file") - def test_GIVEN_1d_interp_with_a_header_missing_variable_WHEN_read_file_THEN_error(self, open_file_mock): + def test_GIVEN_1d_interp_with_a_header_missing_variable_WHEN_read_file_THEN_error( + self, open_file_mock + ): expected_variable_name = "name" - _file_contents = [u"correction".format(expected_variable_name), - u"1, 2", - u"2, 3"] + _file_contents = ["correction".format(), "1, 2", "2, 3"] open_file_mock.configure_mock(return_value=io.StringIO("\n".join(_file_contents))) correction = GridDataFileReader("") assert_that(calling(correction.read), raises(IOError)) @patch("ReflectometryServer.engineering_corrections.GridDataFileReader._open_file") - def test_GIVEN_1d_interp_with_different_item_count_in_a_row_WHEN_read_file_THEN_error(self, open_file_mock): - _file_contents = [u"name, correction", - u"1, 2, 3", - u"2, 3"] + def test_GIVEN_1d_interp_with_different_item_count_in_a_row_WHEN_read_file_THEN_error( + self, open_file_mock + ): + _file_contents = ["name, correction", "1, 2, 3", "2, 3"] open_file_mock.configure_mock(return_value=io.StringIO("\n".join(_file_contents))) correction = GridDataFileReader("") @@ -363,21 +503,21 @@ def test_GIVEN_1d_interp_with_different_item_count_in_a_row_WHEN_read_file_THEN_ @patch("ReflectometryServer.engineering_corrections.GridDataFileReader._open_file") def test_GIVEN_1d_interp_with_string_in_csv_WHEN_read_file_THEN_error(self, open_file_mock): - _file_contents = [u"name, correction", - u"1, orange", - u"2, 3"] + _file_contents = ["name, correction", "1, orange", "2, 3"] open_file_mock.configure_mock(return_value=io.StringIO("\n".join(_file_contents))) correction = GridDataFileReader("") assert_that(calling(correction.read), raises(IOError, "Problem with data in.*")) @patch("ReflectometryServer.engineering_corrections.GridDataFileReader._open_file") - def test_GIVEN_1d_interp_with_header_and_data_WHEN_read_file_THEN_data_is_as_given(self, open_file_mock): + def test_GIVEN_1d_interp_with_header_and_data_WHEN_read_file_THEN_data_is_as_given( + self, open_file_mock + ): expected_correction = [2.0, 4.0, 6.0] expected_values = [1, 2.0, 3] - _file_contents = [u"name, correction"] + _file_contents = ["name, correction"] for value, correction in zip(expected_values, expected_correction): - _file_contents.append(u"{}, {}".format(value, correction)) + _file_contents.append("{}, {}".format(value, correction)) open_file_mock.configure_mock(return_value=io.StringIO("\n".join(_file_contents))) correction = GridDataFileReader("") @@ -387,13 +527,15 @@ def test_GIVEN_1d_interp_with_header_and_data_WHEN_read_file_THEN_data_is_as_giv assert_that(correction.points, contains(*expected_values), "values") @patch("ReflectometryServer.engineering_corrections.GridDataFileReader._open_file") - def test_GIVEN_1d_interp_with_header_and_data_with_2_d_WHEN_read_file_THEN_data_is_as_given(self, open_file_mock): + def test_GIVEN_1d_interp_with_header_and_data_with_2_d_WHEN_read_file_THEN_data_is_as_given( + self, open_file_mock + ): expected_correction = [2.0, 4.0, 6.0] expected_values = [[1, 6], [2.0, 4], [3, -10]] - _file_contents = [u"name1, name2, correction"] + _file_contents = ["name1, name2, correction"] for (value1, value2), correction in zip(expected_values, expected_correction): - _file_contents.append(u"{}, {}, {}".format(value1, value2, correction)) + _file_contents.append("{}, {}, {}".format(value1, value2, correction)) open_file_mock.configure_mock(return_value=io.StringIO("\n".join(_file_contents))) correction = GridDataFileReader("") @@ -405,7 +547,6 @@ def test_GIVEN_1d_interp_with_header_and_data_with_2_d_WHEN_read_file_THEN_data_ class TestEngineeringCorrectionsChangeListener(unittest.TestCase): - def setUp(self): self.engineering_correction_update = None self.engineering_correction_update2 = None @@ -414,8 +555,12 @@ def _setup_driver_axis_and_correction(self, correction): comp = Component("comp", PositionAndAngle(0.0, 0.0, 0.0)) mock_axis = create_mock_axis("MOT:MTR0101", 0, 1) engineering_correction = ConstantCorrection(correction) - driver = IocDriver(comp, ChangeAxis.POSITION, mock_axis, engineering_correction=engineering_correction) - comp.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = lambda: True # simulate that the component has requested a change + driver = IocDriver( + comp, ChangeAxis.POSITION, mock_axis, engineering_correction=engineering_correction + ) + comp.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = ( + lambda: True + ) # simulate that the component has requested a change return driver, mock_axis, comp, engineering_correction def _record_event(self, engineering_correction_update): @@ -424,48 +569,82 @@ def _record_event(self, engineering_correction_update): def _record_event2(self, engineering_correction_update): self.engineering_correction_update2 = engineering_correction_update - def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_told_to_go_to_0_THEN_event_is_triggered_with_description_and_value(self): + def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_told_to_go_to_0_THEN_event_is_triggered_with_description_and_value( + self, + ): expected_correction = 1 - driver, mock_axis, comp, engineering_correction = self._setup_driver_axis_and_correction(expected_correction) + driver, mock_axis, comp, engineering_correction = self._setup_driver_axis_and_correction( + expected_correction + ) driver.add_listener(CorrectionUpdate, self._record_event) driver.perform_move(1) - assert_that(self.engineering_correction_update.correction, is_(close_to(expected_correction, FLOAT_TOLERANCE))) - assert_that(self.engineering_correction_update.description, all_of(contains_string(engineering_correction.description), - contains_string(comp.name), - contains_string(mock_axis.name))) - - def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_is_at_2_THEN_event_is_triggered_with_description_and_value(self): + assert_that( + self.engineering_correction_update.correction, + is_(close_to(expected_correction, FLOAT_TOLERANCE)), + ) + assert_that( + self.engineering_correction_update.description, + all_of( + contains_string(engineering_correction.description), + contains_string(comp.name), + contains_string(mock_axis.name), + ), + ) + + def test_GIVEN_engineering_correction_offset_of_1_WHEN_driver_is_at_2_THEN_event_is_triggered_with_description_and_value( + self, + ): expected_correct_value = 1 correction = 1 move_to = expected_correct_value + correction - driver, mock_axis, comp, engineering_correction = self._setup_driver_axis_and_correction(correction) + driver, mock_axis, comp, engineering_correction = self._setup_driver_axis_and_correction( + correction + ) mock_axis.sp = move_to driver.add_listener(CorrectionUpdate, self._record_event) mock_axis.trigger_rbv_change() - assert_that(self.engineering_correction_update.correction, is_(close_to(correction, FLOAT_TOLERANCE))) - assert_that(self.engineering_correction_update.description, all_of(contains_string(engineering_correction.description), - contains_string(comp.name), - contains_string(mock_axis.name))) + assert_that( + self.engineering_correction_update.correction, + is_(close_to(correction, FLOAT_TOLERANCE)), + ) + assert_that( + self.engineering_correction_update.description, + all_of( + contains_string(engineering_correction.description), + contains_string(comp.name), + contains_string(mock_axis.name), + ), + ) class TestRealisticWithAutosaveInitAndEngineeringCorrections(unittest.TestCase): - @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_theta_and_engineering_correction_on_sm_WHEN_init_THEN_beamline_is_at_given_place(self, param_float_autosave): + def test_GIVEN_beam_line_where_autosave_theta_and_engineering_correction_on_sm_WHEN_init_THEN_beamline_is_at_given_place( + self, param_float_autosave + ): expected_sm_angle = 22.5 expected_theta = 2 param_float_autosave.read_parameter.return_value = expected_theta - bl, axes = DataMother.beamline_sm_theta_detector(expected_sm_angle, expected_theta, autosave_theta_not_offset=True, sm_angle_engineering_correction=True) + bl, axes = DataMother.beamline_sm_theta_detector( + expected_sm_angle, + expected_theta, + autosave_theta_not_offset=True, + sm_angle_engineering_correction=True, + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_offset_and_engineering_correction_on_sm_WHEN_init_THEN_beamline_is_at_given_place(self, param_float_autosave): + def test_GIVEN_beam_line_where_autosave_offset_and_engineering_correction_on_sm_WHEN_init_THEN_beamline_is_at_given_place( + self, param_float_autosave + ): sm_angle = 22.5 expected_theta = 2 # Theta is not autosaved so the correction for theta will not be able to be calculated. Therefore the correction should be 0 and the @@ -475,12 +654,21 @@ def test_GIVEN_beam_line_where_autosave_offset_and_engineering_correction_on_sm_ expected_det_offset = 0.0 param_float_autosave.read_parameter.return_value = expected_det_offset - bl, axes = DataMother.beamline_sm_theta_detector(sm_angle, expected_theta, autosave_theta_not_offset=False, sm_angle_engineering_correction=True) + bl, axes = DataMother.beamline_sm_theta_detector( + sm_angle, + expected_theta, + autosave_theta_not_offset=False, + sm_angle_engineering_correction=True, + ) - assert_that(bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP") + assert_that( + bl.parameter("sm_angle").sp, is_(close_to(expected_sm_angle, 1e-6)), "sm angle SP" + ) @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_and_engineering_correction_on_displacement_WHEN_init_THEN_beamline_is_at_given_place(self, param_float_autosave): + def test_GIVEN_beam_line_where_autosave_and_engineering_correction_on_displacement_WHEN_init_THEN_beamline_is_at_given_place( + self, param_float_autosave + ): expected_setpoint = 1.0 multiple = 2.0 param_float_autosave.read_parameter.return_value = expected_setpoint @@ -488,8 +676,12 @@ def test_GIVEN_beam_line_where_autosave_and_engineering_correction_on_displaceme comp = Component("comp", PositionAndAngle(0.0, 0, 90)) param = AxisParameter("param", comp, ChangeAxis.POSITION, autosave=True) axis = create_mock_axis("MOT:MTR0101", offset + expected_setpoint, 1) - driver = IocDriver(comp, ChangeAxis.POSITION, axis, - engineering_correction=UserFunctionCorrection(lambda sp: sp / multiple)) + driver = IocDriver( + comp, + ChangeAxis.POSITION, + axis, + engineering_correction=UserFunctionCorrection(lambda sp: sp / multiple), + ) nr_mode = BeamlineMode("NR", [param.name], {}) bl = Beamline([comp], [param], [driver], [nr_mode]) bl.active_mode = nr_mode.name @@ -499,8 +691,9 @@ def test_GIVEN_beam_line_where_autosave_and_engineering_correction_on_displaceme assert_that(result, is_(close_to(expected_setpoint, 1e-6))) @patch("ReflectometryServer.parameters.param_float_autosave") - def test_GIVEN_beam_line_where_autosave_and_engineering_correction_on_angle_WHEN_init_THEN_beamline_is_at_given_place(self, param_float_autosave): - + def test_GIVEN_beam_line_where_autosave_and_engineering_correction_on_angle_WHEN_init_THEN_beamline_is_at_given_place( + self, param_float_autosave + ): expected_setpoint = 1.0 multiple = 2.0 param_float_autosave.read_parameter.return_value = expected_setpoint @@ -508,8 +701,12 @@ def test_GIVEN_beam_line_where_autosave_and_engineering_correction_on_angle_WHEN comp = TiltingComponent("comp", PositionAndAngle(0.0, 0, 90)) param = AxisParameter("param", comp, ChangeAxis.ANGLE, autosave=True) axis = create_mock_axis("MOT:MTR0101", offset + expected_setpoint, 1) - driver = IocDriver(comp, ChangeAxis.ANGLE, axis, - engineering_correction=UserFunctionCorrection(lambda sp: sp / multiple)) + driver = IocDriver( + comp, + ChangeAxis.ANGLE, + axis, + engineering_correction=UserFunctionCorrection(lambda sp: sp / multiple), + ) nr_mode = BeamlineMode("NR", [param.name], {}) bl = Beamline([comp], [param], [driver], [nr_mode]) @@ -519,13 +716,13 @@ def test_GIVEN_beam_line_where_autosave_and_engineering_correction_on_angle_WHEN assert_that(result, is_(close_to(expected_setpoint, 1e-6))) + @observable(ActiveModeUpdate) class MockBeamline: pass class TestEngineeringCorrectionsDependOnMode(unittest.TestCase): - def setUp(self) -> None: self.update = None self.correction_update = None @@ -536,9 +733,13 @@ def setUp(self) -> None: self.mode1_offset = 11 self.mode2_offset = 22 self.default_correction = ConstantCorrection(self.default_offset) - self.mode_selection_correction = ModeSelectCorrection(self.default_correction, - {self.mode1.name: (ConstantCorrection(self.mode1_offset)), - self.mode2.name: (ConstantCorrection(self.mode2_offset))}) + self.mode_selection_correction = ModeSelectCorrection( + self.default_correction, + { + self.mode1.name: (ConstantCorrection(self.mode1_offset)), + self.mode2.name: (ConstantCorrection(self.mode2_offset)), + }, + ) self.mock_beamline = MockBeamline() self.mode_selection_correction.set_observe_mode_change_on(self.mock_beamline) self.mode_selection_correction.add_listener(CorrectionRecalculate, self.get_update) @@ -550,14 +751,14 @@ def get_update(self, update: CorrectionRecalculate): def get_correction_update(self, update: CorrectionUpdate): self.correction_update = update - def test_GIVEN_mode_selecting_correction_WHEN_changed_listener_triggered_THEN_correction_update_triggered(self): - + def test_GIVEN_mode_selecting_correction_WHEN_changed_listener_triggered_THEN_correction_update_triggered( + self, + ): self.mock_beamline.trigger_listeners(ActiveModeUpdate(self.mode2)) assert_that(self.update, is_not(None), "the correction has asked for a recalculate") def test_GIVEN_mode_not_yet_selected_WHEN_get_correction_THEN_correction_is_default(self): - result = self.mode_selection_correction.to_axis(0) assert_that(result, is_(self.default_offset)) @@ -569,37 +770,57 @@ def test_GIVEN_mode_selected_WHEN_get_correction_THEN_correction_is_that_mode(se assert_that(result, is_(self.mode2_offset)) - def test_GIVEN_mode_selected_which_has_not_got_a_correction_WHEN_get_correction_THEN_correction_is_default_mode(self): + def test_GIVEN_mode_selected_which_has_not_got_a_correction_WHEN_get_correction_THEN_correction_is_default_mode( + self, + ): self.mock_beamline.trigger_listeners(ActiveModeUpdate(BeamlineMode("another mode", []))) result = self.mode_selection_correction.to_axis(0) assert_that(result, is_(self.default_offset)) assert_that(self.correction_update.correction, is_(self.default_offset)) - assert_that(self.correction_update.description, - is_("Mode Selected: {}".format(self.default_correction.description))) + assert_that( + self.correction_update.description, + is_("Mode Selected: {}".format(self.default_correction.description)), + ) - def test_GIVEN_mode_selected_which_has_not_got_a_correction_WHEN_get_init_from_axis_THEN_correction_is_default_mode(self): + def test_GIVEN_mode_selected_which_has_not_got_a_correction_WHEN_get_init_from_axis_THEN_correction_is_default_mode( + self, + ): self.mock_beamline.trigger_listeners(ActiveModeUpdate(BeamlineMode("another mode", []))) result = self.mode_selection_correction.init_from_axis(0) assert_that(result, is_(-self.default_offset)) assert_that(self.correction_update.correction, is_(self.default_offset)) - assert_that(self.correction_update.description, - is_("Mode Selected: {}".format(self.default_correction.description))) + assert_that( + self.correction_update.description, + is_("Mode Selected: {}".format(self.default_correction.description)), + ) - def test_GIVEN_mode_selected_which_has_not_got_a_correction_WHEN_from_axis_THEN_correction_is_default_mode(self): + def test_GIVEN_mode_selected_which_has_not_got_a_correction_WHEN_from_axis_THEN_correction_is_default_mode( + self, + ): self.mock_beamline.trigger_listeners(ActiveModeUpdate(BeamlineMode("another mode", []))) result = self.mode_selection_correction.from_axis(0, 0) assert_that(result, is_(-self.default_offset)) assert_that(self.correction_update.correction, is_(self.default_offset)) - assert_that(self.correction_update.description, is_("Mode Selected: {}".format(self.default_correction.description))) - - def test_GIVEN_ioc_driver_which_has_not_moved_with_engineering_correction_WHEN_update_mode_THEN_readback_not_fired(self): - driver = IocDriver(Component("comp", PositionAndAngle(0, 0, 0)), ChangeAxis.POSITION, create_mock_axis("mock", 0, 1), engineering_correction=self.mode_selection_correction) + assert_that( + self.correction_update.description, + is_("Mode Selected: {}".format(self.default_correction.description)), + ) + + def test_GIVEN_ioc_driver_which_has_not_moved_with_engineering_correction_WHEN_update_mode_THEN_readback_not_fired( + self, + ): + driver = IocDriver( + Component("comp", PositionAndAngle(0, 0, 0)), + ChangeAxis.POSITION, + create_mock_axis("mock", 0, 1), + engineering_correction=self.mode_selection_correction, + ) driver.set_observe_mode_change_on(self.mock_beamline) mock_listener = Mock() driver.add_listener(CorrectionUpdate, mock_listener) @@ -608,9 +829,16 @@ def test_GIVEN_ioc_driver_which_has_not_moved_with_engineering_correction_WHEN_u mock_listener.assert_not_called() - def test_GIVEN_ioc_driver_with_engineering_correction_WHEN_update_mode_THEN_readback_updated_fired(self): + def test_GIVEN_ioc_driver_with_engineering_correction_WHEN_update_mode_THEN_readback_updated_fired( + self, + ): mock_axis = create_mock_axis("mock", 0, 1) - driver = IocDriver(Component("comp", PositionAndAngle(0, 0, 0)), ChangeAxis.POSITION, mock_axis, engineering_correction=self.mode_selection_correction) + driver = IocDriver( + Component("comp", PositionAndAngle(0, 0, 0)), + ChangeAxis.POSITION, + mock_axis, + engineering_correction=self.mode_selection_correction, + ) mock_axis.trigger_rbv_change() driver.set_observe_mode_change_on(self.mock_beamline) mock_listener = Mock() @@ -620,41 +848,74 @@ def test_GIVEN_ioc_driver_with_engineering_correction_WHEN_update_mode_THEN_read mock_listener.assert_called_once() - def test_GIVEN_ioc_driver_with_engineering_correction_WHEN_update_mode_THEN_setpoint_updated_fired(self): + def test_GIVEN_ioc_driver_with_engineering_correction_WHEN_update_mode_THEN_setpoint_updated_fired( + self, + ): mock_axis = create_mock_axis("mock", 0, 1) component = Component("comp", PositionAndAngle(0, 0, 0)) - driver = IocDriver(component, ChangeAxis.POSITION, mock_axis, engineering_correction=self.mode_selection_correction) - component.beam_path_set_point.axis[ChangeAxis.POSITION].set_displacement(CorrectedReadbackUpdate(0, None, None)) + driver = IocDriver( + component, + ChangeAxis.POSITION, + mock_axis, + engineering_correction=self.mode_selection_correction, + ) + component.beam_path_set_point.axis[ChangeAxis.POSITION].set_displacement( + CorrectedReadbackUpdate(0, None, None) + ) mock_axis.sp = self.default_offset driver.set_observe_mode_change_on(self.mock_beamline) - assert_that(driver.at_target_setpoint(), is_(True), "Should be at the target setpoint before mode is changed") + assert_that( + driver.at_target_setpoint(), + is_(True), + "Should be at the target setpoint before mode is changed", + ) self.mock_beamline.trigger_listeners(ActiveModeUpdate(self.mode1)) result = driver.at_target_setpoint() - assert_that(result, is_(False), "Should NOT be at the target setpoint after mode is changed, because of new correction") - - def test_GIVEN_ioc_driver_with_engineering_correction_containing_a_mode_update_correction_WHEN_update_mode_THEN_correct_readback_updated_fired(self): + assert_that( + result, + is_(False), + "Should NOT be at the target setpoint after mode is changed, because of new correction", + ) + + def test_GIVEN_ioc_driver_with_engineering_correction_containing_a_mode_update_correction_WHEN_update_mode_THEN_correct_readback_updated_fired( + self, + ): mode1_mode1_offset = 11 mode1_mode2_offset = 12 mode2_mode1_offset = 210 mode2_mode2_offset = 220 - mode1_mode_selection = ModeSelectCorrection(self.default_correction, - {self.mode1.name: (ConstantCorrection(mode1_mode1_offset)), - self.mode2.name: (ConstantCorrection(mode1_mode2_offset))}) - mode2_mode_selection = ModeSelectCorrection(self.default_correction, - {self.mode1.name: (ConstantCorrection(mode2_mode1_offset)), - self.mode2.name: (ConstantCorrection(mode2_mode2_offset))}) - mode_selection_correction = ModeSelectCorrection(self.default_correction, - {self.mode1.name: mode1_mode_selection, - self.mode2.name: mode2_mode_selection}) + mode1_mode_selection = ModeSelectCorrection( + self.default_correction, + { + self.mode1.name: (ConstantCorrection(mode1_mode1_offset)), + self.mode2.name: (ConstantCorrection(mode1_mode2_offset)), + }, + ) + mode2_mode_selection = ModeSelectCorrection( + self.default_correction, + { + self.mode1.name: (ConstantCorrection(mode2_mode1_offset)), + self.mode2.name: (ConstantCorrection(mode2_mode2_offset)), + }, + ) + mode_selection_correction = ModeSelectCorrection( + self.default_correction, + {self.mode1.name: mode1_mode_selection, self.mode2.name: mode2_mode_selection}, + ) mode_selection_correction.set_observe_mode_change_on(self.mock_beamline) mode_selection_correction.add_listener(CorrectionRecalculate, self.get_update) mode_selection_correction.add_listener(CorrectionUpdate, self.get_correction_update) mock_axis = create_mock_axis("mock", 0, 1) - driver = IocDriver(Component("comp", PositionAndAngle(0, 0, 0)), ChangeAxis.POSITION, mock_axis, engineering_correction=mode_selection_correction) + driver = IocDriver( + Component("comp", PositionAndAngle(0, 0, 0)), + ChangeAxis.POSITION, + mock_axis, + engineering_correction=mode_selection_correction, + ) mock_axis.trigger_rbv_change() driver.set_observe_mode_change_on(self.mock_beamline) mock_listener = Mock() @@ -665,14 +926,25 @@ def test_GIVEN_ioc_driver_with_engineering_correction_containing_a_mode_update_c args = mock_listener.call_args[0] assert_that(args[0].correction, is_(mode1_mode1_offset)) - def test_GIVEN_beamline_with_engineering_correction_containing_a_mode_update_correction_WHEN_update_mode_THEN_correct_readback_updated_fired(self): - + def test_GIVEN_beamline_with_engineering_correction_containing_a_mode_update_correction_WHEN_update_mode_THEN_correct_readback_updated_fired( + self, + ): comp = Component("comp", PositionAndAngle(0, 0, 90)) param = AxisParameter("comp", comp, ChangeAxis.POSITION) mock_axis = create_mock_axis("mock", 0, 1) - driver = IocDriver(comp, ChangeAxis.POSITION, mock_axis, engineering_correction=self.mode_selection_correction) + driver = IocDriver( + comp, + ChangeAxis.POSITION, + mock_axis, + engineering_correction=self.mode_selection_correction, + ) mock_axis.trigger_rbv_change() - bl = Beamline(components=[comp], beamline_parameters=[param], drivers=[driver], modes=[self.mode1, self.mode2]) + bl = Beamline( + components=[comp], + beamline_parameters=[param], + drivers=[driver], + modes=[self.mode1, self.mode2], + ) bl.active_mode = self.mode1.name @@ -683,13 +955,25 @@ def test_GIVEN_beamline_with_engineering_correction_containing_a_mode_update_cor assert_that(self.correction_update.correction, is_(self.mode2_offset)) @patch("ReflectometryServer.beamline.mode_autosave") - def test_GIVEN_beamline_with_engineering_correction_containing_a_mode_update_correction_WHEN_init_THEN_set_point_includes_correction(self, autosave): + def test_GIVEN_beamline_with_engineering_correction_containing_a_mode_update_correction_WHEN_init_THEN_set_point_includes_correction( + self, autosave + ): autosave.read_parameter = Mock(return_value=self.mode1.name) comp = Component("comp", PositionAndAngle(0, 0, 90)) param = AxisParameter("comp", comp, ChangeAxis.POSITION) mock_axis = create_mock_axis("mock", 0, 1) - driver = IocDriver(comp, ChangeAxis.POSITION, mock_axis, engineering_correction=self.mode_selection_correction) - - bl = Beamline(components=[comp], beamline_parameters=[param], drivers=[driver], modes=[self.mode1, self.mode2]) + driver = IocDriver( + comp, + ChangeAxis.POSITION, + mock_axis, + engineering_correction=self.mode_selection_correction, + ) + + bl = Beamline( + components=[comp], + beamline_parameters=[param], + drivers=[driver], + modes=[self.mode1, self.mode2], + ) assert_that(param.sp, is_(-self.mode1_offset)) # readback is -11 so sp must be set to this diff --git a/ReflectometryServer/test_modules/test_footprint_calc.py b/ReflectometryServer/test_modules/test_footprint_calc.py index f48e588f..0d488d10 100644 --- a/ReflectometryServer/test_modules/test_footprint_calc.py +++ b/ReflectometryServer/test_modules/test_footprint_calc.py @@ -1,7 +1,7 @@ import unittest from hamcrest import * -from mock import patch, Mock +from mock import Mock, patch from parameterized import parameterized from ReflectometryServer.footprint_calc import * @@ -14,11 +14,7 @@ INTER_LAMBDA_MIN = 2 INTER_LAMBDA_MAX = 17 -DEFAULT_GAPS = {S1_ID: 40, - S2_ID: 30, - S3_ID: 30, - S4_ID: 40, - SA_ID: 200} +DEFAULT_GAPS = {S1_ID: 40, S2_ID: 30, S3_ID: 30, S4_ID: 40, SA_ID: 200} TEST_TOLERANCE = 1e-5 @@ -44,19 +40,20 @@ def setUp(self): self.theta.sp = default_theta self.theta.rbv = default_theta - self.calc_setup = FootprintSetup(INTER_SLIT1_POS, - INTER_SLIT2_POS, - INTER_SLIT3_POS, - INTER_SLIT4_POS, - INTER_SAMPLE_POS, - self.set_up_slit(40), - self.set_up_slit(30), - self.set_up_slit(30), - self.set_up_slit(40), - self.theta, - INTER_LAMBDA_MIN, - INTER_LAMBDA_MAX, - ) + self.calc_setup = FootprintSetup( + INTER_SLIT1_POS, + INTER_SLIT2_POS, + INTER_SLIT3_POS, + INTER_SLIT4_POS, + INTER_SAMPLE_POS, + self.set_up_slit(40), + self.set_up_slit(30), + self.set_up_slit(30), + self.set_up_slit(40), + self.theta, + INTER_LAMBDA_MIN, + INTER_LAMBDA_MAX, + ) self.calc = FootprintCalculatorSetpointReadback(self.calc_setup) @@ -72,7 +69,9 @@ def test_GIVEN_two_components_WHEN_calculating_distance_THEN_distance_is_correct self.assertEqual(expected, actual) - def test_GIVEN_two_components_in_reverse_order_WHEN_calculating_distance_THEN_distance_is_correct(self): + def test_GIVEN_two_components_in_reverse_order_WHEN_calculating_distance_THEN_distance_is_correct( + self, + ): pos1 = S2_ID pos2 = S4_ID expected = INTER_SLIT4_POS - INTER_SLIT2_POS @@ -81,105 +80,150 @@ def test_GIVEN_two_components_in_reverse_order_WHEN_calculating_distance_THEN_di self.assertEqual(expected, actual) - @parameterized.expand([(0, 0), - (0.1, 0.349065673), - (0.5, 1.7453071), - (1.0, 3.490481287), - (10, 34.72963553), - (45, 141.4213562), - (90, 200), - (135, 141.4213562)]) - def test_GIVEN_fixed_sample_size_and_variable_theta_value_WHEN_calculating_equivalent_slit_size_of_sample_THEN_result_is_correct(self, theta, expected): + @parameterized.expand( + [ + (0, 0), + (0.1, 0.349065673), + (0.5, 1.7453071), + (1.0, 3.490481287), + (10, 34.72963553), + (45, 141.4213562), + (90, 200), + (135, 141.4213562), + ] + ) + def test_GIVEN_fixed_sample_size_and_variable_theta_value_WHEN_calculating_equivalent_slit_size_of_sample_THEN_result_is_correct( + self, theta, expected + ): self.theta.sp_rbv = theta actual = self.calc.calc_equivalent_gap_by_sample_size() assert_that(actual, is_(close_to(expected, TEST_TOLERANCE))) - @parameterized.expand([(10, 0.043633093), - (50, 0.218165464), - (100, 0.436330928), - (200, 0.872661857), - (500, 2.181654642)]) - def test_GIVEN_variable_sample_size_and_fixed_theta_value_WHEN_calculating_equivalent_slit_size_of_sample_THEN_result_is_correct(self, sample_size, expected): + @parameterized.expand( + [ + (10, 0.043633093), + (50, 0.218165464), + (100, 0.436330928), + (200, 0.872661857), + (500, 2.181654642), + ] + ) + def test_GIVEN_variable_sample_size_and_fixed_theta_value_WHEN_calculating_equivalent_slit_size_of_sample_THEN_result_is_correct( + self, sample_size, expected + ): self.calc.gaps[SA_ID] = sample_size actual = self.calc.calc_equivalent_gap_by_sample_size() assert_that(actual, is_(close_to(expected, TEST_TOLERANCE))) - def test_GIVEN_INTER_parameters_WHEN_calculating_equivalent_slit_of_penumbra_at_sample_THEN_result_is_correct(self): + def test_GIVEN_INTER_parameters_WHEN_calculating_equivalent_slit_of_penumbra_at_sample_THEN_result_is_correct( + self, + ): expected = 43.25013001 actual = self.calc.calc_equivalent_gap_by_penumbra() assert_that(actual, is_(close_to(expected, TEST_TOLERANCE))) - @parameterized.expand([(0.1, 24780.51171), - (0.5, 4956.162731), - (1.0, 2478.175727), - (10, 249.0675721), - (45, 61.16492043), - (90, 43.25013001), - (135, 61.16492043)]) - def test_GIVEN_variable_theta_value_WHEN_calculating_penumbra_footprint_at_sample_THEN_result_is_correct(self, theta, expected): + @parameterized.expand( + [ + (0.1, 24780.51171), + (0.5, 4956.162731), + (1.0, 2478.175727), + (10, 249.0675721), + (45, 61.16492043), + (90, 43.25013001), + (135, 61.16492043), + ] + ) + def test_GIVEN_variable_theta_value_WHEN_calculating_penumbra_footprint_at_sample_THEN_result_is_correct( + self, theta, expected + ): self.theta.sp_rbv = theta actual = self.calc.calc_footprint() assert_that(actual, is_(close_to(expected, TEST_TOLERANCE))) - def test_GIVEN_sample_size_smaller_than_penumbra_size_WHEN_getting_slit_gap_equivalent_to_sample_THEN_return_sample_size_value(self): + def test_GIVEN_sample_size_smaller_than_penumbra_size_WHEN_getting_slit_gap_equivalent_to_sample_THEN_return_sample_size_value( + self, + ): penumbra_size = 300 - with patch.object(self.calc, 'calc_equivalent_gap_by_sample_size') as mock_sample, \ - patch.object(self.calc, 'calc_equivalent_gap_by_penumbra') as mock_sample_penumbra, \ - patch.object(self.calc, 'calc_footprint', return_value=penumbra_size): + with patch.object( + self.calc, "calc_equivalent_gap_by_sample_size" + ) as mock_sample, patch.object( + self.calc, "calc_equivalent_gap_by_penumbra" + ) as mock_sample_penumbra, patch.object( + self.calc, "calc_footprint", return_value=penumbra_size + ): self.calc.get_sample_slit_gap_equivalent() mock_sample_penumbra.assert_not_called() mock_sample.assert_called_once() - def test_GIVEN_penumbra_size_smaller_than_sample_size_WHEN_getting_slit_gap_equivalent_to_sample_THEN_return_sample_penumbra_value(self): + def test_GIVEN_penumbra_size_smaller_than_sample_size_WHEN_getting_slit_gap_equivalent_to_sample_THEN_return_sample_penumbra_value( + self, + ): penumbra_size = 100 - with patch.object(self.calc, 'calc_equivalent_gap_by_sample_size') as mock_sample, \ - patch.object(self.calc, 'calc_equivalent_gap_by_penumbra') as mock_sample_penumbra, \ - patch.object(self.calc, 'calc_footprint', return_value=penumbra_size): + with patch.object( + self.calc, "calc_equivalent_gap_by_sample_size" + ) as mock_sample, patch.object( + self.calc, "calc_equivalent_gap_by_penumbra" + ) as mock_sample_penumbra, patch.object( + self.calc, "calc_footprint", return_value=penumbra_size + ): self.calc.get_sample_slit_gap_equivalent() mock_sample.assert_not_called() mock_sample_penumbra.assert_called_once() - @parameterized.expand([(S1_ID, S2_ID, 416.9432188), - (S1_ID, SA_ID, 204.7719181), - (S1_ID, S3_ID, 266.6636815), - (S1_ID, S4_ID, 183.825933), - (S2_ID, SA_ID, 969.5817187), - (S2_ID, S3_ID, 633.3285232), - (S2_ID, S4_ID, 261.790849), - (SA_ID, S3_ID, 490.7094045), - (SA_ID, S4_ID, 173.4867372), - (S3_ID, S4_ID, 405.1548997)]) - def test_GIVEN_beam_segment_WHEN_calculating_resolution_THEN_result_is_correct(self, comp1, comp2, expected): - + @parameterized.expand( + [ + (S1_ID, S2_ID, 416.9432188), + (S1_ID, SA_ID, 204.7719181), + (S1_ID, S3_ID, 266.6636815), + (S1_ID, S4_ID, 183.825933), + (S2_ID, SA_ID, 969.5817187), + (S2_ID, S3_ID, 633.3285232), + (S2_ID, S4_ID, 261.790849), + (SA_ID, S3_ID, 490.7094045), + (SA_ID, S4_ID, 173.4867372), + (S3_ID, S4_ID, 405.1548997), + ] + ) + def test_GIVEN_beam_segment_WHEN_calculating_resolution_THEN_result_is_correct( + self, comp1, comp2, expected + ): actual = self.calc.calc_resolution(comp1, comp2) assert_that(actual, is_(close_to(expected, TEST_TOLERANCE))) - def test_GIVEN_a_theta_value_WHEN_calculating_minimum_resolution_of_all_beamline_segments_THEN_result_is_minimum(self): + def test_GIVEN_a_theta_value_WHEN_calculating_minimum_resolution_of_all_beamline_segments_THEN_result_is_minimum( + self, + ): expected = 173.4867372 actual = self.calc.calc_min_resolution() assert_that(actual, is_(close_to(expected, TEST_TOLERANCE))) - @parameterized.expand([(0.1, 0.001290144, 0.010966222), - (0.5, 0.00645064, 0.05483044), - (1.0, 0.012900789, 0.109656704), - (10, 0.128360433, 1.091063679), - (45, 0.52269211, 4.442882938), - (90, 0.739198271, 6.283185307), - (135, 0.52269211, 4.442882938)]) - def test_GIVEN_variable_theta_WHEN_calculating_Q_range_THEN_returns_correct_range(self, theta, qmin_expected, qmax_expected): + @parameterized.expand( + [ + (0.1, 0.001290144, 0.010966222), + (0.5, 0.00645064, 0.05483044), + (1.0, 0.012900789, 0.109656704), + (10, 0.128360433, 1.091063679), + (45, 0.52269211, 4.442882938), + (90, 0.739198271, 6.283185307), + (135, 0.52269211, 4.442882938), + ] + ) + def test_GIVEN_variable_theta_WHEN_calculating_Q_range_THEN_returns_correct_range( + self, theta, qmin_expected, qmax_expected + ): self.theta.sp_rbv = theta qmin_actual = self.calc.calc_q_min() @@ -189,5 +233,5 @@ def test_GIVEN_variable_theta_WHEN_calculating_Q_range_THEN_returns_correct_rang assert_that(qmax_actual, is_(close_to(qmax_expected, TEST_TOLERANCE))) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_in_beam_manager.py b/ReflectometryServer/test_modules/test_in_beam_manager.py index bfb2db24..06101024 100644 --- a/ReflectometryServer/test_modules/test_in_beam_manager.py +++ b/ReflectometryServer/test_modules/test_in_beam_manager.py @@ -1,12 +1,10 @@ import unittest -from collections import OrderedDict -from math import tan, radians, sqrt from hamcrest import * -from mock import patch, Mock +from mock import Mock, patch from parameterized import parameterized -from ReflectometryServer.axis import DirectCalcAxis, ParkingSequenceUpdate, InitUpdate +from ReflectometryServer.axis import DirectCalcAxis, InitUpdate, ParkingSequenceUpdate from ReflectometryServer.beam_path_calc import InBeamManager from ReflectometryServer.exceptions import BeamlineConfigurationInvalidException from ReflectometryServer.geometry import ChangeAxis @@ -14,15 +12,22 @@ class TestInBeamManager(unittest.TestCase): - - def set_up_beam_manager(self, current_parking_axis, axis_park_indexs=None, number_of_axes=1, axis_park_sequence_counts=None): + def set_up_beam_manager( + self, + current_parking_axis, + axis_park_indexs=None, + number_of_axes=1, + axis_park_sequence_counts=None, + ): axes_rbv = {} axes_rbv_list = [] axes_sp = {} axes_sp_list = [] if axis_park_indexs is None: axis_park_indexs = [current_parking_axis] * number_of_axes - for axis_park_index, change_axis in zip(axis_park_indexs, list(ChangeAxis)[:number_of_axes]): + for axis_park_index, change_axis in zip( + axis_park_indexs, list(ChangeAxis)[:number_of_axes] + ): axis_rbv = DirectCalcAxis(change_axis) axis_rbv.park_sequence_count = axis_park_index axes_rbv[change_axis] = axis_rbv @@ -40,46 +45,63 @@ def set_up_beam_manager(self, current_parking_axis, axis_park_indexs=None, numbe if axis_park_sequence_counts is None: axis_park_sequence_counts = [1] * number_of_axes - for axis_rbv, axis_sp, park_sequence_count in zip(axes_rbv_list, axes_sp_list, axis_park_sequence_counts): + for axis_rbv, axis_sp, park_sequence_count in zip( + axes_rbv_list, axes_sp_list, axis_park_sequence_counts + ): axis_rbv.park_sequence_count = park_sequence_count axis_sp.park_sequence_count = park_sequence_count beam_manager_sp.parking_index = current_parking_axis return axes_rbv_list, beam_manager_sp - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - def test_GIVEN_in_beam_manager_with_one_axis_parking_starting_in_beam_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_updated(self): - + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + def test_GIVEN_in_beam_manager_with_one_axis_parking_starting_in_beam_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_updated( + self, + ): current_parking_axis = 0 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[None], axis_park_sequence_counts=[4]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, axis_park_indexs=[None], axis_park_sequence_counts=[4] + ) beam_manager_sp.set_is_in_beam(False) axis[0].parking_index = current_parking_axis assert_that(beam_manager_sp.parking_index, is_(current_parking_axis + 1)) - def test_GIVEN_in_beam_manager_with_one_axis_parking_starting_at_first_park_postion_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_updated(self): - + def test_GIVEN_in_beam_manager_with_one_axis_parking_starting_at_first_park_postion_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_updated( + self, + ): current_parking_axis = 1 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[0], axis_park_sequence_counts=[3]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, axis_park_indexs=[0], axis_park_sequence_counts=[3] + ) beam_manager_sp.set_is_in_beam(False) axis[0].parking_index = current_parking_axis assert_that(beam_manager_sp.parking_index, is_(current_parking_axis + 1)) - def test_GIVEN_in_beam_manager_with_one_axis_parking_starting_at_last_park_postion_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_not_updated_updated(self): - + def test_GIVEN_in_beam_manager_with_one_axis_parking_starting_at_last_park_postion_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_not_updated_updated( + self, + ): current_parking_axis = 1 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[0], axis_park_sequence_counts=[current_parking_axis+1]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, + axis_park_indexs=[0], + axis_park_sequence_counts=[current_parking_axis + 1], + ) beam_manager_sp.set_is_in_beam(False) axis[0].parking_index = current_parking_axis assert_that(beam_manager_sp.parking_index, is_(current_parking_axis)) - def test_GIVEN_in_beam_manager_with_one_axis_component_unparking_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_updated(self): - + def test_GIVEN_in_beam_manager_with_one_axis_component_unparking_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_updated( + self, + ): current_parking_axis = 2 axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[3]) @@ -88,46 +110,67 @@ def test_GIVEN_in_beam_manager_with_one_axis_component_unparking_WHEN_axis_finis assert_that(beam_manager_sp.parking_index, is_(current_parking_axis - 1)) - def test_GIVEN_in_beam_manager_with_one_axis_component_unparking_last_step_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_updated_to_None(self): + def test_GIVEN_in_beam_manager_with_one_axis_component_unparking_last_step_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_updated_to_None( + self, + ): current_parking_axis = 0 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[1], axis_park_sequence_counts=[2]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, axis_park_indexs=[1], axis_park_sequence_counts=[2] + ) beam_manager_sp.set_is_in_beam(True) axis[0].parking_index = current_parking_axis assert_that(beam_manager_sp.parking_index, is_(None)) - def test_GIVEN_in_beam_manager_with_one_axis_component_unparking_at_end_of_sequence_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_not_updated(self): + def test_GIVEN_in_beam_manager_with_one_axis_component_unparking_at_end_of_sequence_WHEN_axis_finishing_sequence_THEN_beam_manager_sp_sequence_is_not_updated( + self, + ): current_parking_axis = None - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[0], axis_park_sequence_counts=[2]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, axis_park_indexs=[0], axis_park_sequence_counts=[2] + ) beam_manager_sp.set_is_in_beam(True) axis[0].parking_index = current_parking_axis assert_that(beam_manager_sp.parking_index, is_(None)) - def test_GIVEN_in_beam_manager_with_one_axis_WHEN_axis_finishing_at_different_sequence_index_THEN_beam_manager_sp_sequence_is_not_updated(self): - + def test_GIVEN_in_beam_manager_with_one_axis_WHEN_axis_finishing_at_different_sequence_index_THEN_beam_manager_sp_sequence_is_not_updated( + self, + ): current_parking_axis = 1 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_sequence_counts=[4]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, axis_park_sequence_counts=[4] + ) beam_manager_sp.set_is_in_beam(True) axis[0].parking_index = current_parking_axis + 1 assert_that(beam_manager_sp.parking_index, is_(current_parking_axis)) - def test_GIVEN_in_beam_manager_with_one_axis_WHEN_axis_finishing_un_parked_THEN_beam_manager_sp_sequence_is_not_updated(self): + def test_GIVEN_in_beam_manager_with_one_axis_WHEN_axis_finishing_un_parked_THEN_beam_manager_sp_sequence_is_not_updated( + self, + ): current_parking_axis = 1 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_sequence_counts=[1]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, axis_park_sequence_counts=[1] + ) axis[0].parking_index = current_parking_axis assert_that(beam_manager_sp.parking_index, is_(current_parking_axis)) - def test_GIVEN_in_beam_manager_with_two_axis_WHEN_one_axis_at_end_one_not_THEN_beam_manager_sp_sequence_is_not_updated(self): + def test_GIVEN_in_beam_manager_with_two_axis_WHEN_one_axis_at_end_one_not_THEN_beam_manager_sp_sequence_is_not_updated( + self, + ): current_parking_axis = 0 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[None, None], - number_of_axes=2, axis_park_sequence_counts=[2, 2]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, + axis_park_indexs=[None, None], + number_of_axes=2, + axis_park_sequence_counts=[2, 2], + ) beam_manager_sp.set_is_in_beam(False) axis[0].parking_index = None @@ -135,10 +178,16 @@ def test_GIVEN_in_beam_manager_with_two_axis_WHEN_one_axis_at_end_one_not_THEN_b assert_that(beam_manager_sp.parking_index, is_(current_parking_axis)) - def test_GIVEN_in_beam_manager_with_two_axis_WHEN_one_axis_at_end_one_not_other_way_round_THEN_beam_manager_sp_sequence_is_not_updated(self): + def test_GIVEN_in_beam_manager_with_two_axis_WHEN_one_axis_at_end_one_not_other_way_round_THEN_beam_manager_sp_sequence_is_not_updated( + self, + ): current_parking_axis = 0 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[None, None], - number_of_axes=2, axis_park_sequence_counts=[2, 2]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, + axis_park_indexs=[None, None], + number_of_axes=2, + axis_park_sequence_counts=[2, 2], + ) beam_manager_sp.set_is_in_beam(False) axis[1].parking_index = None @@ -147,9 +196,16 @@ def test_GIVEN_in_beam_manager_with_two_axis_WHEN_one_axis_at_end_one_not_other_ assert_that(beam_manager_sp.parking_index, is_(current_parking_axis)) @no_autosave - def test_GIVEN_in_beam_manager_with_two_axis_WHEN_both_axis_at_end_THEN_beam_manager_sp_sequence_is_updated(self): + def test_GIVEN_in_beam_manager_with_two_axis_WHEN_both_axis_at_end_THEN_beam_manager_sp_sequence_is_updated( + self, + ): current_parking_axis = 1 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, axis_park_indexs=[0, 0], number_of_axes=2, axis_park_sequence_counts=[3, 3]) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, + axis_park_indexs=[0, 0], + number_of_axes=2, + axis_park_sequence_counts=[3, 3], + ) beam_manager_sp.set_is_in_beam(False) axis[1].parking_index = current_parking_axis @@ -166,7 +222,9 @@ def test_GIVEN_in_beam_manager_is_in_WHEN_set_out_THEN_goes_to_first_parking_seq assert_that(result, is_(0)) - def test_GIVEN_in_beam_manager_is_already_out_WHEN_set_out_THEN_parking_sequence_not_reset(self): + def test_GIVEN_in_beam_manager_is_already_out_WHEN_set_out_THEN_parking_sequence_not_reset( + self, + ): current_parking_axis = 2 axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, number_of_axes=2) @@ -175,33 +233,48 @@ def test_GIVEN_in_beam_manager_is_already_out_WHEN_set_out_THEN_parking_sequence assert_that(result, is_(current_parking_axis)) - @parameterized.expand([([5],), - ([2],)]) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - def test_GIVEN_in_beam_manager_is_out_WHEN_set_in_THEN_goes_to_first_parking_sequence(self, park_sequence_count): + @parameterized.expand([([5],), ([2],)]) + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + def test_GIVEN_in_beam_manager_is_out_WHEN_set_in_THEN_goes_to_first_parking_sequence( + self, park_sequence_count + ): current_parking_axis = park_sequence_count[0] - 1 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, number_of_axes=1, axis_park_sequence_counts=park_sequence_count) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, number_of_axes=1, axis_park_sequence_counts=park_sequence_count + ) beam_manager_sp.set_is_in_beam(True) result = beam_manager_sp.parking_index assert_that(result, is_(park_sequence_count[0] - 2)) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - def test_GIVEN_in_beam_manager_is_out_with_length_1_parking_sequence_WHEN_set_in_THEN_goes_to_first_parking_sequence(self): + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + def test_GIVEN_in_beam_manager_is_out_with_length_1_parking_sequence_WHEN_set_in_THEN_goes_to_first_parking_sequence( + self, + ): park_sequence_count = [1] current_parking_axis = park_sequence_count[0] - 1 - axis, beam_manager_sp = self.set_up_beam_manager(current_parking_axis, number_of_axes=1, axis_park_sequence_counts=park_sequence_count) + axis, beam_manager_sp = self.set_up_beam_manager( + current_parking_axis, number_of_axes=1, axis_park_sequence_counts=park_sequence_count + ) beam_manager_sp.set_is_in_beam(True) result = beam_manager_sp.parking_index assert_that(result, is_(None)) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave') - def test_GIVEN_sequence_WHEN_changes_parking_index_THEN_parking_index_auto_saved(self, auto_save): + @patch("ReflectometryServer.beam_path_calc.parking_index_autosave") + def test_GIVEN_sequence_WHEN_changes_parking_index_THEN_parking_index_auto_saved( + self, auto_save + ): auto_save.read_parameter.return_value = None comp_name = "comp" expected_name = f"{comp_name}_parking_index" @@ -215,15 +288,26 @@ def test_GIVEN_sequence_WHEN_changes_parking_index_THEN_parking_index_auto_saved auto_save.write_parameter.assert_called_with(expected_name, 0) - def test_GIVEN_in_beam_manager_with_two_axis_with_sequences_of_different_lengths_WHEN_init_THEN_config_error(self): - - assert_that(calling(self.set_up_beam_manager) - .with_args(None, axis_park_indexs=[None, None], axis_park_sequence_counts=[2, 3], - number_of_axes=2), - raises(BeamlineConfigurationInvalidException)) - - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=2)) - def test_GIVEN_in_beam_manager_WHEN_parking_sequence_fires_but_no_set_in_beam_called_THEN_next_sequence_is_not_triggered(self): + def test_GIVEN_in_beam_manager_with_two_axis_with_sequences_of_different_lengths_WHEN_init_THEN_config_error( + self, + ): + assert_that( + calling(self.set_up_beam_manager).with_args( + None, + axis_park_indexs=[None, None], + axis_park_sequence_counts=[2, 3], + number_of_axes=2, + ), + raises(BeamlineConfigurationInvalidException), + ) + + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=2), + ) + def test_GIVEN_in_beam_manager_WHEN_parking_sequence_fires_but_no_set_in_beam_called_THEN_next_sequence_is_not_triggered( + self, + ): # on setup if we get a sequence finished at the last entry but the beam is in because we haven't setup all # axes yet then we should not get movement unchanged_value = 100 @@ -237,7 +321,6 @@ def test_GIVEN_in_beam_manager_WHEN_parking_sequence_fires_but_no_set_in_beam_ca assert_that(axis.parking_index, is_(unchanged_value)) -if __name__ == '__main__': - unittest.main() - +if __name__ == "__main__": + unittest.main() diff --git a/ReflectometryServer/test_modules/test_ioc_driving_layer.py b/ReflectometryServer/test_modules/test_ioc_driving_layer.py index 083f61b4..31838b52 100644 --- a/ReflectometryServer/test_modules/test_ioc_driving_layer.py +++ b/ReflectometryServer/test_modules/test_ioc_driving_layer.py @@ -1,27 +1,24 @@ import unittest from math import fabs -from parameterized import parameterized - -from mock import MagicMock, patch, Mock from hamcrest import * +from mock import MagicMock, Mock, patch +from parameterized import parameterized +from server_common.channel_access import UnableToConnectToPVException from ReflectometryServer import * from ReflectometryServer.beam_path_calc import BeamPathUpdate from ReflectometryServer.ioc_driver import CorrectedReadbackUpdate, PVWrapperForParameter -from ReflectometryServer.out_of_beam import OutOfBeamPosition, OutOfBeamLookup, OutOfBeamSequence +from ReflectometryServer.out_of_beam import OutOfBeamPosition, OutOfBeamSequence from ReflectometryServer.pv_wrapper import IsChangingUpdate from ReflectometryServer.server_status_manager import STATUS, STATUS_MANAGER -from ReflectometryServer.test_modules.utils import no_autosave -from server_common.channel_access import UnableToConnectToPVException -from ReflectometryServer.exceptions import AxisNotWithinSoftLimitsException from ReflectometryServer.test_modules.data_mother import create_mock_axis +from ReflectometryServer.test_modules.utils import no_autosave FLOAT_TOLERANCE = 1e-9 class TestHeightDriver(unittest.TestCase): - def setUp(self): start_position = 0.0 max_velocity = 10.0 @@ -33,50 +30,68 @@ def setUp(self): self.jaws_driver = IocDriver(self.jaws, ChangeAxis.POSITION, self.height_axis) - def test_GIVEN_backlash_distance_is_none_WHEN_backlash_distance_checked_THEN_returned_value_is_zero(self): + def test_GIVEN_backlash_distance_is_none_WHEN_backlash_distance_checked_THEN_returned_value_is_zero( + self, + ): expected = 0.0 self.height_axis.backlash_distance = None duration = self.jaws_driver.get_max_move_duration() assert_that(duration, is_(expected)) - def test_GIVEN_backlash_velocity_is_none_WHEN_backlash_distance_checked_THEN_returned_value_is_zero(self): + def test_GIVEN_backlash_velocity_is_none_WHEN_backlash_distance_checked_THEN_returned_value_is_zero( + self, + ): expected = 0.0 self.height_axis.backlash_distance = None duration = self.jaws_driver.get_max_move_duration() assert_that(duration, is_(expected)) - def test_GIVEN_component_with_height_setpoint_above_current_position_WHEN_calculating_move_duration_THEN_returned_duration_is_correct(self): + def test_GIVEN_component_with_height_setpoint_above_current_position_WHEN_calculating_move_duration_THEN_returned_duration_is_correct( + self, + ): target_position = 20.0 expected = 2.0 - self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(target_position) + self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + target_position + ) result = self.jaws_driver.get_max_move_duration() assert_that(result, is_(expected)) - def test_GIVEN_component_with_height_setpoint_below_current_position_WHEN_calculating_move_duration_THEN_returned_duration_is_correct(self): + def test_GIVEN_component_with_height_setpoint_below_current_position_WHEN_calculating_move_duration_THEN_returned_duration_is_correct( + self, + ): target_position = -20.0 expected = 2.0 - self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(target_position) + self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + target_position + ) result = self.jaws_driver.get_max_move_duration() assert_that(result, is_(expected)) - def test_GIVEN_move_duration_and_target_position_set_WHEN_moving_axis_THEN_computed_axis_velocity_is_correct_and_setpoint_set(self): + def test_GIVEN_move_duration_and_target_position_set_WHEN_moving_axis_THEN_computed_axis_velocity_is_correct_and_setpoint_set( + self, + ): target_position = 20.0 target_duration = 4.0 expected_velocity = 5.0 - self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(target_position) + self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + target_position + ) self.jaws_driver.perform_move(target_duration, True) assert_that(self.height_axis.velocity, is_(expected_velocity)) assert_that(self.height_axis.sp, is_(target_position)) - def test_GIVEN_displacement_changed_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered(self): + def test_GIVEN_displacement_changed_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered( + self, + ): listener = MagicMock() self.jaws.beam_path_rbv.add_listener(BeamPathUpdate, listener) expected_value = 10.1 @@ -84,11 +99,13 @@ def test_GIVEN_displacement_changed_WHEN_listeners_on_axis_triggered_THEN_listen self.height_axis.sp = expected_value listener.assert_called_once() - assert_that(self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected_value)) + assert_that( + self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), + is_(expected_value), + ) class TestNonSynchronisedHeightDriver(unittest.TestCase): - def setUp(self): start_position = 0.0 max_velocity = 10.0 @@ -98,22 +115,32 @@ def setUp(self): self.jaws.beam_path_set_point.set_incoming_beam(PositionAndAngle(0.0, 0.0, 0.0)) self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = True - self.jaws_driver = IocDriver(self.jaws, ChangeAxis.POSITION, self.height_axis, synchronised=False) + self.jaws_driver = IocDriver( + self.jaws, ChangeAxis.POSITION, self.height_axis, synchronised=False + ) - def test_GIVEN_component_with_height_setpoint_below_current_position_and_not_synchronised_WHEN_calculating_move_duration_THEN_returned_0(self): + def test_GIVEN_component_with_height_setpoint_below_current_position_and_not_synchronised_WHEN_calculating_move_duration_THEN_returned_0( + self, + ): target_position = -20.0 expected = 0.0 - self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(target_position) + self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + target_position + ) result = self.jaws_driver.get_max_move_duration() assert_that(result, is_(expected)) - def test_GIVEN_move_duration_and_target_position_set_on_non_synchronised_axis_WHEN_moving_axis_THEN_computed_axis_velocity_is_correct_and_setpoint_set(self): + def test_GIVEN_move_duration_and_target_position_set_on_non_synchronised_axis_WHEN_moving_axis_THEN_computed_axis_velocity_is_correct_and_setpoint_set( + self, + ): target_position = 20.0 target_duration = 4.0 expected_velocity = self.height_axis.velocity - self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(target_position) + self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + target_position + ) self.jaws_driver.perform_move(target_duration) @@ -126,7 +153,9 @@ class TestHeightAndTiltDriver(unittest.TestCase): def setUp(self): start_position_height = 0.0 max_velocity_height = 10.0 - self.height_axis = create_mock_axis("JAWS:HEIGHT", start_position_height, max_velocity_height) + self.height_axis = create_mock_axis( + "JAWS:HEIGHT", start_position_height, max_velocity_height + ) start_position_tilt = 90.0 max_velocity_tilt = 10.0 @@ -134,10 +163,16 @@ def setUp(self): self.tilting_jaws = TiltingComponent("component", setup=PositionAndAngle(0.0, 10.0, 90.0)) - self.tilting_jaws_driver_disp = IocDriver(self.tilting_jaws, ChangeAxis.POSITION, self.height_axis) - self.tilting_jaws_driver_ang = IocDriver(self.tilting_jaws, ChangeAxis.ANGLE, self.tilt_axis) + self.tilting_jaws_driver_disp = IocDriver( + self.tilting_jaws, ChangeAxis.POSITION, self.height_axis + ) + self.tilting_jaws_driver_ang = IocDriver( + self.tilting_jaws, ChangeAxis.ANGLE, self.tilt_axis + ) - def test_GIVEN_multiple_axes_need_to_move_WHEN_computing_move_duration_THEN_maximum_duration_is_returned(self): + def test_GIVEN_multiple_axes_need_to_move_WHEN_computing_move_duration_THEN_maximum_duration_is_returned( + self, + ): beam_angle = 45.0 expected = 4.5 beam = PositionAndAngle(0.0, 0.0, beam_angle) @@ -146,12 +181,16 @@ def test_GIVEN_multiple_axes_need_to_move_WHEN_computing_move_duration_THEN_maxi self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = True self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.ANGLE].is_changed = True - result = max(self.tilting_jaws_driver_disp.get_max_move_duration(), - self.tilting_jaws_driver_ang.get_max_move_duration()) + result = max( + self.tilting_jaws_driver_disp.get_max_move_duration(), + self.tilting_jaws_driver_ang.get_max_move_duration(), + ) assert_that(result, is_(expected)) - def test_GIVEN_move_duration_and_target_position_set_WHEN_moving_multiple_axes_THEN_computed_axis_velocity_is_correct_and_setpoint_set_for_all_axes(self): + def test_GIVEN_move_duration_and_target_position_set_WHEN_moving_multiple_axes_THEN_computed_axis_velocity_is_correct_and_setpoint_set_for_all_axes( + self, + ): beam_angle = 45.0 beam = PositionAndAngle(0.0, 0.0, beam_angle) target_duration = 10.0 @@ -160,13 +199,17 @@ def test_GIVEN_move_duration_and_target_position_set_WHEN_moving_multiple_axes_T expected_velocity_tilt = 4.5 target_position_tilt = 135.0 self.tilting_jaws.beam_path_set_point.set_incoming_beam(beam) - self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(0.0) # move component into beam + self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + 0.0 + ) # move component into beam self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.ANGLE].set_relative_to_beam(90.0) self.tilting_jaws_driver_disp.perform_move(target_duration, True) self.tilting_jaws_driver_ang.perform_move(target_duration, True) - assert_that(self.height_axis.velocity, is_(close_to(expected_velocity_height, FLOAT_TOLERANCE))) + assert_that( + self.height_axis.velocity, is_(close_to(expected_velocity_height, FLOAT_TOLERANCE)) + ) assert_that(self.height_axis.sp, is_(close_to(target_position_height, FLOAT_TOLERANCE))) assert_that(self.tilt_axis.velocity, is_(close_to(expected_velocity_tilt, FLOAT_TOLERANCE))) assert_that(self.tilt_axis.sp, is_(close_to(target_position_tilt, FLOAT_TOLERANCE))) @@ -176,7 +219,9 @@ class TestNonSynchronisedHeightAndTiltDriver(unittest.TestCase): def setUp(self): start_position_height = 0.0 max_velocity_height = 10.0 - self.height_axis = create_mock_axis("JAWS:HEIGHT", start_position_height, max_velocity_height) + self.height_axis = create_mock_axis( + "JAWS:HEIGHT", start_position_height, max_velocity_height + ) start_position_tilt = 90.0 max_velocity_tilt = 10.0 @@ -187,11 +232,16 @@ def setUp(self): self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = True self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.ANGLE].is_changed = True - self.tilting_jaws_driver_disp = IocDriver(self.tilting_jaws, ChangeAxis.POSITION, self.height_axis) - self.tilting_jaws_driver_ang = IocDriver(self.tilting_jaws, ChangeAxis.ANGLE, self.tilt_axis, - synchronised=False) + self.tilting_jaws_driver_disp = IocDriver( + self.tilting_jaws, ChangeAxis.POSITION, self.height_axis + ) + self.tilting_jaws_driver_ang = IocDriver( + self.tilting_jaws, ChangeAxis.ANGLE, self.tilt_axis, synchronised=False + ) - def test_GIVEN_multiple_axes_need_to_move_and_one_is_not_synchronised_WHEN_computing_move_duration_THEN_maximum_duration_is_returned(self): + def test_GIVEN_multiple_axes_need_to_move_and_one_is_not_synchronised_WHEN_computing_move_duration_THEN_maximum_duration_is_returned( + self, + ): beam_angle = 45.0 expected = 1.0 beam = PositionAndAngle(0.0, 0.0, beam_angle) @@ -200,13 +250,14 @@ def test_GIVEN_multiple_axes_need_to_move_and_one_is_not_synchronised_WHEN_compu self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(0) angle_duration = self.tilting_jaws_driver_ang.get_max_move_duration() - result = max(self.tilting_jaws_driver_disp.get_max_move_duration(), - angle_duration) + result = max(self.tilting_jaws_driver_disp.get_max_move_duration(), angle_duration) assert_that(result, is_(close_to(expected, FLOAT_TOLERANCE))) assert_that(angle_duration, is_(0.0)) - def test_GIVEN_move_duration_and_target_position_set_and_one_is_not_synchronised_WHEN_moving_multiple_axes_THEN_computed_axis_velocity_is_correct_and_setpoint_set_for_all_axes(self): + def test_GIVEN_move_duration_and_target_position_set_and_one_is_not_synchronised_WHEN_moving_multiple_axes_THEN_computed_axis_velocity_is_correct_and_setpoint_set_for_all_axes( + self, + ): beam_angle = 45.0 beam = PositionAndAngle(0.0, 0.0, beam_angle) target_duration = 10.0 @@ -215,13 +266,17 @@ def test_GIVEN_move_duration_and_target_position_set_and_one_is_not_synchronised expected_velocity_tilt = self.tilt_axis.velocity # Velocity should not change target_position_tilt = 135.0 self.tilting_jaws.beam_path_set_point.set_incoming_beam(beam) - self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(0.0) # move component into beam + self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + 0.0 + ) # move component into beam self.tilting_jaws.beam_path_set_point.axis[ChangeAxis.ANGLE].set_relative_to_beam(90.0) self.tilting_jaws_driver_disp.perform_move(target_duration) self.tilting_jaws_driver_ang.perform_move(target_duration) - assert_that(self.height_axis.velocity, is_(close_to(expected_velocity_height, FLOAT_TOLERANCE))) + assert_that( + self.height_axis.velocity, is_(close_to(expected_velocity_height, FLOAT_TOLERANCE)) + ) assert_that(self.height_axis.sp, is_(close_to(target_position_height, FLOAT_TOLERANCE))) assert_that(self.tilt_axis.velocity, is_(close_to(expected_velocity_tilt, FLOAT_TOLERANCE))) assert_that(self.tilt_axis.sp, is_(close_to(target_position_tilt, FLOAT_TOLERANCE))) @@ -240,31 +295,44 @@ def setUp(self): self.supermirror = ReflectingComponent("component", setup=PositionAndAngle(0.0, 10.0, 90.0)) self.supermirror.beam_path_set_point.set_incoming_beam(PositionAndAngle(0.0, 0.0, 0.0)) - self.supermirror_driver_disp = IocDriver(self.supermirror, ChangeAxis.POSITION, self.height_axis) + self.supermirror_driver_disp = IocDriver( + self.supermirror, ChangeAxis.POSITION, self.height_axis + ) self.supermirror_driver_ang = IocDriver(self.supermirror, ChangeAxis.ANGLE, self.angle_axis) - def test_GIVEN_multiple_axes_need_to_move_WHEN_computing_move_duration_THEN_maximum_duration_is_returned(self): + def test_GIVEN_multiple_axes_need_to_move_WHEN_computing_move_duration_THEN_maximum_duration_is_returned( + self, + ): target_angle = 30.0 expected = 3.0 - self.supermirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(target_angle, None, None)) + self.supermirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(target_angle, None, None) + ) self.supermirror.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(10.0) self.supermirror.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = True self.supermirror.beam_path_set_point.axis[ChangeAxis.ANGLE].is_changed = True - result = max(self.supermirror_driver_disp.get_max_move_duration(), - self.supermirror_driver_ang.get_max_move_duration()) + result = max( + self.supermirror_driver_disp.get_max_move_duration(), + self.supermirror_driver_ang.get_max_move_duration(), + ) assert_that(result, is_(expected)) def test_GIVEN_move_duration_and_target_position_set_WHEN_moving_multiple_axes_THEN_computed_axis_velocity_is_correct_and_setpoint_set_for_all_axes( - self): + self, + ): target_duration = 10.0 expected_velocity_height = 1.0 target_position_height = 10.0 expected_velocity_angle = 3.0 target_position_angle = 30.0 - self.supermirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(30.0, None, None)) - self.supermirror.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(10.0) # move component into beam + self.supermirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(30.0, None, None) + ) + self.supermirror.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + 10.0 + ) # move component into beam self.supermirror_driver_disp.perform_move(target_duration, True) self.supermirror_driver_ang.perform_move(target_duration, True) @@ -274,7 +342,9 @@ def test_GIVEN_move_duration_and_target_position_set_WHEN_moving_multiple_axes_T assert_that(fabs(self.angle_axis.velocity - expected_velocity_angle) <= FLOAT_TOLERANCE) assert_that(fabs(self.angle_axis.sp - target_position_angle) <= FLOAT_TOLERANCE) - def test_GIVEN_angle_changed_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered(self): + def test_GIVEN_angle_changed_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered( + self, + ): listener = MagicMock() self.supermirror.beam_path_rbv.add_listener(BeamPathUpdate, listener) expected_value = 10.1 @@ -282,17 +352,21 @@ def test_GIVEN_angle_changed_WHEN_listeners_on_axis_triggered_THEN_listeners_on_ self.angle_axis.sp = expected_value listener.assert_called_once() - assert_that(self.supermirror.beam_path_rbv.axis[ChangeAxis.ANGLE].get_displacement(), is_(expected_value)) + assert_that( + self.supermirror.beam_path_rbv.axis[ChangeAxis.ANGLE].get_displacement(), + is_(expected_value), + ) class TestHeightDriverInAndOutOfBeam(unittest.TestCase): - @no_autosave def setUp(self): self.start_position = 0.0 self.max_velocity = 10.0 self.tolerance_on_out_of_beam_position = 1 - self.out_of_beam_position = OutOfBeamPosition(-20, tolerance=self.tolerance_on_out_of_beam_position) + self.out_of_beam_position = OutOfBeamPosition( + -20, tolerance=self.tolerance_on_out_of_beam_position + ) self.height_axis = create_mock_axis("JAWS:HEIGHT", self.start_position, self.max_velocity) @@ -300,18 +374,25 @@ def setUp(self): self.jaws.beam_path_set_point.set_incoming_beam(PositionAndAngle(0.0, 0.0, 0.0)) self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = True - self.jaws_driver = IocDriver(self.jaws, ChangeAxis.POSITION, self.height_axis, - out_of_beam_positions=[self.out_of_beam_position]) - - def test_GIVEN_component_which_is_out_of_beam_WHEN_calculating_move_duration_THEN_returned_duration_is_0(self): + self.jaws_driver = IocDriver( + self.jaws, + ChangeAxis.POSITION, + self.height_axis, + out_of_beam_positions=[self.out_of_beam_position], + ) + def test_GIVEN_component_which_is_out_of_beam_WHEN_calculating_move_duration_THEN_returned_duration_is_0( + self, + ): self.jaws.beam_path_set_point.is_in_beam = False result = self.jaws_driver.get_max_move_duration() assert_that(result, is_(0.0)) - def test_GIVEN_component_which_is_moving_into_beam_from_out_of_beam_WHEN_calculating_move_duration_THEN_returned_duration_is_0(self): + def test_GIVEN_component_which_is_moving_into_beam_from_out_of_beam_WHEN_calculating_move_duration_THEN_returned_duration_is_0( + self, + ): self.height_axis.sp = self.out_of_beam_position.get_final_position() self.jaws.beam_path_rbv.is_in_beam = False self.jaws.beam_path_set_point.is_in_beam = True @@ -320,17 +401,21 @@ def test_GIVEN_component_which_is_moving_into_beam_from_out_of_beam_WHEN_calcula assert_that(result, is_(0.0)) - def test_GIVEN_component_which_is_out_of_beam_WHEN_moving_axis_THEN_velocity_is_not_set_and_setpoint_is_set(self): + def test_GIVEN_component_which_is_out_of_beam_WHEN_moving_axis_THEN_velocity_is_not_set_and_setpoint_is_set( + self, + ): expected_position = self.out_of_beam_position.get_final_position() target_duration = 4.0 self.jaws.beam_path_set_point.is_in_beam = False self.jaws_driver.perform_move(target_duration, True) - assert_that(self.height_axis.velocity, is_(None)) # i.e. not set because parking + assert_that(self.height_axis.velocity, is_(None)) # i.e. not set because parking assert_that(self.height_axis.sp, is_(expected_position)) - def test_GIVEN_displacement_changed_to_out_of_beam_position_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered_and_have_in_beam_is_false(self): + def test_GIVEN_displacement_changed_to_out_of_beam_position_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered_and_have_in_beam_is_false( + self, + ): listener = MagicMock() self.jaws.beam_path_rbv.add_listener(BeamPathUpdate, listener) expected_value = False @@ -340,22 +425,32 @@ def test_GIVEN_displacement_changed_to_out_of_beam_position_WHEN_listeners_on_ax listener.assert_called() assert_that(self.jaws.beam_path_rbv.is_in_beam, is_(expected_value)) - def test_GIVEN_displacement_changed_to_an_in_beam_position_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered_and_have_in_beam_is_true(self): + def test_GIVEN_displacement_changed_to_an_in_beam_position_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered_and_have_in_beam_is_true( + self, + ): listener = MagicMock() self.jaws.beam_path_rbv.add_listener(BeamPathUpdate, listener) expected_value = True - self.height_axis.sp = self.out_of_beam_position.get_final_position() + 2 * self.tolerance_on_out_of_beam_position + self.height_axis.sp = ( + self.out_of_beam_position.get_final_position() + + 2 * self.tolerance_on_out_of_beam_position + ) listener.assert_called() assert_that(self.jaws.beam_path_rbv.is_in_beam, is_(expected_value)) - def test_GIVEN_displacement_changed_to_out_of_beam_position_within_tolerance_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered_and_have_in_beam_is_false(self): + def test_GIVEN_displacement_changed_to_out_of_beam_position_within_tolerance_WHEN_listeners_on_axis_triggered_THEN_listeners_on_driving_layer_triggered_and_have_in_beam_is_false( + self, + ): listener = MagicMock() self.jaws.beam_path_rbv.add_listener(BeamPathUpdate, listener) expected_value = False - self.height_axis.sp = self.out_of_beam_position.get_final_position() + self.tolerance_on_out_of_beam_position * 0.9 + self.height_axis.sp = ( + self.out_of_beam_position.get_final_position() + + self.tolerance_on_out_of_beam_position * 0.9 + ) listener.assert_called() assert_that(self.jaws.beam_path_rbv.is_in_beam, is_(expected_value)) @@ -396,7 +491,9 @@ def test_GIVEN_sp_value_set_and_not_moved_to_THEN_driver_reports_not_at_setpoint assert_that(actual, is_(expected)) - def test_GIVEN_sp_value_set_and_moved_to_THEN_driver_reports_at_setpoint_and_changed_is_false(self): + def test_GIVEN_sp_value_set_and_moved_to_THEN_driver_reports_at_setpoint_and_changed_is_false( + self, + ): expected = True self.height_axis.sp = 0.0 self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(1.0) @@ -408,7 +505,9 @@ def test_GIVEN_sp_value_set_and_moved_to_THEN_driver_reports_at_setpoint_and_cha assert_that(actual, is_(expected)) assert_that(changed, is_(False)) - def test_GIVEN_sp_value_set_and_moved_to_but_no_distance_travelled_THEN_driver_reports_at_setpoint_and_changed_is_false(self): + def test_GIVEN_sp_value_set_and_moved_to_but_no_distance_travelled_THEN_driver_reports_at_setpoint_and_changed_is_false( + self, + ): expected = True self.height_axis.sp = 0.0 self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(0) @@ -420,11 +519,15 @@ def test_GIVEN_sp_value_set_and_moved_to_but_no_distance_travelled_THEN_driver_r assert_that(actual, is_(expected)) assert_that(changed, is_(False)) - def test_GIVEN_component_sp_is_within_motor_resolution_WHEN_comparing_to_motor_setpoint_THEN_driver_reports_at_setpoint(self): + def test_GIVEN_component_sp_is_within_motor_resolution_WHEN_comparing_to_motor_setpoint_THEN_driver_reports_at_setpoint( + self, + ): expected = True # DEFAULT_TEST_TOLERANCE is 1e-9 self.height_axis.sp = 0.123456789 - self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam(0.1234567890123456789) + self.jaws.beam_path_set_point.axis[ChangeAxis.POSITION].set_relative_to_beam( + 0.1234567890123456789 + ) actual = self.jaws_driver.at_target_setpoint() @@ -434,17 +537,21 @@ def test_GIVEN_component_sp_is_within_motor_resolution_WHEN_comparing_to_motor_s class BeamlineMoveDurationTest(unittest.TestCase): def setUp(self): sm_angle = 0.0 - supermirror = ReflectingComponent("supermirror", setup=PositionAndAngle(y=0.0, z=10.0, angle=90.0)) + supermirror = ReflectingComponent( + "supermirror", setup=PositionAndAngle(y=0.0, z=10.0, angle=90.0) + ) sm_height_axis = create_mock_axis("SM:HEIGHT", 0.0, 10.0) sm_angle_axis = create_mock_axis("SM:ANGLE", sm_angle, 10.0) - supermirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement(CorrectedReadbackUpdate(sm_angle, None, None)) + supermirror.beam_path_set_point.axis[ChangeAxis.ANGLE].set_displacement( + CorrectedReadbackUpdate(sm_angle, None, None) + ) supermirror_driver_disp = IocDriver(supermirror, ChangeAxis.POSITION, sm_height_axis) supermirror_driver_ang = IocDriver(supermirror, ChangeAxis.ANGLE, sm_angle_axis) slit_2 = Component("slit_2", setup=PositionAndAngle(y=0.0, z=20.0, angle=90.0)) slit_2_height_axis = create_mock_axis("SLIT2:HEIGHT", 0.0, 10.0) self.slit_2_driver = MagicMock(IocDriver) - self.slit_2_driver.check_limits_against_sps = MagicMock(return_value=(True, 0,0,0)) + self.slit_2_driver.check_limits_against_sps = MagicMock(return_value=(True, 0, 0, 0)) self.slit_2_driver.get_max_move_duration = MagicMock(return_value=0) self.slit_2_driver.component = slit_2 self.slit_2_driver.component_axis = ChangeAxis.POSITION @@ -470,8 +577,18 @@ def setUp(self): components = [supermirror, slit_2, slit_3, detector] beamline_parameters = [self.smangle, slit_2_pos, slit_3_pos, det_pos, det_ang] - self.drivers = [supermirror_driver_disp, supermirror_driver_ang, self.slit_2_driver, slit_3_driver, detector_driver_disp, detector_driver_ang] - mode = BeamlineMode("mode name", [self.smangle.name, slit_2_pos.name, slit_3_pos.name, det_pos.name, det_ang.name]) + self.drivers = [ + supermirror_driver_disp, + supermirror_driver_ang, + self.slit_2_driver, + slit_3_driver, + detector_driver_disp, + detector_driver_ang, + ] + mode = BeamlineMode( + "mode name", + [self.smangle.name, slit_2_pos.name, slit_3_pos.name, det_pos.name, det_ang.name], + ) beam_start = PositionAndAngle(0.0, 0.0, 0.0) self.beamline = Beamline(components, beamline_parameters, self.drivers, [mode], beam_start) @@ -482,13 +599,15 @@ def setUp(self): det_pos.sp_no_move = 0.0 det_ang.sp_no_move = 0 - def test_GIVEN_multiple_components_in_beamline_WHEN_triggering_move_THEN_components_move_at_speed_of_slowest_axis(self): + def test_GIVEN_multiple_components_in_beamline_WHEN_triggering_move_THEN_components_move_at_speed_of_slowest_axis( + self, + ): # detector angle axis takes longest expected_max_duration = 4.5 sm_angle_to_set = 22.5 self.smangle.sp_no_move = sm_angle_to_set - with patch.object(self.beamline, '_perform_move_for_all_drivers') as mock: + with patch.object(self.beamline, "_perform_move_for_all_drivers") as mock: self.beamline.move = 1 mock.assert_called_with(expected_max_duration) @@ -498,7 +617,9 @@ def test_GIVEN_driver_contains_disconnected_pv_WHEN_beamline_moves_THEN_status_s self.smangle.sp_no_move = sm_angle_to_set self.slit_3_pos.sp_no_move = -10 - self.slit_3_driver.perform_move = MagicMock(side_effect=UnableToConnectToPVException("A_PV", "ERROR")) + self.slit_3_driver.perform_move = MagicMock( + side_effect=UnableToConnectToPVException("A_PV", "ERROR") + ) self.beamline.move = 1 assert_that(STATUS_MANAGER.status, is_(STATUS.ERROR)) @@ -515,85 +636,288 @@ def test_GIVEN_driver_not_within_soft_limits_WHEN_beamline_moves_THEN_status_set class BeamlineBacklashMoveDurationTest(unittest.TestCase): - - @parameterized.expand([ - ( # Error when max_vel is 0 - {"max_vel": 0, "start": 0, "set": 0.5, "back_dist": 0, "back_speed": 0, "dir": "Pos"}, - {"max_vel": 0, "start": 0, "set": 10, "back_dist": 0, "back_speed": 0, "dir": "Pos"}, - "ERROR" - ), - ( # No error when back_speed is 0 if back_dist is also 0 - {"max_vel": 1, "start": 0, "set": 0.5, "back_dist": 0, "back_speed": 0, "dir": "Pos"}, - {"max_vel": 1, "start": 0, "set": 10, "back_dist": 0, "back_speed": 0, "dir": "Pos"}, - 10 # 10/1 - ), - ( # No backlash distance - {"max_vel": 20, "start": 0, "set": 0.5, "back_dist": 0, "back_speed": 1, "dir": "Pos"}, - {"max_vel": 0.2, "start": 0, "set": 10, "back_dist": 0, "back_speed": 0.1, "dir": "Pos"}, - 50 # 10/0.2 - ), - ( # Test where backlash is in same direction as motion - {"max_vel": 20, "start": 0, "set": 0.5, "back_dist": 0, "back_speed": 1, "dir": "Pos"}, - {"max_vel": 0.2, "start": 0, "set": 10, "back_dist": 2, "back_speed": 0.1, "dir": "Pos"}, - 60 # (10-2)/0.2 + 2/0.1 - ), - ( # Test where backlash is in the opposite direction to set point - {"max_vel": 20, "start": 0.5, "set": 0, "back_dist": 0, "back_speed": 1, "dir": "Pos"}, - {"max_vel": 0.2, "start": 10, "set": 0, "back_dist": 2, "back_speed": 0.1, "dir": "Pos"}, - 80 # (10+2)/0.2 + 2/0.1 - ), - ( # Test where move starts already within backlash distance - {"max_vel": 20, "start": 0, "set": 0.5, "back_dist": 0, "back_speed": 1, "dir": "Pos"}, - {"max_vel": 0.2, "start": 0, "set": 1, "back_dist": 2, "back_speed": 0.1, "dir": "Pos"}, - 10 # 1/0.1 - ), - ( # Test where move starts within backlash distance but not from backlash direction - {"max_vel": 20, "start": 0.5, "set": 0, "back_dist": 0, "back_speed": 1, "dir": "Pos"}, - {"max_vel": 0.2, "start": 1, "set": 0, "back_dist": 2, "back_speed": 0.1, "dir": "Pos"}, - 35 # (1+2)/0.2 + 2/0.1 - ), - ( # Test where both axes have backlash - {"max_vel": 20, "start": 0, "set": 0.5, "back_dist": 0.1, "back_speed": 0.1, "dir": "Pos"}, - {"max_vel": 0.2, "start": 0, "set": 0.1, "back_dist": 2, "back_speed": 0.1, "dir": "Pos"}, - 1.02 # (0.5-0.1)/20 + 0.1/0.1 - ), - ( # Test where backlash is in same direction as motion and axes are Neg - {"max_vel": 20, "start": 0, "set": 0.5, "back_dist": 0, "back_speed": 1, "dir": "Neg"}, - {"max_vel": 0.2, "start": 0, "set": 10, "back_dist": 2, "back_speed": 0.1, "dir": "Neg"}, - 80 # (10+2)/0.2 + 2/0.1 - ), - ( # Test where backlash is in the opposite direction to set point and axes are Neg - {"max_vel": 20, "start": 0.5, "set": 0, "back_dist": 0, "back_speed": 1, "dir": "Neg"}, - {"max_vel": 0.2, "start": 10, "set": 0, "back_dist": 2, "back_speed": 0.1, "dir": "Neg"}, - 60 # (10-2)/0.2 + 2/0.1 - ), - ( # Test where move starts already within backlash distance and axes are Neg - {"max_vel": 20, "start": 0, "set": 0.5, "back_dist": 0, "back_speed": 1, "dir": "Neg"}, - {"max_vel": 0.2, "start": 0, "set": 1, "back_dist": 2, "back_speed": 0.1, "dir": "Neg"}, - 35 # (1+2)/0.2 + 2/0.1 - ), - ( # Test where move starts within backlash distance but not from backlash direction and axes are Neg - {"max_vel": 20, "start": 0.5, "set": 0, "back_dist": 0, "back_speed": 1, "dir": "Neg"}, - {"max_vel": 0.2, "start": 1, "set": 0, "back_dist": 2, "back_speed": 0.1, "dir": "Neg"}, - 10 # 1/0.1 - ), - ( # Test where both axes have backlash and axes are Neg - {"max_vel": 20, "start": 0, "set": 0.5, "back_dist": 0.1, "back_speed": 0.1, "dir": "Neg"}, - {"max_vel": 0.2, "start": 0, "set": 0.1, "back_dist": 2, "back_speed": 0.1, "dir": "Neg"}, - 30.5 # (0.1+2)/0.2 + 2/0.1 - ), - ( # Test where both axes have backlash and one axis is Neg - {"max_vel": 20, "start": 0, "set": 0.5, "back_dist": 0.1, "back_speed": 0.1, "dir": "Neg"}, - {"max_vel": 0.2, "start": 0, "set": 0.1, "back_dist": 2, "back_speed": 0.1, "dir": "Pos"}, - 1.03 # (0.5+0.1)/20 + 0.1/0.1 - ), - ]) + @parameterized.expand( + [ + ( # Error when max_vel is 0 + { + "max_vel": 0, + "start": 0, + "set": 0.5, + "back_dist": 0, + "back_speed": 0, + "dir": "Pos", + }, + { + "max_vel": 0, + "start": 0, + "set": 10, + "back_dist": 0, + "back_speed": 0, + "dir": "Pos", + }, + "ERROR", + ), + ( # No error when back_speed is 0 if back_dist is also 0 + { + "max_vel": 1, + "start": 0, + "set": 0.5, + "back_dist": 0, + "back_speed": 0, + "dir": "Pos", + }, + { + "max_vel": 1, + "start": 0, + "set": 10, + "back_dist": 0, + "back_speed": 0, + "dir": "Pos", + }, + 10, # 10/1 + ), + ( # No backlash distance + { + "max_vel": 20, + "start": 0, + "set": 0.5, + "back_dist": 0, + "back_speed": 1, + "dir": "Pos", + }, + { + "max_vel": 0.2, + "start": 0, + "set": 10, + "back_dist": 0, + "back_speed": 0.1, + "dir": "Pos", + }, + 50, # 10/0.2 + ), + ( # Test where backlash is in same direction as motion + { + "max_vel": 20, + "start": 0, + "set": 0.5, + "back_dist": 0, + "back_speed": 1, + "dir": "Pos", + }, + { + "max_vel": 0.2, + "start": 0, + "set": 10, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Pos", + }, + 60, # (10-2)/0.2 + 2/0.1 + ), + ( # Test where backlash is in the opposite direction to set point + { + "max_vel": 20, + "start": 0.5, + "set": 0, + "back_dist": 0, + "back_speed": 1, + "dir": "Pos", + }, + { + "max_vel": 0.2, + "start": 10, + "set": 0, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Pos", + }, + 80, # (10+2)/0.2 + 2/0.1 + ), + ( # Test where move starts already within backlash distance + { + "max_vel": 20, + "start": 0, + "set": 0.5, + "back_dist": 0, + "back_speed": 1, + "dir": "Pos", + }, + { + "max_vel": 0.2, + "start": 0, + "set": 1, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Pos", + }, + 10, # 1/0.1 + ), + ( # Test where move starts within backlash distance but not from backlash direction + { + "max_vel": 20, + "start": 0.5, + "set": 0, + "back_dist": 0, + "back_speed": 1, + "dir": "Pos", + }, + { + "max_vel": 0.2, + "start": 1, + "set": 0, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Pos", + }, + 35, # (1+2)/0.2 + 2/0.1 + ), + ( # Test where both axes have backlash + { + "max_vel": 20, + "start": 0, + "set": 0.5, + "back_dist": 0.1, + "back_speed": 0.1, + "dir": "Pos", + }, + { + "max_vel": 0.2, + "start": 0, + "set": 0.1, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Pos", + }, + 1.02, # (0.5-0.1)/20 + 0.1/0.1 + ), + ( # Test where backlash is in same direction as motion and axes are Neg + { + "max_vel": 20, + "start": 0, + "set": 0.5, + "back_dist": 0, + "back_speed": 1, + "dir": "Neg", + }, + { + "max_vel": 0.2, + "start": 0, + "set": 10, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Neg", + }, + 80, # (10+2)/0.2 + 2/0.1 + ), + ( # Test where backlash is in the opposite direction to set point and axes are Neg + { + "max_vel": 20, + "start": 0.5, + "set": 0, + "back_dist": 0, + "back_speed": 1, + "dir": "Neg", + }, + { + "max_vel": 0.2, + "start": 10, + "set": 0, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Neg", + }, + 60, # (10-2)/0.2 + 2/0.1 + ), + ( # Test where move starts already within backlash distance and axes are Neg + { + "max_vel": 20, + "start": 0, + "set": 0.5, + "back_dist": 0, + "back_speed": 1, + "dir": "Neg", + }, + { + "max_vel": 0.2, + "start": 0, + "set": 1, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Neg", + }, + 35, # (1+2)/0.2 + 2/0.1 + ), + ( # Test where move starts within backlash distance but not from backlash direction and axes are Neg + { + "max_vel": 20, + "start": 0.5, + "set": 0, + "back_dist": 0, + "back_speed": 1, + "dir": "Neg", + }, + { + "max_vel": 0.2, + "start": 1, + "set": 0, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Neg", + }, + 10, # 1/0.1 + ), + ( # Test where both axes have backlash and axes are Neg + { + "max_vel": 20, + "start": 0, + "set": 0.5, + "back_dist": 0.1, + "back_speed": 0.1, + "dir": "Neg", + }, + { + "max_vel": 0.2, + "start": 0, + "set": 0.1, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Neg", + }, + 30.5, # (0.1+2)/0.2 + 2/0.1 + ), + ( # Test where both axes have backlash and one axis is Neg + { + "max_vel": 20, + "start": 0, + "set": 0.5, + "back_dist": 0.1, + "back_speed": 0.1, + "dir": "Neg", + }, + { + "max_vel": 0.2, + "start": 0, + "set": 0.1, + "back_dist": 2, + "back_speed": 0.1, + "dir": "Pos", + }, + 1.03, # (0.5+0.1)/20 + 0.1/0.1 + ), + ] + ) def test_GIVEN_two_axes_with_backlash_WHEN_triggering_move_THEN_components_move_at_speed_of_slowest_axis( - self, pos, ang, expected_max_duration): - - detector = TiltingComponent("point_detector", setup=PositionAndAngle(y=0.0, z=6.0, angle=90.0)) - detector_height_axis = create_mock_axis("HEIGHT", pos["start"], pos["max_vel"], pos["back_dist"], pos["back_speed"], pos["dir"]) - detector_tilt_axis = create_mock_axis("TILT", ang["start"], ang["max_vel"], ang["back_dist"], ang["back_speed"], ang["dir"]) + self, pos, ang, expected_max_duration + ): + detector = TiltingComponent( + "point_detector", setup=PositionAndAngle(y=0.0, z=6.0, angle=90.0) + ) + detector_height_axis = create_mock_axis( + "HEIGHT", pos["start"], pos["max_vel"], pos["back_dist"], pos["back_speed"], pos["dir"] + ) + detector_tilt_axis = create_mock_axis( + "TILT", ang["start"], ang["max_vel"], ang["back_dist"], ang["back_speed"], ang["dir"] + ) detector_driver_disp = IocDriver(detector, ChangeAxis.POSITION, detector_height_axis) detector_driver_ang = IocDriver(detector, ChangeAxis.ANGLE, detector_tilt_axis) det_pos = AxisParameter("det_pos", detector, ChangeAxis.POSITION) @@ -602,8 +926,7 @@ def test_GIVEN_two_axes_with_backlash_WHEN_triggering_move_THEN_components_move_ components = [detector] beamline_parameters = [det_pos, det_ang] drivers = [detector_driver_disp, detector_driver_ang] - mode = BeamlineMode("mode name", - [det_pos.name, det_ang.name]) + mode = BeamlineMode("mode name", [det_pos.name, det_ang.name]) beam_start = PositionAndAngle(0.0, 0.0, 0.0) beamline = Beamline(components, beamline_parameters, drivers, [mode], beam_start) @@ -612,7 +935,7 @@ def test_GIVEN_two_axes_with_backlash_WHEN_triggering_move_THEN_components_move_ det_pos.sp_no_move = pos["set"] det_ang.sp_no_move = ang["set"] - with patch.object(beamline, '_perform_move_for_all_drivers') as mock: + with patch.object(beamline, "_perform_move_for_all_drivers") as mock: beamline.move = 1 if expected_max_duration == "ERROR": @@ -622,7 +945,6 @@ def test_GIVEN_two_axes_with_backlash_WHEN_triggering_move_THEN_components_move_ class TestIocDriverWithAxesDependentOnParam(unittest.TestCase): - def setUp(self): start_position = 0.0 max_velocity = 10.0 @@ -638,35 +960,51 @@ def setUp(self): self.opt3 = "other" self.param = EnumParameter("param", options=[self.opt1, self.opt2, self.opt3]) - self.jaws_driver = IocDriver(self.jaws, ChangeAxis.POSITION, self.height_axis, - pv_wrapper_for_parameter=PVWrapperForParameter(self.param, {self.opt2: self.alt_axis})) + self.jaws_driver = IocDriver( + self.jaws, + ChangeAxis.POSITION, + self.height_axis, + pv_wrapper_for_parameter=PVWrapperForParameter(self.param, {self.opt2: self.alt_axis}), + ) def test_GIVEN_component_WHEN_both_axis_moves_THEN_displacement_is_axis_value(self): expected = 1 self.height_axis.sp = expected self.alt_axis.sp = 99 - assert_that(self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected)) + assert_that( + self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected) + ) - def test_GIVEN_component_and_param_set_to_alt_WHEN_alt_axis_moves_THEN_displacement_is_alt_axis_value(self): + def test_GIVEN_component_and_param_set_to_alt_WHEN_alt_axis_moves_THEN_displacement_is_alt_axis_value( + self, + ): expected = 1 self.param.sp = self.opt2 self.height_axis.sp = 99 self.alt_axis.sp = expected - assert_that(self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected)) + assert_that( + self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected) + ) - def test_GIVEN_component_and_axis_position_set_WHEN_change_param_THEN_displacement_changes(self): + def test_GIVEN_component_and_axis_position_set_WHEN_change_param_THEN_displacement_changes( + self, + ): expected = 1 - self.height_axis.sp =99 + self.height_axis.sp = 99 self.alt_axis.sp = expected self.param.sp = self.opt2 - assert_that(self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected)) + assert_that( + self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected) + ) - def test_GIVEN_component_and_axis_position_set_WHEN_change_param_to_non_change_THEN_displacement_changes_to_default_axis(self): + def test_GIVEN_component_and_axis_position_set_WHEN_change_param_to_non_change_THEN_displacement_changes_to_default_axis( + self, + ): expected = 1 self.height_axis.sp = expected self.alt_axis.sp = 99 @@ -674,9 +1012,13 @@ def test_GIVEN_component_and_axis_position_set_WHEN_change_param_to_non_change_T self.param.sp = self.opt3 - assert_that(self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected)) + assert_that( + self.jaws.beam_path_rbv.axis[ChangeAxis.POSITION].get_displacement(), is_(expected) + ) - def test_GIVEN_component_and_param_set_to_alt_WHEN_alt_axis_changes_THEN_change_is_alt_axis_value(self): + def test_GIVEN_component_and_param_set_to_alt_WHEN_alt_axis_changes_THEN_change_is_alt_axis_value( + self, + ): expected = True self.param.sp = self.opt2 @@ -704,18 +1046,23 @@ def test_GIVEN_axis_to_change_on_param_WHEN_init_THEN_all_axes_are_initialised(s class TestIOCDriverInAndOutOfBeamWithOffsetOutOfBeamPosition(unittest.TestCase): - @parameterized.expand([(0,), (1,)]) - def test_GIVEN_out_of_beam_with_offset_WHEN_move_THEN_component_moves_to_correct_place(self, correction): + def test_GIVEN_out_of_beam_with_offset_WHEN_move_THEN_component_moves_to_correct_place( + self, correction + ): offset = 10 beam_height = 2 expected_position = beam_height + offset + correction comp = Component("Comp", PositionAndAngle(0, 1, 90)) mock_axis = create_mock_axis("axis", 0, 1) - driver = IocDriver(comp, ChangeAxis.POSITION, mock_axis, - out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)], - engineering_correction=ConstantCorrection(correction)) + driver = IocDriver( + comp, + ChangeAxis.POSITION, + mock_axis, + out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)], + engineering_correction=ConstantCorrection(correction), + ) comp.beam_path_set_point.set_incoming_beam(PositionAndAngle(beam_height, 0, 0)) comp.beam_path_set_point.is_in_beam = False @@ -726,15 +1073,21 @@ def test_GIVEN_out_of_beam_with_offset_WHEN_move_THEN_component_moves_to_correct assert_that(result, is_(expected_position)) - def test_GIVEN_out_of_beam_with_offset_on_angle_WHEN_move_THEN_component_moves_to_correct_place(self): + def test_GIVEN_out_of_beam_with_offset_on_angle_WHEN_move_THEN_component_moves_to_correct_place( + self, + ): offset = 10 beam_angle = 2 expected_position = beam_angle + offset comp = TiltingComponent("Comp", PositionAndAngle(0, 1, 90)) mock_axis = create_mock_axis("axis", 0, 1) - driver = IocDriver(comp, ChangeAxis.ANGLE, mock_axis, - out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)]) + driver = IocDriver( + comp, + ChangeAxis.ANGLE, + mock_axis, + out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)], + ) comp.beam_path_set_point.set_incoming_beam(PositionAndAngle(0, 0, beam_angle)) comp.beam_path_set_point.is_in_beam = False @@ -750,7 +1103,12 @@ def test_GIVEN_out_of_beam_with_offset_WHEN_axis_move_THEN_component_is_out_of_b beam_height = 2 comp = Component("Comp", PositionAndAngle(0, 1, 90)) mock_axis = create_mock_axis("axis", 0, 1) - IocDriver(comp, ChangeAxis.POSITION, mock_axis, out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)]) + IocDriver( + comp, + ChangeAxis.POSITION, + mock_axis, + out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)], + ) comp.beam_path_rbv.set_incoming_beam(PositionAndAngle(beam_height, 0, 0)) @@ -760,12 +1118,19 @@ def test_GIVEN_out_of_beam_with_offset_WHEN_axis_move_THEN_component_is_out_of_b assert_that(result, is_(False)) - def test_GIVEN_out_of_beam_with_offset_for_angle_WHEN_axis_move_THEN_component_is_out_of_beam(self): + def test_GIVEN_out_of_beam_with_offset_for_angle_WHEN_axis_move_THEN_component_is_out_of_beam( + self, + ): offset = 10 beam_angle = 2 comp = TiltingComponent("Comp", PositionAndAngle(0, 1, 90)) mock_axis = create_mock_axis("axis", 0, 1) - IocDriver(comp, ChangeAxis.ANGLE, mock_axis, out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)]) + IocDriver( + comp, + ChangeAxis.ANGLE, + mock_axis, + out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)], + ) comp.beam_path_rbv.set_incoming_beam(PositionAndAngle(0, 0, beam_angle)) @@ -781,8 +1146,12 @@ def test_GIVEN_out_of_beam_with_offset_for_angle_WHEN_init_THEN_component_is_out comp = Component("Comp", PositionAndAngle(0, 1, 90)) mock_axis = create_mock_axis("axis", 0, 1) mock_axis.sp = offset + beam_height - driver = IocDriver(comp, ChangeAxis.POSITION, mock_axis, - out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)]) + driver = IocDriver( + comp, + ChangeAxis.POSITION, + mock_axis, + out_of_beam_positions=[OutOfBeamPosition(offset, is_offset=True)], + ) comp.beam_path_set_point.set_incoming_beam(PositionAndAngle(beam_height, 0, 0)) @@ -794,26 +1163,35 @@ def test_GIVEN_out_of_beam_with_offset_for_angle_WHEN_init_THEN_component_is_out class TestIOCDriverInAndOutOfBeamWithParkingsequence(unittest.TestCase): - def setUp(self): self.start_position = 0.0 self.max_velocity = 10.0 self.tolerance_on_out_of_beam_position = 1 self.parking_sequence = [-1, -2, -3] - self.out_of_beam_position = OutOfBeamSequence(self.parking_sequence, tolerance=self.tolerance_on_out_of_beam_position) + self.out_of_beam_position = OutOfBeamSequence( + self.parking_sequence, tolerance=self.tolerance_on_out_of_beam_position + ) self.axis = create_mock_axis("HEIGHT", self.start_position, self.max_velocity) - with patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', - new=Mock(return_value=None)): + with patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ): self.comp = Component("component", setup=PositionAndAngle(0.0, 10.0, 90.0)) self.comp.beam_path_set_point.set_incoming_beam(PositionAndAngle(0.0, 0.0, 0.0)) self.comp.beam_path_set_point.axis[ChangeAxis.POSITION].is_changed = True - self.jaws_driver = IocDriver(self.comp, ChangeAxis.POSITION, self.axis, - out_of_beam_positions=[self.out_of_beam_position]) + self.jaws_driver = IocDriver( + self.comp, + ChangeAxis.POSITION, + self.axis, + out_of_beam_positions=[self.out_of_beam_position], + ) - def test_GIVEN_parking_sequence_WHEN_ioc_driver_moves_to_be_at_end_of_sequence_THEN_axis_has_end_of_sequence_set(self): + def test_GIVEN_parking_sequence_WHEN_ioc_driver_moves_to_be_at_end_of_sequence_THEN_axis_has_end_of_sequence_set( + self, + ): parking_index = 1 self.comp.beam_path_set_point.in_beam_manager.parking_index = parking_index self.axis._value = self.parking_sequence[parking_index] @@ -823,7 +1201,9 @@ def test_GIVEN_parking_sequence_WHEN_ioc_driver_moves_to_be_at_end_of_sequence_T assert_that(result, is_(parking_index)) - def test_GIVEN_parking_sequence_WHEN_ioc_driver_moves_to_be_not_at_end_of_sequence_THEN_parking_sequence_not_changed(self): + def test_GIVEN_parking_sequence_WHEN_ioc_driver_moves_to_be_not_at_end_of_sequence_THEN_parking_sequence_not_changed( + self, + ): expected_sequence = 200 self.comp.beam_path_rbv.axis[ChangeAxis.POSITION].parking_index = expected_sequence parking_index = 1 @@ -834,4 +1214,3 @@ def test_GIVEN_parking_sequence_WHEN_ioc_driver_moves_to_be_not_at_end_of_sequen result = self.comp.beam_path_rbv.axis[ChangeAxis.POSITION].parking_index assert_that(result, is_(expected_sequence)) - diff --git a/ReflectometryServer/test_modules/test_movement.py b/ReflectometryServer/test_modules/test_movement.py index 348d36bf..dd8bc344 100644 --- a/ReflectometryServer/test_modules/test_movement.py +++ b/ReflectometryServer/test_modules/test_movement.py @@ -1,24 +1,27 @@ import unittest +from math import radians, sqrt, tan -from math import tan, radians, sqrt from hamcrest import * from parameterized import parameterized -from ReflectometryServer.movement_strategy import ANGULAR_TOLERANCE, LinearMovementCalc from ReflectometryServer.geometry import Position, PositionAndAngle +from ReflectometryServer.movement_strategy import ANGULAR_TOLERANCE, LinearMovementCalc from ReflectometryServer.test_modules.utils import position class TestMovementIntercept(unittest.TestCase): - - def test_GIVEN_movement_and_beam_at_the_same_angle_WHEN_get_intercept_THEN_raises_calc_error(self): + def test_GIVEN_movement_and_beam_at_the_same_angle_WHEN_get_intercept_THEN_raises_calc_error( + self, + ): angle = 12.3 movement = LinearMovementCalc(PositionAndAngle(1, 1, angle)) beam = PositionAndAngle(0, 0, angle) assert_that(calling(movement.calculate_interception).with_args(beam), raises(ValueError)) - def test_GIVEN_movement_and_beam_at_the_same_angle_within_tolerance_WHEN_get_intercept_THEN_raises_calc_error(self): + def test_GIVEN_movement_and_beam_at_the_same_angle_within_tolerance_WHEN_get_intercept_THEN_raises_calc_error( + self, + ): angle = 12.3 tolerance = ANGULAR_TOLERANCE movement = LinearMovementCalc(PositionAndAngle(1, 1, angle + tolerance * 0.99)) @@ -26,7 +29,9 @@ def test_GIVEN_movement_and_beam_at_the_same_angle_within_tolerance_WHEN_get_int assert_that(calling(movement.calculate_interception).with_args(beam), raises(ValueError)) - def test_GIVEN_movement_and_beam_at_the_opposite_angles_within_tolerance_WHEN_get_intercept_THEN_raises_calc_error(self): + def test_GIVEN_movement_and_beam_at_the_opposite_angles_within_tolerance_WHEN_get_intercept_THEN_raises_calc_error( + self, + ): angle = 12.3 + 180.0 tolerance = ANGULAR_TOLERANCE movement = LinearMovementCalc(PositionAndAngle(1, 1, angle + tolerance * 0.99)) @@ -34,7 +39,9 @@ def test_GIVEN_movement_and_beam_at_the_opposite_angles_within_tolerance_WHEN_ge assert_that(calling(movement.calculate_interception).with_args(beam), raises(ValueError)) - def test_GIVEN_movement_perpendicular_to_z_at_beam_angle_0_WHEN_get_intercept_THEN_position_is_initial_position(self): + def test_GIVEN_movement_perpendicular_to_z_at_beam_angle_0_WHEN_get_intercept_THEN_position_is_initial_position( + self, + ): y = 0 z = 10 movement = LinearMovementCalc(PositionAndAngle(y, z, 90)) @@ -44,7 +51,9 @@ def test_GIVEN_movement_perpendicular_to_z_at_beam_angle_0_WHEN_get_intercept_TH assert_that(result, is_(position(Position(y, z)))) - def test_GIVEN_movement_perpendicular_to_z_at_beam_angle_10_WHEN_get_intercept_THEN_position_is_z_as_initial_y_as_right_angle_triangle(self): + def test_GIVEN_movement_perpendicular_to_z_at_beam_angle_10_WHEN_get_intercept_THEN_position_is_z_as_initial_y_as_right_angle_triangle( + self, + ): y = 0 z = 10 beam_angle = 10 @@ -56,7 +65,9 @@ def test_GIVEN_movement_perpendicular_to_z_at_beam_angle_10_WHEN_get_intercept_T assert_that(result, is_(position(Position(expected_y, z)))) - def test_GIVEN_movement_anti_perpendicular_to_z_at_beam_angle_10_WHEN_get_intercept_THEN_position_is_z_as_initial_y_as_right_angle_triangle(self): + def test_GIVEN_movement_anti_perpendicular_to_z_at_beam_angle_10_WHEN_get_intercept_THEN_position_is_z_as_initial_y_as_right_angle_triangle( + self, + ): y = 0 z = 10 beam_angle = 10 @@ -68,7 +79,9 @@ def test_GIVEN_movement_anti_perpendicular_to_z_at_beam_angle_10_WHEN_get_interc assert_that(result, is_(position(Position(expected_y, z)))) - def test_GIVEN_beam_perpendicular_to_z_at_movement_angle_10_WHEN_get_intercept_THEN_position_is_z_as_initial_y_as_right_angle_triangle(self): + def test_GIVEN_beam_perpendicular_to_z_at_movement_angle_10_WHEN_get_intercept_THEN_position_is_z_as_initial_y_as_right_angle_triangle( + self, + ): y = 0 z = 10 angle = 10 @@ -82,7 +95,9 @@ def test_GIVEN_beam_perpendicular_to_z_at_movement_angle_10_WHEN_get_intercept_T assert_that(result, is_(position(Position(expected_y, beam_z)))) @parameterized.expand([(180,), (0,)]) - def test_GIVEN_movement_45_to_z_at_beam_angle_along_z_WHEN_get_intercept_THEN_position_is_initial_position(self, angle): + def test_GIVEN_movement_45_to_z_at_beam_angle_along_z_WHEN_get_intercept_THEN_position_is_initial_position( + self, angle + ): y = 0 z = 10 movement = LinearMovementCalc(PositionAndAngle(y, z, 45)) @@ -92,7 +107,9 @@ def test_GIVEN_movement_45_to_z_at_beam_angle_along_z_WHEN_get_intercept_THEN_po assert_that(result, is_(position(Position(y, z)))) - def test_GIVEN_movement_0_to_z_and_beam_angle_45_WHEN_get_intercept_THEN_position_is_on_movement_axis(self): + def test_GIVEN_movement_0_to_z_and_beam_angle_45_WHEN_get_intercept_THEN_position_is_on_movement_axis( + self, + ): y = 20 z = 10 movement = LinearMovementCalc(PositionAndAngle(y, z, 0)) @@ -104,7 +121,9 @@ def test_GIVEN_movement_0_to_z_and_beam_angle_45_WHEN_get_intercept_THEN_positio assert_that(result, is_(position(Position(expected_y, expected_z)))) - def test_GIVEN_movement_20_to_z_and_beam_angle_45_WHEN_get_intercept_THEN_position_is_on_movement_axis(self): + def test_GIVEN_movement_20_to_z_and_beam_angle_45_WHEN_get_intercept_THEN_position_is_on_movement_axis( + self, + ): beam = PositionAndAngle(0, 0, 45) expected_y = 4 expected_z = 4 @@ -118,7 +137,9 @@ def test_GIVEN_movement_20_to_z_and_beam_angle_45_WHEN_get_intercept_THEN_positi assert_that(result, is_(position(Position(expected_y, expected_z)))) - def test_GIVEN_displacement_WHEN_calculating_position_in_mantid_coordinates_THEN_coordinates_at_given_displacement_are_returned(self): + def test_GIVEN_displacement_WHEN_calculating_position_in_mantid_coordinates_THEN_coordinates_at_given_displacement_are_returned( + self, + ): y = 0 z = 10 angle = 90 @@ -129,7 +150,9 @@ def test_GIVEN_displacement_WHEN_calculating_position_in_mantid_coordinates_THEN assert_that(result, is_(position(Position(displacement, z)))) - def test_GIVEN_movement_45_to_z_at_beam_along_z_WHEN_z_of_movement_axis_changes_THEN_position_is_new_z(self): + def test_GIVEN_movement_45_to_z_at_beam_along_z_WHEN_z_of_movement_axis_changes_THEN_position_is_new_z( + self, + ): y = 0 z = 7 z_offset = 9 @@ -144,7 +167,9 @@ def test_GIVEN_movement_45_to_z_at_beam_along_z_WHEN_z_of_movement_axis_changes_ result = movement.calculate_interception(beam) assert_that(result, is_(position(Position(y, z + z_offset)))) - def test_GIVEN_movement_45_to_z_at_beam_offset_along_z_WHEN_z_of_movement_axis_changes_THEN_position_is_as_expected(self): + def test_GIVEN_movement_45_to_z_at_beam_offset_along_z_WHEN_z_of_movement_axis_changes_THEN_position_is_as_expected( + self, + ): y = 0 z = 7 z_offset = 9 @@ -160,7 +185,9 @@ def test_GIVEN_movement_45_to_z_at_beam_offset_along_z_WHEN_z_of_movement_axis_c result = movement.calculate_interception(beam) assert_that(result, is_(position(Position(beam_y, z + beam_y + z_offset)))) - def test_GIVEN_movement_perp_to_z_at_beam_angle_45_WHEN_z_of_movement_axis_changes_THEN_position_is_as_expected(self): + def test_GIVEN_movement_perp_to_z_at_beam_angle_45_WHEN_z_of_movement_axis_changes_THEN_position_is_as_expected( + self, + ): y = 0 z = 7 z_offset = 9 @@ -177,8 +204,9 @@ def test_GIVEN_movement_perp_to_z_at_beam_angle_45_WHEN_z_of_movement_axis_chang class TestMovementRelativeToBeam(unittest.TestCase): - - def test_GIVEN_movement_along_y_WHEN_set_position_relative_to_beam_to_0_THEN_position_is_at_intercept(self): + def test_GIVEN_movement_along_y_WHEN_set_position_relative_to_beam_to_0_THEN_position_is_at_intercept( + self, + ): movement = LinearMovementCalc(PositionAndAngle(0, 10, 90)) beam_intercept = Position(0, 10) beam = PositionAndAngle(0, 0, 0) @@ -190,7 +218,9 @@ def test_GIVEN_movement_along_y_WHEN_set_position_relative_to_beam_to_0_THEN_pos assert_that(result, is_(position(Position(beam_intercept.y, beam_intercept.z)))) - def test_GIVEN_movement_along_y_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_above_intercept(self): + def test_GIVEN_movement_along_y_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_above_intercept( + self, + ): movement = LinearMovementCalc(PositionAndAngle(0, 10, 90)) beam_intercept = Position(0, 10) beam = PositionAndAngle(0, 0, 0) @@ -201,7 +231,9 @@ def test_GIVEN_movement_along_y_WHEN_set_position_relative_to_beam_to_10_THEN_po assert_that(result, is_(position(Position(beam_intercept.y + dist, beam_intercept.z)))) - def test_GIVEN_movement_along_z_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept(self): + def test_GIVEN_movement_along_z_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept( + self, + ): movement = LinearMovementCalc(PositionAndAngle(0, 10, 0)) beam_intercept = Position(0, 10) beam = PositionAndAngle(0, 10, 270) @@ -213,7 +245,9 @@ def test_GIVEN_movement_along_z_WHEN_set_position_relative_to_beam_to_10_THEN_po assert_that(result, is_(position(Position(beam_intercept.y, beam_intercept.z + dist)))) @parameterized.expand([(0,), (360,), (-360,)]) - def test_GIVEN_movement_at_30_to_z__beam_intercept_above_and_to_the_right_of_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept(self, add_angle): + def test_GIVEN_movement_at_30_to_z__beam_intercept_above_and_to_the_right_of_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept( + self, add_angle + ): # here the beam intercept is above and to the right of the zero point movement = LinearMovementCalc(PositionAndAngle(0, 10, 30 + add_angle)) y_diff = 2.0 @@ -224,10 +258,19 @@ def test_GIVEN_movement_at_30_to_z__beam_intercept_above_and_to_the_right_of_zer movement.set_distance_relative_to_beam(beam, dist) result = movement.position_in_mantid_coordinates() - assert_that(result, is_(position(Position(beam_intercept.y + dist/2.0, beam_intercept.z + dist * sqrt(3)/2.0)))) + assert_that( + result, + is_( + position( + Position(beam_intercept.y + dist / 2.0, beam_intercept.z + dist * sqrt(3) / 2.0) + ) + ), + ) @parameterized.expand([(0,), (360,), (-360,)]) - def test_GIVEN_movement_at_30_to_z_beam_intercept_below_and_to_the_left_of_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept(self, add_angle): + def test_GIVEN_movement_at_30_to_z_beam_intercept_below_and_to_the_left_of_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept( + self, add_angle + ): # here the beam intercept is above and to the right of the zero point movement = LinearMovementCalc(PositionAndAngle(0, 10, 30 + add_angle)) y_diff = -2.0 @@ -238,10 +281,19 @@ def test_GIVEN_movement_at_30_to_z_beam_intercept_below_and_to_the_left_of_zero_ movement.set_distance_relative_to_beam(beam, dist) result = movement.position_in_mantid_coordinates() - assert_that(result, is_(position(Position(beam_intercept.y + dist/2.0, beam_intercept.z + dist * sqrt(3)/2.0)))) + assert_that( + result, + is_( + position( + Position(beam_intercept.y + dist / 2.0, beam_intercept.z + dist * sqrt(3) / 2.0) + ) + ), + ) @parameterized.expand([(0,), (360,), (-360,)]) - def test_GIVEN_movement_at_30_to_z_beam_intercept_is_at_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept(self, add_angle): + def test_GIVEN_movement_at_30_to_z_beam_intercept_is_at_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept( + self, add_angle + ): # here the beam intercept is above and to the right of the zero point movement = LinearMovementCalc(PositionAndAngle(0, 10, 30 + add_angle)) beam = PositionAndAngle(0, 0, 0) @@ -251,10 +303,19 @@ def test_GIVEN_movement_at_30_to_z_beam_intercept_is_at_zero_WHEN_set_position_r movement.set_distance_relative_to_beam(beam, dist) result = movement.position_in_mantid_coordinates() - assert_that(result, is_(position(Position(beam_intercept.y + dist/2.0, beam_intercept.z + dist * sqrt(3)/2.0)))) + assert_that( + result, + is_( + position( + Position(beam_intercept.y + dist / 2.0, beam_intercept.z + dist * sqrt(3) / 2.0) + ) + ), + ) @parameterized.expand([(0,), (360,), (-360,)]) - def test_GIVEN_movement_at_minus_30_to_z_beam_intercept_above_and_to_the_left_of_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept(self, add_angle): + def test_GIVEN_movement_at_minus_30_to_z_beam_intercept_above_and_to_the_left_of_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept( + self, add_angle + ): # here the beam intercept is above and to the right of the zero point movement = LinearMovementCalc(PositionAndAngle(0, 10, -30 + add_angle)) y_diff = 2.0 @@ -265,10 +326,19 @@ def test_GIVEN_movement_at_minus_30_to_z_beam_intercept_above_and_to_the_left_of movement.set_distance_relative_to_beam(beam, dist) result = movement.position_in_mantid_coordinates() - assert_that(result, is_(position(Position(beam_intercept.y - dist/2.0, beam_intercept.z + dist * sqrt(3)/2.0)))) + assert_that( + result, + is_( + position( + Position(beam_intercept.y - dist / 2.0, beam_intercept.z + dist * sqrt(3) / 2.0) + ) + ), + ) @parameterized.expand([(0,), (360,), (-360,)]) - def test_GIVEN_movement_at_minus_30_and_similar_to_z_beam_intercept_below_and_to_the_right_of_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept(self, add_angle): + def test_GIVEN_movement_at_minus_30_and_similar_to_z_beam_intercept_below_and_to_the_right_of_zero_WHEN_set_position_relative_to_beam_to_10_THEN_position_is_at_10_along_intercept( + self, add_angle + ): # here the beam intercept is above and to the right of the zero point movement = LinearMovementCalc(PositionAndAngle(0, 10, -30 + add_angle)) y_diff = 2.0 @@ -279,13 +349,21 @@ def test_GIVEN_movement_at_minus_30_and_similar_to_z_beam_intercept_below_and_to movement.set_distance_relative_to_beam(beam, dist) result = movement.position_in_mantid_coordinates() - assert_that(result, is_(position(Position(beam_intercept.y - dist/2.0, beam_intercept.z + dist * sqrt(3)/2.0)))) + assert_that( + result, + is_( + position( + Position(beam_intercept.y - dist / 2.0, beam_intercept.z + dist * sqrt(3) / 2.0) + ) + ), + ) class TestMovementValueObserver(unittest.TestCase): - @parameterized.expand([(1,), (1.8,)]) - def test_GIVEN_set_point_value_set_WHEN_there_is_a_value_observer_THEN_observer_triggered_with_new_value(self, expected_value): + def test_GIVEN_set_point_value_set_WHEN_there_is_a_value_observer_THEN_observer_triggered_with_new_value( + self, expected_value + ): movement = LinearMovementCalc(PositionAndAngle(0, 0, 90)) beam = PositionAndAngle(0, 0, 0) self._value = None @@ -296,5 +374,5 @@ def test_GIVEN_set_point_value_set_WHEN_there_is_a_value_observer_THEN_observer_ assert_that(result, is_(expected_value)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_out_of_beam_lookup.py b/ReflectometryServer/test_modules/test_out_of_beam_lookup.py index d2688ce2..15d61183 100644 --- a/ReflectometryServer/test_modules/test_out_of_beam_lookup.py +++ b/ReflectometryServer/test_modules/test_out_of_beam_lookup.py @@ -6,7 +6,7 @@ from parameterized import parameterized from ReflectometryServer import Position -from ReflectometryServer.out_of_beam import OutOfBeamPosition, OutOfBeamLookup, OutOfBeamSequence +from ReflectometryServer.out_of_beam import OutOfBeamLookup, OutOfBeamPosition, OutOfBeamSequence PARK_HIGH_POS = 10 PARK_HIHI_POS = 20 @@ -22,7 +22,9 @@ def setUp(self): self.positions = [self.park_high, self.park_low, self.park_hihi] @parameterized.expand([(-5, PARK_HIGH_POS), (0, PARK_HIHI_POS), (50, PARK_LOW_POS)]) - def test_GIVEN_multiple_out_of_beam_position_WHEN_getting_position_for_given_intercept_THEN_correct_position_returned(self, beam_height, expected): + def test_GIVEN_multiple_out_of_beam_position_WHEN_getting_position_for_given_intercept_THEN_correct_position_returned( + self, beam_height, expected + ): lookup = OutOfBeamLookup(self.positions) beam_intercept = Position(beam_height, 0) @@ -30,44 +32,60 @@ def test_GIVEN_multiple_out_of_beam_position_WHEN_getting_position_for_given_int assert_that(actual.get_final_position(), is_(expected)) - @parameterized.expand([(-5, PARK_LOW_POS, True), - (-5, PARK_HIGH_POS, False), - (-5, PARK_HIHI_POS, True), - (0, PARK_LOW_POS, True), - (0, PARK_HIGH_POS, True), - (0, PARK_HIHI_POS, False), - (20, PARK_LOW_POS, False), - (20, PARK_HIGH_POS, True), - (20, PARK_HIHI_POS, True), - ]) + @parameterized.expand( + [ + (-5, PARK_LOW_POS, True), + (-5, PARK_HIGH_POS, False), + (-5, PARK_HIHI_POS, True), + (0, PARK_LOW_POS, True), + (0, PARK_HIGH_POS, True), + (0, PARK_HIHI_POS, False), + (20, PARK_LOW_POS, False), + (20, PARK_HIGH_POS, True), + (20, PARK_HIHI_POS, True), + ] + ) def test_GIVEN_multiple_out_of_beam_positions_WHEN_checking_whether_component_is_in_beam_THEN_lookup_provides_correct_answer_for_given_beam_intersect( - self, beam_height, displacement, expected): + self, beam_height, displacement, expected + ): lookup = OutOfBeamLookup(self.positions) beam_intercept = Position(beam_height, 0) - actual, _ = lookup.out_of_beam_status(beam_intercept, displacement, None, None) # out of been distance and parking sequence index should not mater + actual, _ = lookup.out_of_beam_status( + beam_intercept, displacement, None, None + ) # out of been distance and parking sequence index should not mater assert_that(actual, is_(expected)) - @parameterized.expand([(PARK_LOW_POS, 0.5, False), - (PARK_LOW_POS, 2, True), - (PARK_LOW_POS, 4, True), - (PARK_HIGH_POS, 0.5, False), - (PARK_HIGH_POS, 2, False), - (PARK_HIGH_POS, 4, True),]) - def test_GIVEN_positions_with_tolerance_WHEN_checking_whether_component_is_in_beam_THEN_returns_correct_status_for_each_position(self, position_to_check, offset_from_pos, expected): + @parameterized.expand( + [ + (PARK_LOW_POS, 0.5, False), + (PARK_LOW_POS, 2, True), + (PARK_LOW_POS, 4, True), + (PARK_HIGH_POS, 0.5, False), + (PARK_HIGH_POS, 2, False), + (PARK_HIGH_POS, 4, True), + ] + ) + def test_GIVEN_positions_with_tolerance_WHEN_checking_whether_component_is_in_beam_THEN_returns_correct_status_for_each_position( + self, position_to_check, offset_from_pos, expected + ): beam_intercept = Position(0, 0) if position_to_check is PARK_HIGH_POS: beam_intercept = Position(2, 0) tolerance_low = 0.5 tolerance_high = 2 park_low_with_tolerance = OutOfBeamPosition(PARK_LOW_POS, tolerance=tolerance_low) - park_high_with_tolerance = OutOfBeamPosition(PARK_HIGH_POS, threshold=1, tolerance=tolerance_high) + park_high_with_tolerance = OutOfBeamPosition( + PARK_HIGH_POS, threshold=1, tolerance=tolerance_high + ) lookup = OutOfBeamLookup([park_low_with_tolerance, park_high_with_tolerance]) for position in [position_to_check + offset_from_pos, position_to_check - offset_from_pos]: - in_beam_status, _ = lookup.out_of_beam_status(beam_intercept, position, None, None) # only absolute position is used + in_beam_status, _ = lookup.out_of_beam_status( + beam_intercept, position, None, None + ) # only absolute position is used assert_that(in_beam_status, is_(expected)) def test_GIVEN_no_positions_given_WHEN_creating_lookup_THEN_exception_thrown(self): @@ -87,7 +105,9 @@ def test_GIVEN_multiple_default_positions_WHEN_creating_lookup_THEN_exception_th assert_that(calling(OutOfBeamLookup).with_args(positions), raises(ValueError)) - def test_GIVEN_positions_with_identical_thresholds_WHEN_creating_lookup_THEN_exception_thrown(self): + def test_GIVEN_positions_with_identical_thresholds_WHEN_creating_lookup_THEN_exception_thrown( + self, + ): pos_1 = OutOfBeamPosition(1, threshold=0) pos_2 = OutOfBeamPosition(2, threshold=0) positions = [pos_1, pos_2] @@ -96,8 +116,9 @@ def test_GIVEN_positions_with_identical_thresholds_WHEN_creating_lookup_THEN_exc class TestComponentWithSequenceOutOfBeamPositions(unittest.TestCase): - - def test_GIVEN_sequence_number_is_None_WHEN_get_parking_position_THEN_last_position_in_sequence_returned(self): + def test_GIVEN_sequence_number_is_None_WHEN_get_parking_position_THEN_last_position_in_sequence_returned( + self, + ): expected_value = 10 lookup = OutOfBeamLookup([OutOfBeamSequence([0.1, expected_value])]) beam_intercept = Position(0, 0) @@ -114,44 +135,69 @@ def test_GIVEN_sequence_of_3_WHEN_get_sequence_length_THEN_3_returned(self): assert_that(actual, is_(expected_value)) - @parameterized.expand([([111, 222, 333], 0, 111), # first item - ([111, 222, 333], None, None), # no item so None - ([111, 222, 333], 1, 222), # middle item - ([111, 222, 333], 2, 333), # last item - ([111, 222, 333], 5, None), # past last item returns None - ([None, None, 333], 1, None), # none is fine to return - ]) - def test_GIVEN_sequence_index_WHEN_get_parking_position_THEN_correct_position_in_sequence_returned_or_None(self, sequence, sequence_number, expected_value): - + @parameterized.expand( + [ + ([111, 222, 333], 0, 111), # first item + ([111, 222, 333], None, None), # no item so None + ([111, 222, 333], 1, 222), # middle item + ([111, 222, 333], 2, 333), # last item + ([111, 222, 333], 5, None), # past last item returns None + ([None, None, 333], 1, None), # none is fine to return + ] + ) + def test_GIVEN_sequence_index_WHEN_get_parking_position_THEN_correct_position_in_sequence_returned_or_None( + self, sequence, sequence_number, expected_value + ): lookup = OutOfBeamLookup([OutOfBeamSequence(sequence)]) beam_intercept = Position(0, 0) - actual = lookup.get_position_for_intercept(beam_intercept).get_sequence_position(sequence_number) + actual = lookup.get_position_for_intercept(beam_intercept).get_sequence_position( + sequence_number + ) assert_that(actual, is_(expected_value)) - - @parameterized.expand([([1, 2, 3, 4], 0, 1 + 0.1, True), # within tolerance of sequence end - ([1, 2, 3, 4], 1, 1 + 0.1, False), # within tolerance of first sequence end but not current end - ([1, 2, 3, 4], 0, 1 + 0.3, False), # out of tolerance - ([1, 2, 3, 4], None, 4, True), # sequence index is none so it is at the end of the current sequence - ([None, None, 3, 4], 1, 4, True), # sequence position is None - ([None, None, 3, 4], 4, 4, True), # sequence index is greater than sequence length - ]) - def test_GIVEN_positions_with_tolerance_WHEN_checking_whether_component_is_at_end_of_sequence_THEN_returns_correct_status(self, sequence, parking_index, position, expected): + @parameterized.expand( + [ + ([1, 2, 3, 4], 0, 1 + 0.1, True), # within tolerance of sequence end + ( + [1, 2, 3, 4], + 1, + 1 + 0.1, + False, + ), # within tolerance of first sequence end but not current end + ([1, 2, 3, 4], 0, 1 + 0.3, False), # out of tolerance + ( + [1, 2, 3, 4], + None, + 4, + True, + ), # sequence index is none so it is at the end of the current sequence + ([None, None, 3, 4], 1, 4, True), # sequence position is None + ([None, None, 3, 4], 4, 4, True), # sequence index is greater than sequence length + ] + ) + def test_GIVEN_positions_with_tolerance_WHEN_checking_whether_component_is_at_end_of_sequence_THEN_returns_correct_status( + self, sequence, parking_index, position, expected + ): tolerance = 0.2 lookup = OutOfBeamLookup([OutOfBeamSequence(sequence, tolerance=tolerance)]) - - _, result = lookup.out_of_beam_status(Position(0, 0), position, None, parking_index=parking_index) # only absolute position is used + _, result = lookup.out_of_beam_status( + Position(0, 0), position, None, parking_index=parking_index + ) # only absolute position is used assert_that(result, is_(expected)) - def test_GIVEN_sequence_positions_ending_in_none_WHEN_creating_lookup_THEN_exception_thrown(self): + def test_GIVEN_sequence_positions_ending_in_none_WHEN_creating_lookup_THEN_exception_thrown( + self, + ): positions = [1, None] assert_that(calling(OutOfBeamSequence).with_args(positions), raises(ValueError)) - def test_GIVEN_sequence_positions_with_none_between_values_WHEN_creating_lookup_THEN_exception_thrown(self): + def test_GIVEN_sequence_positions_with_none_between_values_WHEN_creating_lookup_THEN_exception_thrown( + self, + ): positions = [1, None, 2] assert_that(calling(OutOfBeamSequence).with_args(positions), raises(ValueError)) diff --git a/ReflectometryServer/test_modules/test_pv_manager.py b/ReflectometryServer/test_modules/test_pv_manager.py index d4b23948..69b69d61 100644 --- a/ReflectometryServer/test_modules/test_pv_manager.py +++ b/ReflectometryServer/test_modules/test_pv_manager.py @@ -1,17 +1,16 @@ import unittest from hamcrest import * +from server_common.utilities import convert_from_json, dehex_and_decompress -from ReflectometryServer.ChannelAccess.pv_manager import PVManager, PARAM_INFO_LOOKUP -from ReflectometryServer.components import Component -from ReflectometryServer.geometry import PositionAndAngle, ChangeAxis from ReflectometryServer.beamline import Beamline, BeamlineMode +from ReflectometryServer.ChannelAccess.pv_manager import PARAM_INFO_LOOKUP, PVManager +from ReflectometryServer.components import Component +from ReflectometryServer.geometry import ChangeAxis, PositionAndAngle from ReflectometryServer.parameters import AxisParameter, BeamlineParameterGroup -from server_common.utilities import dehex_and_decompress, convert_from_json class TestDriverUtils(unittest.TestCase): - def setUp(self) -> None: self.comp = Component("comp", PositionAndAngle(0, 0, 0)) @@ -22,10 +21,16 @@ def create_beamline(self, param): return pvmanager - def test_GIVEN_axis_param_with_characteristic_value_WHEN_create_beamline_THEN_param_info_contains_value(self): + def test_GIVEN_axis_param_with_characteristic_value_WHEN_create_beamline_THEN_param_info_contains_value( + self, + ): expected_characteristic_value = "MOT:MTR0101.RBV" - param = AxisParameter("MYVALUE", self.comp, ChangeAxis.POSITION, - characteristic_value=expected_characteristic_value) + param = AxisParameter( + "MYVALUE", + self.comp, + ChangeAxis.POSITION, + characteristic_value=expected_characteristic_value, + ) pvmanager = self.create_beamline(param) pv_definition = pvmanager.PVDB[PARAM_INFO_LOOKUP[BeamlineParameterGroup.COLLIMATION_PLANE]] @@ -33,7 +38,9 @@ def test_GIVEN_axis_param_with_characteristic_value_WHEN_create_beamline_THEN_pa assert_that(pv_value[0], has_entry("characteristic_value", expected_characteristic_value)) - def test_GIVEN_axis_param_with_NO_characteristic_value_WHEN_create_beamline_THEN_param_info_contains_empty_value(self): + def test_GIVEN_axis_param_with_NO_characteristic_value_WHEN_create_beamline_THEN_param_info_contains_empty_value( + self, + ): param = AxisParameter("MYVALUE", self.comp, ChangeAxis.POSITION) pvmanager = self.create_beamline(param) @@ -42,10 +49,13 @@ def test_GIVEN_axis_param_with_NO_characteristic_value_WHEN_create_beamline_THEN assert_that(pv_value[0], has_entry("characteristic_value", "")) - def test_GIVEN_axis_param_with_description_WHEN_create_beamline_THEN_param_info_contains_description(self): + def test_GIVEN_axis_param_with_description_WHEN_create_beamline_THEN_param_info_contains_description( + self, + ): expected_description = "MOT:MTR0101.RBV" - param = AxisParameter("MYVALUE", self.comp, ChangeAxis.POSITION, - description=expected_description) + param = AxisParameter( + "MYVALUE", self.comp, ChangeAxis.POSITION, description=expected_description + ) pvmanager = self.create_beamline(param) pv_definition = pvmanager.PVDB[PARAM_INFO_LOOKUP[BeamlineParameterGroup.COLLIMATION_PLANE]] @@ -53,7 +63,9 @@ def test_GIVEN_axis_param_with_description_WHEN_create_beamline_THEN_param_info_ assert_that(pv_value[0], has_entry("description", expected_description)) - def test_GIVEN_axis_param_with_sp_mirrors_rbv_WHEN_create_beamline_THEN_sp_no_action_disp_is_1(self): + def test_GIVEN_axis_param_with_sp_mirrors_rbv_WHEN_create_beamline_THEN_sp_no_action_disp_is_1( + self, + ): expected_value = 1 param_name = "MYVALUE" param = AxisParameter(param_name, self.comp, ChangeAxis.POSITION, sp_mirrors_rbv=True) @@ -65,5 +77,5 @@ def test_GIVEN_axis_param_with_sp_mirrors_rbv_WHEN_create_beamline_THEN_sp_no_ac assert_that(pv_value, is_(expected_value)) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/ReflectometryServer/test_modules/test_pv_wrapper.py b/ReflectometryServer/test_modules/test_pv_wrapper.py index 85b67eb3..86a7d708 100644 --- a/ReflectometryServer/test_modules/test_pv_wrapper.py +++ b/ReflectometryServer/test_modules/test_pv_wrapper.py @@ -1,17 +1,15 @@ -import time +import unittest from hamcrest import * from mock import Mock, patch from parameterized import parameterized +from server_common.channel_access import UnableToConnectToPVException import ReflectometryServer -import unittest - from ReflectometryServer import * from ReflectometryServer.ChannelAccess.constants import MOTOR_MOVING_PV from ReflectometryServer.pv_wrapper import DEFAULT_SCALE_FACTOR, ProcessMonitorEvents from ReflectometryServer.test_modules.data_mother import MockChannelAccess -from server_common.channel_access import UnableToConnectToPVException FLOAT_TOLERANCE = 1e-9 @@ -23,7 +21,6 @@ def _create_pv(pv, suffix): class TestMotorPVWrapper(unittest.TestCase): - def setUp(self): self.motor_name = "MOTOR" ReflectometryServer.pv_wrapper.MYPVPREFIX = TEST_PREFIX @@ -51,23 +48,26 @@ def setUp(self): self.dmov = 1 self.hlm = 10 self.llm = -10 - self.mock_motor_pvs = {self.sp_pv: self.sp, - self.rbv_pv: self.rbv, - self.mres_pv: self.mres, - self.vmax_pv: self.vmax, - self.vbas_pv: self.vbas, - self.velo_pv: self.velo, - self.bdst_pv: self.bdst, - self.bvel_pv: self.bvel, - self.dir_pv: self.dir, - self.dmov_pv: self.dmov, - self.hlm_pv: self.hlm, - self.llm_pv: self.llm - } + self.mock_motor_pvs = { + self.sp_pv: self.sp, + self.rbv_pv: self.rbv, + self.mres_pv: self.mres, + self.vmax_pv: self.vmax, + self.vbas_pv: self.vbas, + self.velo_pv: self.velo, + self.bdst_pv: self.bdst, + self.bvel_pv: self.bvel, + self.dir_pv: self.dir, + self.dmov_pv: self.dmov, + self.hlm_pv: self.hlm, + self.llm_pv: self.llm, + } self.mock_ca = MockChannelAccess(self.mock_motor_pvs) self.wrapper = MotorPVWrapper(self.motor_name, ca=self.mock_ca) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_resolution_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_resolution_is_initialised_correctly( + self, + ): expected_resolution = self.mres self.wrapper.initialise() @@ -75,7 +75,9 @@ def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_resolution_is_initial self.assertEqual(expected_resolution, actual_resolution) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_velocity_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_velocity_is_initialised_correctly( + self, + ): expected_velocity = self.velo self.wrapper.initialise() @@ -102,7 +104,9 @@ def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_cached_velocity_is_re self.assertEqual(expected_velocity, self.velo) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_max_velocity_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_max_velocity_is_initialised_correctly( + self, + ): expected_max_velocity = self.vmax self.wrapper.initialise() @@ -110,21 +114,27 @@ def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_max_velocity_is_initi self.assertEqual(expected_max_velocity, actual_max_velocity) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_setpoint_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_setpoint_is_initialised_correctly( + self, + ): expected_sp = self.sp actual_sp = self.wrapper.sp self.assertEqual(expected_sp, actual_sp) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_readback_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_readback_is_initialised_correctly( + self, + ): expected_rbv = self.rbv actual_rbv = self.wrapper.rbv self.assertEqual(expected_rbv, actual_rbv) - def test_GIVEN_base_pv_and_positive_dir_WHEN_creating_motor_pv_wrapper_THEN_backlash_distance_is_initialised_correctly(self): + def test_GIVEN_base_pv_and_positive_dir_WHEN_creating_motor_pv_wrapper_THEN_backlash_distance_is_initialised_correctly( + self, + ): self.mock_ca.caput(self.dir_pv, "Pos") expected_bdst = self.bdst * -1 @@ -133,7 +143,9 @@ def test_GIVEN_base_pv_and_positive_dir_WHEN_creating_motor_pv_wrapper_THEN_back self.assertEqual(expected_bdst, actual_bdst) - def test_GIVEN_base_pv_and_negative_dir_WHEN_creating_motor_pv_wrapper_THEN_backlash_distance_is_initialised_correctly(self): + def test_GIVEN_base_pv_and_negative_dir_WHEN_creating_motor_pv_wrapper_THEN_backlash_distance_is_initialised_correctly( + self, + ): self.mock_ca.caput(self.dir_pv, "Neg") expected_bdst = self.bdst @@ -142,7 +154,9 @@ def test_GIVEN_base_pv_and_negative_dir_WHEN_creating_motor_pv_wrapper_THEN_back self.assertEqual(expected_bdst, actual_bdst) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_backlash_velocity_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_backlash_velocity_is_initialised_correctly( + self, + ): expected_bvel = self.bvel self.wrapper.initialise() @@ -150,7 +164,9 @@ def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_backlash_velocity_is_ self.assertEqual(expected_bvel, actual_bvel) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_high_soft_limit_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_high_soft_limit_is_initialised_correctly( + self, + ): expected_hlm = self.hlm self.wrapper.initialise() @@ -158,7 +174,9 @@ def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_high_soft_limit_is_in self.assertEqual(expected_hlm, actual_hlm) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_low_soft_limit_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_low_soft_limit_is_initialised_correctly( + self, + ): expected_llm = self.llm self.wrapper.initialise() @@ -166,7 +184,9 @@ def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_low_soft_limit_is_ini self.assertEqual(expected_llm, actual_llm) - def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_direction_is_initialised_correctly(self): + def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_direction_is_initialised_correctly( + self, + ): expected_dir = self.dir self.wrapper.initialise() @@ -174,7 +194,9 @@ def test_GIVEN_base_pv_WHEN_creating_motor_pv_wrapper_THEN_direction_is_initiali self.assertEqual(expected_dir, actual_dir) - def test_GIVEN_base_velocity_is_non_zero_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_vbas(self): + def test_GIVEN_base_velocity_is_non_zero_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_vbas( + self, + ): vbas = 0.125 self.mock_ca.caput(self.vbas_pv, vbas) expected_min_velocity = vbas @@ -185,27 +207,37 @@ def test_GIVEN_base_velocity_is_non_zero_WHEN_initialising_motor_pv_wrapper_THEN assert_that(expected_min_velocity, is_(close_to(actual_min_velocity, FLOAT_TOLERANCE))) @parameterized.expand([(1,), (8,), (50,), (1000,)]) - def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_custom_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_custom_fraction_of_vmax(self, scale_factor): + def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_custom_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_custom_fraction_of_vmax( + self, scale_factor + ): expected_min_velocity = self.vmax / scale_factor - self.wrapper_with_custom_scale_level = MotorPVWrapper(self.motor_name, ca=self.mock_ca, min_velocity_scale_factor=scale_factor) + self.wrapper_with_custom_scale_level = MotorPVWrapper( + self.motor_name, ca=self.mock_ca, min_velocity_scale_factor=scale_factor + ) self.wrapper_with_custom_scale_level.initialise() actual_min_velocity = self.wrapper_with_custom_scale_level.min_velocity assert_that(expected_min_velocity, is_(close_to(actual_min_velocity, FLOAT_TOLERANCE))) - def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_nonsensical_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_default_fraction_of_vmax(self): + def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_nonsensical_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_default_fraction_of_vmax( + self, + ): scale_level = -4 expected_scale_factor = 1.0 / DEFAULT_SCALE_FACTOR expected_min_velocity = self.vmax * expected_scale_factor - self.wrapper_with_custom_scale_level = MotorPVWrapper(self.motor_name, ca=self.mock_ca, min_velocity_scale_factor=scale_level) + self.wrapper_with_custom_scale_level = MotorPVWrapper( + self.motor_name, ca=self.mock_ca, min_velocity_scale_factor=scale_level + ) self.wrapper_with_custom_scale_level.initialise() actual_min_velocity = self.wrapper_with_custom_scale_level.min_velocity assert_that(expected_min_velocity, is_(close_to(actual_min_velocity, FLOAT_TOLERANCE))) - def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_default_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_default_fraction_of_vmax(self): + def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_default_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_default_fraction_of_vmax( + self, + ): expected_min_velocity = self.vmax / DEFAULT_SCALE_FACTOR self.wrapper.initialise() @@ -213,10 +245,14 @@ def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_default_WHEN_initialisi assert_that(expected_min_velocity, is_(close_to(actual_min_velocity, FLOAT_TOLERANCE))) - def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_zero_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_default(self): + def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_zero_WHEN_initialising_motor_pv_wrapper_THEN_min_velocity_set_to_default( + self, + ): expected_min_velocity = self.vmax / DEFAULT_SCALE_FACTOR scale_factor = 0 - self.wrapper_with_custom_scale_level = MotorPVWrapper(self.motor_name, ca=self.mock_ca, min_velocity_scale_factor=scale_factor) + self.wrapper_with_custom_scale_level = MotorPVWrapper( + self.motor_name, ca=self.mock_ca, min_velocity_scale_factor=scale_factor + ) self.wrapper_with_custom_scale_level.initialise() actual_min_velocity = self.wrapper_with_custom_scale_level.min_velocity @@ -225,7 +261,6 @@ def test_GIVEN_base_velocity_is_zero_and_scale_factor_is_zero_WHEN_initialising_ class TestJawsAxisPVWrapper(unittest.TestCase): - def setUp(self): ReflectometryServer.pv_wrapper.MYPVPREFIX = TEST_PREFIX self.jaws_name = "JAWS" @@ -254,18 +289,20 @@ def setUp(self): self.vmax = 1 self.vbas = 0.0 self.velo = 0.5 - self.mock_axis_pvs = {self.vgap_sp_pv: self.vgap_sp, - self.vgap_rbv_pv: self.vgap_rbv, - self.vgap_dmov_pv: self.dmov, - self.vcent_sp_pv: self.vcent_sp, - self.vcent_rbv_pv: self.vcent_rbv, - self.vcent_dmov_pv: self.dmov, - self.hgap_sp_pv: self.hgap_sp, - self.hgap_rbv_pv: self.hgap_rbv, - self.hgap_dmov_pv: self.dmov, - self.hcent_sp_pv: self.hcent_sp, - self.hcent_rbv_pv: self.hcent_rbv, - self.hcent_dmov_pv: self.dmov} + self.mock_axis_pvs = { + self.vgap_sp_pv: self.vgap_sp, + self.vgap_rbv_pv: self.vgap_rbv, + self.vgap_dmov_pv: self.dmov, + self.vcent_sp_pv: self.vcent_sp, + self.vcent_rbv_pv: self.vcent_rbv, + self.vcent_dmov_pv: self.dmov, + self.hgap_sp_pv: self.hgap_sp, + self.hgap_rbv_pv: self.hgap_rbv, + self.hgap_dmov_pv: self.dmov, + self.hcent_sp_pv: self.hcent_sp, + self.hcent_rbv_pv: self.hcent_rbv, + self.hcent_dmov_pv: self.dmov, + } for direction in ["JN", "JE", "JS", "JW"]: direction_pv = "{}:{}".format(self.jaws_name, direction) mres_pv = _create_pv(direction_pv, ":MTR.MRES") @@ -278,7 +315,9 @@ def setUp(self): self.mock_axis_pvs[velo_pv] = self.velo self.mock_ca = MockChannelAccess(self.mock_axis_pvs) - def test_GIVEN_base_pv_and_motor_is_vertical_WHEN_creating_jawgap_wrapper_THEN_pvs_are_correct(self): + def test_GIVEN_base_pv_and_motor_is_vertical_WHEN_creating_jawgap_wrapper_THEN_pvs_are_correct( + self, + ): expected_sp = self.vgap_sp expected_rbv = self.vgap_rbv @@ -289,7 +328,9 @@ def test_GIVEN_base_pv_and_motor_is_vertical_WHEN_creating_jawgap_wrapper_THEN_p self.assertEqual(expected_sp, actual_sp) self.assertEqual(expected_rbv, actual_rbv) - def test_GIVEN_base_pv_and_motor_is_horizontal_WHEN_creating_jawgap_wrapper_THEN_pvs_are_correct(self): + def test_GIVEN_base_pv_and_motor_is_horizontal_WHEN_creating_jawgap_wrapper_THEN_pvs_are_correct( + self, + ): expected_sp = self.hgap_sp expected_rbv = self.hgap_rbv @@ -300,7 +341,9 @@ def test_GIVEN_base_pv_and_motor_is_horizontal_WHEN_creating_jawgap_wrapper_THEN self.assertEqual(expected_sp, actual_sp) self.assertEqual(expected_rbv, actual_rbv) - def test_GIVEN_base_pv_and_motor_is_vertical_WHEN_creating_jawcent_wrapper_THEN_pvs_are_correct(self): + def test_GIVEN_base_pv_and_motor_is_vertical_WHEN_creating_jawcent_wrapper_THEN_pvs_are_correct( + self, + ): expected_sp = self.vcent_sp expected_rbv = self.vcent_rbv @@ -311,7 +354,9 @@ def test_GIVEN_base_pv_and_motor_is_vertical_WHEN_creating_jawcent_wrapper_THEN_ self.assertEqual(expected_sp, actual_sp) self.assertEqual(expected_rbv, actual_rbv) - def test_GIVEN_base_pv_and_motor_is_horizontal_WHEN_creating_jawcent_wrapper_THEN_pvs_are_correct(self): + def test_GIVEN_base_pv_and_motor_is_horizontal_WHEN_creating_jawcent_wrapper_THEN_pvs_are_correct( + self, + ): expected_sp = self.hcent_sp expected_rbv = self.hcent_rbv @@ -322,7 +367,9 @@ def test_GIVEN_base_pv_and_motor_is_horizontal_WHEN_creating_jawcent_wrapper_THE self.assertEqual(expected_sp, actual_sp) self.assertEqual(expected_rbv, actual_rbv) - def test_GIVEN_initialised_jaw_gap_WHEN_get_backlash_distance_THEN_distance_is_0_because_jaws_do_not_backlash(self): + def test_GIVEN_initialised_jaw_gap_WHEN_get_backlash_distance_THEN_distance_is_0_because_jaws_do_not_backlash( + self, + ): wrapper = JawsGapPVWrapper(self.jaws_name, is_vertical=False, ca=self.mock_ca) wrapper.initialise() @@ -330,7 +377,9 @@ def test_GIVEN_initialised_jaw_gap_WHEN_get_backlash_distance_THEN_distance_is_0 assert_that(result, is_(0), "Jaws should not have backlash distance") - def test_GIVEN_vbas_not_set_and_jaw_gap_initialised_WHEN_get_minimum_velocity_THEN_minimum_velocity_is_default(self): + def test_GIVEN_vbas_not_set_and_jaw_gap_initialised_WHEN_get_minimum_velocity_THEN_minimum_velocity_is_default( + self, + ): expected = self.vmax / DEFAULT_SCALE_FACTOR wrapper = JawsGapPVWrapper(self.jaws_name, is_vertical=False, ca=self.mock_ca) wrapper.initialise() @@ -339,21 +388,27 @@ def test_GIVEN_vbas_not_set_and_jaw_gap_initialised_WHEN_get_minimum_velocity_TH assert_that(result, is_(expected)) - def test_GIVEN_soft_limits_not_set_and_jaw_gap_initialised_WHEN_get_limits_THEN_limits_are_infinity(self): + def test_GIVEN_soft_limits_not_set_and_jaw_gap_initialised_WHEN_get_limits_THEN_limits_are_infinity( + self, + ): wrapper = JawsGapPVWrapper(self.jaws_name, is_vertical=False, ca=self.mock_ca) wrapper.initialise() - assert_that(wrapper.llm, is_(float('-inf'))) - assert_that(wrapper.hlm, is_(float('inf'))) + assert_that(wrapper.llm, is_(float("-inf"))) + assert_that(wrapper.hlm, is_(float("inf"))) - def test_GIVEN_soft_limits_not_set_and_jaw_centre_initialised_WHEN_get_limits_THEN_limits_are_infinity(self): + def test_GIVEN_soft_limits_not_set_and_jaw_centre_initialised_WHEN_get_limits_THEN_limits_are_infinity( + self, + ): wrapper = JawsCentrePVWrapper(self.jaws_name, is_vertical=False, ca=self.mock_ca) wrapper.initialise() - assert_that(wrapper.llm, is_(float('-inf'))) - assert_that(wrapper.hlm, is_(float('inf'))) + assert_that(wrapper.llm, is_(float("-inf"))) + assert_that(wrapper.hlm, is_(float("inf"))) - def test_GIVEN_vbas_set_and_jaw_gap_initialised_WHEN_get_minimum_velocity_THEN_minimum_velocity_is_default(self): + def test_GIVEN_vbas_set_and_jaw_gap_initialised_WHEN_get_minimum_velocity_THEN_minimum_velocity_is_default( + self, + ): expected = 0.123 for direction in ["JN", "JE", "JS", "JW"]: direction_pv = "{}:{}".format(self.jaws_name, direction) @@ -368,7 +423,6 @@ def test_GIVEN_vbas_set_and_jaw_gap_initialised_WHEN_get_minimum_velocity_THEN_m class TestAggregateMonitorEvents(unittest.TestCase): - def setUp(self): self.pme = ProcessMonitorEvents() self.event_arg = [] @@ -403,7 +457,9 @@ def test_GIVEN_two_events_of_different_type_WHEN_processed_THEN_both_events_trig assert_that(self.event_arg, contains_inanyorder(expected_value1, expected_value2)) - def test_GIVEN_two_events_of_different_type_WHEN_processed_one_has_exception_THEN_only_non_exception_events_triggered(self): + def test_GIVEN_two_events_of_different_type_WHEN_processed_one_has_exception_THEN_only_non_exception_events_triggered( + self, + ): expected_value1 = "HI" expected_value2 = 1 mock = Mock(side_effect=ValueError) @@ -431,7 +487,9 @@ def test_GIVEN_one_event_WHEN_added_THEN_event_is_processed(self): assert_that(self.event_arg, is_([expected_value])) - def test_GIVEN_nothing_WHEN_one_event_wait_then_second_event_THEN_events_are_both_processed(self): + def test_GIVEN_nothing_WHEN_one_event_wait_then_second_event_THEN_events_are_both_processed( + self, + ): # check that the thread can be restarted for expected_value in ["HI", "THERE", "WORKS"]: @@ -442,7 +500,7 @@ def test_GIVEN_nothing_WHEN_one_event_wait_then_second_event_THEN_events_are_bot assert_that(self.event_arg, is_([expected_value])) self.event_arg = [] - @patch('ReflectometryServer.pv_wrapper.ChannelAccess') + @patch("ReflectometryServer.pv_wrapper.ChannelAccess") def test_GIVEN_nothing_WHEN_event_THEN_in_motion_flag_is_set(self, channel_access): expected_value = "HI" self.pme.add_trigger(self.event, expected_value, start_processing=False) @@ -451,17 +509,18 @@ def test_GIVEN_nothing_WHEN_event_THEN_in_motion_flag_is_set(self, channel_acces channel_access.caput.assert_any_call(MOTOR_MOVING_PV, 1, safe_not_quick=False) - @patch('ReflectometryServer.pv_wrapper.ChannelAccess') + @patch("ReflectometryServer.pv_wrapper.ChannelAccess") def test_GIVEN_nothing_WHEN_no_more_events_THEN_in_motion_flag_is_cleared(self, channel_access): - expected_value = "HI" self.pme.add_trigger(self.event, expected_value, start_processing=False) self.pme.process_triggers_loop() channel_access.caput.assert_called_with(MOTOR_MOVING_PV, 0, safe_not_quick=False) - @patch('ReflectometryServer.pv_wrapper.ChannelAccess') - def test_GIVEN_moving_pv_does_not_exist_WHEN_event_THEN_event_is_processed(self, channel_access): + @patch("ReflectometryServer.pv_wrapper.ChannelAccess") + def test_GIVEN_moving_pv_does_not_exist_WHEN_event_THEN_event_is_processed( + self, channel_access + ): channel_access.caput.side_effect = UnableToConnectToPVException("pvname", "error") expected_value = "HI" self.pme.add_trigger(self.event, expected_value, start_processing=False) diff --git a/ReflectometryServer/test_modules/test_set_current_motor_position.py b/ReflectometryServer/test_modules/test_set_current_motor_position.py index fefff7e4..785345e1 100644 --- a/ReflectometryServer/test_modules/test_set_current_motor_position.py +++ b/ReflectometryServer/test_modules/test_set_current_motor_position.py @@ -1,34 +1,32 @@ -from math import tan +import unittest +from hamcrest import * from mock import Mock from parameterized import parameterized -import ReflectometryServer -import unittest - from ReflectometryServer import * -from ReflectometryServer.axis import DefineValueAsEvent from ReflectometryServer import ChangeAxis +from ReflectometryServer.axis import DefineValueAsEvent from ReflectometryServer.parameters import RequestMoveEvent -from ReflectometryServer.pv_wrapper import JawsAxisPVWrapper -from ReflectometryServer.test_modules.data_mother import MockChannelAccess, create_mock_JawsCentrePVWrapper, \ - create_mock_axis +from ReflectometryServer.test_modules.data_mother import ( + create_mock_axis, + create_mock_JawsCentrePVWrapper, +) from ReflectometryServer.test_modules.utils import create_parameter_with_initial_value -from server_common.channel_access import UnableToConnectToPVException -from hamcrest import * - class TestCurrentMotorPositionParametersToEven_inDriver(unittest.TestCase): """ Test for setting the current motor position """ + def setUp(self): self.set_position_to = None - @parameterized.expand([(0,), - (1,)]) - def test_GIVEN_tracking_beamline_parameter_and_component_WHEN_set_position_to_THEN_component_has_set_position_on(self, beam_path_height): + @parameterized.expand([(0,), (1,)]) + def test_GIVEN_tracking_beamline_parameter_and_component_WHEN_set_position_to_THEN_component_has_set_position_on( + self, beam_path_height + ): incoming_beam = PositionAndAngle(beam_path_height, 0, 0) def _listener(set_position_to): @@ -42,16 +40,19 @@ def _listener(set_position_to): theta_component.add_angle_to(component) parameter = AxisParameter("param", component, ChangeAxis.POSITION) theta = AxisParameter("theta", theta_component, ChangeAxis.ANGLE) - component.beam_path_rbv.axis[ChangeAxis.POSITION].add_listener(DefineValueAsEvent, _listener) + component.beam_path_rbv.axis[ChangeAxis.POSITION].add_listener( + DefineValueAsEvent, _listener + ) parameter.define_current_value_as.new_value_sp_rbv = position_to_set assert_that(self.set_position_to.new_position, is_(expected_position)) assert_that(self.set_position_to.change_axis, is_(ChangeAxis.POSITION)) - @parameterized.expand([(0,), - (1,)]) - def test_GIVEN_angle_beamline_parameter_and_reflecting_component_WHEN_set_position_to_THEN_component_has_set_position_on(self, beam_path_angle): + @parameterized.expand([(0,), (1,)]) + def test_GIVEN_angle_beamline_parameter_and_reflecting_component_WHEN_set_position_to_THEN_component_has_set_position_on( + self, beam_path_angle + ): incoming_beam = PositionAndAngle(0, 0, beam_path_angle) def _listener(set_position_to): @@ -69,9 +70,10 @@ def _listener(set_position_to): assert_that(self.set_position_to.new_position, is_(expected_position)) assert_that(self.set_position_to.change_axis, is_(ChangeAxis.ANGLE)) - @parameterized.expand([(0,), - (1,)]) - def test_GIVEN_angle_beamline_parameter_and_tilting_component_WHEN_set_position_to_THEN_component_has_set_position_on(self, beam_path_angle): + @parameterized.expand([(0,), (1,)]) + def test_GIVEN_angle_beamline_parameter_and_tilting_component_WHEN_set_position_to_THEN_component_has_set_position_on( + self, beam_path_angle + ): incoming_beam = PositionAndAngle(0, 0, beam_path_angle) def _listener(set_position_to): @@ -89,7 +91,9 @@ def _listener(set_position_to): assert_that(self.set_position_to.new_position, is_(expected_position)) assert_that(self.set_position_to.change_axis, is_(ChangeAxis.ANGLE)) - def test_GIVEN_angle_beamline_parameter_and_theta_component_WHEN_set_position_to_THEN_error(self): + def test_GIVEN_angle_beamline_parameter_and_theta_component_WHEN_set_position_to_THEN_error( + self, + ): component = ThetaComponent("comp", PositionAndAngle(0, 0, 90)) detector = Component("detector", PositionAndAngle(1, 0, 90)) component.add_angle_to(detector) @@ -97,14 +101,17 @@ def test_GIVEN_angle_beamline_parameter_and_theta_component_WHEN_set_position_to assert_that(parameter.define_current_value_as, is_(None)) - def test_GIVEN_beamline_parameter_and_DirectParameter_component_WHEN_set_position_to_THEN_component_has_set_position_on(self): - + def test_GIVEN_beamline_parameter_and_DirectParameter_component_WHEN_set_position_to_THEN_component_has_set_position_on( + self, + ): def _listener(set_position_to): self.set_position_to = set_position_to expected_position = 1 mock_jaws_wrapper = create_mock_JawsCentrePVWrapper("jaws_centre", expected_position, 1) - parameter = create_parameter_with_initial_value(0, DirectParameter, "param", mock_jaws_wrapper) + parameter = create_parameter_with_initial_value( + 0, DirectParameter, "param", mock_jaws_wrapper + ) mock_jaws_wrapper.sp = 0 parameter.define_current_value_as.new_value_sp_rbv = expected_position @@ -112,12 +119,13 @@ def _listener(set_position_to): mock_jaws_wrapper.define_position_as.assert_called_once_with(expected_position) def test_GIVEN_InOutParameter_WHEN_get_set_position_to_THEN_it_is_none(self): - parameter = InBeamParameter("param", Component("comp", PositionAndAngle(0, 0, 0))) assert_that(parameter.define_current_value_as, is_(None)) - def test_GIVEN_tracking_beamline_parameter_and_component_WHEN_set_position_to_THEN_component_and_parameter_set_points_are_new_values_motor_is_not_moved(self): + def test_GIVEN_tracking_beamline_parameter_and_component_WHEN_set_position_to_THEN_component_and_parameter_set_points_are_new_values_motor_is_not_moved( + self, + ): incoming_beam = PositionAndAngle(0, 0, 0) expected_position = 1 component = Component("comp", PositionAndAngle(0, 0, 90)) @@ -130,13 +138,18 @@ def test_GIVEN_tracking_beamline_parameter_and_component_WHEN_set_position_to_TH parameter.define_current_value_as.new_value_sp_rbv = expected_position - assert_that(component.beam_path_set_point.axis[ChangeAxis.POSITION].get_relative_to_beam(), is_(expected_position), - "component setpoint") + assert_that( + component.beam_path_set_point.axis[ChangeAxis.POSITION].get_relative_to_beam(), + is_(expected_position), + "component setpoint", + ) assert_that(parameter.sp, is_(expected_position), "Parameter setpoint") assert_that(parameter.sp_rbv, is_(expected_position), "Parameter setpoint read back") move_listener.assert_not_called() - def test_GIVEN_angle_beamline_parameter_and_component_WHEN_set_position_to_THEN_component_and_parameter_set_points_are_new_values(self): + def test_GIVEN_angle_beamline_parameter_and_component_WHEN_set_position_to_THEN_component_and_parameter_set_points_are_new_values( + self, + ): incoming_beam = PositionAndAngle(0, 0, 0) expected_position = 1 component = TiltingComponent("comp", PositionAndAngle(0, 0, 90)) @@ -147,18 +160,26 @@ def test_GIVEN_angle_beamline_parameter_and_component_WHEN_set_position_to_THEN_ parameter.define_current_value_as.new_value_sp_rbv = expected_position - assert_that(component.beam_path_set_point.axis[ChangeAxis.ANGLE].get_relative_to_beam(), is_(expected_position), - "component setpoint") + assert_that( + component.beam_path_set_point.axis[ChangeAxis.ANGLE].get_relative_to_beam(), + is_(expected_position), + "component setpoint", + ) assert_that(parameter.sp, is_(expected_position), "Parameter setpoint") assert_that(parameter.sp_rbv, is_(expected_position), "Parameter setpoint read back") - def test_GIVEN_beamline_parameter_and_DirectParameter_component_WHEN_set_position_to_THEN_setpoint_is_set_to_new_position(self): - + def test_GIVEN_beamline_parameter_and_DirectParameter_component_WHEN_set_position_to_THEN_setpoint_is_set_to_new_position( + self, + ): expected_position = 1 expected_mock_jaws_wrapper_value = 10 - mock_jaws_wrapper = create_mock_JawsCentrePVWrapper("jaws_centre", expected_mock_jaws_wrapper_value, 1) + mock_jaws_wrapper = create_mock_JawsCentrePVWrapper( + "jaws_centre", expected_mock_jaws_wrapper_value, 1 + ) - parameter = create_parameter_with_initial_value(0, DirectParameter, "param", mock_jaws_wrapper) + parameter = create_parameter_with_initial_value( + 0, DirectParameter, "param", mock_jaws_wrapper + ) mock_jaws_wrapper.sp = 0 parameter.sp = expected_mock_jaws_wrapper_value @@ -166,7 +187,11 @@ def test_GIVEN_beamline_parameter_and_DirectParameter_component_WHEN_set_positio assert_that(parameter.sp, is_(expected_position)) assert_that(parameter.sp_rbv, is_(expected_position)) - assert_that(mock_jaws_wrapper.last_set_point_set, is_(expected_mock_jaws_wrapper_value), "sp should not be set") + assert_that( + mock_jaws_wrapper.last_set_point_set, + is_(expected_mock_jaws_wrapper_value), + "sp should not be set", + ) class TestCurrentMotorPositionEventsToMotor(unittest.TestCase): @@ -174,48 +199,68 @@ class TestCurrentMotorPositionEventsToMotor(unittest.TestCase): Test for setting the current motor position """ - def test_GIVEN_displacement_driver_no_engineering_correction_WHEN_receive_set_position_as_event_for_positionset_THEN_motor_position_is_set(self): + def test_GIVEN_displacement_driver_no_engineering_correction_WHEN_receive_set_position_as_event_for_positionset_THEN_motor_position_is_set( + self, + ): expected_position = 1 component = Component("comp", PositionAndAngle(0, 0, 0)) mock_axis = create_mock_axis("axis", 0, 1) driver = IocDriver(component, ChangeAxis.POSITION, mock_axis) - component.beam_path_rbv.axis[ChangeAxis.POSITION].trigger_listeners(DefineValueAsEvent(expected_position, ChangeAxis.POSITION)) + component.beam_path_rbv.axis[ChangeAxis.POSITION].trigger_listeners( + DefineValueAsEvent(expected_position, ChangeAxis.POSITION) + ) assert_that(mock_axis.set_position_as_value, is_(expected_position)) - def test_GIVEN_displacement_driver_with_engineering_correction_WHEN_receive_set_position_as_event_for_positionset_THEN_motor_position_is_set_with_correction(self): + def test_GIVEN_displacement_driver_with_engineering_correction_WHEN_receive_set_position_as_event_for_positionset_THEN_motor_position_is_set_with_correction( + self, + ): expected_position = 1 correction = 1 component = Component("comp", PositionAndAngle(0, 0, 0)) mock_axis = create_mock_axis("axis", 0, 1) - driver = IocDriver(component, ChangeAxis.POSITION, mock_axis, - engineering_correction=ConstantCorrection(correction)) + driver = IocDriver( + component, + ChangeAxis.POSITION, + mock_axis, + engineering_correction=ConstantCorrection(correction), + ) - component.beam_path_rbv.axis[ChangeAxis.POSITION].trigger_listeners(DefineValueAsEvent(expected_position, ChangeAxis.POSITION)) + component.beam_path_rbv.axis[ChangeAxis.POSITION].trigger_listeners( + DefineValueAsEvent(expected_position, ChangeAxis.POSITION) + ) assert_that(mock_axis.set_position_as_value, is_(expected_position + correction)) - def test_GIVEN_displacement_driver_no_engineering_correction_WHEN_receive_set_angle_as_event_for_position_set_THEN_motor_position_is_not_set(self): + def test_GIVEN_displacement_driver_no_engineering_correction_WHEN_receive_set_angle_as_event_for_position_set_THEN_motor_position_is_not_set( + self, + ): expected_position = 1 component = TiltingComponent("comp", PositionAndAngle(0, 0, 0)) mock_axis = create_mock_axis("axis", 0, 1) driver = IocDriver(component, ChangeAxis.POSITION, mock_axis) - component.beam_path_rbv.axis[ChangeAxis.ANGLE].trigger_listeners(DefineValueAsEvent(expected_position, ChangeAxis.ANGLE)) + component.beam_path_rbv.axis[ChangeAxis.ANGLE].trigger_listeners( + DefineValueAsEvent(expected_position, ChangeAxis.ANGLE) + ) assert_that(mock_axis.set_position_as_value, is_(None)) - def test_GIVEN_angle_driver_no_engineering_correction_WHEN_receive_set_angle_as_event_for_positionset_THEN_motor_position_is_set(self): + def test_GIVEN_angle_driver_no_engineering_correction_WHEN_receive_set_angle_as_event_for_positionset_THEN_motor_position_is_set( + self, + ): expected_position = 1 component = TiltingComponent("comp", PositionAndAngle(0, 0, 0)) mock_axis = create_mock_axis("axis", 0, 1) driver = IocDriver(component, ChangeAxis.ANGLE, mock_axis) - component.beam_path_rbv.axis[ChangeAxis.ANGLE].trigger_listeners(DefineValueAsEvent(expected_position, ChangeAxis.ANGLE)) + component.beam_path_rbv.axis[ChangeAxis.ANGLE].trigger_listeners( + DefineValueAsEvent(expected_position, ChangeAxis.ANGLE) + ) assert_that(mock_axis.set_position_as_value, is_(expected_position)) diff --git a/ReflectometryServer/test_modules/test_status_manager.py b/ReflectometryServer/test_modules/test_status_manager.py index c543bccc..55c73245 100644 --- a/ReflectometryServer/test_modules/test_status_manager.py +++ b/ReflectometryServer/test_modules/test_status_manager.py @@ -4,7 +4,7 @@ from parameterized import parameterized from pcaspy import Severity -from ReflectometryServer.server_status_manager import _ServerStatusManager, STATUS, ProblemInfo +from ReflectometryServer.server_status_manager import STATUS, ProblemInfo, _ServerStatusManager class TestStatusManager(unittest.TestCase): @@ -26,7 +26,9 @@ def test_GIVEN_initial_state_WHEN_reading_server_problems_THEN_list_is_blank(sel self.assertEqual(expected, actual) - def test_GIVEN_major_severity_WHEN_new_problem_occurs_THEN_new_error_added_as_key_with_source_as_value(self): + def test_GIVEN_major_severity_WHEN_new_problem_occurs_THEN_new_error_added_as_key_with_source_as_value( + self, + ): problem_description = "problem_description" source = Mock() problem_severity = Severity.MAJOR_ALARM @@ -40,7 +42,9 @@ def test_GIVEN_major_severity_WHEN_new_problem_occurs_THEN_new_error_added_as_ke self.assertEqual({}, self.status_manager.active_warnings) self.assertEqual({}, self.status_manager.active_other_problems) - def test_GIVEN_minor_severity_WHEN_new_problem_occurs_THEN_new_warning_added_as_key_with_source_as_value(self): + def test_GIVEN_minor_severity_WHEN_new_problem_occurs_THEN_new_warning_added_as_key_with_source_as_value( + self, + ): problem_description = "problem_description" source = Mock() problem_severity = Severity.MINOR_ALARM @@ -54,7 +58,9 @@ def test_GIVEN_minor_severity_WHEN_new_problem_occurs_THEN_new_warning_added_as_ self.assertEqual(expected, actual) self.assertEqual({}, self.status_manager.active_other_problems) - def test_GIVEN_unknown_severity_WHEN_new_problem_occurs_THEN_new_other_problem_added_as_key_with_source_as_value(self): + def test_GIVEN_unknown_severity_WHEN_new_problem_occurs_THEN_new_other_problem_added_as_key_with_source_as_value( + self, + ): problem_description = "problem_description" source = Mock() problem_severity = None @@ -68,28 +74,40 @@ def test_GIVEN_unknown_severity_WHEN_new_problem_occurs_THEN_new_other_problem_a self.assertEqual({}, self.status_manager.active_warnings) self.assertEqual(expected, actual) - def test_GIVEN_new_source_WHEN_known_problem_occurs_THEN_problem_entry_modified_by_adding_another_source(self): + def test_GIVEN_new_source_WHEN_known_problem_occurs_THEN_problem_entry_modified_by_adding_another_source( + self, + ): problem_description = "problem_description" source_1 = Mock() source_2 = Mock() problem_severity = Severity.MAJOR_ALARM expected = {problem_description: {source_1, source_2}} - self.status_manager.update_active_problems(ProblemInfo(problem_description, source_1, problem_severity)) - self.status_manager.update_active_problems(ProblemInfo(problem_description, source_2, problem_severity)) + self.status_manager.update_active_problems( + ProblemInfo(problem_description, source_1, problem_severity) + ) + self.status_manager.update_active_problems( + ProblemInfo(problem_description, source_2, problem_severity) + ) actual = self.status_manager.active_errors self.assertEqual(expected, actual) - def test_GIVEN_known_source_WHEN_new_problem_occurs_THEN_new_problem_added_as_key_with_source_as_value(self): + def test_GIVEN_known_source_WHEN_new_problem_occurs_THEN_new_problem_added_as_key_with_source_as_value( + self, + ): problem_description_1 = "problem_description_1" problem_description_2 = "problem_description_2" source = Mock() problem_severity = Severity.MAJOR_ALARM expected = {problem_description_1: {source}, problem_description_2: {source}} - self.status_manager.update_active_problems(ProblemInfo(problem_description_1, source, problem_severity)) - self.status_manager.update_active_problems(ProblemInfo(problem_description_2, source, problem_severity)) + self.status_manager.update_active_problems( + ProblemInfo(problem_description_1, source, problem_severity) + ) + self.status_manager.update_active_problems( + ProblemInfo(problem_description_2, source, problem_severity) + ) actual = self.status_manager.active_errors self.assertEqual(expected, actual) @@ -107,7 +125,9 @@ def test_GIVEN_known_source_WHEN_known_problem_occurs_THEN_problem_not_added_aga self.assertEqual(expected, actual) - def test_GIVEN_non_empty_list_of_problems_WHEN_server_status_cleared_THEN_all_problems_are_cleared(self): + def test_GIVEN_non_empty_list_of_problems_WHEN_server_status_cleared_THEN_all_problems_are_cleared( + self, + ): source = Mock() problem_1 = ProblemInfo("problem_description_1", source, Severity.MAJOR_ALARM) problem_2 = ProblemInfo("problem_description_2", source, Severity.MINOR_ALARM) @@ -123,15 +143,19 @@ def test_GIVEN_non_empty_list_of_problems_WHEN_server_status_cleared_THEN_all_pr self.assertEqual(expected, self.status_manager.active_warnings) self.assertEqual(expected, self.status_manager.active_other_problems) - @parameterized.expand([ - (Severity.MAJOR_ALARM, Severity.MAJOR_ALARM, STATUS.ERROR), - (Severity.MAJOR_ALARM, Severity.MINOR_ALARM, STATUS.ERROR), - (Severity.MAJOR_ALARM, Severity.INVALID_ALARM, STATUS.ERROR), - (Severity.MINOR_ALARM, Severity.MINOR_ALARM, STATUS.WARNING), - (Severity.MINOR_ALARM, Severity.INVALID_ALARM, STATUS.WARNING), - (Severity.INVALID_ALARM, Severity.INVALID_ALARM, STATUS.UNKNOWN), - ]) - def test_GIVEN_different_severity_levels_WHEN_adding_problems_THEN_server_status_reflects_highest_severity(self, problem_severity_1, problem_severity_2, expected): + @parameterized.expand( + [ + (Severity.MAJOR_ALARM, Severity.MAJOR_ALARM, STATUS.ERROR), + (Severity.MAJOR_ALARM, Severity.MINOR_ALARM, STATUS.ERROR), + (Severity.MAJOR_ALARM, Severity.INVALID_ALARM, STATUS.ERROR), + (Severity.MINOR_ALARM, Severity.MINOR_ALARM, STATUS.WARNING), + (Severity.MINOR_ALARM, Severity.INVALID_ALARM, STATUS.WARNING), + (Severity.INVALID_ALARM, Severity.INVALID_ALARM, STATUS.UNKNOWN), + ] + ) + def test_GIVEN_different_severity_levels_WHEN_adding_problems_THEN_server_status_reflects_highest_severity( + self, problem_severity_1, problem_severity_2, expected + ): problem_description_1 = "problem_description_1" problem_description_2 = "problem_description_2" source = Mock() diff --git a/ReflectometryServer/test_modules/utils.py b/ReflectometryServer/test_modules/utils.py index 758ff9bb..86afb646 100644 --- a/ReflectometryServer/test_modules/utils.py +++ b/ReflectometryServer/test_modules/utils.py @@ -1,11 +1,11 @@ """ Utils for testing """ + from math import fabs -from unittest import skipIf from hamcrest.core.base_matcher import BaseMatcher -from mock import patch, Mock +from mock import Mock, patch from ReflectometryServer import file_io @@ -18,6 +18,7 @@ class IsPositionAndAngle(BaseMatcher): Checks that the beam values are all the same """ + def __init__(self, expected_beam, do_compare_angle, tolerance=None): self.expected_position_and_angle = expected_beam self.compare_angle = do_compare_angle @@ -32,11 +33,20 @@ def _matches(self, beam): :param beam: beam given :return: True if it matches; False otherwise """ - if not hasattr(beam, 'z') or not hasattr(beam, 'y') or (self.compare_angle and not hasattr(beam, 'angle')): + if ( + not hasattr(beam, "z") + or not hasattr(beam, "y") + or (self.compare_angle and not hasattr(beam, "angle")) + ): return False - return fabs(beam.z - self.expected_position_and_angle.z) <= self._tolerance and \ - fabs(beam.y - self.expected_position_and_angle.y) <= self._tolerance and \ - (not self.compare_angle or fabs(beam.angle - self.expected_position_and_angle.angle) <= self._tolerance) + return ( + fabs(beam.z - self.expected_position_and_angle.z) <= self._tolerance + and fabs(beam.y - self.expected_position_and_angle.y) <= self._tolerance + and ( + not self.compare_angle + or fabs(beam.angle - self.expected_position_and_angle.angle) <= self._tolerance + ) + ) def describe_to(self, description): """ @@ -46,8 +56,11 @@ def describe_to(self, description): if self.compare_angle: description.append_text(self.expected_position_and_angle) else: - description.append_text("{} (compare position to within {})".format(self.expected_position_and_angle, - self._tolerance)) + description.append_text( + "{} (compare position to within {})".format( + self.expected_position_and_angle, self._tolerance + ) + ) def position_and_angle(expected_beam, tolerance=None): @@ -119,14 +132,37 @@ def no_autosave(func): """ A wrapper to make setup and tests not load autosave values """ - @patch('ReflectometryServer.parameters.param_float_autosave.read_parameter', new=Mock(return_value=None)) # use new so that we don't create another argument - @patch('ReflectometryServer.parameters.param_bool_autosave.read_parameter', new=Mock(return_value=None)) - @patch('ReflectometryServer.parameters.param_string_autosave.read_parameter', new=Mock(return_value=None)) - @patch('ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter', new=Mock(return_value=None)) - @patch('ReflectometryServer.beam_path_calc.disable_mode_autosave.read_parameter', new=Mock(return_value=None)) - @patch('ReflectometryServer.beamline.mode_autosave.read_parameter', new=Mock(return_value=None)) - @patch('ReflectometryServer.pv_wrapper.velocity_float_autosave.read_parameter', new=Mock(return_value=None)) - @patch('ReflectometryServer.pv_wrapper.velocity_bool_autosave.read_parameter', new=Mock(return_value=None)) + + @patch( + "ReflectometryServer.parameters.param_float_autosave.read_parameter", + new=Mock(return_value=None), + ) # use new so that we don't create another argument + @patch( + "ReflectometryServer.parameters.param_bool_autosave.read_parameter", + new=Mock(return_value=None), + ) + @patch( + "ReflectometryServer.parameters.param_string_autosave.read_parameter", + new=Mock(return_value=None), + ) + @patch( + "ReflectometryServer.beam_path_calc.parking_index_autosave.read_parameter", + new=Mock(return_value=None), + ) + @patch( + "ReflectometryServer.beam_path_calc.disable_mode_autosave.read_parameter", + new=Mock(return_value=None), + ) + @patch("ReflectometryServer.beamline.mode_autosave.read_parameter", new=Mock(return_value=None)) + @patch( + "ReflectometryServer.pv_wrapper.velocity_float_autosave.read_parameter", + new=Mock(return_value=None), + ) + @patch( + "ReflectometryServer.pv_wrapper.velocity_bool_autosave.read_parameter", + new=Mock(return_value=None), + ) def _wrapper(*args, **kwargs): return func(*args, **kwargs) + return _wrapper diff --git a/reflectometry_server.py b/reflectometry_server.py index d2f7887a..b0658f67 100644 --- a/reflectometry_server.py +++ b/reflectometry_server.py @@ -1,42 +1,35 @@ """ Reflectometry Server """ + import logging.config -import sys import os +import sys +from threading import Thread from pcaspy import SimpleServer -from threading import Thread logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) -logging.config.dictConfig({ - 'version': 1, - 'disable_existing_loggers': False, # this fixes the problem - 'formatters': { - 'standard': { - 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s' - }, - }, - 'handlers': { - 'default': { - 'level': 'DEBUG', - 'class': 'logging.StreamHandler', +logging.config.dictConfig( + { + "version": 1, + "disable_existing_loggers": False, # this fixes the problem + "formatters": { + "standard": {"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"}, }, - }, - 'loggers': { - '': { - 'handlers': ['default'], - 'level': 'DEBUG', - 'propagate': True - }, - 'pcaspy': { - 'handlers': ['default'], - 'level': 'INFO', - 'propagate': True + "handlers": { + "default": { + "level": "DEBUG", + "class": "logging.StreamHandler", }, + }, + "loggers": { + "": {"handlers": ["default"], "level": "DEBUG", "propagate": True}, + "pcaspy": {"handlers": ["default"], "level": "INFO", "propagate": True}, + }, } -}) +) sys.path.insert(2, os.path.join(os.getenv("EPICS_KIT_ROOT"), "ISIS", "inst_servers", "master")) @@ -46,12 +39,17 @@ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) from ReflectometryServer.ChannelAccess.reflectometry_driver import ReflectometryDriver +from server_common.channel_access import ChannelAccess +from server_common.helpers import get_macro_values, register_ioc_start + from ReflectometryServer.beamline_configuration import create_beamline_from_configuration -from ReflectometryServer.ChannelAccess.constants import REFLECTOMETRY_PREFIX, MYPVPREFIX, DEFAULT_ASG_RULES, \ - REFL_IOC_NAME +from ReflectometryServer.ChannelAccess.constants import ( + DEFAULT_ASG_RULES, + MYPVPREFIX, + REFL_IOC_NAME, + REFLECTOMETRY_PREFIX, +) from ReflectometryServer.ChannelAccess.pv_manager import PVManager -from server_common.helpers import register_ioc_start, get_macro_values -from server_common.channel_access import ChannelAccess def process_ca_loop(): @@ -60,9 +58,10 @@ def process_ca_loop(): try: SERVER.process(0.1) ChannelAccess.poll() - except Exception as err: + except Exception: break + logger.info("Initialising...") logger.info("Prefix: {}".format(REFLECTOMETRY_PREFIX)) @@ -78,8 +77,12 @@ def process_ca_loop(): SERVER.createPV(REFLECTOMETRY_PREFIX, pv_manager.PVDB) # Run heartbeat IOC, this is done with a different prefix -SERVER.createPV(prefix="{pv_prefix}CS:IOC:{ioc_name}:DEVIOS:".format(pv_prefix=MYPVPREFIX, ioc_name=REFL_IOC_NAME), - pvdb={"HEARTBEAT": {"type": "int", "value": 0}}) +SERVER.createPV( + prefix="{pv_prefix}CS:IOC:{ioc_name}:DEVIOS:".format( + pv_prefix=MYPVPREFIX, ioc_name=REFL_IOC_NAME + ), + pvdb={"HEARTBEAT": {"type": "int", "value": 0}}, +) driver = ReflectometryDriver(SERVER, pv_manager) diff --git a/run_all_tests.py b/run_all_tests.py index 29f3acb8..6ab3a824 100644 --- a/run_all_tests.py +++ b/run_all_tests.py @@ -18,19 +18,20 @@ """ # Standard imports +import argparse import os import sys import unittest import xmlrunner -import argparse from coverage import Coverage + try: from contextlib import contextmanager, nullcontext except ImportError: from contextlib2 import contextmanager, nullcontext -DEFAULT_DIRECTORY = os.path.join('.', 'test-reports') +DEFAULT_DIRECTORY = os.path.join(".", "test-reports") @contextmanager @@ -43,14 +44,20 @@ def coverage_analysis(): cov.stop() cov.report() print("------ SAVING COVERAGE REPORTS ------ ") - cov.xml_report(outfile=os.path.join(".", 'cobertura.xml')) + cov.xml_report(outfile=os.path.join(".", "cobertura.xml")) -if __name__ == '__main__': +if __name__ == "__main__": # get output directory from command line arguments parser = argparse.ArgumentParser() - parser.add_argument('-o', '--output_dir', nargs=1, type=str, default=[DEFAULT_DIRECTORY], - help='The directory to save the test reports') + parser.add_argument( + "-o", + "--output_dir", + nargs=1, + type=str, + default=[DEFAULT_DIRECTORY], + help="The directory to save the test reports", + ) args = parser.parse_args() xml_dir = args.output_dir[0]