Coverage for /opt/conda/envs/apienv/lib/python3.10/site-packages/daiquiri/core/hardware/bliss/__init__.py: 84%
67 statements
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-15 02:12 +0000
« prev ^ index » next coverage.py v7.6.5, created at 2024-11-15 02:12 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3import importlib
4import logging
6from marshmallow import ValidationError, fields, validates_schema, post_load
7from bliss.config.static import get_config
9from daiquiri.core.hardware.abstract import ProtocolHandler
10from daiquiri.core.hardware.bliss.object import BlissDummyObject
11from daiquiri.core.schema.hardware import HOConfigSchema
12from daiquiri.core.exceptions import InvalidYAML
13from daiquiri.core.utils import loader
16logger = logging.getLogger(__name__)
18bl = logging.getLogger("bliss")
19bl.setLevel(logging.WARNING)
20bl.disabled = True
22bl = logging.getLogger("bliss.common.mapping")
23bl.disabled = True
26class BlissHOConfigSchema(HOConfigSchema):
27 """The Bliss Hardware Object Config Schema"""
29 address = fields.Str(metadata={"description": "Beacon object id"})
30 type = fields.Str(metadata={"description": "Object type for objects without id"})
32 @validates_schema
33 def schema_validate(self, data, **kwargs):
34 if not (data.get("address") or data.get("url")):
35 raise ValidationError(
36 "Object must have either an `address` or `url` defined"
37 )
39 @post_load
40 def populate(self, data, **kwargs):
41 """Generate the device address from its url"""
42 if data.get("url"):
43 _, address = data["url"].split("://")
44 data["address"] = address
46 return data
49class BlissHandler(ProtocolHandler):
50 """The bliss protocol handler
52 Returns an instance of an abstracted bliss object
54 The bliss protocol handler first checks the kwargs conform to the BlissHOConfigSchema
55 defined above. This address is used to retrieve the bliss object. Its class is then mapped
56 to an abstract class and a bliss specific instance is created (see hardware/bliss/motor.py)
57 """
59 library = "bliss"
61 _class_map = {
62 "bliss.common.axis.Axis": "motor",
63 "bliss.controllers.actuator.Actuator": "actuator",
64 "bliss.controllers.multiplepositions.MultiplePositions": "multiposition",
65 "bliss.common.shutter.BaseShutter": "shutter",
66 "bliss.controllers.test.objectref.ObjectRef": "objectref",
67 "bliss.controllers.intraled.Intraled": "intraled",
68 "bliss.controllers.lima.lima_base.Lima": "lima",
69 "tomo.procedures.base_procedure.SessionProcedure": "procedure",
70 "tomo.procedures.user_script.UserScriptProcedure": "procedure",
71 "tomo.controllers.tomo_config.TomoConfig": "tomoconfig",
72 "tomo.controllers.tomo_detector.TomoDetector": "tomodetector",
73 "tomo.controllers.tomo_detectors.TomoDetectors": "tomodetectors",
74 "tomo.controllers.tomo_imaging.TomoImaging": "tomoimaging",
75 "tomo.controllers.tomo_sample_stage.TomoSampleStage": "tomosamplestage",
76 "tomo.controllers.flat_motion.FlatMotion": "tomoflatmotion",
77 "tomo.controllers.holotomo.Holotomo": "tomoholo",
78 "tomo.controllers.flat_motion.FlatMotion": "tomoflatmotion",
79 "tomo.controllers.reference_position.ReferencePosition": "tomoreferenceposition",
80 "tomo.optic.base_optic.BaseOptic": "tomooptic",
81 }
83 _class_name_map = {
84 "EBV": "beamviewer",
85 "ChannelFromConfig": "channelfromconfig",
86 "volpi": "volpi",
87 "TestObject": "test",
88 "Fshutter": "shutter",
89 "transmission": "transmission",
90 "tango_attr_as_counter": "tango_attr_as_counter",
91 "ShimadzuCBM20": "shimadzucbm20",
92 "ShimadzuPDA": "shimadzupda",
93 "ID26Attenuator": "attenuator_wago",
94 "PresetManager": "presetmanager",
95 "LaserController": "laser",
96 "LaserHeating": "laserheating",
97 "IcePapTrack": "remotemotor",
98 }
100 def get(self, **kwargs):
101 try:
102 kwargs = BlissHOConfigSchema().load(kwargs)
103 except ValidationError as err:
104 raise InvalidYAML(
105 {
106 "message": "Bliss hardware object definition is invalid",
107 "file": "hardware.yml",
108 "obj": kwargs,
109 "errors": err.messages,
110 }
111 ) from err
113 obj_type = kwargs.get("type")
115 if obj_type == "activetomoconfig":
116 # It's a global proxy without real instance
117 class GlobalBlissObject:
118 name = None
120 obj = GlobalBlissObject()
121 else:
122 config = get_config()
123 try:
124 obj = config.get(kwargs.get("address"))
125 except Exception:
126 logger.exception(f"Couldn't get bliss object {kwargs.get('address')}")
127 return BlissDummyObject(**kwargs)
128 kwargs["obj"] = obj
130 obj_type = kwargs.get("type")
131 if obj_type is not None:
132 return loader(
133 "daiquiri.core.hardware.bliss",
134 "",
135 obj_type,
136 **kwargs,
137 )
139 for bliss_mapping, mapped_class in self._class_map.items():
140 bliss_file, bliss_class_name = bliss_mapping.rsplit(".", 1)
141 # Some classes may not be available depending on the bliss version
142 try:
143 bliss_module = importlib.import_module(bliss_file)
144 bliss_class = getattr(bliss_module, bliss_class_name)
145 except ModuleNotFoundError:
146 logger.warning(f"Could not find bliss module {bliss_mapping}")
147 continue
149 if isinstance(kwargs["obj"], bliss_class):
150 return loader(
151 "daiquiri.core.hardware.bliss", "", mapped_class, **kwargs
152 )
154 cls = kwargs["obj"].__class__.__name__
155 if cls in self._class_name_map:
156 return loader(
157 "daiquiri.core.hardware.bliss",
158 "",
159 self._class_name_map[cls],
160 **kwargs,
161 )
163 logger.error("No class found for {cls}".format(cls=cls))
164 return BlissDummyObject(**kwargs)