Newer
Older
# -*- coding: utf-8 -*-
#
# This file is part of the bliss project
#
# Copyright (c) 2015-2019 Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.
import logging
import errno
import os
from bliss.scanning.chain import AcquisitionDevice, AcquisitionMaster
from bliss.common.event import connect, disconnect
Matias Guijarro
committed
class _EventReceiver(object):
def __init__(self, device, parent_entry, callback):
self.device = device
Matias Guijarro
committed
self.parent_entry = parent_entry
self.callback = callback
Matias Guijarro
committed
def __call__(self, event_dict=None, signal=None, sender=None):
if callable(self.callback):
self.callback(self.parent_entry, event_dict, signal, sender)
for signal in ("start", "end"):
connect(self.device, signal, self)
for channel in self.device.channels:
def disconnect(self):
if self.device is None:
return
for signal in ("start", "end"):
disconnect(self.device, signal, self)
for channel in self.device.channels:
disconnect(self.device, "new_data", self)
class FileWriter(object):
def __init__(
self,
root_path,
images_root_path,
bliss administrator
committed
data_filename,
master_event_callback=None,
device_event_callback=None,
Matias Guijarro
committed
**keys,
""" A default way to organize file structure
"""
Matias Guijarro
committed
self._save_images = True
self._root_path_template = root_path
bliss administrator
committed
self._data_filename_template = data_filename
self._template_dict = {}
self._images_root_path_template = images_root_path
Matias Guijarro
committed
self._master_event_callback = master_event_callback
self._device_event_callback = device_event_callback
self._event_receivers = list()
Matias Guijarro
committed
self.log = logging.getLogger(type(self).__name__)
@property
def template(self):
return self._template_dict
Matias Guijarro
committed
@property
def root_path(self):
return self._root_path_template.format(**self._template_dict)
Matias Guijarro
committed
bliss administrator
committed
@property
def data_filename(self):
return self._data_filename_template.format(**self._template_dict)
@property
def filename(self):
raise NotImplementedError
bliss administrator
committed
Matias Guijarro
committed
def create_path(self, full_path):
try:
os.makedirs(full_path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(full_path):
pass
else:
raise
def new_file(self, scan_name, scan_info):
Matias Guijarro
committed
"""Create a new scan file
Matias Guijarro
committed
Filename is stored in the class as the 'filename' property
"""
raise NotImplementedError
Matias Guijarro
committed
def new_scan(self, scan_name, scan_info):
raise NotImplementedError
def new_master(self, master, scan_entry):
return scan_entry
Matias Guijarro
committed
def prepare_saving(self, device, images_path):
Matias Guijarro
committed
any_image = any(
channel.reference and len(channel.shape) == 2 for channel in device.channels
)
Matias Guijarro
committed
if any_image and self._save_images:
directory = os.path.dirname(images_path)
prefix = os.path.basename(images_path)
self.create_path(directory)
device.set_image_saving(directory, prefix)
else:
device.set_image_saving(None, None, force_no_saving=True)
Matias Guijarro
committed
Matias Guijarro
committed
def _prepare_callbacks(self, device, master_entry, callback):
ev_receiver = _EventReceiver(device, master_entry, callback)
ev_receiver.connect()
Matias Guijarro
committed
self._event_receivers.append(ev_receiver)
def _remove_callbacks(self):
for ev_receiver in self._event_receivers:
ev_receiver.disconnect()
self._event_receivers = []
Matias Guijarro
committed
def prepare(self, scan):
Matias Guijarro
committed
self.create_path(self.root_path)
self.new_file(scan.node.name, scan.scan_info)
scan_entry = self.new_scan(scan.node.name, scan.scan_info)
Matias Guijarro
committed
self._event_receivers = []
scan_counter = itertools.count()
if isinstance(dev, AcquisitionMaster):
if dev.parent is None:
# top-level master
scan_index = next(scan_counter)
if scan_index > 0:
# multiple top-level masters: create a new scan with sub-scan
# convention: scan number will get a .1, .2, etc suffix
scan_number, scan_name = scan.node.name.split("_", maxsplit=1)
subscan_name = f"{scan_number}{'.%d_' % scan_index}{scan_name}"
scan_entry = self.new_scan(subscan_name, scan.scan_info)
Matias Guijarro
committed
master_entry = self.new_master(dev, scan_entry)
Matias Guijarro
committed
self._prepare_callbacks(dev, master_entry, self._master_event_callback)
images_path = self._images_root_path_template.format(
scan_name=scan.name,
img_acq_device=dev.name,
scan_number=scan.scan_number,
Matias Guijarro
committed
self.prepare_saving(dev, images_path)
for slave in dev.slaves:
if isinstance(slave, AcquisitionDevice) and callable(
self._device_event_callback
):
self._prepare_callbacks(
slave, master_entry, self._device_event_callback
)
def close(self):
def get_scan_entries(self):
"""
Should return all scan entries from this path
"""
return []