diff --git a/doc/supported-devices.rst b/doc/supported-devices.rst index ee390fe8..4ebc1f2d 100644 --- a/doc/supported-devices.rst +++ b/doc/supported-devices.rst @@ -32,4 +32,5 @@ Lasers - Cobolt (:class:`microscope.lasers.cobolt`) - Coherent Sapphire (:class:`microscope.lasers.saphhire`) +- Coherent OBIS (:class:`microscope.lasers.obis`) - Omicron Deepstar (:class:`microscope.lasers.deepstar`) diff --git a/microscope/cameras/SDK3.py b/microscope/cameras/SDK3.py index 89753eea..eeaafe90 100644 --- a/microscope/cameras/SDK3.py +++ b/microscope/cameras/SDK3.py @@ -202,11 +202,11 @@ def __call__(self, *args): ars.append(ar) ret.append(r) #print r, r._type_ - + #print ars res = self.f(*ars) #print res - + if not res == AT_SUCCESS: if res == AT_ERR_TIMEDOUT or res == AT_ERR_NODATA: #handle timeouts as a special case, as we expect to get them diff --git a/microscope/cameras/SDK3Cam.py b/microscope/cameras/SDK3Cam.py index e2f97764..1b58ac2c 100644 --- a/microscope/cameras/SDK3Cam.py +++ b/microscope/cameras/SDK3Cam.py @@ -157,14 +157,14 @@ def connectProperties(self): for name, var in self.__dict__.items(): if isinstance(var, ATProperty): var.connect(self.handle, name) - - + + def shutdown(self): SDK3.Close(self.handle) #camReg.unregCamera() - - - - + + + + diff --git a/microscope/cameras/andorsdk3.py b/microscope/cameras/andorsdk3.py index dad1d120..bb4cb2e9 100644 --- a/microscope/cameras/andorsdk3.py +++ b/microscope/cameras/andorsdk3.py @@ -27,7 +27,6 @@ import time from six.moves import queue - from .SDK3Cam import * # SDK data pointer type @@ -103,8 +102,6 @@ def __init__(self, *args, **kwargs): SDK3.InitialiseLibrary() self._index = kwargs.get('index', 0) self.handle = None - #self._sdk3cam = SDK3Camera(self._index) - #SDK3Camera.__init__(self, self._index) self.add_setting('use_callback', 'bool', lambda: self._using_callback, self._enable_callback, diff --git a/microscope/lasers/deepstar.py b/microscope/lasers/deepstar.py index e904f744..92f9ced2 100644 --- a/microscope/lasers/deepstar.py +++ b/microscope/lasers/deepstar.py @@ -18,6 +18,7 @@ # along with this program. If not, see . import serial +import time import Pyro4 @@ -45,6 +46,7 @@ def _write(self, command): # towards the byte limit, hence 14 (16-2) command = command.ljust(14) + b'\r\n' response = self.connection.write(command) + time.sleep(.2) return response @@ -119,11 +121,11 @@ def _set_power(self, level): if (level > 1.0) : return self._logger.info("level=%d", level) - power=int (level*0xFFF) + power = int(level*0xFFF) self._logger.info("power=%d", power) strPower = "PP%03X" % power self._logger.info("power level=%s", strPower) - self._write(six.b(strPower)) + self._write(strPower.encode()) response = self._readline() self._logger.info("Power response [%s]", response.decode()) return response diff --git a/microscope/lasers/obis.py b/microscope/lasers/obis.py new file mode 100644 index 00000000..97a9c0ac --- /dev/null +++ b/microscope/lasers/obis.py @@ -0,0 +1,196 @@ +#!/usr/bin/python +# -*- coding: utf-8 +# +# Copyright 2016 Mick Phillips (mick.phillips@gmail.com) +# Copyright 2018 David Pinto +# Copyright 2018 Julio Mateos Langerak +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import serial + +import Pyro4 + +from microscope import devices + + +@Pyro4.expose +class ObisLaser(devices.SerialDeviceMixIn, devices.LaserDevice): + def __init__(self, com, baud, timeout, *args, **kwargs): + super(ObisLaser, self).__init__(*args, **kwargs) + self.connection = serial.Serial(port=com, + baudrate=baud, + timeout=timeout, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + parity=serial.PARITY_NONE) + # Start a logger. + self._write(b'SYSTem:INFormation:MODel?') + response = self._readline() + self._logger.info('OBIS laser model: [%s]' % response.decode()) + self._write(b'SYSTem:INFormation:SNUMber?') + response = self._readline() + self._logger.info('OBIS laser serial number: [%s]' % response.decode()) + self._write(b'SYSTem:CDRH?') + response = self._readline() + self._logger.info('CDRH safety: [%s]' % response.decode()) + self._write(b'SOURce:TEMPerature:APRobe?') + response = self._readline() + self._logger.info('TEC temperature control: [%s]' % response.decode()) + self._write(b'*TST?') + response = self._readline() + self._logger.info('Self test procedure: [%s]' % response.decode()) + + # We need to ensure that autostart is disabled so that we can switch emission + # on/off remotely. + self._write(b'SYSTem:AUTostart?') + response = self._readline() + self._logger.info('Response to Autostart: [%s]' % response.decode()) + + def _write(self, command): + """Send a command.""" + response = self.connection.write(command + b'\r\n') + return response + + def _readline(self): + """Read a line from connection without leading and trailing whitespace. + We override from serialDeviceMixIn + """ + response = self.connection.readline().strip() + if self.connection.readline().strip() != b'OK': + raise Exception('Did not get a proper answer from the laser serial comm.') + return response + + def _flush_handshake(self): + self.connection.readline() + + @devices.SerialDeviceMixIn.lock_comms + def get_status(self): + result = [] + for cmd, stat in [(b'SOURce:AM:STATe?', 'Emission on?'), + (b'SOURce:POWer:LEVel:IMMediate:AMPLitude?', 'Target power:'), + (b'SOURce:POWer:LEVel?', 'Measured power:'), + (b'SYSTem:STATus?', 'Status code?'), + (b'SYSTem:FAULt?', 'Fault code?'), + (b'SYSTem:HOURs?', 'Head operating hours:')]: + self._write(cmd) + result.append(stat + ' ' + self._readline().decode()) + return result + + @devices.SerialDeviceMixIn.lock_comms + def enable(self): + """Turn the laser ON. Return True if we succeeded, False otherwise.""" + self._logger.info('Turning laser ON.') + # Exiting Sleep Mode. + self._write(b'SOURce:TEMPerature:APRobe ON') + self._flush_handshake() + # Turn on emission. + self._write(b'SOURce:AM:STATe ON') + self._flush_handshake() + self._write(b'SOURce:AM:STATe?') + response = self._readline() + self._logger.info("SOURce:AM:STATe? [%s]" % response.decode()) + + if not self.get_is_on(): + # Something went wrong. + self._logger.error("Failed to turn ON. Current status:\r\n") + self._logger.error(self.get_status()) + return False + return True + + def _on_shutdown(self): + self.disable() + # We set the power to a safe level + self._set_power_mw(2) + # We want it back into direct control mode. + self._write(b'SOURce:AM:INTernal CWP') + self._flush_handshake() + + # Going into Sleep mode + self._write(b'SOURce:TEMPerature:APRobe OFF') + self._flush_handshake() + + + def initialize(self): + """Initialization to do when cockpit connects.""" + # self.flush_buffer() + # We ensure that handshaking is off. + self._write(b'SYSTem:COMMunicate:HANDshaking ON') + self._flush_handshake() + # We don't want 'direct control' mode. + # TODO: Change to MIXED when analogue output is available + self._write(b'SOURce:AM:EXTernal DIGital') + self._flush_handshake() + + @devices.SerialDeviceMixIn.lock_comms + def disable(self): + """Turn the laser OFF. Return True if we succeeded, False otherwise.""" + self._logger.info('Turning laser OFF.') + # Turning LASER OFF + self._write(b'SOURce:AM:STATe OFF') + self._flush_handshake() + + if self.get_is_on(): + # Something went wrong. + self._logger.error("Failed to turn OFF. Current status:\r\n") + self._logger.error(self.get_status()) + return False + return True + + @devices.SerialDeviceMixIn.lock_comms + def isAlive(self): + return self.get_is_on + + @devices.SerialDeviceMixIn.lock_comms + def get_is_on(self): + """Return True if the laser is currently able to produce light.""" + self._write(b'SOURce:AM:STATe?') + response = self._readline() + self._logger.info("Are we on? [%s]", response.decode()) + return response == b'ON' + + @devices.SerialDeviceMixIn.lock_comms + def _set_power(self, power_w): + """Sets the power level in Watts""" + if power_w > (self.get_max_power_mw() / 1000): + return + self._logger.info("Setting laser power to %.7sW", power_w) + self._write(b'SOURce:POWer:LEVel:IMMediate:AMPLitude %.5f' % power_w) + self._flush_handshake() + curr_power = self._get_power() + self._logger.info("Power response [%s]", curr_power) + return curr_power + + @devices.SerialDeviceMixIn.lock_comms + def get_max_power_mw(self): + """Gets the maximum laser power in mW.""" + self._write(b'SYSTem:INFormation:POWer?') + power_w = self._readline() + return int(float(power_w.decode()) * 1000) + + @devices.SerialDeviceMixIn.lock_comms + def _get_power(self): + if not self.get_is_on(): + # Laser is not on. + return 0 + self._write(b'SOURce:POWer:LEVel?') + response = self._readline() + return float(response.decode()) + + def get_power_mw(self): + return 1000 * self._get_power() + + def _set_power_mw(self, mw): + mw = min(mw, self.get_max_power_mw()) + return self._set_power(mw / 1000)