Working config again

This commit is contained in:
gac-x05la
2025-01-20 15:29:49 +01:00
parent 650fa3f4a3
commit e1c866fd30
6 changed files with 299 additions and 156 deletions

View File

@@ -49,9 +49,9 @@ femto_mean_curr:
softwareTrigger: false softwareTrigger: false
es1_roty: es1_roty:
readoutPriority: baseline readoutPriority: monitored
description: 'Test rotation stage' description: 'Test rotation stage'
deviceClass: tomcat_bec.devices.psimotor.EpicsMotorMR deviceClass: ophyd.EpicsMotor
deviceConfig: deviceConfig:
prefix: X02DA-ES1-SMP1:ROTY prefix: X02DA-ES1-SMP1:ROTY
deviceTags: deviceTags:
@@ -61,59 +61,59 @@ es1_roty:
readOnly: false readOnly: false
softwareTrigger: false softwareTrigger: false
es1_ismc: # es1_ismc:
description: 'Automation1 iSMC interface' # description: 'Automation1 iSMC interface'
deviceClass: tomcat_bec.devices.aa1Controller # deviceClass: tomcat_bec.devices.aa1Controller
deviceConfig: # deviceConfig:
prefix: 'X02DA-ES1-SMP1:CTRL:' # prefix: 'X02DA-ES1-SMP1:CTRL:'
deviceTags: # deviceTags:
- es1 # - es1
enabled: true # enabled: true
onFailure: buffer # onFailure: buffer
readOnly: false # readOnly: false
readoutPriority: monitored # readoutPriority: monitored
softwareTrigger: false # softwareTrigger: false
es1_tasks: # es1_tasks:
description: 'Automation1 task management interface' # description: 'Automation1 task management interface'
deviceClass: tomcat_bec.devices.aa1Tasks # deviceClass: tomcat_bec.devices.aa1Tasks
deviceConfig: # deviceConfig:
prefix: 'X02DA-ES1-SMP1:TASK:' # prefix: 'X02DA-ES1-SMP1:TASK:'
deviceTags: # deviceTags:
- es1 # - es1
enabled: true # enabled: true
onFailure: buffer # onFailure: buffer
readOnly: false # readOnly: false
readoutPriority: monitored # readoutPriority: monitored
softwareTrigger: false # softwareTrigger: false
es1_psod: # es1_psod:
description: 'AA1 PSO output interface (trigger)' # description: 'AA1 PSO output interface (trigger)'
deviceClass: tomcat_bec.devices.aa1AxisPsoDistance # deviceClass: tomcat_bec.devices.aa1AxisPsoDistance
deviceConfig: # deviceConfig:
prefix: 'X02DA-ES1-SMP1:ROTY:PSO:' # prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'
deviceTags: # deviceTags:
- es1 # - es1
enabled: true # enabled: true
onFailure: buffer # onFailure: buffer
readOnly: false # readOnly: false
readoutPriority: monitored # readoutPriority: monitored
softwareTrigger: true # softwareTrigger: true
es1_ddaq: # es1_ddaq:
description: 'Automation1 position recording interface' # description: 'Automation1 position recording interface'
deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection # deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
deviceConfig: # deviceConfig:
prefix: 'X02DA-ES1-SMP1:ROTY:DDC:' # prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'
deviceTags: # deviceTags:
- es1 # - es1
enabled: true # enabled: true
onFailure: buffer # onFailure: buffer
readOnly: false # readOnly: false
readoutPriority: monitored # readoutPriority: monitored
softwareTrigger: false # softwareTrigger: false
#camera: #camera:

View File

