bec-lib 3.150.2__tar.gz → 3.151.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bec_lib-3.150.2 → bec_lib-3.151.1}/PKG-INFO +1 -1
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/bl_state_manager.py +22 -1
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/bl_states.py +0 -27
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/messages.py +2 -2
- {bec_lib-3.150.2 → bec_lib-3.151.1}/pyproject.toml +1 -1
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_beamline_states.py +128 -58
- {bec_lib-3.150.2 → bec_lib-3.151.1}/.gitignore +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/README.md +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/_print_versions.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/acl_login.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/actors.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/alarm_handler.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/atlas_models.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/bec_errors.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/bec_service.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/bec_yaml_loader.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/builtin_actor_hli.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/callback_handler.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/channel_monitor.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/client.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/codecs.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/config_helper.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/config_values.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/configs/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/configs/demo_config.yaml +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/configs/openapi_schema.json +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/connector.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/dap_plugin_objects.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/dap_plugins.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/device.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/devicemanager.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/endpoints.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/file_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/live_scan_data.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/lmfit_serializer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/logger.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/macro_update_handler.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/messaging_hooks.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/messaging_services.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/metadata_schema.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/numpy_encoder.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/observer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/pdf_writer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/plugin_helper.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/procedures/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/procedures/helper.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/procedures/hli.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/procedures/status.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/queue_items.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/redis_connector/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/redis_connector/constants.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/redis_connector/hli.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/redis_connector/managed_redis_connection.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/redis_connector/streams.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/redis_connector/validation.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/request_context.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/request_items.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_args.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_data_container.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_history.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_input_validator.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_items.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_manager.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_number_container.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_repeat.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scan_report.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scans.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/scibec_validator.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/script_executor.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/serialization.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/serialization_registry.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/service_config.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/signature_serializer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/tests/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/tests/fixtures.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/tests/test_config.yaml +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/tests/test_service_config.yaml +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/tests/utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/user_macros.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/copier_jinja_filters.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/import_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/json_extended.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/_constants.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/_util.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/create/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/create/device.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/create/scan.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/create/templates/v4_scan.py.jinja +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/main.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/proxy.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/pydantic_pretty_print.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/rpc_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/scan_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/threading_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/user_acls_test.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/__init__.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/_additional_for_signature_serializer_test.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/conftest.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_acl_login.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_alarm_handler.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_atlas_models.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_bec_logger.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_bec_messages.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_bec_service.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_callback_handler.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_channel_monitor.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_client.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_config_helper.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_core_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_dap_plugins.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_data_container.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_device_hashing.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_device_manager.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_devices.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_file_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_hli_connector_fakeredis.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_hli_redis_connector.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_import_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_jinja_plugins.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_lmfit_serializer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_managed_config_values.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_managed_redis_connection.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_messaging_hooks.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_messaging_service.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_metadata_schema.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_observer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_pdf_writer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_plugin_helper.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_plugin_manager.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_plugin_system.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_pydantic_pretty_print.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_queue_items.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_redis_connector_fakeredis.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scan_context.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scan_data.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scan_history.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scan_items.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scan_manager.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scan_object.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scan_repeat.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scan_report.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_scibec_validator.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_script_executor.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_serializer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_signature_serializer.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_signature_serializer_with_future_import.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_stream_subs.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_user_macros.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_utils.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/tests/test_yaml_loader.py +0 -0
- {bec_lib-3.150.2 → bec_lib-3.151.1}/util_scripts/init_config.py +0 -0
|
@@ -44,6 +44,26 @@ def build_signature_from_model(model: BaseModel, skip: set[str] | None = None) -
|
|
|
44
44
|
return Signature(parameters)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def _state_class_for_state_type(state_type: str) -> type[bl_states.BeamlineState]:
|
|
48
|
+
"""
|
|
49
|
+
Resolve and validate a serialized beamline state type.
|
|
50
|
+
|
|
51
|
+
The state type identifies the concrete runtime state class that will be
|
|
52
|
+
started by the scan server.
|
|
53
|
+
"""
|
|
54
|
+
state_class = getattr(bl_states, state_type, None)
|
|
55
|
+
if (
|
|
56
|
+
not inspect.isclass(state_class)
|
|
57
|
+
or not issubclass(state_class, bl_states.BeamlineState)
|
|
58
|
+
or inspect.isabstract(state_class)
|
|
59
|
+
):
|
|
60
|
+
raise ValueError(f"State type {state_type!r} is not a concrete beamline state.")
|
|
61
|
+
if getattr(state_class, "CONFIG_CLASS", None) is None:
|
|
62
|
+
raise ValueError(f"State type {state_type!r} does not define a config class.")
|
|
63
|
+
|
|
64
|
+
return state_class
|
|
65
|
+
|
|
66
|
+
|
|
47
67
|
class BeamlineStateGet(TypedDict):
|
|
48
68
|
"""
|
|
49
69
|
TypedDict for the return value of the get method of a beamline state client.
|
|
@@ -154,10 +174,11 @@ class BeamlineStateManager:
|
|
|
154
174
|
self, state: messages.BeamlineStateConfig | bl_states.BeamlineStateConfig
|
|
155
175
|
) -> None:
|
|
156
176
|
if isinstance(state, messages.BeamlineStateConfig):
|
|
157
|
-
state_class =
|
|
177
|
+
state_class = _state_class_for_state_type(state.state_type)
|
|
158
178
|
model_cls = state_class.CONFIG_CLASS
|
|
159
179
|
model_instance = model_cls(**state.parameters)
|
|
160
180
|
else:
|
|
181
|
+
_state_class_for_state_type(state.state_type)
|
|
161
182
|
model_instance = state
|
|
162
183
|
instance = BeamlineStateClientBase(manager=self, state=model_instance)
|
|
163
184
|
setattr(self, state.name, instance)
|
|
@@ -408,33 +408,6 @@ class DeviceBeamlineState(BeamlineState[D], Generic[D]):
|
|
|
408
408
|
return self.evaluate(msg)
|
|
409
409
|
|
|
410
410
|
|
|
411
|
-
class ShutterState(DeviceBeamlineState[DeviceStateConfig]):
|
|
412
|
-
"""
|
|
413
|
-
A state that checks if the shutter is open.
|
|
414
|
-
|
|
415
|
-
Example:
|
|
416
|
-
shutter_state = DeviceStateConfig(
|
|
417
|
-
name="shutter_open",
|
|
418
|
-
device="shutter1",
|
|
419
|
-
)
|
|
420
|
-
bec.beamline_states.add(shutter_state)
|
|
421
|
-
"""
|
|
422
|
-
|
|
423
|
-
CONFIG_CLASS = DeviceStateConfig
|
|
424
|
-
|
|
425
|
-
def evaluate(
|
|
426
|
-
self, msg: messages.DeviceMessage, *args, **kwargs
|
|
427
|
-
) -> messages.BeamlineStateMessage:
|
|
428
|
-
val = msg.signals.get(self.signal_name, {}).get("value", "").lower()
|
|
429
|
-
if val == "open":
|
|
430
|
-
return messages.BeamlineStateMessage(
|
|
431
|
-
name=self.config.name, status="valid", label="Shutter is open."
|
|
432
|
-
)
|
|
433
|
-
return messages.BeamlineStateMessage(
|
|
434
|
-
name=self.config.name, status="invalid", label="Shutter is closed."
|
|
435
|
-
)
|
|
436
|
-
|
|
437
|
-
|
|
438
411
|
class DeviceWithinLimitsState(DeviceBeamlineState[DeviceWithinLimitsStateConfig]):
|
|
439
412
|
"""
|
|
440
413
|
A state that checks if a device signal is within limits.
|
|
@@ -2093,7 +2093,7 @@ class FeedbackMessage(BECMessage):
|
|
|
2093
2093
|
|
|
2094
2094
|
Args:
|
|
2095
2095
|
feedback (str): User feedback
|
|
2096
|
-
rating (int): User rating (
|
|
2096
|
+
rating (int): User rating (0-5)
|
|
2097
2097
|
feedback_type (Literal["bug_report", "feature_request", "general_feedback"]): Type of feedback. Defaults to "general_feedback"
|
|
2098
2098
|
contact (str | None): User contact information, if they want to be contacted regarding their feedback
|
|
2099
2099
|
realm_id (str | None): Realm ID, if applicable
|
|
@@ -2109,7 +2109,7 @@ class FeedbackMessage(BECMessage):
|
|
|
2109
2109
|
|
|
2110
2110
|
feedback: str
|
|
2111
2111
|
|
|
2112
|
-
rating: int = Field(ge=
|
|
2112
|
+
rating: int = Field(ge=0, le=5)
|
|
2113
2113
|
feedback_type: Literal["bug_report", "feature_request", "general_feedback"] = "general_feedback"
|
|
2114
2114
|
contact: str | None = None
|
|
2115
2115
|
realm_id: str | None = None
|
|
@@ -44,8 +44,8 @@ class TestHelpers:
|
|
|
44
44
|
|
|
45
45
|
class TestConfigModels:
|
|
46
46
|
def test_beamline_state_config_valid_name(self):
|
|
47
|
-
config = bl_states.BeamlineStateConfig(name="
|
|
48
|
-
assert config.name == "
|
|
47
|
+
config = bl_states.BeamlineStateConfig(name="sample_x_limits")
|
|
48
|
+
assert config.name == "sample_x_limits"
|
|
49
49
|
|
|
50
50
|
@pytest.mark.parametrize("invalid_name", ["state-name", "class", "add", "remove", "show_all"])
|
|
51
51
|
def test_beamline_state_config_invalid_name(self, invalid_name):
|
|
@@ -91,18 +91,25 @@ class TestBeamlineStateBase:
|
|
|
91
91
|
|
|
92
92
|
class TestDeviceBeamlineState:
|
|
93
93
|
def test_start_requires_connector(self, dm_with_devices):
|
|
94
|
-
state = bl_states.
|
|
95
|
-
name="
|
|
94
|
+
state = bl_states.DeviceWithinLimitsState(
|
|
95
|
+
name="sample_y_limits",
|
|
96
|
+
device="samy",
|
|
97
|
+
signal="samy",
|
|
98
|
+
low_limit=0.0,
|
|
99
|
+
high_limit=10.0,
|
|
100
|
+
device_manager=dm_with_devices,
|
|
96
101
|
)
|
|
97
102
|
|
|
98
103
|
with pytest.raises(RuntimeError, match="Redis connector is not set"):
|
|
99
104
|
state.start()
|
|
100
105
|
|
|
101
106
|
def test_start_registers_device_callback(self, connected_connector, dm_with_devices):
|
|
102
|
-
state = bl_states.
|
|
103
|
-
name="
|
|
107
|
+
state = bl_states.DeviceWithinLimitsState(
|
|
108
|
+
name="sample_x_limits",
|
|
104
109
|
device="samx",
|
|
105
110
|
signal="samx",
|
|
111
|
+
low_limit=0.0,
|
|
112
|
+
high_limit=10.0,
|
|
106
113
|
redis_connector=connected_connector,
|
|
107
114
|
device_manager=dm_with_devices,
|
|
108
115
|
)
|
|
@@ -115,10 +122,12 @@ class TestDeviceBeamlineState:
|
|
|
115
122
|
)
|
|
116
123
|
|
|
117
124
|
def test_stop_unregisters_device_callback(self, connected_connector, dm_with_devices):
|
|
118
|
-
state = bl_states.
|
|
119
|
-
name="
|
|
125
|
+
state = bl_states.DeviceWithinLimitsState(
|
|
126
|
+
name="sample_x_limits",
|
|
120
127
|
device="samx",
|
|
121
128
|
signal="samx",
|
|
129
|
+
low_limit=0.0,
|
|
130
|
+
high_limit=10.0,
|
|
122
131
|
redis_connector=connected_connector,
|
|
123
132
|
device_manager=dm_with_devices,
|
|
124
133
|
)
|
|
@@ -134,50 +143,55 @@ class TestDeviceBeamlineState:
|
|
|
134
143
|
def test_update_device_state_publishes_when_state_changes(
|
|
135
144
|
self, connected_connector, dm_with_devices
|
|
136
145
|
):
|
|
137
|
-
state = bl_states.
|
|
138
|
-
name="
|
|
146
|
+
state = bl_states.DeviceWithinLimitsState(
|
|
147
|
+
name="sample_x_limits",
|
|
139
148
|
device="samx",
|
|
140
149
|
signal="samx",
|
|
150
|
+
low_limit=0.0,
|
|
151
|
+
high_limit=10.0,
|
|
141
152
|
redis_connector=connected_connector,
|
|
142
153
|
device_manager=dm_with_devices,
|
|
143
154
|
)
|
|
144
155
|
|
|
145
156
|
msg = messages.DeviceMessage(
|
|
146
|
-
signals={"samx": {"value":
|
|
157
|
+
signals={"samx": {"value": 5.0, "timestamp": 1.0}}, metadata={"stream": "primary"}
|
|
147
158
|
)
|
|
148
|
-
msg_obj = MessageObject(value=msg, topic="test")
|
|
149
159
|
|
|
150
160
|
state._update_device_state(MessageObject(value=msg, topic="test"))
|
|
151
161
|
|
|
152
162
|
assert state._last_state is not None
|
|
153
163
|
assert state._last_state.status == "valid"
|
|
154
164
|
out = connected_connector.xread(
|
|
155
|
-
MessageEndpoints.beamline_state("
|
|
165
|
+
MessageEndpoints.beamline_state("sample_x_limits"), from_start=True
|
|
156
166
|
)
|
|
157
167
|
assert out is not None
|
|
158
168
|
assert out[0]["data"].status == "valid"
|
|
159
169
|
|
|
160
170
|
|
|
161
171
|
class TestConcreteStates:
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
def test_device_within_limits_state_valid_and_invalid(
|
|
173
|
+
self, connected_connector, dm_with_devices
|
|
174
|
+
):
|
|
175
|
+
state = bl_states.DeviceWithinLimitsState(
|
|
176
|
+
name="sample_x_limits",
|
|
165
177
|
device="samx",
|
|
166
178
|
signal="samx",
|
|
179
|
+
low_limit=0.0,
|
|
180
|
+
high_limit=10.0,
|
|
167
181
|
redis_connector=connected_connector,
|
|
168
182
|
device_manager=dm_with_devices,
|
|
169
183
|
)
|
|
170
184
|
state.start()
|
|
171
185
|
|
|
172
|
-
|
|
173
|
-
signals={"samx": {"value":
|
|
186
|
+
valid_msg = messages.DeviceMessage(
|
|
187
|
+
signals={"samx": {"value": 5.0, "timestamp": 1.0}}, metadata={"stream": "primary"}
|
|
174
188
|
)
|
|
175
|
-
|
|
176
|
-
signals={"samx": {"value":
|
|
189
|
+
invalid_msg = messages.DeviceMessage(
|
|
190
|
+
signals={"samx": {"value": 11.0, "timestamp": 2.0}}, metadata={"stream": "primary"}
|
|
177
191
|
)
|
|
178
192
|
|
|
179
|
-
assert state.evaluate(
|
|
180
|
-
assert state.evaluate(
|
|
193
|
+
assert state.evaluate(valid_msg).status == "valid"
|
|
194
|
+
assert state.evaluate(invalid_msg).status == "invalid"
|
|
181
195
|
|
|
182
196
|
def test_device_within_limits_state(self, connected_connector, dm_with_devices):
|
|
183
197
|
state = bl_states.DeviceWithinLimitsState(
|
|
@@ -253,9 +267,14 @@ class TestBeamlineStateManager:
|
|
|
253
267
|
|
|
254
268
|
def test_manager_loads_existing_state_update_on_init(self, connected_connector):
|
|
255
269
|
config = messages.BeamlineStateConfig(
|
|
256
|
-
name="
|
|
257
|
-
state_type="
|
|
258
|
-
parameters={
|
|
270
|
+
name="sample_y_limits",
|
|
271
|
+
state_type="DeviceWithinLimitsState",
|
|
272
|
+
parameters={
|
|
273
|
+
"name": "sample_y_limits",
|
|
274
|
+
"device": "samy",
|
|
275
|
+
"low_limit": 0.0,
|
|
276
|
+
"high_limit": 10.0,
|
|
277
|
+
},
|
|
259
278
|
)
|
|
260
279
|
connected_connector.xadd(
|
|
261
280
|
MessageEndpoints.available_beamline_states(),
|
|
@@ -268,22 +287,46 @@ class TestBeamlineStateManager:
|
|
|
268
287
|
manager = BeamlineStateManager(client)
|
|
269
288
|
|
|
270
289
|
assert manager.ready is True
|
|
271
|
-
assert "
|
|
272
|
-
assert isinstance(getattr(manager, "
|
|
290
|
+
assert "sample_y_limits" in manager._states
|
|
291
|
+
assert isinstance(getattr(manager, "sample_y_limits"), BeamlineStateClientBase)
|
|
292
|
+
|
|
293
|
+
def test_manager_rejects_abstract_state_type_on_init(self, connected_connector):
|
|
294
|
+
config = messages.BeamlineStateConfig(
|
|
295
|
+
name="generic_device_state",
|
|
296
|
+
state_type="DeviceBeamlineState",
|
|
297
|
+
parameters={"name": "generic_device_state", "device": "samy"},
|
|
298
|
+
)
|
|
299
|
+
connected_connector.xadd(
|
|
300
|
+
MessageEndpoints.available_beamline_states(),
|
|
301
|
+
{"data": messages.AvailableBeamlineStatesMessage(states=[config])},
|
|
302
|
+
max_size=1,
|
|
303
|
+
)
|
|
304
|
+
client = mock.MagicMock()
|
|
305
|
+
client.connector = connected_connector
|
|
306
|
+
|
|
307
|
+
with pytest.raises(ValueError, match="not a concrete beamline state"):
|
|
308
|
+
BeamlineStateManager(client)
|
|
273
309
|
|
|
274
310
|
def test_on_state_update_creates_client_attribute(self, state_manager):
|
|
275
311
|
config = messages.BeamlineStateConfig(
|
|
276
|
-
name="
|
|
277
|
-
state_type="
|
|
278
|
-
parameters={
|
|
312
|
+
name="sample_y_limits",
|
|
313
|
+
state_type="DeviceWithinLimitsState",
|
|
314
|
+
parameters={
|
|
315
|
+
"name": "sample_y_limits",
|
|
316
|
+
"device": "samy",
|
|
317
|
+
"low_limit": 0.0,
|
|
318
|
+
"high_limit": 10.0,
|
|
319
|
+
},
|
|
279
320
|
)
|
|
280
321
|
update = messages.AvailableBeamlineStatesMessage(states=[config])
|
|
281
322
|
|
|
282
323
|
state_manager._on_state_update({"data": update}, parent=state_manager)
|
|
283
324
|
|
|
284
|
-
assert "
|
|
285
|
-
assert isinstance(
|
|
286
|
-
|
|
325
|
+
assert "sample_y_limits" in state_manager._states
|
|
326
|
+
assert isinstance(
|
|
327
|
+
state_manager._states["sample_y_limits"], bl_states.DeviceWithinLimitsStateConfig
|
|
328
|
+
)
|
|
329
|
+
assert isinstance(getattr(state_manager, "sample_y_limits"), BeamlineStateClientBase)
|
|
287
330
|
|
|
288
331
|
def test_update_parameters_from_client_updates_state_and_publishes(self, state_manager):
|
|
289
332
|
config = messages.BeamlineStateConfig(
|
|
@@ -338,48 +381,60 @@ class TestBeamlineStateManager:
|
|
|
338
381
|
|
|
339
382
|
def test_client_get_returns_unknown_without_status_message(self, state_manager):
|
|
340
383
|
config = messages.BeamlineStateConfig(
|
|
341
|
-
name="
|
|
342
|
-
state_type="
|
|
343
|
-
parameters={
|
|
384
|
+
name="sample_y_limits",
|
|
385
|
+
state_type="DeviceWithinLimitsState",
|
|
386
|
+
parameters={
|
|
387
|
+
"name": "sample_y_limits",
|
|
388
|
+
"device": "samy",
|
|
389
|
+
"low_limit": 0.0,
|
|
390
|
+
"high_limit": 10.0,
|
|
391
|
+
},
|
|
344
392
|
)
|
|
345
393
|
update = messages.AvailableBeamlineStatesMessage(states=[config])
|
|
346
394
|
state_manager._on_state_update({"data": update}, parent=state_manager)
|
|
347
395
|
|
|
348
|
-
result = state_manager.
|
|
396
|
+
result = state_manager.sample_y_limits.get()
|
|
349
397
|
assert result == {"status": "unknown", "label": "No state information available."}
|
|
350
398
|
|
|
351
399
|
def test_client_get_returns_latest_status_message(self, state_manager):
|
|
352
400
|
config = messages.BeamlineStateConfig(
|
|
353
|
-
name="
|
|
354
|
-
state_type="
|
|
355
|
-
parameters={
|
|
401
|
+
name="sample_y_limits",
|
|
402
|
+
state_type="DeviceWithinLimitsState",
|
|
403
|
+
parameters={
|
|
404
|
+
"name": "sample_y_limits",
|
|
405
|
+
"device": "samy",
|
|
406
|
+
"low_limit": 0.0,
|
|
407
|
+
"high_limit": 10.0,
|
|
408
|
+
},
|
|
356
409
|
)
|
|
357
410
|
update = messages.AvailableBeamlineStatesMessage(states=[config])
|
|
358
411
|
state_manager._on_state_update({"data": update}, parent=state_manager)
|
|
359
412
|
|
|
360
413
|
state_manager._connector.xadd(
|
|
361
|
-
MessageEndpoints.beamline_state("
|
|
414
|
+
MessageEndpoints.beamline_state("sample_y_limits"),
|
|
362
415
|
{
|
|
363
416
|
"data": messages.BeamlineStateMessage(
|
|
364
|
-
name="
|
|
417
|
+
name="sample_y_limits", status="valid", label="ok"
|
|
365
418
|
)
|
|
366
419
|
},
|
|
367
420
|
max_size=1,
|
|
368
421
|
)
|
|
369
422
|
|
|
370
|
-
result = state_manager.
|
|
423
|
+
result = state_manager.sample_y_limits.get()
|
|
371
424
|
assert result == {"status": "valid", "label": "ok"}
|
|
372
425
|
|
|
373
426
|
def test_add_waits_for_initial_state_message(self, state_manager):
|
|
374
|
-
state = bl_states.
|
|
427
|
+
state = bl_states.DeviceWithinLimitsStateConfig(
|
|
428
|
+
name="sample_y_limits", device="samy", low_limit=0.0, high_limit=10.0
|
|
429
|
+
)
|
|
375
430
|
|
|
376
431
|
def publish_initial_state():
|
|
377
432
|
time.sleep(0.05)
|
|
378
433
|
state_manager._connector.xadd(
|
|
379
|
-
MessageEndpoints.beamline_state("
|
|
434
|
+
MessageEndpoints.beamline_state("sample_y_limits"),
|
|
380
435
|
{
|
|
381
436
|
"data": messages.BeamlineStateMessage(
|
|
382
|
-
name="
|
|
437
|
+
name="sample_y_limits", status="valid", label="ok"
|
|
383
438
|
)
|
|
384
439
|
},
|
|
385
440
|
max_size=1,
|
|
@@ -392,37 +447,52 @@ class TestBeamlineStateManager:
|
|
|
392
447
|
finally:
|
|
393
448
|
publisher.join()
|
|
394
449
|
|
|
395
|
-
assert state_manager.
|
|
450
|
+
assert state_manager.sample_y_limits.get() == {"status": "valid", "label": "ok"}
|
|
451
|
+
|
|
452
|
+
def test_add_rejects_abstract_device_state_config(self, state_manager):
|
|
453
|
+
state = bl_states.DeviceStateConfig(name="generic_device_state", device="samy")
|
|
454
|
+
|
|
455
|
+
with pytest.raises(ValueError, match="not a concrete beamline state"):
|
|
456
|
+
state_manager.add(state)
|
|
396
457
|
|
|
397
458
|
def test_add_and_delete_publish_updates(self, state_manager):
|
|
398
|
-
state = bl_states.
|
|
459
|
+
state = bl_states.DeviceWithinLimitsStateConfig(
|
|
460
|
+
name="sample_y_limits", device="samy", low_limit=0.0, high_limit=10.0
|
|
461
|
+
)
|
|
399
462
|
|
|
400
463
|
with mock.patch.object(state_manager, "_wait_for_initial_state"):
|
|
401
464
|
state_manager.add(state)
|
|
402
|
-
assert "
|
|
465
|
+
assert "sample_y_limits" in state_manager._states
|
|
403
466
|
|
|
404
|
-
state_manager.delete("
|
|
405
|
-
assert "
|
|
467
|
+
state_manager.delete("sample_y_limits")
|
|
468
|
+
assert "sample_y_limits" not in state_manager._states
|
|
406
469
|
|
|
407
470
|
def test_client_remove_state(self, state_manager):
|
|
408
471
|
config = messages.BeamlineStateConfig(
|
|
409
|
-
name="
|
|
410
|
-
state_type="
|
|
411
|
-
parameters={
|
|
472
|
+
name="sample_y_limits",
|
|
473
|
+
state_type="DeviceWithinLimitsState",
|
|
474
|
+
parameters={
|
|
475
|
+
"name": "sample_y_limits",
|
|
476
|
+
"device": "samy",
|
|
477
|
+
"low_limit": 0.0,
|
|
478
|
+
"high_limit": 10.0,
|
|
479
|
+
},
|
|
412
480
|
)
|
|
413
481
|
update = messages.AvailableBeamlineStatesMessage(states=[config])
|
|
414
482
|
state_manager._on_state_update({"data": update}, parent=state_manager)
|
|
415
483
|
|
|
416
|
-
state_manager.
|
|
484
|
+
state_manager.sample_y_limits.remove()
|
|
417
485
|
|
|
418
|
-
assert "
|
|
486
|
+
assert "sample_y_limits" not in state_manager._states
|
|
419
487
|
|
|
420
488
|
def test_show_all_prints_table(self, state_manager, capsys):
|
|
421
|
-
state = bl_states.
|
|
489
|
+
state = bl_states.DeviceWithinLimitsStateConfig(
|
|
490
|
+
name="sample_y_limits", device="samy", low_limit=0.0, high_limit=10.0
|
|
491
|
+
)
|
|
422
492
|
with mock.patch.object(state_manager, "_wait_for_initial_state"):
|
|
423
493
|
state_manager.add(state)
|
|
424
494
|
|
|
425
495
|
state_manager.show_all()
|
|
426
496
|
|
|
427
497
|
captured = capsys.readouterr()
|
|
428
|
-
assert "
|
|
498
|
+
assert "sample_y_limits" in (captured.out + captured.err)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bec_lib-3.150.2 → bec_lib-3.151.1}/bec_lib/utils/plugin_manager/create/templates/v4_scan.py.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|