From a15a07d6a76238482e7f384beb778e74deb94e7e Mon Sep 17 00:00:00 2001 From: Alrik Date: Wed, 17 Jun 2020 19:11:36 +0200 Subject: [PATCH 1/6] added thorlabs flipper --- hardware/switches/thorlabs_flipper.py | 129 ++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 hardware/switches/thorlabs_flipper.py diff --git a/hardware/switches/thorlabs_flipper.py b/hardware/switches/thorlabs_flipper.py new file mode 100644 index 0000000000..a7d88c277b --- /dev/null +++ b/hardware/switches/thorlabs_flipper.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +""" +Qudi 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. + +Qudi 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 Qudi. If not, see . + +Copyright (c) the Qudi Developers. See the COPYRIGHT.txt file at the +top-level directory of this distribution and at +""" + +import os +import ctypes as ct +from core.module import Base +from core.configoption import ConfigOption +from interface.switch_interface import SwitchInterface + + +class Main(Base, SwitchInterface): + """ This class is implements communication with Thorlabs MFF101 flipper via Kinesis dll + + Example config for copy-paste: + + flipper: + module.Class: 'switches.thorlabs_flipper.Main' + dll_folder: 'C:\Program Files\Thorlabs\Kinesis' + serial_numbers: [000000123] + + Description of the hardware provided by Thorlabs: + These Two-Position, High-Speed Flip Mounts flip lenses, filters, and other optical components into and out of a + free-space beam. + """ + dll_folder = ConfigOption('dll_folder', default=r'C:\Program Files\Thorlabs\Kinesis') + dll_file = ConfigOption('dll_ffile', default='Thorlabs.MotionControl.FilterFlipper.dll') + serial_numbers = ConfigOption('serial_numbers', missing='error') + polling_rate_ms = ConfigOption('polling_rate_ms', default=200) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._dll = None + self._codes = None + self._serial_numbers = None + + def on_activate(self): + """ Module activation method """ + os.chdir(self.dll_folder) + self._dll = ct.cdll.LoadLibrary(self.dll_file) + self._dll.TLI_BuildDeviceList() + + self._serial_numbers = [] + for serial_number in self.serial_numbers: + serial_number = ct.c_char_p(str(serial_number).encode('utf-8')) + self._dll.FF_Open(serial_number) + self._dll.FF_StartPolling(serial_number, ct.c_int(200)) + self._serial_numbers.append(serial_number) + + def on_deactivate(self): + """ Disconnect from hardware on deactivation. """ + for serial_number in self._serial_numbers: + self._dll.FF_ClearMessageQueue(serial_number) + self._dll.FF_StopPolling(serial_number) + self._dll.FF_Close(serial_number) + + def getNumberOfSwitches(self): + """ Gives the number of switches connected to this hardware. + + @return int: number of swiches on this hardware + """ + return len(self._serial_numbers) + + def getSwitchState(self, switchNumber): + """ Get the state of the switch. + + @param int switchNumber: index of switch + + @return bool: True if 2, False if 1 (homed) + """ + return self._dll.FF_GetPosition(self._serial_numbers[switchNumber]) == 2 + + def getCalibration(self, switchNumber, state): + """ Get calibration parameter for switch. + + Function not used by this module + """ + return 0 + + def setCalibration(self, switchNumber, state, value): + """ Set calibration parameter for switch. + + Function not used by this module + """ + return True + + def switchOn(self, switchNumber): + """ Set the state to on (channel 1) + + @param (int) switchNumber: number of switch to be switched + + @return (bool): True if succeeds, False otherwise + """ + self._dll.FF_MoveToPosition(self._serial_numbers[switchNumber], 2) + return True + + def switchOff(self, switchNumber): + """ Set the state to off (channel 2) + + @param (int) switchNumber: number of switch to be switched + + @return (bool): True if suceeds, False otherwise + """ + self._dll.FF_MoveToPosition(self._serial_numbers[switchNumber], 1) + return True + + def getSwitchTime(self, switchNumber): + """ Give switching time for switch. + + @param int switchNumber: number of switch + + @return float: time needed for switch state change + """ + return 500e-3 # max. 1 ms; typ. 0.5 ms From 3ea366dea46a33c42e4fe76d56ea7f5e41a3923f Mon Sep 17 00:00:00 2001 From: Alrik Date: Wed, 17 Jun 2020 19:21:17 +0200 Subject: [PATCH 2/6] updated the changelog --- documentation/changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/changelog.md b/documentation/changelog.md index e918907d5b..ab8dd89d00 100644 --- a/documentation/changelog.md +++ b/documentation/changelog.md @@ -77,6 +77,7 @@ please use _ni_x_series_in_streamer.py_ as hardware module. * Set proper minimum wavelength value in constraints of Tektronix AWG7k series HW module * Added a hardware file for fibered optical switch Thorlabs OSW12/22 via SwitchInterface * Fixed bug affecting interface overloading of Qudi modules +* Added a hardware file to interface Thorlabs MFF101 flipper via SwitchInterface * From d1cc6615949a1bb4ac17d1fbfc3c3e79ea38fd0c Mon Sep 17 00:00:00 2001 From: Alrik Date: Thu, 18 Jun 2020 18:56:49 +0200 Subject: [PATCH 3/6] fixed bug --- hardware/switches/thorlabs_flipper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/switches/thorlabs_flipper.py b/hardware/switches/thorlabs_flipper.py index a7d88c277b..4a806d5a7c 100644 --- a/hardware/switches/thorlabs_flipper.py +++ b/hardware/switches/thorlabs_flipper.py @@ -51,7 +51,7 @@ def __init__(self, *args, **kwargs): def on_activate(self): """ Module activation method """ - os.chdir(self.dll_folder) + os.environ['PATH'] = str(self.dll_folder) + os.pathsep + os.environ['PATH'] self._dll = ct.cdll.LoadLibrary(self.dll_file) self._dll.TLI_BuildDeviceList() From aa9b858dd2b45d60ec09caf201a60915a4e83152 Mon Sep 17 00:00:00 2001 From: Alrik Date: Thu, 18 Jun 2020 19:07:41 +0200 Subject: [PATCH 4/6] fixed doc --- hardware/switches/thorlabs_flipper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hardware/switches/thorlabs_flipper.py b/hardware/switches/thorlabs_flipper.py index 4a806d5a7c..8d1ac0d4b4 100644 --- a/hardware/switches/thorlabs_flipper.py +++ b/hardware/switches/thorlabs_flipper.py @@ -51,7 +51,7 @@ def __init__(self, *args, **kwargs): def on_activate(self): """ Module activation method """ - os.environ['PATH'] = str(self.dll_folder) + os.pathsep + os.environ['PATH'] + os.environ['PATH'] = str(self.dll_folder) + os.pathsep + os.environ['PATH'] # needed otherwise dll don't load self._dll = ct.cdll.LoadLibrary(self.dll_file) self._dll.TLI_BuildDeviceList() @@ -126,4 +126,4 @@ def getSwitchTime(self, switchNumber): @return float: time needed for switch state change """ - return 500e-3 # max. 1 ms; typ. 0.5 ms + return 500e-3 From 86b848011e63fa9181c1d0bb173500a9f5678d49 Mon Sep 17 00:00:00 2001 From: Alrik Date: Mon, 22 Jun 2020 16:01:23 +0200 Subject: [PATCH 5/6] added invert config to thorlabs_flipper --- hardware/switches/thorlabs_flipper.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hardware/switches/thorlabs_flipper.py b/hardware/switches/thorlabs_flipper.py index 8d1ac0d4b4..50c508a84b 100644 --- a/hardware/switches/thorlabs_flipper.py +++ b/hardware/switches/thorlabs_flipper.py @@ -19,6 +19,7 @@ import os import ctypes as ct +from operator import xor from core.module import Base from core.configoption import ConfigOption from interface.switch_interface import SwitchInterface @@ -42,6 +43,7 @@ class Main(Base, SwitchInterface): dll_file = ConfigOption('dll_ffile', default='Thorlabs.MotionControl.FilterFlipper.dll') serial_numbers = ConfigOption('serial_numbers', missing='error') polling_rate_ms = ConfigOption('polling_rate_ms', default=200) + invert_axis = ConfigOption('invert_axis', default=[False]) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -76,6 +78,10 @@ def getNumberOfSwitches(self): """ return len(self._serial_numbers) + def _is_inverted(self, axis=0): + """ Helper function to get if axis is inverted in config """ + return self.invert_axis is not None and len(self.invert_axis) > axis and bool(self.invert_axis[axis]) + def getSwitchState(self, switchNumber): """ Get the state of the switch. @@ -83,7 +89,8 @@ def getSwitchState(self, switchNumber): @return bool: True if 2, False if 1 (homed) """ - return self._dll.FF_GetPosition(self._serial_numbers[switchNumber]) == 2 + state = self._dll.FF_GetPosition(self._serial_numbers[switchNumber]) == 2 + return xor(state, self._is_inverted(switchNumber)) def getCalibration(self, switchNumber, state): """ Get calibration parameter for switch. @@ -106,7 +113,8 @@ def switchOn(self, switchNumber): @return (bool): True if succeeds, False otherwise """ - self._dll.FF_MoveToPosition(self._serial_numbers[switchNumber], 2) + setpoint = 2 if not self._is_inverted() else 1 + self._dll.FF_MoveToPosition(self._serial_numbers[switchNumber], setpoint) return True def switchOff(self, switchNumber): @@ -116,7 +124,8 @@ def switchOff(self, switchNumber): @return (bool): True if suceeds, False otherwise """ - self._dll.FF_MoveToPosition(self._serial_numbers[switchNumber], 1) + setpoint = 1 if not self._is_inverted() else 2 + self._dll.FF_MoveToPosition(self._serial_numbers[switchNumber], setpoint) return True def getSwitchTime(self, switchNumber): From 3f527c4ab9a600eca0b45bed5460d0e475ddcf60 Mon Sep 17 00:00:00 2001 From: Alrik Date: Fri, 18 Dec 2020 17:10:06 +0100 Subject: [PATCH 6/6] updated thorlabs flipper --- hardware/switches/thorlabs_flipper.py | 120 ++++++++++---------------- 1 file changed, 44 insertions(+), 76 deletions(-) diff --git a/hardware/switches/thorlabs_flipper.py b/hardware/switches/thorlabs_flipper.py index 50c508a84b..b7d5fbc97b 100644 --- a/hardware/switches/thorlabs_flipper.py +++ b/hardware/switches/thorlabs_flipper.py @@ -43,7 +43,6 @@ class Main(Base, SwitchInterface): dll_file = ConfigOption('dll_ffile', default='Thorlabs.MotionControl.FilterFlipper.dll') serial_numbers = ConfigOption('serial_numbers', missing='error') polling_rate_ms = ConfigOption('polling_rate_ms', default=200) - invert_axis = ConfigOption('invert_axis', default=[False]) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -57,82 +56,51 @@ def on_activate(self): self._dll = ct.cdll.LoadLibrary(self.dll_file) self._dll.TLI_BuildDeviceList() - self._serial_numbers = [] + self._serial_numbers = {} for serial_number in self.serial_numbers: - serial_number = ct.c_char_p(str(serial_number).encode('utf-8')) - self._dll.FF_Open(serial_number) - self._dll.FF_StartPolling(serial_number, ct.c_int(200)) - self._serial_numbers.append(serial_number) + serial_number_c = ct.c_char_p(str(serial_number).encode('utf-8')) + self._dll.FF_Open(serial_number_c) + self._dll.FF_StartPolling(serial_number_c, ct.c_int(200)) + self._serial_numbers[serial_number] = serial_number_c def on_deactivate(self): """ Disconnect from hardware on deactivation. """ - for serial_number in self._serial_numbers: - self._dll.FF_ClearMessageQueue(serial_number) - self._dll.FF_StopPolling(serial_number) - self._dll.FF_Close(serial_number) - - def getNumberOfSwitches(self): - """ Gives the number of switches connected to this hardware. - - @return int: number of swiches on this hardware - """ - return len(self._serial_numbers) - - def _is_inverted(self, axis=0): - """ Helper function to get if axis is inverted in config """ - return self.invert_axis is not None and len(self.invert_axis) > axis and bool(self.invert_axis[axis]) - - def getSwitchState(self, switchNumber): - """ Get the state of the switch. - - @param int switchNumber: index of switch - - @return bool: True if 2, False if 1 (homed) - """ - state = self._dll.FF_GetPosition(self._serial_numbers[switchNumber]) == 2 - return xor(state, self._is_inverted(switchNumber)) - - def getCalibration(self, switchNumber, state): - """ Get calibration parameter for switch. - - Function not used by this module - """ - return 0 - - def setCalibration(self, switchNumber, state, value): - """ Set calibration parameter for switch. - - Function not used by this module - """ - return True - - def switchOn(self, switchNumber): - """ Set the state to on (channel 1) - - @param (int) switchNumber: number of switch to be switched - - @return (bool): True if succeeds, False otherwise - """ - setpoint = 2 if not self._is_inverted() else 1 - self._dll.FF_MoveToPosition(self._serial_numbers[switchNumber], setpoint) - return True - - def switchOff(self, switchNumber): - """ Set the state to off (channel 2) - - @param (int) switchNumber: number of switch to be switched - - @return (bool): True if suceeds, False otherwise - """ - setpoint = 1 if not self._is_inverted() else 2 - self._dll.FF_MoveToPosition(self._serial_numbers[switchNumber], setpoint) - return True - - def getSwitchTime(self, switchNumber): - """ Give switching time for switch. - - @param int switchNumber: number of switch - - @return float: time needed for switch state change - """ - return 500e-3 + for serial_number in self.serial_numbers: + serial_number_c = self._serial_numbers[serial_number] + self._dll.FF_ClearMessageQueue(serial_number_c) + self._dll.FF_StopPolling(serial_number_c) + self._dll.FF_Close(serial_number_c) + + @property + def name(self): + """ Name of the hardware as string. """ + return 'Thorlabs flippers' + + @property + def available_states(self): + """ Names of the states as a dict of tuples.""" + states = {} + for serial in self.serial_numbers: + states[str(serial)] = ('1', '2') + return states + + def get_state(self, switch): + """ Get the state of the switch.""" + return str(self._dll.FF_GetPosition(self._serial_numbers[int(switch)])) + + def set_state(self, switch, state): + """ Query state of single switch by name """ + self._dll.FF_MoveToPosition(self._serial_numbers[int(switch)], int(state)) + + # Bellow is a just a copy paste to prevent a conflict between interface overloading and non abstract methods + @property + def states(self): + """ The setter for the states of the hardware. """ + return {switch: self.get_state(switch) for switch in self.available_states} + + @states.setter + def states(self, state_dict): + """ The setter for the states of the hardware.""" + assert isinstance(state_dict), 'Parameter "state_dict" must be dict type' + for switch, state in state_dict.items(): + self.set_state(switch, state) \ No newline at end of file