@@ -0,0 +1,186 @@
eyex:
readoutPriority: baseline
description: X-ray eye axis X
deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC
deviceConfig:
prefix: MTEST-X05LA-ES2-XRAYEYE:M1
deviceTags:
- xray-eye
onFailure: buffer
enabled: true
readOnly: false
softwareTrigger: false
# eyey:
# readoutPriority: baseline
# description: X-ray eye axis Y
# deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC
# deviceConfig:
# prefix: MTEST-X05LA-ES2-XRAYEYE:M2
# deviceTags:
# - xray-eye
# onFailure: buffer
# enabled: true
# readOnly: false
# softwareTrigger: false
# eyez:
# readoutPriority: baseline
# description: X-ray eye axis Z
# deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC
# deviceConfig:
# prefix: MTEST-X05LA-ES2-XRAYEYE:M3
# deviceTags:
# - xray-eye
# onFailure: buffer
# enabled: true
# readOnly: false
# softwareTrigger: false
femto_mean_curr:
readoutPriority: monitored
description: Femto mean current
deviceClass: ophyd.EpicsSignal
deviceConfig:
auto_monitor: true
read_pv: MTEST-X05LA-ES2-XRAYEYE:FEMTO-MEAN-CURR
deviceTags:
- xray-eye
onFailure: buffer
enabled: true
readOnly: true
softwareTrigger: false
es1_roty:
readoutPriority: baseline
description: 'Test rotation stage'
deviceClass: tomcat_bec.devices.psimotor.EpicsMotorMR
deviceConfig:
prefix: X02DA-ES1-SMP1:ROTY
deviceTags:
- es1-sam
onFailure: buffer
enabled: true
readOnly: false
softwareTrigger: false
es1_ismc:
description: 'Automation1 iSMC interface'
deviceClass: tomcat_bec.devices.aa1Controller
deviceConfig:
prefix: 'X02DA-ES1-SMP1:CTRL:'
deviceTags:
- es1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
es1_tasks:
description: 'Automation1 task management interface'
deviceClass: tomcat_bec.devices.aa1Tasks
deviceConfig:
prefix: 'X02DA-ES1-SMP1:TASK:'
deviceTags:
- es1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
es1_psod:
description: 'AA1 PSO output interface (trigger)'
deviceClass: tomcat_bec.devices.aa1AxisPsoDistance
deviceConfig:
prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'
deviceTags:
- es1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: true
es1_ddaq:
description: 'Automation1 position recording interface'
deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
deviceConfig:
prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'
deviceTags:
- es1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
#camera:
# description: Grashopper Camera
# deviceClass: tomcat_bec.devices.GrashopperTOMCAT
# deviceConfig:
# prefix: 'X02DA-PG-USB:'
# deviceTags:
# - camera
# enabled: true
# onFailure: buffer
# readOnly: false
# readoutPriority: monitored
# softwareTrigger: true
gfcam:
description: GigaFrost camera client
deviceClass: tomcat_bec.devices.GigaFrostCamera
deviceConfig:
prefix: 'X02DA-CAM-GF2:'
backend_url: 'http://sls-daq-001:8080'
auto_soft_enable: true
deviceTags:
- camera
- trigger
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: true
gfdaq:
description: GigaFrost stdDAQ client
deviceClass: tomcat_bec.devices.StdDaqClient
deviceConfig:
ws_url: 'ws://129.129.95.111:8080'
rest_url: 'http://129.129.95.111:5000'
deviceTags:
- std-daq
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
daq_stream0:
description: stdDAQ preview (2 every 555)
deviceClass: tomcat_bec.devices.StdDaqPreviewDetector
deviceConfig:
url: 'tcp://129.129.95.111:20000'
deviceTags:
- std-daq
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
daq_stream1:
description: stdDAQ preview (1 at 5 Hz)
deviceClass: tomcat_bec.devices.StdDaqPreviewDetector
deviceConfig:
url: 'tcp://129.129.95.111:20001'
deviceTags:
- std-daq
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false

View File

@@ -256,7 +256,7 @@ class GigaFrostCamera(PSIDetectorBase):
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
custom_prepare_cls = GigaFrostCameraMixin custom_prepare_cls = GigaFrostCameraMixin
USER_ACCESS = [""] USER_ACCESS = ["initialize"]
_initialized = False _initialized = False
infoBusyFlag = Component(EpicsSignalRO, "BUSY_STAT", auto_monitor=True) infoBusyFlag = Component(EpicsSignalRO, "BUSY_STAT", auto_monitor=True)
@@ -466,7 +466,7 @@ class GigaFrostCamera(PSIDetectorBase):
def initialize(self): def initialize(self):
""" Initialization in separate command""" """ Initialization in separate command"""
self.custom_prepare_cls._init_gigafrost() self.custom_prepare._init_gigafrost()
self._initialized = True self._initialized = True
def trigger(self) -> DeviceStatus: def trigger(self) -> DeviceStatus:

View File

