- added camera viewer device
- fixed some issues in flomni sample storage device
This commit is contained in:
@@ -364,3 +364,29 @@ rtz:
|
||||
onFailure: buffer
|
||||
readOnly: false
|
||||
readoutPriority: on_request
|
||||
|
||||
|
||||
|
||||
cam_flomni_gripper:
|
||||
description: Camera sample changer
|
||||
deviceClass: csaxs_bec.devices.omny.webcam_viewer.WebcamViewer
|
||||
deviceConfig:
|
||||
url: http://flomnicamserver:5000/video_high
|
||||
num_rotation_90: 3
|
||||
transpose: false
|
||||
enabled: true
|
||||
onFailure: buffer
|
||||
readOnly: false
|
||||
readoutPriority: on_request
|
||||
|
||||
cam_flomni_overview:
|
||||
description: Camera flomni overview
|
||||
deviceClass: csaxs_bec.devices.omny.webcam_viewer.WebcamViewer
|
||||
deviceConfig:
|
||||
url: http://flomnicamserver:5001/video_high
|
||||
num_rotation_90: 3
|
||||
transpose: false
|
||||
enabled: true
|
||||
onFailure: buffer
|
||||
readOnly: false
|
||||
readoutPriority: on_request
|
||||
@@ -24,24 +24,25 @@ class FlomniSampleStorage(Device):
|
||||
SUB_VALUE = "value"
|
||||
_default_sub = SUB_VALUE
|
||||
sample_placed = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_flomni{i}:GET", {}) for i in range(21)
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_flomni{i}:GET", {"auto_monitor": True}) for i in range(21)
|
||||
}
|
||||
sample_placed = Dcpt(sample_placed)
|
||||
|
||||
sample_names = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_flomni{i}:GET.DESC", {"string": True})
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_flomni{i}:GET.DESC", {"string": True, "auto_monitor": True})
|
||||
for i in range(21)
|
||||
}
|
||||
sample_names = Dcpt(sample_names)
|
||||
|
||||
sample_in_gripper = Cpt(
|
||||
EpicsSignal, name="sample_in_gripper", read_pv="XOMNY-SAMPLE_DB_flomni100:GET"
|
||||
EpicsSignal, name="sample_in_gripper", read_pv="XOMNY-SAMPLE_DB_flomni100:GET", auto_monitor=True
|
||||
)
|
||||
sample_in_gripper_name = Cpt(
|
||||
EpicsSignal,
|
||||
name="sample_in_gripper_name",
|
||||
read_pv="XOMNY-SAMPLE_DB_flomni100:GET.DESC",
|
||||
string=True,
|
||||
auto_monitor=True
|
||||
)
|
||||
|
||||
def __init__(self, prefix="", *, name, **kwargs):
|
||||
|
||||
@@ -37,84 +37,86 @@ class OMNYSampleStorage(Device):
|
||||
_default_sub = SUB_VALUE
|
||||
|
||||
sample_shuttle_A_placed = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_A:{i}", {}) for i in range(1, 7)
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_A:{i}", {"auto_monitor": True}) for i in range(1, 7)
|
||||
}
|
||||
sample_shuttle_A_placed = Dcpt(sample_shuttle_A_placed)
|
||||
|
||||
sample_shuttle_B_placed = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_B:{i}", {}) for i in range(1, 7)
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_B:{i}", {"auto_monitor": True}) for i in range(1, 7)
|
||||
}
|
||||
sample_shuttle_B_placed = Dcpt(sample_shuttle_B_placed)
|
||||
|
||||
sample_shuttle_C_placed = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}", {}) for i in range(1, 7)
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}", {"auto_monitor": True}) for i in range(1, 7)
|
||||
}
|
||||
sample_shuttle_C_placed = Dcpt(sample_shuttle_C_placed)
|
||||
|
||||
sample_shuttle_C_placed = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}", {}) for i in range(1, 7)
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}", {"auto_monitor": True}) for i in range(1, 7)
|
||||
}
|
||||
sample_shuttle_C_placed = Dcpt(sample_shuttle_C_placed)
|
||||
|
||||
parking_placed = {
|
||||
f"parking{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_parking:{i}", {}) for i in range(1, 7)
|
||||
f"parking{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_parking:{i}", {"auto_monitor": True}) for i in range(1, 7)
|
||||
}
|
||||
parking_placed = Dcpt(parking_placed)
|
||||
|
||||
sample_placed = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_omny:{i}", {})
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_omny:{i}", {"auto_monitor": True})
|
||||
for i in [10, 11, 12, 13, 14, 32, 33, 34, 100, 101]
|
||||
}
|
||||
sample_placed = Dcpt(sample_placed)
|
||||
|
||||
sample_shuttle_A_names = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_A:{i}.DESC", {"string": True})
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_A:{i}.DESC", {"string": True, "auto_monitor": True})
|
||||
for i in range(1, 7)
|
||||
}
|
||||
sample_shuttle_A_names = Dcpt(sample_shuttle_A_names)
|
||||
|
||||
sample_shuttle_B_names = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_B:{i}.DESC", {"string": True})
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_B:{i}.DESC", {"string": True, "auto_monitor": True})
|
||||
for i in range(1, 7)
|
||||
}
|
||||
sample_shuttle_B_names = Dcpt(sample_shuttle_B_names)
|
||||
|
||||
sample_shuttle_C_names = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}.DESC", {"string": True})
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}.DESC", {"string": True, "auto_monitor": True})
|
||||
for i in range(1, 7)
|
||||
}
|
||||
sample_shuttle_C_names = Dcpt(sample_shuttle_C_names)
|
||||
|
||||
parking_names = {
|
||||
f"parking{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_parking:{i}.DESC", {"string": True})
|
||||
f"parking{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_parking:{i}.DESC", {"string": True, "auto_monitor": True})
|
||||
for i in range(1, 7)
|
||||
}
|
||||
parking_names = Dcpt(parking_names)
|
||||
|
||||
sample_names = {
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_omny:{i}.DESC", {"string": True})
|
||||
f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_omny:{i}.DESC", {"string": True, "auto_monitor": True})
|
||||
for i in [10, 11, 12, 13, 14, 32, 33, 34, 100, 101]
|
||||
}
|
||||
sample_names = Dcpt(sample_names)
|
||||
|
||||
sample_in_gripper = Cpt(
|
||||
EpicsSignal, name="sample_in_gripper", read_pv="XOMNY-SAMPLE_DB_omny:110.VAL"
|
||||
EpicsSignal, name="sample_in_gripper", read_pv="XOMNY-SAMPLE_DB_omny:110.VAL", auto_monitor=True
|
||||
)
|
||||
sample_in_gripper_name = Cpt(
|
||||
EpicsSignal,
|
||||
name="sample_in_gripper_name",
|
||||
read_pv="XOMNY-SAMPLE_DB_omny:110.DESC",
|
||||
string=True,
|
||||
auto_monitor=True
|
||||
)
|
||||
|
||||
sample_in_samplestage = Cpt(
|
||||
EpicsSignal, name="sample_in_samplestage", read_pv="XOMNY-SAMPLE_DB_omny:0.VAL"
|
||||
EpicsSignal, name="sample_in_samplestage", read_pv="XOMNY-SAMPLE_DB_omny:0.VAL", auto_monitor=True
|
||||
)
|
||||
sample_in_samplestage_name = Cpt(
|
||||
EpicsSignal,
|
||||
name="sample_in_samplestage_name",
|
||||
read_pv="XOMNY-SAMPLE_DB_omny:0.DESC",
|
||||
string=True,
|
||||
auto_monitor=True
|
||||
)
|
||||
|
||||
def __init__(self, prefix="", *, name, **kwargs):
|
||||
|
||||
65
csaxs_bec/devices/omny/webcam_viewer.py
Normal file
65
csaxs_bec/devices/omny/webcam_viewer.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import requests
|
||||
import threading
|
||||
import cv2
|
||||
import numpy as np
|
||||
from ophyd import Device, Component as Cpt
|
||||
from ophyd_devices import PreviewSignal
|
||||
import traceback
|
||||
|
||||
from bec_lib.logger import bec_logger
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
class WebcamViewer(Device):
|
||||
USER_ACCESS = ["start", "stop"]
|
||||
preview = Cpt(PreviewSignal, ndim=2, num_rotation_90=0, transpose=False)
|
||||
|
||||
def __init__(self, url:str, name:str, num_rotation_90=0, transpose=False, **kwargs) -> None:
|
||||
super().__init__(name=name, **kwargs)
|
||||
self.url = url
|
||||
self._connection = None
|
||||
self._update_thread = None
|
||||
self._buffer = b""
|
||||
self._shutdown_event = threading.Event()
|
||||
self.preview.num_rotation_90 = num_rotation_90
|
||||
self.preview.transpose = transpose
|
||||
|
||||
def start(self) -> None:
|
||||
if self._connection is not None:
|
||||
return
|
||||
self._update_thread = threading.Thread(target=self._update_loop, daemon=True)
|
||||
self._update_thread.start()
|
||||
|
||||
def _update_loop(self) -> None:
|
||||
while not self._shutdown_event.is_set():
|
||||
try:
|
||||
self._connection = requests.get(self.url, stream=True)
|
||||
for chunk in self._connection.iter_content(chunk_size=1024):
|
||||
self._buffer += chunk
|
||||
start = self._buffer.find(b'\xff\xd8') # JPEG start
|
||||
end = self._buffer.find(b'\xff\xd9') # JPEG end
|
||||
if start == -1 or end == -1:
|
||||
continue
|
||||
jpg = self._buffer[start:end+2]
|
||||
self._buffer = self._buffer[end+2:]
|
||||
|
||||
image = cv2.imdecode(np.frombuffer(jpg, np.uint8), cv2.IMREAD_COLOR)
|
||||
if image is not None:
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||
self.preview.put(image)
|
||||
except Exception as exc:
|
||||
content = traceback.format_exc()
|
||||
logger.error(f"Image update loop failed: {content}")
|
||||
|
||||
def stop(self) -> None:
|
||||
if self._connection is None:
|
||||
return
|
||||
self._shutdown_event.set()
|
||||
if self._connection is not None:
|
||||
self._connection.close()
|
||||
self._connection = None
|
||||
if self._update_thread is not None:
|
||||
self._update_thread.join()
|
||||
self._update_thread = None
|
||||
|
||||
self._shutdown_event.clear()
|
||||
Reference in New Issue
Block a user