diff --git a/tomcat_bec/devices/stddaqclient.py b/tomcat_bec/devices/stddaqclient.py new file mode 100644 index 0000000..e07cebf --- /dev/null +++ b/tomcat_bec/devices/stddaqclient.py @@ -0,0 +1,115 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Jun 3 14:16:29 2024 + +@author: mohacsi_i +""" +from time import sleep +from ophyd import Device, Signal, Component +from websockets.sync.client import connect +from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError + +import json + +class StdDaqClientDevice(Device): + """ Lightweight wrapper around the undocumented StdDaq websocket interface. + This was meant to replace the documented python client. + """ + # Status attributes + n_image = Component(Signal) + file_path = Component(Signal) + + def __init__(self, *args, parent: Device = None, **kwargs) -> None: + super().__init__(*args, parent=parent, **kwargs) + self.ws_server_url = ( + kwargs["daq_url"] if "daq_url" in kwargs else "ws://xbl-daq-29:8080") + self._client = connect(self.ws_server_url) + + self.n_image.set(100) + self.file_path.set("/gpfs/test/test-beamline") + + def configure(self, d: dict) -> tuple: + """ + Example: + std.configure(d={'n_images': 234, 'file_path': "/data/test/raw"}) + """ + if "num_images" in d: + self.n_images.set(d['n_images']) + del d['num_images'] + if "file_path" in d: + self.output_file.set(d['file_path']) + del d['file_path'] + return (old_config, new_config) + + def stage(self): + file_path = self.file_path.get() + n_image = self.n_image.get() + + message = {"command":"start", "path": file_path, "n_image": n_image} + self.message(message) + return super().stage() + + def unstage(self): + """ Stop a running acquisition + + WARN: This will also close the connection!!! + """ + message = {"command":"stop"} + self.message(message) + return super().unstage() + + def stop(self, *, success=False): + """ Stop a running acquisition + + WARN: This will also close the connection!!! + """ + message = {"command":"stop"} + self.message(message) + + def status(self): + return self.message({"command": "status"}) + + def abort(self): + return self.message({"command": "abort"}) + + def message(self, d: dict, timeout=1): + """ + + Note: finishing acquisition meang StdDAQ will close connections + """ + print(d) + reply = None + if isinstance(d, dict): + msg = json.dumps(d) + else: + msg = str(d) + # Send message (reopen connection if needed) + try: + self._client.send(msg) + except ConnectionClosedError: + # StdDAQ may reject connection for a few seconds + try: + self._client = connect(self.ws_server_url) + except ConnectionRefusedError: + sleep(5) + self._client = connect(self.ws_server_url) + self._client.send(msg) + except ConnectionClosedOK: + # StdDAQ may reject connection for a few seconds + try: + self._client = connect(self.ws_server_url) + except ConnectionRefusedError: + sleep(5) + self._client = connect(self.ws_server_url) + self._client.send(msg) + # Wait for reply + try: + reply = self._client.recv(timeout) + print(reply) + except ConnectionClosedError: + pass + except TimeoutError: + pass + return reply + +