@@ -56,10 +56,10 @@ class StdDaqMixin(CustomDeviceMixin):
if "steps" in scanargs and scanargs['steps'] is not None: if "steps" in scanargs and scanargs['steps'] is not None:
num_points *= scanargs["steps"] num_points *= scanargs["steps"]
points_valid = True points_valid = True
elif "exp_burst" in scanargs and scanargs['exp_burst'] is not None: if "exp_burst" in scanargs and scanargs['exp_burst'] is not None:
num_points *= scanargs["exp_burst"] num_points *= scanargs["exp_burst"]
points_valid = True points_valid = True
elif "repeats" in scanargs and scanargs['repeats'] is not None: if "repeats" in scanargs and scanargs['repeats'] is not None:
num_points *= scanargs["repeats"] num_points *= scanargs["repeats"]
points_valid = True points_valid = True
if points_valid: if points_valid:
@@ -67,19 +67,16 @@ class StdDaqMixin(CustomDeviceMixin):
# Perform bluesky-style configuration # Perform bluesky-style configuration
if len(d) > 0: if len(d) > 0:
print('Reconfiguring')
# Stop if current status is not idle
if self.parent.state() != "idle":
self.parent.blueunstage()
# Configure new run (will restart the stdDAQ) # Configure new run (will restart the stdDAQ)
logger.warning(f"[{self.parent.name}] Configuring with:\n{d}") logger.warning(f"[{self.parent.name}] stdDAQ needs reconfiguring with:\n{d}")
self.parent.configure(d=d) self.parent.configure(d=d)
# Wait for REST API to kill the DAQ
sleep(0.5)
# Try to start a new run # Try to start a new run (reconnects)
self.parent.bluestage() self.parent.bluestage()
# And start status monitoring # And start status monitoring
self._mon = Thread(target=self.poll, daemon=True) self._mon = Thread(target=self.monitor, daemon=True)
self._mon.start() self._mon.start()
def on_unstage(self): def on_unstage(self):
@@ -93,7 +90,7 @@ class StdDaqMixin(CustomDeviceMixin):
""" """
self.parent.blueunstage() self.parent.blueunstage()
def poll(self) -> None: def monitor(self) -> None:
""" Monitor status messages while connection is open. This will block the reply monitoring """ Monitor status messages while connection is open. This will block the reply monitoring
to calling unstage() might throw. Status updates are sent every 1 seconds, but finishing to calling unstage() might throw. Status updates are sent every 1 seconds, but finishing
acquisition means StdDAQ will close connection, so there's no idle state polling. acquisition means StdDAQ will close connection, so there's no idle state polling.
@@ -269,8 +266,6 @@ class StdDaqClient(PSIDeviceBase):
* 4: Invert pixel values, but do not apply any linearity correction * 4: Invert pixel values, but do not apply any linearity correction
* 5: Apply the full linearity correction * 5: Apply the full linearity correction
""" """
print('This is d')
print(d)
if 'image_width' in d and d['image_width']!=None: if 'image_width' in d and d['image_width']!=None:
self.cfg_pixel_width.set(d['image_width']).wait() self.cfg_pixel_width.set(d['image_width']).wait()
if 'image_height' in d and d['image_height']!=None: if 'image_height' in d and d['image_height']!=None:
@@ -283,9 +278,6 @@ class StdDaqClient(PSIDeviceBase):
# Restart the DAQ if resolution changed # Restart the DAQ if resolution changed
cfg = self.get_daq_config() cfg = self.get_daq_config()
print(cfg)
print(cfg['number_of_writers'])
print(self.cfg_nr_writers.get())
if cfg['image_pixel_height'] != self.cfg_pixel_height.get() or \ if cfg['image_pixel_height'] != self.cfg_pixel_height.get() or \
cfg['image_pixel_width'] != self.cfg_pixel_width.get() or \ cfg['image_pixel_width'] != self.cfg_pixel_width.get() or \
cfg['bit_depth'] != self.cfg_bit_depth.get() or \ cfg['bit_depth'] != self.cfg_bit_depth.get() or \
@@ -293,29 +285,22 @@ class StdDaqClient(PSIDeviceBase):
# Stop if current status is not idle # Stop if current status is not idle
status = self.state() status = self.state()
print(status) if self.state() != "idle":
raise RuntimeWarning(f"[{self.name}] stdDAQ reconfiguration might corrupt files")
# Stop running acquisition
try:
self.stop()
except RuntimeError:
raise RuntimeWarning(f"[{self.name}] Told ya!")
# if self.state() != "idle":
# if status != "idle":
# self.surestop()
status = self.state()
print(status)
# Stop running acquisition
print('Before unstage')
self.unstage()
print('After unstage')
# Update retrieved config # Update retrieved config
cfg['image_pixel_height'] = int(self.cfg_pixel_height.get()) cfg['image_pixel_height'] = int(self.cfg_pixel_height.get())
cfg['image_pixel_width'] = int(self.cfg_pixel_width.get()) cfg['image_pixel_width'] = int(self.cfg_pixel_width.get())
cfg['bit_depth'] = int(self.cfg_bit_depth.get()) cfg['bit_depth'] = int(self.cfg_bit_depth.get())
cfg['number_of_writers'] = int(self.cfg_nr_writers.get()) cfg['number_of_writers'] = int(self.cfg_nr_writers.get())
print(cfg)
r = self.set_daq_config(cfg) r = self.set_daq_config(cfg)
print(r)
cfg=self.read_daq_config() cfg=self.read_daq_config()
print(cfg)
def bluestage(self): def bluestage(self):
""" Stages the stdDAQ """ Stages the stdDAQ
@@ -324,34 +309,37 @@ class StdDaqClient(PSIDeviceBase):
the current configuration. It waits for the first reply and checks the current configuration. It waits for the first reply and checks
it for obvious failures. it for obvious failures.
""" """
# Can't stage into a running exposure
if self.state() != 'idle':
raise RuntimeError(f"[{self.name}] stdDAQ can't stage from state: {self.state()}")
file_path = self.file_path.get() file_path = self.file_path.get()
num_images = self.num_images.get() num_images = self.num_images.get()
# New connection
self.connect()
message = {"command": "start", "path": file_path, "n_image": num_images, } message = {"command": "start", "path": file_path, "n_image": num_images, }
reply = self.message(message)
ii = 0 if reply is not None:
while ii<5: reply = json.loads(reply)
self.connect() self.status.set(reply["status"], force=True).wait()
reply = self.message(message) logger.info(f"[{self.name}] Start DAQ reply: {reply}")
if reply is not None: # Give it more time to reconfigure
reply = json.loads(reply) if reply["status"] in ("rejected"):
self.status.set(reply["status"], force=True).wait() # FIXME: running exposure is a nogo
logger.info(f"[{self.name}] Start DAQ reply: {reply}") if reply['reason'] == "driver is busy!":
raise RuntimeError(f"[{self.name}] Start stdDAQ command rejected: already running")
# Give it more time to reconfigure
if reply["status"] in ("rejected"):
# FIXME: running exposure is a nogo
if reply['reason'] == "driver is busy!":
raise RuntimeError(f"[{self.name}] Start stdDAQ command rejected: already running")
else:
# Give it more time to consolidate
sleep(1)
else: else:
# Success!!! # Give it more time to consolidate
print(f"[{self.name}] Started stdDAQ on try {ii} in: {reply['status']}") sleep(1)
return else:
ii += 1 # Success!!!
raise RuntimeError(f"[{self.name}] Failed to start the stdDAQ in 5 tries, reason: {reply['reason']}") print(f"[{self.name}] Started stdDAQ in: {reply['status']}")
return
raise RuntimeError(f"[{self.name}] Failed to start the stdDAQ in 1 tries, reason: {reply['reason']}")
def blueunstage(self): def blueunstage(self):
""" Unstages the stdDAQ """ Unstages the stdDAQ
@@ -439,11 +427,14 @@ class StdDaqClient(PSIDeviceBase):
headers = {'Content-type': 'application/json'} headers = {'Content-type': 'application/json'}
) )
def nuke(self): def nuke(self, restarttime=5):
""" Reconfigures the stdDAQ to restart the services """ Reconfigures the stdDAQ to restart the services. This causes
systemd to kill the current DAQ service and restart it with the same
configuration. Which might corrupt the currently written file...
""" """
cfg = self.get_daq_config() cfg = self.get_daq_config()
self.set_daq_config(cfg) self.set_daq_config(cfg)
sleep(restarttime)
def state(self) -> str | None: def state(self) -> str | None:
""" Querry the current system state""" """ Querry the current system state"""
@@ -458,45 +449,6 @@ class StdDaqClient(PSIDeviceBase):
except ConnectionRefusedError: except ConnectionRefusedError:
raise raise
def surestop(self, timeout=5):
""" Stops a running acquisition
REST reconfiguration restarts with systemd and can corrupt currently written files.
"""
# Retries to steal connection from poller
for rr in range(5):
client = connect(self.ws_url.get())
msg = json.dumps({"command": "stop_all"})
client.send(msg)
reply = client.recv(timeout=1)
reply = json.loads(reply)
# logger.warning(reply)
# if r['status'] == 'success':
# break
sleep(0.5)
client = connect(self.ws_url.get())
msg = json.dumps({"command": "status"})
client.send(msg)
reply = client.recv(timeout=1)
reply = json.loads(reply)
print(reply['status'])
if reply['status'] in ['idle', 'stoped']:
logger.warning(f"[{self.name}] Stop-all command finished in {reply['status']}")
return
# If stop_all didn't stop, nuke the whole thing
logger.error(f"[{self.name}] Don't stop, make it rock!!!")
self.nuke()
sleep(timeout)
# Automatically connect to microXAS testbench if directly invoked # Automatically connect to microXAS testbench if directly invoked
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -102,6 +102,7 @@ class StdDaqPreviewMixin(CustomDetectorMixin):
self.parent.frame.put(header['frame'], force=True) self.parent.frame.put(header['frame'], force=True)
self.parent.image_shape.put(header['shape'], force=True) self.parent.image_shape.put(header['shape'], force=True)
self.parent.image.put(image, force=True) self.parent.image.put(image, force=True)
self.parent._last_image = image
self.parent._run_subs(sub_type=self.parent.SUB_MONITOR, value=image) self.parent._run_subs(sub_type=self.parent.SUB_MONITOR, value=image)
t_last = t_curr t_last = t_curr
logger.info( logger.info(
@@ -134,7 +135,7 @@ class StdDaqPreviewDetector(PSIDetectorBase):
cam_widget = gui.add_dock('cam_dock1').add_widget('BECFigure').image('daq_stream1') cam_widget = gui.add_dock('cam_dock1').add_widget('BECFigure').image('daq_stream1')
""" """
# Subscriptions for plotting image # Subscriptions for plotting image
USER_ACCESS = ["kickoff"] USER_ACCESS = ["kickoff", "get_last_image"]
SUB_MONITOR = "device_monitor_2d" SUB_MONITOR = "device_monitor_2d"
_default_sub = SUB_MONITOR _default_sub = SUB_MONITOR
@@ -147,7 +148,8 @@ class StdDaqPreviewDetector(PSIDetectorBase):
frame = Component(Signal, kind=Kind.hinted) frame = Component(Signal, kind=Kind.hinted)
image_shape = Component(Signal, kind=Kind.normal) image_shape = Component(Signal, kind=Kind.normal)
# FIXME: The BEC client caches the read()s from the last 50 scans # FIXME: The BEC client caches the read()s from the last 50 scans
image = Component(Signal, kind=Kind.config) image = Component(Signal, kind=Kind.omitted)
_last_image = None
def __init__( def __init__(
self, *args, url: str = "tcp://129.129.95.38:20000", parent: Device = None, **kwargs self, *args, url: str = "tcp://129.129.95.38:20000", parent: Device = None, **kwargs
@@ -180,6 +182,9 @@ class StdDaqPreviewDetector(PSIDetectorBase):
sleep(1) sleep(1)
self._socket.connect(self.url.get()) self._socket.connect(self.url.get())
def get_image(self):
return self._last_image
def kickoff(self) -> DeviceStatus: def kickoff(self) -> DeviceStatus:
""" The DAQ was not meant to be toggled""" """ The DAQ was not meant to be toggled"""
return DeviceStatus(self, done=True, success=True, settle_time=0.1) return DeviceStatus(self, done=True, success=True, settle_time=0.1)

View File

@@ -14,8 +14,8 @@
def dev_disable_all(): def dev_disable_all():
"""Disable all devices """ """Disable all devices """
for d in dev: for k in dev:
d.enabled = False dev[k].enabled = False
@@ -41,12 +41,12 @@ def anotherstepscan(
-------- --------
demostepscan(scan_start=-32, scan_end=148, steps=180, exp_time=0.005, exp_burst=5) demostepscan(scan_start=-32, scan_end=148, steps=180, exp_time=0.005, exp_burst=5)
""" """
if not bl_check_beam(): # if not bl_check_beam():
raise RuntimeError("Beamline is not in ready state") # raise RuntimeError("Beamline is not in ready state")
dev.es1_tasks.enabled = False dev_disable_all()
dev.es1_psod.enabled = False dev.es1_roty.enabled = True
dev.es1_ddaq.enabled = True #dev.es1_ddaq.enabled = True
dev.gfcam.enabled = True dev.gfcam.enabled = True
dev.gfdaq.enabled = True dev.gfdaq.enabled = True
dev.daq_stream0.enabled = True dev.daq_stream0.enabled = True