"""
Establishes a connection to the ``DATARAYOCX`` library developed by DataRay Inc.
"""
from __future__ import annotations
import os
import numpy as np
from msl.loadlib import Client64
from msl.loadlib import ConnectionTimeoutError
from msl.loadlib import Server32Error
from msl.equipment.connection import Connection
from msl.equipment.exceptions import DataRayError
from msl.equipment.resources import register
[docs]
@register(manufacturer=r'Data\s*Ray', model=r'.')
class DataRayOCX64(Connection):
def __init__(self, record):
"""A wrapper around the :class:`~.datarayocx_32.DataRayOCX32` class.
This class can be used with either a 32- or 64-bit Python interpreter
to call the 32-bit functions in the ``DATARAYOCX`` library. A GUI is
created to configure and visualize the images taken by the camera.
Tested with the WinCamD-LCM-8.0D36 software version.
The :attr:`~msl.equipment.record_types.ConnectionRecord.properties`
for a DataRay connection supports the following key-value pairs in the
:ref:`connections-database`::
'area_filter': int, area filter: 1=1pixel, 2=3pixels, 3=5pixels, 4=7pixels, 5=9pixels [default: 1]
'camera_index': int, the camera to use (between 0 and 7; 0=first camera found) [default: 0]
'centroid_method': int, the centroid method to use (0, 1 or 2) [default: 0]
'filter': float, percent full scale filter (0, 0.1, 0.2, 0.5, 1, 2, 5 or 10) [default: 0.2]
'major_minor_method': int, the major/minor method to use (0, 1 or 2) [default: 0]
'ui_size': int, the size of the User Interface (value=height of a button in pixels) [default: 25]
Do not instantiate this class directly. Use the :meth:`~.EquipmentRecord.connect`
method to connect to the equipment.
Parameters
----------
record : :class:`~.EquipmentRecord`
A record from an :ref:`equipment-database`.
"""
super(DataRayOCX64, self).__init__(record)
self.set_exception_class(DataRayError)
self._client = None
error = None
try:
self._client = Client64(
os.path.join(os.path.dirname(__file__), 'datarayocx_32.py'),
prog_id_prefix=record.connection.address[5:],
**record.connection.properties
)
except ConnectionTimeoutError as err:
error = err.reason
# check for errors instantiating the DataRayOCX32 class
if error:
self.raise_exception(
'Cannot connect to the DataRay Beam Profiler.\n{}'.format(error)
)
# check for errors starting the camera
error = self._client.request32('error')
if error:
self.disconnect()
self.raise_exception('Error initializing the DataRay OCX library\n{}'.format(error))
self.log_debug('Connected to %s', record.connection)
[docs]
def capture(self, timeout=10, restart=False):
"""Capture an image.
Parameters
----------
timeout : :class:`float`, optional
The maximum number of seconds to wait to capture an image.
restart : :class:`bool`, optional
Whether to keep the camera running after the image is captured.
Returns
-------
:class:`dict`
The information about the captured image. The key-value pairs are:
* ``adc_peak_%``: :class:`float`, the maximum ADC value (as a percentage)
* ``area_filter``: :class:`int`, area filtering applies a convolution to the pixels (1=1pixel, 2=3pixels, 3=5pixels, 4=7pixels, 5=9pixels)
* ``centroid``: :class:`tuple`, the (x, y) centroid value
* ``centroid_filter_size``: :class:`int`, centroid filter size (in pixels)
* ``centroid_type``: :class:`int`, the centroid type (0, 1 or 2)
* ``clip_level_centroid``: :class:`float`, the centroid clip level (between 0 and 1)
* ``clip_level_geo``: :class:`float`, the geometric clip level (between 0 and 1)
* ``crosshair``: :class:`float`, the angle between the horizontal x-axis and the solid crosshair line (in degrees)
* ``eff_2w``: :class:`float`, the effective beam size (in um)
* ``effective_centroid``: :class:`tuple`, the effective (x, y) centroid value
* ``effective_geo_centroid``: :class:`tuple`, the effective (x, y) geometric centroid value
* ``ellip``: :class:`float`, the ratio between the minor/major axis
* ``elp``: :class:`float`, the beam azimutal angle for ISO 11146 (in degrees)
* ``exposure_time``: :class:`float`, the exposure time (in ms)
* ``filter_full_scale``: :class:`float`, used in the triangular weighting smoothing function
* ``image``: :class:`numpy.ndarray`, the camera image
* ``image_zoom``: :class:`float`, the zoom factor of the image
* ``imager_gain``: :class:`float`, the gain used by the imager
* ``inc_p``: :class:`float`, Dxx power (in Watts)
* ``inc_power_area``: :class:`float`, Dxx beam area (in mm^2)
* ``inc_power_major``: :class:`float`, Dxx beam diameter along the major axis (in mm)
* ``inc_power_mean``: :class:`float`, Dxx mean beam diameter (in mm)
* ``inc_power_minor``: :class:`float`, Dxx beam diameter along the minor axis (in mm)
* ``is_fast_update``: :class:`bool`, whether the camera is in fast update or normal mode
* ``is_full_resolution``: :class:`bool`, whether the camera is set to full resolution
* ``maj_iso``: :class:`float`, the ISO 11146 beam size along the major axis (in um)
* ``major``: :class:`float`, the beam size along the major axis (in um)
* ``major_minor_method``: :class:`int`, the major/minor method used (0, 1 or 2)
* ``mean``: :class:`float`, the mean beam size (in um)
* ``mean_theta``: :class:`float`, Dxx mean angle (in mrad)
* ``min_iso``: :class:`float`, the ISO 11146 beam size along the minor axis (in um)
* ``minor``: :class:`float`, the beam size along the minor axis (in um)
* ``num_averages``: :class:`int`, the number of averages per capture
* ``num_captures``: :class:`int`, the number of images that have been captured
* ``orient``: :class:`float`, the angle between the horizontal x-axis and the major or minor axis closest to the horizontal x-axis (in degrees)
* ``peak``: :class:`tuple`, the peak location in (x, y), usually zero
* ``pixel_width_um``: :class:`float`, the width of a pixel (in um)
* ``pixel_height_um``: :class:`float`, the height of a pixel (in um)
* ``pk_to_avg``: :class:`float`, the peak to average value
* ``plateau_uniformity``: :class:`float`, the flatness of the plateau (between 0 and 1)
* ``profile_x``: :class:`numpy.ndarray`, the profile in the X direction
* ``profile_y``: :class:`numpy.ndarray`, the profile in the Y direction
* ``roi``: :class:`tuple`, the selected region of interest (x, y, width, height)
* ``xc``: :class:`float`, the centroid position along the x axis (in um)
* ``xg``: :class:`float`, the geometric centroid position along the x axis (in um)
* ``xp``: :class:`float`, the peak-intensity centroid position along the x axis (in um)
* ``xu``: :class:`float`, the user-selected centroid position along the x axis (in um)
* ``yc``: :class:`float`, the centroid position along the y axis (in um)
* ``yg``: :class:`float`, the geometric centroid position along the y axis (in um)
* ``yp``: :class:`float`, the peak-intensity centroid position along the y axis (in um)
* ``yu``: :class:`float`, the user-selected centroid position along the y axis (in um)
Raises
------
~msl.equipment.exceptions.DataRayError
If not successful.
"""
self.log_debug('DataRayOCX64.capture(timeout=%s)', timeout)
info = {}
error = None
try:
info = self._client.request32('capture', timeout)
except Server32Error as err:
error = err # TODO avoid raising nested exceptions in Python 2.7
if error:
self.raise_exception(error)
shape = info.pop('shape')
if not info['is_full_resolution']:
shape = (shape[0]//2, shape[1]//2)
info['image'] = np.asarray(info['image']).reshape(shape)
info['profile_x'] = np.asarray(info['profile_x'])[:shape[1]]
info['profile_y'] = np.asarray(info['profile_y'])[:shape[0]]
if restart:
self.start()
return info
[docs]
def disconnect(self):
"""Disconnect from the camera."""
if not self._client:
return
try:
stdout, stderr = self._client.shutdown_server32()
stdout.close()
stderr.close()
except:
pass
self._client = None
self.log_debug('Disconnected from %s', self.equipment_record.connection)
[docs]
def start(self):
"""Start the camera."""
self.log_debug('DataRayOCX64.start()')
try:
return self._client.request32('start')
except Server32Error as err:
error = err # TODO avoid raising nested exceptions in Python 2.7
self.raise_exception(error)
[docs]
def stop(self):
"""Stop the camera."""
self.log_debug('DataRayOCX64.stop()')
try:
return self._client.request32('stop')
except Server32Error as err:
error = err # TODO avoid raising nested exceptions in Python 2.7
self.raise_exception(error)