From b371ae6f32b329f1a5e411bf4f4287514755af89 Mon Sep 17 00:00:00 2001 From: ibwharri Date: Mon, 26 Jul 2021 11:51:26 -0400 Subject: [PATCH 01/18] basic structure of kinesis drivers/stages --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 16 ++++++++++++++++ Modules/+Drivers/+Kinesis/Kinesis_invisble.m | 17 +++++++++++++++++ Modules/+Stages/NanoMax300.m | 2 ++ 3 files changed, 35 insertions(+) create mode 100644 Modules/+Drivers/+Kinesis/KinesisBSC203.m create mode 100644 Modules/+Drivers/+Kinesis/Kinesis_invisble.m create mode 100644 Modules/+Stages/NanoMax300.m diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m new file mode 100644 index 000000000..25abb7ddf --- /dev/null +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -0,0 +1,16 @@ +classdef KinesisBSC203 < Drivers.Kinesis.Kinesis_invisible & Modules.Driver + +methods + loaddlls + + connect + + disconnect % write out how to connect here + + home + + move + + GetPosition + +end \ No newline at end of file diff --git a/Modules/+Drivers/+Kinesis/Kinesis_invisble.m b/Modules/+Drivers/+Kinesis/Kinesis_invisble.m new file mode 100644 index 000000000..b56479af3 --- /dev/null +++ b/Modules/+Drivers/+Kinesis/Kinesis_invisble.m @@ -0,0 +1,17 @@ +classdef Kinesis_invisible + +methods(abstract) + + loaddlls(obj) + +end + +methods + connect(obj,serialNum) + + disconnect(obj) +end + +methods(Static) + getDevices() +end \ No newline at end of file diff --git a/Modules/+Stages/NanoMax300.m b/Modules/+Stages/NanoMax300.m new file mode 100644 index 000000000..4d8e80ce7 --- /dev/null +++ b/Modules/+Stages/NanoMax300.m @@ -0,0 +1,2 @@ +classdef NanoMax300 < Modules.Stage +end \ No newline at end of file From 82a7d62319af41d337329ccd24cd75bc73e83251 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Tue, 27 Jul 2021 16:09:36 +0100 Subject: [PATCH 02/18] Defining basic function for Kinesis .NET --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 223 ++++++++++++++++++- Modules/+Drivers/+Kinesis/Kinesis_invisble.m | 50 ++++- 2 files changed, 263 insertions(+), 10 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index 25abb7ddf..f9c832e63 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -1,16 +1,227 @@ classdef KinesisBSC203 < Drivers.Kinesis.Kinesis_invisible & Modules.Driver +properties(Constant, Hidden) + GENERICMOTORDLL='Thorlabs.MotionControl.GenericMotorCLI.dll'; + GENERICMOTORCLASSNAME='Thorlabs.MotionControl.GenericMotorCLI.GenericMotorCLI'; + STEPPERMOTORDLL='Thorlabs.MotionControl.Benchtop.StepperMotorCLI.dll'; + STEPPERMOTORCLASSNAME='Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor'; +end + +properties(SetAccess = private) + isconnected = false; % Flag set if device connected + serialnumbers; % Device serial numbers + controllername; % Controller Name + controllerdescription % Controller Description + stagename; % Stage Name + acceleration; % Acceleration + maxvelocity; % Maximum velocity limit + minvelocity; % Minimum velocity limit + positions; % Motor position (1 * 3 array) + + Homed; + IsMoving; +end + +properties(Constant) + Travel = [-2 2] * 1000; +end + +properties(Hidden) + deviceNET; % Device object within .NET + channelsNET; % Channel object within .NET (1 * 3 cell) + motorSettingsNET; % motorSettings within .NET (1 * 3 cell) + currentDeviceSettingsNET; % currentDeviceSetings within .NET (1 * 3 cell) + deviceInfoNET; % deviceInfo within .NET (1 * 3 cell) +end + +methods(Access=private) + % Constructor + function obj = KinesisBSC203(serialNum, name) % Instantiate the KinesisBSC203 motor object + KinesisBSC203.loaddlls; % Load DLLs if not already loaded + KinesisBSC203.connect(serialNum); % Connect device + end +end + methods - loaddlls + function obj = instance(serialNum,name) + mlock; + if nargin < 3 + name = serialNum; + end + persistent Objects + if isempty(Objects) + Objects = Drivers.Kinesis.KinesisBSC203.empty(1,0); + end + for i = 1:length(Objects) + if isvalid(Objects(i)) && isequal(serialNum,Objects(i).singleton_id) + obj = Objects(i); + return + end + end + obj = Drivers.Kinesis.KinesisBSC203(serialNum,name); + obj.singleton_id = serialNum; + Objects(end+1) = obj; + end + + + % + function connect(obj, serialNo) % serialNo := str + obj.GetDevices; % Call this to build device list if not already done + + if ~exist(obj.deviceNET) % Checking if a .NET BenchtopStepperMotor instance is already created + if str2double(serialNo(1:2)) == double(Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.DevicePrefix70) % Checking whether ther serial number corresponds to a BenchtopStepperMotor + obj.deviceNET = Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.CreateBenchtopStepperMotor(serialNo); % Create an instance of .NET BenchtopStepperMotor + else % Serial number prefix does not belong to Benchtop Stepper Motor + error('Serial Number is not Benchtop Stepper Motor.') + end + end + + for i = 1:3 + obj.channelsNET{i} = obj.deviceNET.GetChannel(i); % Get channel objects of the device + obj.channelsNET{i}.ClearDeviceExceptions(); % Clear device exceptions via .NET interface + end + + if ~obj.isconnected() % Connect and initialize device if not connected + obj.deviceNET.Connect(serialNo); % Connect to device via .NET interface + obj.initialize(serialNo) % Initialize the device + else % Device already connected + error('Device is already connected') + end + obj.updatestatus % Update status variables from device + end + + function initialize(obj, serialNo) % Initialize all three channels of the device, serialNo := str + for i = 1:3 + try + if ~obj.channelsNET{i}.IsSettingsInitialized() + obj.channelsNET{i}.WaitForSettingsInitialized(obj.TIMEOUTSETTINGS); % Initialize the ith channel + else + disp('Device Already Initialized.') + end + if ~obj.channelsNET{i}.IsSettingsInitialized() % Device not successfully initialized + error('Unable to initialize device') + end + obj.channelsNET{i}.StartPolling(obj.TPOLLING); % Start polling device via .NET interface + obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface + + % Initialize motor configuration + deviceID = obj.channelsNET{i}.DeviceID; + settingsLoadOption = Kinesis_invisible.GetSettingsLoadOption(serialNo, deviceID); + obj.motorSettingsNET{i} = obj.channelsNET{i}.GetMotorConfiguration(serialNo, settingsLoadOption); + + % Initialize current motor settings + obj.currentDeviceSettingsNET{i}=obj.channelsNET{i}.MotorDeviceSettings; + obj.deviceInfoNET{i} = obj.channelsNET{i}.GetDeviceInfo(); % Get deviceInfo via .NET interface + catch + error(['Unable to initialize channel ', num2str(i)]); + end + end + + end + + function updatestatus(obj) + obj.isconnected = obj.deviceNET.IsConnected(); % connection status + for i = 1:3 + obj.serialnumbers{i}=char(obj.channelsNET{i}.DeviceID); % update serial number + obj.controllername{i}=char(obj.deviceInfoNET{i}.Name); % update controleller name + obj.controllerdescription{i}=char(obj.deviceInfoNET{i}.Description); % update controller description + obj.stagename{i}=char(obj.motorSettingsNET{i}.DeviceSettingsName); % update stagename + velocityparams{i}=obj.channelsNET{i}.GetVelocityParams(); % update velocity parameter + obj.acceleration{i}=System.Decimal.ToDouble(velocityparams{i}.Acceleration); % update acceleration parameter + obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter + obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter + obj.position(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions + + + end + end + + function disconnect(obj) + obj.isconnected = obj.deviceNET.IsConnected(); % Read connection status + if obj.isconnected % Disconnect device if connected + try + for i = 1:3 + obj.channelsNET{i}.StopPolling(); % Stop polling device via .NET interface + obj.channelsNET{i}.DisableDevice(); % Disable device via .NET interface + end + obj.deviceNET.Disconnect(true) + catch + error(['Unable to disconnect device',obj.serialnumbers{i}]); + end + obj.isconnected = obj.deviceNET.IsConnected(); + else % Cannot disconnect because device not connected + error('Device not connected.') + end + end + + function home(obj) + for i = 1:3 + workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{i}.Home(workDone); % Home device via .NET interface + obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + end + obj.updatestatus; % Update status variables from device + end + + function tf = checkMove(obj, target_pos) + % Check to make sure target_pos is ok to execute + % Error if it is outside limits + % Error if the channel needs to be homed + % Otherwise returns true + tf = true; + for i = 1 : 3 + assert(~obj.channelsNET{i}.NeedsHoming,'Motor %f is not homed!', ) + assert(target_pos(i) <= max(obj.Travel) && target_pos >= min(obj.Travel),... + 'Attempted to move motor %f to %f, but it is limited to %f, %f', i, target_pos, min(obj.Travel), max(obj.Travel)) + end + end - connect + function moveto(obj, target_pos) + tf = obj.checkMove(target_pos); + if tf + for i = 1:3 + try + workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{i}.MoveTo(target_pos(i), workDone); % Move device to position via .NET interface + obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + catch + error(['Unable to Move channel ',obj.serialnumber{i},' to ',num2str(target_pos(i))]); + end + end + else + error('Target position is not valid') + end + obj.updatestatus + end - disconnect % write out how to connect here + function step(obj, distance) - home + end - move + function pos = GetPosition(obj) + pos = [0 0 0]; + for i = 1:3 + pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); + end + end - GetPosition +end +methods (Static) + function loaddlls() % Load DLLs (Load all relevant dlls in case the GetDevices function was not called) + if ~exist(KinesisBSC203.DEVICEMANAGERCLASSNAME,'class') + try % Load DeviceManagerCLI dll if not already loaded + NET.addAssembly([KinesisBSC203.MOTORPATHDEFAULT,KinesisBSC203.DEVICEMANAGERDLL]); + catch + error('Unable to load .NET assemblies') + end + end + if ~exist(KinesisBSC203.GENERICMOTORCLASSNAME,'class') + try % Load in DLLs if not already loaded + NET.addAssembly([KinesisBSC203.MOTORPATHDEFAULT,KinesisBSC203.GENERICMOTORDLL]); + NET.addAssembly([KinesisBSC203.MOTORPATHDEFAULT,KinesisBSC203.STEPPERMOTORDLL]); + catch % DLLs did not load + error('Unable to load .NET assemblies') + end + end end \ No newline at end of file diff --git a/Modules/+Drivers/+Kinesis/Kinesis_invisble.m b/Modules/+Drivers/+Kinesis/Kinesis_invisble.m index b56479af3..372838d34 100644 --- a/Modules/+Drivers/+Kinesis/Kinesis_invisble.m +++ b/Modules/+Drivers/+Kinesis/Kinesis_invisble.m @@ -1,17 +1,59 @@ classdef Kinesis_invisible +properties(Constant, Hidden) + MOTORPATHDEFAULT='C:\Program Files\Thorlabs\Kinesis\'; + DEVICEMANAGERDLL='Thorlabs.MotionControl.DeviceManagerCLI.dll'; + DEVICEMANAGERCLASSNAME='Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI'; + + TPOLLING=250; % Default polling time + TIMEOUTSETTINGS=7000; % Default timeout time for settings change + TIMEOUTMOVE=100000; % Default time out time for motor move +end + methods(abstract) - loaddlls(obj) + loaddlls() + + connect(obj, serialNum) + + disconnect(obj) % add comments end methods - connect(obj,serialNum) + function obj = Kinesis_invisible() + Kinesis_invisible.loadDeviceManagerdll + end + - disconnect(obj) end methods(Static) - getDevices() + function loadDeviceManagerdll() % Load DeviceManagerCLI dll + if ~exist(Kinesis_invisible.DEVICEMANAGERCLASSNAME,'class') + try + NET.addAssembly([Kinesis_invisible.MOTORPATHDEFAULT,Kinesis_invisible.DEVICEMANAGERDLL]); + catch + error('Unable to load .NET assemblies') + end + end + end + + function serialNumbers=GetDevices() % Returns a cell array of serial numbers of connected devices + Kinesis_invisible.loadDeviceManagerdll; % Load DeviceMnagerCLI dll if not already loaded + + Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI.BuildDeviceList(); % Build device list + serialNumbersNet = Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI.GetDeviceList(); % Get device list + serialNumbers=cell(ToArray(serialNumbersNet)); % Convert serial numbers to cell array + end + + function settingsLoadOption = GetSettingsLoadOption(serialNo, deviceID) + Kinesis_invisible.loadDeviceManagerdll; % Load DeviceMnagerCLI dll if not already loaded + + deviceConfigMag = Thorlabs.MotionControl.DeviceManagerCLI.DeviceConfigurationManager; + deviceConfigMagInstance = deviceConfigMag.Instance(); + deviceConfigMagInstance.CreateDeviceConfiguration(serialNo, uint32(str2double(serialNo(1:2))), true); + deviceConfig = deviceConfigMag.Instance().GetDeviceConfiguration(deviceID); + settingsLoadOption = deviceConfig.ApplicationSettingsLoadOption; + end end \ No newline at end of file From 6cd4363f1861d04cdfaa7e957a267e758a07b918 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Tue, 27 Jul 2021 22:04:34 +0100 Subject: [PATCH 03/18] Added step function --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 39 ++++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index f9c832e63..722ac01e7 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -7,7 +7,7 @@ STEPPERMOTORCLASSNAME='Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor'; end -properties(SetAccess = private) +properties(SetAccess = private, SetObservable, AbortSet) isconnected = false; % Flag set if device connected serialnumbers; % Device serial numbers controllername; % Controller Name @@ -19,7 +19,7 @@ positions; % Motor position (1 * 3 array) Homed; - IsMoving; + Moving = false; end properties(Constant) @@ -43,24 +43,25 @@ end methods + % Use this to create/retrieve instance associated with serialNum function obj = instance(serialNum,name) mlock; - if nargin < 3 + if nargin < 2 name = serialNum; end persistent Objects if isempty(Objects) - Objects = Drivers.Kinesis.KinesisBSC203.empty(1,0); + Objects = Drivers.Kinesis.KinesisBSC203.empty(1,0); % Create an empty class end for i = 1:length(Objects) - if isvalid(Objects(i)) && isequal(serialNum,Objects(i).singleton_id) + if isvalid(Objects(i)) && isequal(serialNum,Objects(i).singleton_id) % Find instance with the same singleton ID obj = Objects(i); return end end - obj = Drivers.Kinesis.KinesisBSC203(serialNum,name); - obj.singleton_id = serialNum; - Objects(end+1) = obj; + obj = Drivers.Kinesis.KinesisBSC203(serialNum,name); % Create an instance + obj.singleton_id = serialNum; % Define singleton ID + Objects(end+1) = obj; % Add the instance to the object list end @@ -131,8 +132,6 @@ function updatestatus(obj) obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter obj.position(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions - - end end @@ -177,6 +176,7 @@ function home(obj) end function moveto(obj, target_pos) + % Move to target position, target_pos := 1 * 3 array of double tf = obj.checkMove(target_pos); if tf for i = 1:3 @@ -189,13 +189,28 @@ function moveto(obj, target_pos) end end else - error('Target position is not valid') + error('Target position is out of range') end obj.updatestatus end - function step(obj, distance) + function step(obj, channelNo, distance) + if distance < 0 + motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Backward; + elseif distance > 0 + motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Forward; + else + error('Step size cannot be zero') + end + obj.channelsNET{channelNo}.SetJogStepSize(abs(distance)) % Set the step size for jog + try + workDone = obj.channelsNET{channelNo}.InitializeWaitHandler(); + obj.channelsNET{channelNo}.MoveJog(motordirection, workDone); + obj.channelsNET{channelNo}.Wait(obj.TIMEOUTMOVE); + catch + error('Unable to execute jog') + obj.updatestatus end function pos = GetPosition(obj) From 3aded0298c4d684d031ef35c4060c1f3f3d0253b Mon Sep 17 00:00:00 2001 From: cassie-song Date: Wed, 28 Jul 2021 00:38:07 +0100 Subject: [PATCH 04/18] Tested on setup, added move continuously function --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 429 ++++++++++-------- Modules/+Drivers/+Kinesis/Kinesis_invisble.m | 59 --- Modules/+Drivers/+Kinesis/Kinesis_invisible.m | 58 +++ 3 files changed, 287 insertions(+), 259 deletions(-) delete mode 100644 Modules/+Drivers/+Kinesis/Kinesis_invisble.m create mode 100644 Modules/+Drivers/+Kinesis/Kinesis_invisible.m diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index 722ac01e7..66f1b3e8c 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -1,242 +1,271 @@ classdef KinesisBSC203 < Drivers.Kinesis.Kinesis_invisible & Modules.Driver -properties(Constant, Hidden) - GENERICMOTORDLL='Thorlabs.MotionControl.GenericMotorCLI.dll'; - GENERICMOTORCLASSNAME='Thorlabs.MotionControl.GenericMotorCLI.GenericMotorCLI'; - STEPPERMOTORDLL='Thorlabs.MotionControl.Benchtop.StepperMotorCLI.dll'; - STEPPERMOTORCLASSNAME='Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor'; -end + properties(Constant, Hidden) + GENERICMOTORDLL='Thorlabs.MotionControl.GenericMotorCLI.dll'; + GENERICMOTORCLASSNAME='Thorlabs.MotionControl.GenericMotorCLI.GenericMotorCLI'; + STEPPERMOTORDLL='Thorlabs.MotionControl.Benchtop.StepperMotorCLI.dll'; + STEPPERMOTORCLASSNAME='Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor'; + end -properties(SetAccess = private, SetObservable, AbortSet) - isconnected = false; % Flag set if device connected - serialnumbers; % Device serial numbers - controllername; % Controller Name - controllerdescription % Controller Description - stagename; % Stage Name - acceleration; % Acceleration - maxvelocity; % Maximum velocity limit - minvelocity; % Minimum velocity limit - positions; % Motor position (1 * 3 array) - - Homed; - Moving = false; -end + properties(SetAccess = private, SetObservable, AbortSet) + isconnected = false; % Flag set if device connected + serialnumbers; % Device serial numbers + controllername; % Controller Name + controllerdescription % Controller Description + stagename; % Stage Name + acceleration; % Acceleration + maxvelocity; % Maximum velocity limit + minvelocity; % Minimum velocity limit + positions; % Motor position (1 * 3 array) -properties(Constant) - Travel = [-2 2] * 1000; -end + Homed; + Moving = false; + end -properties(Hidden) - deviceNET; % Device object within .NET - channelsNET; % Channel object within .NET (1 * 3 cell) - motorSettingsNET; % motorSettings within .NET (1 * 3 cell) - currentDeviceSettingsNET; % currentDeviceSetings within .NET (1 * 3 cell) - deviceInfoNET; % deviceInfo within .NET (1 * 3 cell) -end + properties(Constant) + Travel = [-2 2] * 1000; + end -methods(Access=private) - % Constructor - function obj = KinesisBSC203(serialNum, name) % Instantiate the KinesisBSC203 motor object - KinesisBSC203.loaddlls; % Load DLLs if not already loaded - KinesisBSC203.connect(serialNum); % Connect device + properties(Hidden) + deviceNET; % Device object within .NET + channelsNET; % Channel object within .NET (1 * 3 cell) + motorSettingsNET; % motorSettings within .NET (1 * 3 cell) + currentDeviceSettingsNET; % currentDeviceSetings within .NET (1 * 3 cell) + deviceInfoNET; % deviceInfo within .NET (1 * 3 cell) end -end -methods - % Use this to create/retrieve instance associated with serialNum - function obj = instance(serialNum,name) - mlock; - if nargin < 2 - name = serialNum; + methods(Access=private) + % Constructor + function obj = KinesisBSC203(serialNo, name) % Instantiate the KinesisBSC203 motor object + Drivers.Kinesis.KinesisBSC203.loaddlls; % Load DLLs if not already loaded + obj.connect(serialNo); % Connect device end - persistent Objects - if isempty(Objects) - Objects = Drivers.Kinesis.KinesisBSC203.empty(1,0); % Create an empty class - end - for i = 1:length(Objects) - if isvalid(Objects(i)) && isequal(serialNum,Objects(i).singleton_id) % Find instance with the same singleton ID - obj = Objects(i); - return + end + + methods(Static) + % Use this to create/retrieve instance associated with serialNo + function obj = instance(serialNo,name) + mlock; + if nargin < 2 + name = serialNo; + end + persistent Objects + if isempty(Objects) + Objects = Drivers.Kinesis.KinesisBSC203.empty(1,0); % Create an empty class end + for i = 1:length(Objects) + if isvalid(Objects(i)) && isequal(serialNo,Objects(i).singleton_id) % Find instance with the same singleton ID + obj = Objects(i); + return + end + end + obj = Drivers.Kinesis.KinesisBSC203(serialNo,name); % Create an instance + obj.singleton_id = serialNo; % Define singleton ID + Objects(end+1) = obj; % Add the instance to the object list end - obj = Drivers.Kinesis.KinesisBSC203(serialNum,name); % Create an instance - obj.singleton_id = serialNum; % Define singleton ID - Objects(end+1) = obj; % Add the instance to the object list end - - % - function connect(obj, serialNo) % serialNo := str - obj.GetDevices; % Call this to build device list if not already done - - if ~exist(obj.deviceNET) % Checking if a .NET BenchtopStepperMotor instance is already created - if str2double(serialNo(1:2)) == double(Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.DevicePrefix70) % Checking whether ther serial number corresponds to a BenchtopStepperMotor - obj.deviceNET = Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.CreateBenchtopStepperMotor(serialNo); % Create an instance of .NET BenchtopStepperMotor - else % Serial number prefix does not belong to Benchtop Stepper Motor - error('Serial Number is not Benchtop Stepper Motor.') + methods + % + function connect(obj, serialNo) % serialNo := str + obj.GetDevices; % Call this to build device list if not already done + if ~obj.isconnected() % Connect and initialize device if not connected + if str2double(serialNo(1:2)) == double(Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.DevicePrefix70) % Checking whether ther serial number corresponds to a BenchtopStepperMotor + obj.deviceNET = Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.CreateBenchtopStepperMotor(serialNo); % Create an instance of .NET BenchtopStepperMotor + else % Serial number prefix does not belong to Benchtop Stepper Motor + error('Serial Number is not Benchtop Stepper Motor.') + end + for i = 1:3 + obj.channelsNET{i} = obj.deviceNET.GetChannel(i); % Get channel objects of the device + obj.channelsNET{i}.ClearDeviceExceptions(); % Clear device exceptions via .NET interface + end + + obj.deviceNET.Connect(serialNo); % Connect to device via .NET interface + obj.initialize(serialNo) % Initialize the device + else % Device already connected + error('Device is already connected') end - end - - for i = 1:3 - obj.channelsNET{i} = obj.deviceNET.GetChannel(i); % Get channel objects of the device - obj.channelsNET{i}.ClearDeviceExceptions(); % Clear device exceptions via .NET interface + obj.updatestatus % Update status variables from device end - if ~obj.isconnected() % Connect and initialize device if not connected - obj.deviceNET.Connect(serialNo); % Connect to device via .NET interface - obj.initialize(serialNo) % Initialize the device - else % Device already connected - error('Device is already connected') - end - obj.updatestatus % Update status variables from device - end + function initialize(obj, serialNo) % Initialize all three channels of the device, serialNo := str + for i = 1:3 + try + if ~obj.channelsNET{i}.IsSettingsInitialized() + obj.channelsNET{i}.WaitForSettingsInitialized(obj.TIMEOUTSETTINGS); % Initialize the ith channel + else + disp('Device Already Initialized.') + end + if ~obj.channelsNET{i}.IsSettingsInitialized() % Device not successfully initialized + error('Unable to initialize device') + end + obj.channelsNET{i}.StartPolling(obj.TPOLLING); % Start polling device via .NET interface + obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface - function initialize(obj, serialNo) % Initialize all three channels of the device, serialNo := str - for i = 1:3 - try - if ~obj.channelsNET{i}.IsSettingsInitialized() - obj.channelsNET{i}.WaitForSettingsInitialized(obj.TIMEOUTSETTINGS); % Initialize the ith channel - else - disp('Device Already Initialized.') - end - if ~obj.channelsNET{i}.IsSettingsInitialized() % Device not successfully initialized - error('Unable to initialize device') + % Initialize motor configuration + deviceID = obj.channelsNET{i}.DeviceID; + settingsLoadOption = Drivers.Kinesis.Kinesis_invisible.GetSettingsLoadOption(serialNo, deviceID); + obj.motorSettingsNET{i} = obj.channelsNET{i}.GetMotorConfiguration(serialNo, settingsLoadOption); + + % Initialize current motor settings + obj.currentDeviceSettingsNET{i}=obj.channelsNET{i}.MotorDeviceSettings; + obj.deviceInfoNET{i} = obj.channelsNET{i}.GetDeviceInfo(); % Get deviceInfo via .NET interface + catch + error(['Unable to initialize channel ', num2str(i)]); end - obj.channelsNET{i}.StartPolling(obj.TPOLLING); % Start polling device via .NET interface - obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface - - % Initialize motor configuration - deviceID = obj.channelsNET{i}.DeviceID; - settingsLoadOption = Kinesis_invisible.GetSettingsLoadOption(serialNo, deviceID); - obj.motorSettingsNET{i} = obj.channelsNET{i}.GetMotorConfiguration(serialNo, settingsLoadOption); - - % Initialize current motor settings - obj.currentDeviceSettingsNET{i}=obj.channelsNET{i}.MotorDeviceSettings; - obj.deviceInfoNET{i} = obj.channelsNET{i}.GetDeviceInfo(); % Get deviceInfo via .NET interface - catch - error(['Unable to initialize channel ', num2str(i)]); end + end - - end - function updatestatus(obj) - obj.isconnected = obj.deviceNET.IsConnected(); % connection status - for i = 1:3 - obj.serialnumbers{i}=char(obj.channelsNET{i}.DeviceID); % update serial number - obj.controllername{i}=char(obj.deviceInfoNET{i}.Name); % update controleller name - obj.controllerdescription{i}=char(obj.deviceInfoNET{i}.Description); % update controller description - obj.stagename{i}=char(obj.motorSettingsNET{i}.DeviceSettingsName); % update stagename - velocityparams{i}=obj.channelsNET{i}.GetVelocityParams(); % update velocity parameter - obj.acceleration{i}=System.Decimal.ToDouble(velocityparams{i}.Acceleration); % update acceleration parameter - obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter - obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter - obj.position(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions + function updatestatus(obj) + obj.isconnected = obj.deviceNET.IsConnected(); % connection status + for i = 1:3 + obj.serialnumbers{i}=char(obj.channelsNET{i}.DeviceID); % update serial number + obj.controllername{i}=char(obj.deviceInfoNET{i}.Name); % update controleller name + obj.controllerdescription{i}=char(obj.deviceInfoNET{i}.Description); % update controller description + obj.stagename{i}=char(obj.motorSettingsNET{i}.DeviceSettingsName); % update stagename + velocityparams{i}=obj.channelsNET{i}.GetVelocityParams(); % update velocity parameter + obj.acceleration{i}=System.Decimal.ToDouble(velocityparams{i}.Acceleration); % update acceleration parameter + obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter + obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter + obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions + end end - end - function disconnect(obj) - obj.isconnected = obj.deviceNET.IsConnected(); % Read connection status - if obj.isconnected % Disconnect device if connected - try - for i = 1:3 - obj.channelsNET{i}.StopPolling(); % Stop polling device via .NET interface - obj.channelsNET{i}.DisableDevice(); % Disable device via .NET interface + function disconnect(obj) + obj.isconnected = obj.deviceNET.IsConnected(); % Read connection status + if obj.isconnected % Disconnect device if connected + try + for i = 1:3 + obj.channelsNET{i}.StopPolling(); % Stop polling device via .NET interface + obj.channelsNET{i}.DisableDevice(); % Disable device via .NET interface + end + obj.deviceNET.Disconnect(true) + catch + error(['Unable to disconnect device',obj.serialnumbers{i}]); end - obj.deviceNET.Disconnect(true) - catch - error(['Unable to disconnect device',obj.serialnumbers{i}]); + obj.isconnected = obj.deviceNET.IsConnected(); + else % Cannot disconnect because device not connected + error('Device not connected.') end - obj.isconnected = obj.deviceNET.IsConnected(); - else % Cannot disconnect because device not connected - error('Device not connected.') end - end - function home(obj) - for i = 1:3 - workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout - obj.channelsNET{i}.Home(workDone); % Home device via .NET interface - obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + function home(obj) + for i = 1:3 + workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{i}.Home(workDone); % Home device via .NET interface + obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + end + obj.updatestatus; % Update status variables from device end - obj.updatestatus; % Update status variables from device - end - function tf = checkMove(obj, target_pos) - % Check to make sure target_pos is ok to execute - % Error if it is outside limits - % Error if the channel needs to be homed - % Otherwise returns true - tf = true; - for i = 1 : 3 - assert(~obj.channelsNET{i}.NeedsHoming,'Motor %f is not homed!', ) - assert(target_pos(i) <= max(obj.Travel) && target_pos >= min(obj.Travel),... - 'Attempted to move motor %f to %f, but it is limited to %f, %f', i, target_pos, min(obj.Travel), max(obj.Travel)) + function tf = checkMove(obj, target_pos) + % Check to make sure target_pos is ok to execute + % Error if it is outside limits + % Error if the channel needs to be homed + % Otherwise returns true + tf = true; + for i = 1 : 3 + assert(~obj.channelsNET{i}.NeedsHoming,'Motor %f is not homed!', i) + assert(target_pos(i) <= max(obj.Travel) && target_pos(i) >= min(obj.Travel),... + 'Attempted to move motor %f to %f, but it is limited to %f, %f', i, target_pos, min(obj.Travel), max(obj.Travel)) + end end - end - function moveto(obj, target_pos) - % Move to target position, target_pos := 1 * 3 array of double - tf = obj.checkMove(target_pos); - if tf - for i = 1:3 + function moveto(obj, target_pos) + % Move to target position, target_pos := 1 * 3 array of double + tf = obj.checkMove(target_pos); + if tf + for i = 1:3 + try + workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{i}.MoveTo(target_pos(i), workDone); % Move device to position via .NET interface + obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + catch + error(['Unable to Move channel ',obj.serialnumber{i},' to ',num2str(target_pos(i))]); + end + end + else + error('Target position is out of range') + end + obj.updatestatus + end + + function step(obj, channelNo, distance) + % Method to move the motor by a jog + % channelNo := int, distance : double, + if distance < 0 % Set jog direction to backwards + motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Backward; + elseif distance > 0 % Set jog direction to forwards + motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Forward; + else + error('Step size cannot be zero') + end + + % Calculate the position after the step + step_pos = [0 0 0]; + obj.channelsNET{channelNo}.SetJogStepSize(abs(distance)) % Set the step size for jog + step_pos(channelNo) = obj.channelsNET{channelNo}.GetJogStepSize(); + target_pos = obj.positions + step_pos; + + % Check whether the position after the step exceeds the travel + tf = obj.checkMove(target_pos); + + if tf try - workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout - obj.channelsNET{i}.MoveTo(target_pos(i), workDone); % Move device to position via .NET interface - obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish - catch - error(['Unable to Move channel ',obj.serialnumber{i},' to ',num2str(target_pos(i))]); + workDone = obj.channelsNET{channelNo}.InitializeWaitHandler(); + obj.channelsNET{channelNo}.MoveJog(motordirection, workDone); % Execute jog + obj.channelsNET{channelNo}.Wait(obj.TIMEOUTMOVE); + catch + error('Unable to execute jog') end + else + error('Target position is out of range') end - else - error('Target position is out of range') + obj.updatestatus + end + + function movecont(h, channelNo, varargin) % Set motor to move continuously + if (nargin>2) && (varargin{1}) % if parameter given (e.g. 1) move backwards + motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Backward; + else % if no parametr given move forwards + motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Forward; + end + h.channelsNET{channelNo}.MoveContinuous(motordirection); % Set motor into continous move via .NET interface + updatestatus(h); % Update status variables from device + end + + function stop(h, channelNo) % Stop the motor moving (needed if set motor to continous) + h.channelsNET{channelNo}.Stop(h.TIMEOUTMOVE); % Stop motor movement via.NET interface + updatestatus(h); % Update status variables from device end - obj.updatestatus - end - - function step(obj, channelNo, distance) - - if distance < 0 - motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Backward; - elseif distance > 0 - motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Forward; - else - error('Step size cannot be zero') - end - obj.channelsNET{channelNo}.SetJogStepSize(abs(distance)) % Set the step size for jog - try - workDone = obj.channelsNET{channelNo}.InitializeWaitHandler(); - obj.channelsNET{channelNo}.MoveJog(motordirection, workDone); - obj.channelsNET{channelNo}.Wait(obj.TIMEOUTMOVE); - catch - error('Unable to execute jog') - obj.updatestatus - end - function pos = GetPosition(obj) - pos = [0 0 0]; - for i = 1:3 - pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); + function pos = GetPosition(obj) + pos = [0 0 0]; + for i = 1:3 + pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); + end end - end -end + end -methods (Static) - function loaddlls() % Load DLLs (Load all relevant dlls in case the GetDevices function was not called) - if ~exist(KinesisBSC203.DEVICEMANAGERCLASSNAME,'class') - try % Load DeviceManagerCLI dll if not already loaded - NET.addAssembly([KinesisBSC203.MOTORPATHDEFAULT,KinesisBSC203.DEVICEMANAGERDLL]); - catch - error('Unable to load .NET assemblies') + methods (Static) + function loaddlls() % Load DLLs (Load all relevant dlls in case the GetDevices function was not called) + if ~exist(Drivers.Kinesis.KinesisBSC203.DEVICEMANAGERCLASSNAME,'class') + try % Load DeviceManagerCLI dll if not already loaded + NET.addAssembly([Drivers.Kinesis.KinesisBSC203.MOTORPATHDEFAULT,Drivers.Kinesis.KinesisBSC203.DEVICEMANAGERDLL]); + catch + error('Unable to load .NET assemblies') + end end - end - if ~exist(KinesisBSC203.GENERICMOTORCLASSNAME,'class') - try % Load in DLLs if not already loaded - NET.addAssembly([KinesisBSC203.MOTORPATHDEFAULT,KinesisBSC203.GENERICMOTORDLL]); - NET.addAssembly([KinesisBSC203.MOTORPATHDEFAULT,KinesisBSC203.STEPPERMOTORDLL]); - catch % DLLs did not load - error('Unable to load .NET assemblies') + if ~exist(Drivers.Kinesis.KinesisBSC203.GENERICMOTORCLASSNAME,'class') + try % Load in DLLs if not already loaded + NET.addAssembly([Drivers.Kinesis.KinesisBSC203.MOTORPATHDEFAULT,Drivers.Kinesis.KinesisBSC203.GENERICMOTORDLL]); + NET.addAssembly([Drivers.Kinesis.KinesisBSC203.MOTORPATHDEFAULT,Drivers.Kinesis.KinesisBSC203.STEPPERMOTORDLL]); + catch % DLLs did not load + error('Unable to load .NET assemblies') + end end + end end -end \ No newline at end of file +end diff --git a/Modules/+Drivers/+Kinesis/Kinesis_invisble.m b/Modules/+Drivers/+Kinesis/Kinesis_invisble.m deleted file mode 100644 index 372838d34..000000000 --- a/Modules/+Drivers/+Kinesis/Kinesis_invisble.m +++ /dev/null @@ -1,59 +0,0 @@ -classdef Kinesis_invisible - -properties(Constant, Hidden) - MOTORPATHDEFAULT='C:\Program Files\Thorlabs\Kinesis\'; - DEVICEMANAGERDLL='Thorlabs.MotionControl.DeviceManagerCLI.dll'; - DEVICEMANAGERCLASSNAME='Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI'; - - TPOLLING=250; % Default polling time - TIMEOUTSETTINGS=7000; % Default timeout time for settings change - TIMEOUTMOVE=100000; % Default time out time for motor move -end - -methods(abstract) - - loaddlls() - - connect(obj, serialNum) - - disconnect(obj) % add comments - -end - -methods - function obj = Kinesis_invisible() - Kinesis_invisible.loadDeviceManagerdll - end - - -end - -methods(Static) - function loadDeviceManagerdll() % Load DeviceManagerCLI dll - if ~exist(Kinesis_invisible.DEVICEMANAGERCLASSNAME,'class') - try - NET.addAssembly([Kinesis_invisible.MOTORPATHDEFAULT,Kinesis_invisible.DEVICEMANAGERDLL]); - catch - error('Unable to load .NET assemblies') - end - end - end - - function serialNumbers=GetDevices() % Returns a cell array of serial numbers of connected devices - Kinesis_invisible.loadDeviceManagerdll; % Load DeviceMnagerCLI dll if not already loaded - - Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI.BuildDeviceList(); % Build device list - serialNumbersNet = Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI.GetDeviceList(); % Get device list - serialNumbers=cell(ToArray(serialNumbersNet)); % Convert serial numbers to cell array - end - - function settingsLoadOption = GetSettingsLoadOption(serialNo, deviceID) - Kinesis_invisible.loadDeviceManagerdll; % Load DeviceMnagerCLI dll if not already loaded - - deviceConfigMag = Thorlabs.MotionControl.DeviceManagerCLI.DeviceConfigurationManager; - deviceConfigMagInstance = deviceConfigMag.Instance(); - deviceConfigMagInstance.CreateDeviceConfiguration(serialNo, uint32(str2double(serialNo(1:2))), true); - deviceConfig = deviceConfigMag.Instance().GetDeviceConfiguration(deviceID); - settingsLoadOption = deviceConfig.ApplicationSettingsLoadOption; - end -end \ No newline at end of file diff --git a/Modules/+Drivers/+Kinesis/Kinesis_invisible.m b/Modules/+Drivers/+Kinesis/Kinesis_invisible.m new file mode 100644 index 000000000..5bef7a270 --- /dev/null +++ b/Modules/+Drivers/+Kinesis/Kinesis_invisible.m @@ -0,0 +1,58 @@ +classdef Kinesis_invisible < handle + + properties(Constant, Hidden) + MOTORPATHDEFAULT='C:\Program Files\Thorlabs\Kinesis\'; + DEVICEMANAGERDLL='Thorlabs.MotionControl.DeviceManagerCLI.dll'; + DEVICEMANAGERCLASSNAME='Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI'; + + TPOLLING=250; % Default polling time + TIMEOUTSETTINGS=7000; % Default timeout time for settings change + TIMEOUTMOVE=100000; % Default time out time for motor move + end + + methods(Abstract) + + loaddlls() + + connect(obj, serialNum) + + disconnect(obj) % add comments + + end + + methods + function obj = Kinesis_invisible() + Drivers.Kinesis.Kinesis_invisible.loadDeviceManagerdll(); + end + end + + methods(Static) + function loadDeviceManagerdll() % Load DeviceManagerCLI dll + if ~exist(Drivers.Kinesis.Kinesis_invisible.DEVICEMANAGERCLASSNAME,'class') + try + NET.addAssembly([Drivers.Kinesis.Kinesis_invisible.MOTORPATHDEFAULT,Drivers.Kinesis.Kinesis_invisible.DEVICEMANAGERDLL]); + catch + error('Unable to load .NET assemblies') + end + end + end + + function serialNumbers = GetDevices() % Returns a cell array of serial numbers of connected devices + Drivers.Kinesis.Kinesis_invisible.loadDeviceManagerdll(); % Load DeviceMnagerCLI dll if not already loaded + + Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI.BuildDeviceList(); % Build device list + serialNumbersNet = Thorlabs.MotionControl.DeviceManagerCLI.DeviceManagerCLI.GetDeviceList(); % Get device list + serialNumbers=cell(ToArray(serialNumbersNet)); % Convert serial numbers to cell array + end + + function settingsLoadOption = GetSettingsLoadOption(serialNo, deviceID) + Drivers.Kinesis.Kinesis_invisible.loadDeviceManagerdll(); % Load DeviceMnagerCLI dll if not already loaded + + deviceConfigMag = Thorlabs.MotionControl.DeviceManagerCLI.DeviceConfigurationManager; + deviceConfigMagInstance = deviceConfigMag.Instance(); + deviceConfigMagInstance.CreateDeviceConfiguration(serialNo, uint32(str2double(serialNo(1:2))), true); + deviceConfig = deviceConfigMag.Instance().GetDeviceConfiguration(deviceID); + settingsLoadOption = deviceConfig.ApplicationSettingsLoadOption; + end + end +end \ No newline at end of file From c7f79bbfb30c4b3a1c1e894db1cd21ddf3527045 Mon Sep 17 00:00:00 2001 From: ibwharri Date: Thu, 29 Jul 2021 11:12:11 -0400 Subject: [PATCH 05/18] Basic structure of BSC203 stage --- Modules/+Stages/BSC203.m | 227 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 Modules/+Stages/BSC203.m diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m new file mode 100644 index 000000000..f76d8258c --- /dev/null +++ b/Modules/+Stages/BSC203.m @@ -0,0 +1,227 @@ +classdef BSC203 < Modules.Stage + %MAX302 Control the motors of this stage. + % Uses 3 instances of APTMotor for x,y,z. + % + % All values are in microns. It is good practice to home after + % construction. + % + % Home is -2,-2,-2 + + properties + prefs = {'controller','calibration','x_motor','y_motor','z_motor'}; + end + properties(SetObservable,AbortSet) + controller = @Drivers.Kinesis.KinesisBSC203.getAvailMotors; % Motor serial number + calibration = Prefs.DoubleArray([2 2 2],'help_text','Calibration for the motor distance to true distance'); + x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'empty_val',true); + y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'empty_val',true); + z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'empty_val',true); + end + properties(SetAccess=private,SetObservable,AbortSet) + % Default here will only matter if motors aren't set + Homed = false; + Moving = false; % Track this to update position + end + properties(SetAccess=private) + position + motors = cell(1,3); % A cell array for the 3 motors {X,Y,Z} (list would require having "null" objects) + end + properties(Constant) + xRange = [-2 2]*1000; + yRange = [-2 2]*1000; + zRange = [-2 2]*1000; + end + + methods(Static) + function obj = instance() + mlock; + persistent Object + if isempty(Object) || ~isvalid(Object) + Object = Stages.BSC203(); + end + obj = Object; + end + + end + methods(Access=private) + function obj = BSC203() + obj.loadPrefs; + end + end + % Callback functions for APTMotor + methods(Access=?APTMotor) + function homedCallback(obj,varargin) + homed = false(1,3); + for i = 1:length(obj.motors) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + homed(i) = obj.motors{i}.Homed; + end + end + obj.Homed = all(homed); + end + function movingCallback(obj,varargin) + moving = false(1,3); + for i = 1:length(obj.motors) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + moving(i) = obj.motors{i}.Moving; + end + end + obj.Moving = any(moving); + end + end + methods + function delete(obj) + cellfun(@delete,obj.motors); + % No need to delete APTSystem + end + function pos = get.position(obj) + % This function takes a long time to execute. + pos = NaN(1,3); + range = [obj.xRange;obj.yRange;obj.zRange]; + for i = 1:length(obj.motors) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + pos(i) = obj.motors{i}.Position*1000+min(range(i,:)); + end + end + pos = pos.*obj.direction; + end + function move(obj,x,y,z) + pos = obj.position; + range = [obj.xRange;obj.yRange;obj.zRange]; + new_pos = {x,y,z}; % Allow for empty inputs in for loop below + for i = 1:length(obj.motors) + if ~isempty(new_pos{i}) && new_pos{i}~=pos(i) && ~isnan(new_pos{i}) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + new_pos{i} = (new_pos{i}*obj.direction(1) - min(range(i,:)))/1000; + obj.motors{i}.move(new_pos{i}) + else + error('Tried to move axis without motor loaded!'); + end + end + end + end + + function enable(obj) + %Method to enable motors drive + for i = 1:length(obj.motors) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + obj.motors{i}.enable(); + end + end + drawnow; % Flush callback queue + end + function disable(obj) + %Method to disable motors drive + for i = 1:length(obj.motors) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + obj.motors{i}.disable(); + end + end + drawnow; % Flush callback queue + end + function home(obj) + %Method to home/zero the motors + for i = 1:length(obj.motors) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + obj.motors{i}.home(); + end + end + drawnow; % Flush callback queue + end + function abort(obj,varargin) + %Method to try and stop the motors + for i = 1:length(obj.motors) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + obj.motors{i}.abort(varargin{:}); + end + end + drawnow; % Flush callback queue + end + + % Settings and Callback + function settings(obj,panelH,varargin) + settings@Modules.Stage(obj,panelH,varargin{:}); % Add in prefs + % Adjust for pref space usage (use character units) + start = 0; + children = allchild(panelH); + set(children,'units','characters'); + for i = 1:length(children) + h = sum(children(i).Position([2 4])); + if h > start + start = h; + end + end + start = start + 1; % Give a bit of space between + spacing = 1.5; + num_lines = 3; + line = 1; + uicontrol(panelH,'style','PushButton','string','X Settings','callback',@obj.motorSettings,... + 'units','characters','position',[0 start+spacing*(num_lines-line) 18 1.25],'UserData',1); + line = 2; + uicontrol(panelH,'style','PushButton','string','Y Settings','callback',@obj.motorSettings,... + 'units','characters','position',[0 start+spacing*(num_lines-line) 18 1.25],'UserData',2); + line = 3; + uicontrol(panelH,'style','PushButton','string','Z Settings','callback',@obj.motorSettings,... + 'units','characters','position',[0 start+spacing*(num_lines-line) 18 1.25],'UserData',3); + end + function motorSettings(obj,hObj,~,~) + axis = hObj.UserData; + if ~isempty(obj.motors{axis})&&isobject(obj.motors{axis}) && isvalid(obj.motors{axis}) + obj.motors{axis}.settings; + else + error('No motor is loaded for this axis!') + end + end + + % Motor construction callbacks + function setMotor(obj,val) + val = str2double(val); + assert(~isnan(val),'Motor SN must be a valid number.') + if val == 0 + return % Short circuit + end + % Remove old motor if not loaded by other axis + motorOld = obj.motors{axis}; % Either motor obj or empty + obj.motors{axis} = []; + inuse = false; + for i = 1:length(obj.motors) + if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) + if obj.motors{i} == motorOld + inuse = true; break + end + end + end + if ~inuse + delete(motorOld) % Fine deleting [] + drawnow; + end + % Add new motor + obj.motors{axis} = Drivers.Kinesis.KinesisBSC203.instance(val, [0 4]); + % Trick to update position + obj.Moving = true; + obj.Moving = false; + % Listeners will follow lifecycle of their motor + addlistener(obj.motors{axis},'Moving','PostSet',@obj.movingCallback); + addlistener(obj.motors{axis},'Homed','PostSet',@obj.homedCallback); + % Intialize values + obj.homedCallback; + obj.movingCallback; + end + + function set.controller(obj,val) + % Validate that the serial number is the correct type for BSC203 + if val(1:2)='70' + try + obj.setMotor(val); %#ok<*MCSUP> + obj.controller = val; + catch err + obj.X_Motor = 'None'; + rethrow(err) + end + else + error('This device is not of the correct type; serial number should start with a 70') + end + end + end +end + From 3eab2f50fcd50dffa12c9dd1462ed850fcfad8eb Mon Sep 17 00:00:00 2001 From: cassie-song Date: Sun, 8 Aug 2021 20:42:47 +0100 Subject: [PATCH 06/18] Updated BCS203 Stage module --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 64 +++++-- Modules/+Drivers/+Kinesis/Kinesis_invisible.m | 4 +- Modules/+Stages/BSC203.m | 173 +++++------------- 3 files changed, 100 insertions(+), 141 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index 66f1b3e8c..c17ecef75 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -19,10 +19,8 @@ positions; % Motor position (1 * 3 array) Homed; - Moving = false; - end + isMoving = false; - properties(Constant) Travel = [-2 2] * 1000; end @@ -36,15 +34,16 @@ methods(Access=private) % Constructor - function obj = KinesisBSC203(serialNo, name) % Instantiate the KinesisBSC203 motor object + function obj = KinesisBSC203(serialNo, travel, name) % Instantiate the KinesisBSC203 motor object Drivers.Kinesis.KinesisBSC203.loaddlls; % Load DLLs if not already loaded obj.connect(serialNo); % Connect device + obj.Travel = travel; end end methods(Static) % Use this to create/retrieve instance associated with serialNo - function obj = instance(serialNo,name) + function obj = instance(serialNo, travel, name) mlock; if nargin < 2 name = serialNo; @@ -59,7 +58,7 @@ return end end - obj = Drivers.Kinesis.KinesisBSC203(serialNo,name); % Create an instance + obj = Drivers.Kinesis.KinesisBSC203(serialNo, travel, name); % Create an instance obj.singleton_id = serialNo; % Define singleton ID Objects(end+1) = obj; % Add the instance to the object list end @@ -118,7 +117,9 @@ function initialize(obj, serialNo) % Initialize all three channels of the device end function updatestatus(obj) - obj.isconnected = obj.deviceNET.IsConnected(); % connection status + obj.isconnected = obj.deviceNET.IsConnected(); % connection status + homed = false(1, 3); + moving = false(1, 3); for i = 1:3 obj.serialnumbers{i}=char(obj.channelsNET{i}.DeviceID); % update serial number obj.controllername{i}=char(obj.deviceInfoNET{i}.Name); % update controleller name @@ -129,7 +130,11 @@ function updatestatus(obj) obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions + homed(i) = ~obj.channelsNET{i}.NeedsHoming; + moving(i) = obj.motors{i}.State.Moving; end + obj.Homed = all(homed); + obj.isMoving = any(moving); end function disconnect(obj) @@ -151,7 +156,7 @@ function disconnect(obj) end function home(obj) - for i = 1:3 + for i = 1 : 3 workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout obj.channelsNET{i}.Home(workDone); % Home device via .NET interface obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish @@ -176,7 +181,7 @@ function moveto(obj, target_pos) % Move to target position, target_pos := 1 * 3 array of double tf = obj.checkMove(target_pos); if tf - for i = 1:3 + for i = 1 : 3 try workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout obj.channelsNET{i}.MoveTo(target_pos(i), workDone); % Move device to position via .NET interface @@ -232,21 +237,40 @@ function movecont(h, channelNo, varargin) % Set motor to move continuously motordirection=Thorlabs.MotionControl.GenericMotorCLI.MotorDirection.Forward; end h.channelsNET{channelNo}.MoveContinuous(motordirection); % Set motor into continous move via .NET interface - updatestatus(h); % Update status variables from device + obj.updatestatus; % Update status variables from device end - function stop(h, channelNo) % Stop the motor moving (needed if set motor to continous) - h.channelsNET{channelNo}.Stop(h.TIMEOUTMOVE); % Stop motor movement via.NET interface - updatestatus(h); % Update status variables from device + function stop(h, immediate) % Stop the motor moving (needed if set motor to continous) + for i = 1 : 3 + if nargin > 1 && immediate + h.channelsNET{i}.StopImmediate(); + else + h.channelsNET{channelNo}.Stop(h.TIMEOUTMOVE); % Stop motor movement via.NET interface + end + obj.updatestatus % Update status variables from device end - function pos = GetPosition(obj) + function pos = get.positions(obj) pos = [0 0 0]; - for i = 1:3 + for i = 1 : 3 pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); end end + function enable(obj) + for i = 1 : 3 + obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface + end + obj.updatestatus + end + + function disable(obj) + for i = 1 : 3 + obj.channelsNET{i}.DisableDevice(); % Enable device via .NET interface + end + obj.updatestatus + end + end methods (Static) @@ -267,5 +291,15 @@ function loaddlls() % Load DLLs (Load all relevant dlls in case the GetDevices f end end end + + function motorSerialNumbers = getAvailMotors() + motorSerialNumbers = {}; + serialNumbers = Drivers.Kinesis.Kinesis_invisible.GetDevices + for i = 1 : length(serialNumbers) + if strcmp(serialNumbers{i}(1:2), '70') + motorSerialNumbers{end + 1} = serialNumbers{i}; + end + end + end end end diff --git a/Modules/+Drivers/+Kinesis/Kinesis_invisible.m b/Modules/+Drivers/+Kinesis/Kinesis_invisible.m index 5bef7a270..4502f7770 100644 --- a/Modules/+Drivers/+Kinesis/Kinesis_invisible.m +++ b/Modules/+Drivers/+Kinesis/Kinesis_invisible.m @@ -12,9 +12,9 @@ methods(Abstract) - loaddlls() + loaddlls() % Load dlls - connect(obj, serialNum) + connect(obj, serialNum) % Connect devices with a specified serial number disconnect(obj) % add comments diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index f76d8258c..d9e66d87d 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -11,7 +11,8 @@ prefs = {'controller','calibration','x_motor','y_motor','z_motor'}; end properties(SetObservable,AbortSet) - controller = @Drivers.Kinesis.KinesisBSC203.getAvailMotors; % Motor serial number + availMotors = @Drivers.Kinesis.KinesisBSC203.getAvailMotors + controller = Prefs.MultipleChoice(availMotors{1}, 'choices', availMotors, 'empty_val', true); % Motor serial number calibration = Prefs.DoubleArray([2 2 2],'help_text','Calibration for the motor distance to true distance'); x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'empty_val',true); y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'empty_val',true); @@ -20,16 +21,12 @@ properties(SetAccess=private,SetObservable,AbortSet) % Default here will only matter if motors aren't set Homed = false; - Moving = false; % Track this to update position + isMoving = false; % Track this to update position end properties(SetAccess=private) position - motors = cell(1,3); % A cell array for the 3 motors {X,Y,Z} (list would require having "null" objects) - end - properties(Constant) - xRange = [-2 2]*1000; - yRange = [-2 2]*1000; - zRange = [-2 2]*1000; + motors = []; + MotorSerialNo end methods(Static) @@ -48,131 +45,73 @@ obj.loadPrefs; end end - % Callback functions for APTMotor - methods(Access=?APTMotor) + % Callback functions for BSC203 Motor + methods(Access=?KinesisBSC203) function homedCallback(obj,varargin) - homed = false(1,3); - for i = 1:length(obj.motors) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - homed(i) = obj.motors{i}.Homed; - end + if ~isempty(obj.motors) && isobject(obj.motors) && isvalid(obj.motors) + obj.Homed = obj.motors.Homed; + else + obj.Homed = false; end - obj.Homed = all(homed); end function movingCallback(obj,varargin) - moving = false(1,3); - for i = 1:length(obj.motors) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - moving(i) = obj.motors{i}.Moving; - end - end - obj.Moving = any(moving); + if ~isempty(obj.motors) && isobject(obj.motors) && isvalid(obj.motors) + obj.isMoving = obj.motors.isMoving; + else + obj.isMoving = false; + end end end methods function delete(obj) cellfun(@delete,obj.motors); - % No need to delete APTSystem end function pos = get.position(obj) - % This function takes a long time to execute. - pos = NaN(1,3); - range = [obj.xRange;obj.yRange;obj.zRange]; - for i = 1:length(obj.motors) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - pos(i) = obj.motors{i}.Position*1000+min(range(i,:)); - end - end - pos = pos.*obj.direction; + % this function reads the current motor position + pos = obj.motors.positions; end function move(obj,x,y,z) pos = obj.position; - range = [obj.xRange;obj.yRange;obj.zRange]; - new_pos = {x,y,z}; % Allow for empty inputs in for loop below - for i = 1:length(obj.motors) - if ~isempty(new_pos{i}) && new_pos{i}~=pos(i) && ~isnan(new_pos{i}) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - new_pos{i} = (new_pos{i}*obj.direction(1) - min(range(i,:)))/1000; - obj.motors{i}.move(new_pos{i}) - else - error('Tried to move axis without motor loaded!'); - end + new_pos = [x,y,z]; % Allow for empty inputs in for loop below + for i = 1:length(new_pos) + if isempty(new_pos(i)) || new_pos{i}==pos(i) || isnan(new_pos(i)) + new_pos(i) = pos(i); end end + if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) + obj.motors.moveto(new_pos) + end end function enable(obj) %Method to enable motors drive - for i = 1:length(obj.motors) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - obj.motors{i}.enable(); - end + if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) + obj.motors{i}.enable();; end drawnow; % Flush callback queue end function disable(obj) %Method to disable motors drive - for i = 1:length(obj.motors) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - obj.motors{i}.disable(); - end + if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) + obj.motors.disable(); end drawnow; % Flush callback queue end function home(obj) %Method to home/zero the motors - for i = 1:length(obj.motors) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - obj.motors{i}.home(); - end + if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) + obj.motors.home() end drawnow; % Flush callback queue end function abort(obj,varargin) %Method to try and stop the motors - for i = 1:length(obj.motors) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - obj.motors{i}.abort(varargin{:}); - end + if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) + obj.motors.stop(varargin{:}); end drawnow; % Flush callback queue end - % Settings and Callback - function settings(obj,panelH,varargin) - settings@Modules.Stage(obj,panelH,varargin{:}); % Add in prefs - % Adjust for pref space usage (use character units) - start = 0; - children = allchild(panelH); - set(children,'units','characters'); - for i = 1:length(children) - h = sum(children(i).Position([2 4])); - if h > start - start = h; - end - end - start = start + 1; % Give a bit of space between - spacing = 1.5; - num_lines = 3; - line = 1; - uicontrol(panelH,'style','PushButton','string','X Settings','callback',@obj.motorSettings,... - 'units','characters','position',[0 start+spacing*(num_lines-line) 18 1.25],'UserData',1); - line = 2; - uicontrol(panelH,'style','PushButton','string','Y Settings','callback',@obj.motorSettings,... - 'units','characters','position',[0 start+spacing*(num_lines-line) 18 1.25],'UserData',2); - line = 3; - uicontrol(panelH,'style','PushButton','string','Z Settings','callback',@obj.motorSettings,... - 'units','characters','position',[0 start+spacing*(num_lines-line) 18 1.25],'UserData',3); - end - function motorSettings(obj,hObj,~,~) - axis = hObj.UserData; - if ~isempty(obj.motors{axis})&&isobject(obj.motors{axis}) && isvalid(obj.motors{axis}) - obj.motors{axis}.settings; - else - error('No motor is loaded for this axis!') - end - end - % Motor construction callbacks function setMotor(obj,val) val = str2double(val); @@ -180,29 +119,13 @@ function setMotor(obj,val) if val == 0 return % Short circuit end - % Remove old motor if not loaded by other axis - motorOld = obj.motors{axis}; % Either motor obj or empty - obj.motors{axis} = []; - inuse = false; - for i = 1:length(obj.motors) - if ~isempty(obj.motors{i})&&isobject(obj.motors{i}) && isvalid(obj.motors{i}) - if obj.motors{i} == motorOld - inuse = true; break - end - end - end - if ~inuse - delete(motorOld) % Fine deleting [] - drawnow; - end + % Add new motor - obj.motors{axis} = Drivers.Kinesis.KinesisBSC203.instance(val, [0 4]); - % Trick to update position - obj.Moving = true; - obj.Moving = false; + obj.motors = Drivers.Kinesis.KinesisBSC203.instance(val, [0 8]); + % Listeners will follow lifecycle of their motor - addlistener(obj.motors{axis},'Moving','PostSet',@obj.movingCallback); - addlistener(obj.motors{axis},'Homed','PostSet',@obj.homedCallback); + addlistener(obj.motors,'isMoving','PostSet',@obj.movingCallback); + addlistener(obj.motors,'Homed','PostSet',@obj.homedCallback); % Intialize values obj.homedCallback; obj.movingCallback; @@ -210,16 +133,18 @@ function setMotor(obj,val) function set.controller(obj,val) % Validate that the serial number is the correct type for BSC203 - if val(1:2)='70' - try - obj.setMotor(val); %#ok<*MCSUP> - obj.controller = val; - catch err - obj.X_Motor = 'None'; - rethrow(err) + if ~isempty(val) + if strcmp(val(1:2) == '70') + try + obj.setMotor(val); + obj.MotorSerialNo = val; + catch err + obj.MotorSerialNo = 'None'; + rethrow(err) + end + else + error('This device is not of the correct type; serial number should start with a 70') end - else - error('This device is not of the correct type; serial number should start with a 70') end end end From f4475d317864ab8effa048668be5e27c6e1bb26d Mon Sep 17 00:00:00 2001 From: cassie-song Date: Tue, 10 Aug 2021 13:09:23 +0100 Subject: [PATCH 07/18] Tested stage module on setup --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 27 +++++++---- Modules/+Stages/BSC203.m | 58 +++++++++++++---------- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index c17ecef75..60315e913 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -1,5 +1,8 @@ classdef KinesisBSC203 < Drivers.Kinesis.Kinesis_invisible & Modules.Driver - + + properties + name + end properties(Constant, Hidden) GENERICMOTORDLL='Thorlabs.MotionControl.GenericMotorCLI.dll'; GENERICMOTORCLASSNAME='Thorlabs.MotionControl.GenericMotorCLI.GenericMotorCLI'; @@ -38,13 +41,16 @@ Drivers.Kinesis.KinesisBSC203.loaddlls; % Load DLLs if not already loaded obj.connect(serialNo); % Connect device obj.Travel = travel; + obj.name = name; end end methods(Static) % Use this to create/retrieve instance associated with serialNo function obj = instance(serialNo, travel, name) + disp('ok 4') mlock; + disp('ok 3') if nargin < 2 name = serialNo; end @@ -53,10 +59,12 @@ Objects = Drivers.Kinesis.KinesisBSC203.empty(1,0); % Create an empty class end for i = 1:length(Objects) + disp('ok 1') if isvalid(Objects(i)) && isequal(serialNo,Objects(i).singleton_id) % Find instance with the same singleton ID obj = Objects(i); return end + disp('ok 2') end obj = Drivers.Kinesis.KinesisBSC203(serialNo, travel, name); % Create an instance obj.singleton_id = serialNo; % Define singleton ID @@ -65,15 +73,11 @@ end methods - % + % Connect to the device with a specified serial number and initialize the device function connect(obj, serialNo) % serialNo := str obj.GetDevices; % Call this to build device list if not already done if ~obj.isconnected() % Connect and initialize device if not connected - if str2double(serialNo(1:2)) == double(Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.DevicePrefix70) % Checking whether ther serial number corresponds to a BenchtopStepperMotor - obj.deviceNET = Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.CreateBenchtopStepperMotor(serialNo); % Create an instance of .NET BenchtopStepperMotor - else % Serial number prefix does not belong to Benchtop Stepper Motor - error('Serial Number is not Benchtop Stepper Motor.') - end + obj.deviceNET = Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.CreateBenchtopStepperMotor(serialNo); % Create an instance of .NET BenchtopStepperMotor for i = 1:3 obj.channelsNET{i} = obj.deviceNET.GetChannel(i); % Get channel objects of the device obj.channelsNET{i}.ClearDeviceExceptions(); % Clear device exceptions via .NET interface @@ -131,7 +135,11 @@ function updatestatus(obj) obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions homed(i) = ~obj.channelsNET{i}.NeedsHoming; - moving(i) = obj.motors{i}.State.Moving; + if obj.channelsNET{i}.State == Thorlabs.MotionControl.GenericMotorCLI.MotorStates.Idle + moving(i) = false; + else + moving(i) = true; + end end obj.Homed = all(homed); obj.isMoving = any(moving); @@ -246,6 +254,7 @@ function stop(h, immediate) % Stop the motor moving (needed if set motor to cont h.channelsNET{i}.StopImmediate(); else h.channelsNET{channelNo}.Stop(h.TIMEOUTMOVE); % Stop motor movement via.NET interface + end end obj.updatestatus % Update status variables from device end @@ -294,7 +303,7 @@ function loaddlls() % Load DLLs (Load all relevant dlls in case the GetDevices f function motorSerialNumbers = getAvailMotors() motorSerialNumbers = {}; - serialNumbers = Drivers.Kinesis.Kinesis_invisible.GetDevices + serialNumbers = Drivers.Kinesis.Kinesis_invisible.GetDevices; for i = 1 : length(serialNumbers) if strcmp(serialNumbers{i}(1:2), '70') motorSerialNumbers{end + 1} = serialNumbers{i}; diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index d9e66d87d..c24dd7cbf 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -8,25 +8,34 @@ % Home is -2,-2,-2 properties - prefs = {'controller','calibration','x_motor','y_motor','z_motor'}; + prefs = {'x_motor','y_motor','z_motor'}; end properties(SetObservable,AbortSet) - availMotors = @Drivers.Kinesis.KinesisBSC203.getAvailMotors - controller = Prefs.MultipleChoice(availMotors{1}, 'choices', availMotors, 'empty_val', true); % Motor serial number - calibration = Prefs.DoubleArray([2 2 2],'help_text','Calibration for the motor distance to true distance'); - x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'empty_val',true); - y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'empty_val',true); - z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'empty_val',true); + availMotors = Drivers.Kinesis.KinesisBSC203.getAvailMotors; + controller +% calibration = Prefs.DoubleArray([2 2 2],'help_text','Calibration for the motor distance to true distance'); + end + + properties(GetObservable, SetObservable, AbortSet) + x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true); + y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true); + z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true); end properties(SetAccess=private,SetObservable,AbortSet) % Default here will only matter if motors aren't set Homed = false; - isMoving = false; % Track this to update position + Moving = false; % Track this to update position end properties(SetAccess=private) position - motors = []; - MotorSerialNo + motors; + end + + properties (Constant) + % Currently used as placeholders to avoid the error as Abstract properties + xRange = [0 8]; + yRange = [0 8]; + zRange = [0 8]; end methods(Static) @@ -42,7 +51,8 @@ end methods(Access=private) function obj = BSC203() - obj.loadPrefs; + obj.loadPrefs; + obj.controller = obj.availMotors{1}; end end % Callback functions for BSC203 Motor @@ -56,15 +66,17 @@ function homedCallback(obj,varargin) end function movingCallback(obj,varargin) if ~isempty(obj.motors) && isobject(obj.motors) && isvalid(obj.motors) - obj.isMoving = obj.motors.isMoving; + obj.Moving = obj.motors.isMoving; else - obj.isMoving = false; + obj.Moving = false; end end end methods function delete(obj) - cellfun(@delete,obj.motors); + if isobject(obj.motors) + delete(obj.motors); + end end function pos = get.position(obj) % this function reads the current motor position @@ -86,7 +98,7 @@ function move(obj,x,y,z) function enable(obj) %Method to enable motors drive if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) - obj.motors{i}.enable();; + obj.motors.enable(); end drawnow; % Flush callback queue end @@ -114,15 +126,11 @@ function abort(obj,varargin) % Motor construction callbacks function setMotor(obj,val) - val = str2double(val); - assert(~isnan(val),'Motor SN must be a valid number.') - if val == 0 - return % Short circuit - end - + disp('true 3') % Add new motor - obj.motors = Drivers.Kinesis.KinesisBSC203.instance(val, [0 8]); - + disp(isstring(val)) + obj.motors = Drivers.Kinesis.KinesisBSC203.instance(val, [0 8], val); + disp('true 4') % Listeners will follow lifecycle of their motor addlistener(obj.motors,'isMoving','PostSet',@obj.movingCallback); addlistener(obj.motors,'Homed','PostSet',@obj.homedCallback); @@ -134,12 +142,10 @@ function setMotor(obj,val) function set.controller(obj,val) % Validate that the serial number is the correct type for BSC203 if ~isempty(val) - if strcmp(val(1:2) == '70') + if strcmp(val(1:2), '70') try obj.setMotor(val); - obj.MotorSerialNo = val; catch err - obj.MotorSerialNo = 'None'; rethrow(err) end else From 293ec36b808bbcd12c4eb245b196c09b9c9a4860 Mon Sep 17 00:00:00 2001 From: ibwharri Date: Tue, 10 Aug 2021 11:02:51 -0400 Subject: [PATCH 08/18] minor changes to be able to load bsc203; prefs still not loading --- Modules/+Stages/BSC203.m | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index c24dd7cbf..3f77a28c9 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -8,7 +8,7 @@ % Home is -2,-2,-2 properties - prefs = {'x_motor','y_motor','z_motor'}; + prefs = {'availMotors','x_motor','y_motor','z_motor'}; end properties(SetObservable,AbortSet) availMotors = Drivers.Kinesis.KinesisBSC203.getAvailMotors; @@ -51,8 +51,10 @@ end methods(Access=private) function obj = BSC203() - obj.loadPrefs; - obj.controller = obj.availMotors{1}; + obj.loadPrefs; + if ~isempty(obj.controller) + obj.controller = obj.availMotors{1}; + end end end % Callback functions for BSC203 Motor From e4fa32a7689e0cfcc30cab98bf0d95f560f8ea2f Mon Sep 17 00:00:00 2001 From: ibwharri Date: Thu, 12 Aug 2021 11:08:19 -0400 Subject: [PATCH 09/18] Structure of changes required to fix bugs in CC --- Modules/+Stages/BSC203.m | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index 3f77a28c9..bbbc42a14 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -13,13 +13,13 @@ properties(SetObservable,AbortSet) availMotors = Drivers.Kinesis.KinesisBSC203.getAvailMotors; controller -% calibration = Prefs.DoubleArray([2 2 2],'help_text','Calibration for the motor distance to true distance'); +% calibration = [500 500 500] Prefs.DoubleArray([2 2 2],'help_text','Calibration for the motor distance to true distance'); end properties(GetObservable, SetObservable, AbortSet) - x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true); - y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true); - z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true); + x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set_function','help_text','Which channel in the controller controls the x direction motor'); + y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'help_text','Which channel in the controller controls the y direction motor'); + z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'help_text','Which channel in the controller controls the z direction motor'); end properties(SetAccess=private,SetObservable,AbortSet) % Default here will only matter if motors aren't set @@ -28,7 +28,8 @@ end properties(SetAccess=private) position - motors; + motors; + motor_channels = [1 2 3]; end properties (Constant) @@ -128,11 +129,9 @@ function abort(obj,varargin) % Motor construction callbacks function setMotor(obj,val) - disp('true 3') % Add new motor disp(isstring(val)) obj.motors = Drivers.Kinesis.KinesisBSC203.instance(val, [0 8], val); - disp('true 4') % Listeners will follow lifecycle of their motor addlistener(obj.motors,'isMoving','PostSet',@obj.movingCallback); addlistener(obj.motors,'Homed','PostSet',@obj.homedCallback); @@ -141,7 +140,7 @@ function setMotor(obj,val) obj.movingCallback; end - function set.controller(obj,val) + function set.availMotors(obj,val) % Validate that the serial number is the correct type for BSC203 if ~isempty(val) if strcmp(val(1:2), '70') @@ -153,8 +152,19 @@ function setMotor(obj,val) else error('This device is not of the correct type; serial number should start with a 70') end + % set the controller using val end end + + function val = set_x_motor(obj,val,~) + % check to make sure that no other motor is using this channel + % beforer setting + end + + function set.controller(obj,val) + % take val, and instantiate the driver for the BSC203 + Drivers.Kinesis.BSC203 + end end end From a46b47987e1f4504834079ab5c27ad7b7ee9de26 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Tue, 17 Aug 2021 13:12:59 +0100 Subject: [PATCH 10/18] Updated functions to integrate BSC203 to CC --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 192 +++++++++++++--------- Modules/+Stages/BSC203.m | 119 ++++++++++---- 2 files changed, 199 insertions(+), 112 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index 60315e913..d89a47e1a 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -2,6 +2,7 @@ properties name + motor_channels end properties(Constant, Hidden) GENERICMOTORDLL='Thorlabs.MotionControl.GenericMotorCLI.dll'; @@ -19,7 +20,7 @@ acceleration; % Acceleration maxvelocity; % Maximum velocity limit minvelocity; % Minimum velocity limit - positions; % Motor position (1 * 3 array) + positions = [NaN, NaN, NaN]; % Motor position (1 * 3 array) Homed; isMoving = false; @@ -37,20 +38,19 @@ methods(Access=private) % Constructor - function obj = KinesisBSC203(serialNo, travel, name) % Instantiate the KinesisBSC203 motor object + function obj = KinesisBSC203(serialNo, travel, name, motor_channels) % Instantiate the KinesisBSC203 motor object Drivers.Kinesis.KinesisBSC203.loaddlls; % Load DLLs if not already loaded obj.connect(serialNo); % Connect device obj.Travel = travel; obj.name = name; + obj.motor_channels = motor_channels; end end methods(Static) % Use this to create/retrieve instance associated with serialNo - function obj = instance(serialNo, travel, name) - disp('ok 4') + function obj = instance(serialNo, travel, name, motor_channels) mlock; - disp('ok 3') if nargin < 2 name = serialNo; end @@ -59,14 +59,12 @@ Objects = Drivers.Kinesis.KinesisBSC203.empty(1,0); % Create an empty class end for i = 1:length(Objects) - disp('ok 1') if isvalid(Objects(i)) && isequal(serialNo,Objects(i).singleton_id) % Find instance with the same singleton ID obj = Objects(i); return end - disp('ok 2') end - obj = Drivers.Kinesis.KinesisBSC203(serialNo, travel, name); % Create an instance + obj = Drivers.Kinesis.KinesisBSC203(serialNo, travel, name, motor_channels); % Create an instance obj.singleton_id = serialNo; % Define singleton ID Objects(end+1) = obj; % Add the instance to the object list end @@ -78,9 +76,11 @@ function connect(obj, serialNo) % serialNo := str obj.GetDevices; % Call this to build device list if not already done if ~obj.isconnected() % Connect and initialize device if not connected obj.deviceNET = Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.CreateBenchtopStepperMotor(serialNo); % Create an instance of .NET BenchtopStepperMotor - for i = 1:3 - obj.channelsNET{i} = obj.deviceNET.GetChannel(i); % Get channel objects of the device - obj.channelsNET{i}.ClearDeviceExceptions(); % Clear device exceptions via .NET interface + for i = obj.motor_channels + if ~isnan(i) + obj.channelsNET{i} = obj.deviceNET.GetChannel(i); % Get channel objects of the device + obj.channelsNET{i}.ClearDeviceExceptions(); % Clear device exceptions via .NET interface + end end obj.deviceNET.Connect(serialNo); % Connect to device via .NET interface @@ -92,29 +92,31 @@ function connect(obj, serialNo) % serialNo := str end function initialize(obj, serialNo) % Initialize all three channels of the device, serialNo := str - for i = 1:3 - try - if ~obj.channelsNET{i}.IsSettingsInitialized() - obj.channelsNET{i}.WaitForSettingsInitialized(obj.TIMEOUTSETTINGS); % Initialize the ith channel - else - disp('Device Already Initialized.') - end - if ~obj.channelsNET{i}.IsSettingsInitialized() % Device not successfully initialized - error('Unable to initialize device') - end - obj.channelsNET{i}.StartPolling(obj.TPOLLING); % Start polling device via .NET interface - obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface + for i = obj.motor_channels + if ~isnan(i) + try + if ~obj.channelsNET{i}.IsSettingsInitialized() + obj.channelsNET{i}.WaitForSettingsInitialized(obj.TIMEOUTSETTINGS); % Initialize the ith channel + else + disp('Device Already Initialized.') + end + if ~obj.channelsNET{i}.IsSettingsInitialized() % Device not successfully initialized + error('Unable to initialize device') + end + obj.channelsNET{i}.StartPolling(obj.TPOLLING); % Start polling device via .NET interface + obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface - % Initialize motor configuration - deviceID = obj.channelsNET{i}.DeviceID; - settingsLoadOption = Drivers.Kinesis.Kinesis_invisible.GetSettingsLoadOption(serialNo, deviceID); - obj.motorSettingsNET{i} = obj.channelsNET{i}.GetMotorConfiguration(serialNo, settingsLoadOption); + % Initialize motor configuration + deviceID = obj.channelsNET{i}.DeviceID; + settingsLoadOption = Drivers.Kinesis.Kinesis_invisible.GetSettingsLoadOption(serialNo, deviceID); + obj.motorSettingsNET{i} = obj.channelsNET{i}.GetMotorConfiguration(serialNo, settingsLoadOption); - % Initialize current motor settings - obj.currentDeviceSettingsNET{i}=obj.channelsNET{i}.MotorDeviceSettings; - obj.deviceInfoNET{i} = obj.channelsNET{i}.GetDeviceInfo(); % Get deviceInfo via .NET interface - catch - error(['Unable to initialize channel ', num2str(i)]); + % Initialize current motor settings + obj.currentDeviceSettingsNET{i}=obj.channelsNET{i}.MotorDeviceSettings; + obj.deviceInfoNET{i} = obj.channelsNET{i}.GetDeviceInfo(); % Get deviceInfo via .NET interface + catch + error(['Unable to initialize channel ', num2str(i)]); + end end end @@ -122,23 +124,25 @@ function initialize(obj, serialNo) % Initialize all three channels of the device function updatestatus(obj) obj.isconnected = obj.deviceNET.IsConnected(); % connection status - homed = false(1, 3); + homed = true(1, 3); moving = false(1, 3); - for i = 1:3 - obj.serialnumbers{i}=char(obj.channelsNET{i}.DeviceID); % update serial number - obj.controllername{i}=char(obj.deviceInfoNET{i}.Name); % update controleller name - obj.controllerdescription{i}=char(obj.deviceInfoNET{i}.Description); % update controller description - obj.stagename{i}=char(obj.motorSettingsNET{i}.DeviceSettingsName); % update stagename - velocityparams{i}=obj.channelsNET{i}.GetVelocityParams(); % update velocity parameter - obj.acceleration{i}=System.Decimal.ToDouble(velocityparams{i}.Acceleration); % update acceleration parameter - obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter - obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter - obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions - homed(i) = ~obj.channelsNET{i}.NeedsHoming; - if obj.channelsNET{i}.State == Thorlabs.MotionControl.GenericMotorCLI.MotorStates.Idle - moving(i) = false; - else - moving(i) = true; + for i = obj.motor_channels + if ~isnan(i) + obj.serialnumbers{i}=char(obj.channelsNET{i}.DeviceID); % update serial number + obj.controllername{i}=char(obj.deviceInfoNET{i}.Name); % update controleller name + obj.controllerdescription{i}=char(obj.deviceInfoNET{i}.Description); % update controller description + obj.stagename{i}=char(obj.motorSettingsNET{i}.DeviceSettingsName); % update stagename + velocityparams{i}=obj.channelsNET{i}.GetVelocityParams(); % update velocity parameter + obj.acceleration{i}=System.Decimal.ToDouble(velocityparams{i}.Acceleration); % update acceleration parameter + obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter + obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter + obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions + homed(i) = ~obj.channelsNET{i}.NeedsHoming; + if obj.channelsNET{i}.State == Thorlabs.MotionControl.GenericMotorCLI.MotorStates.Idle + moving(i) = false; + else + moving(i) = true; + end end end obj.Homed = all(homed); @@ -148,11 +152,18 @@ function updatestatus(obj) function disconnect(obj) obj.isconnected = obj.deviceNET.IsConnected(); % Read connection status if obj.isconnected % Disconnect device if connected - try - for i = 1:3 - obj.channelsNET{i}.StopPolling(); % Stop polling device via .NET interface - obj.channelsNET{i}.DisableDevice(); % Disable device via .NET interface + for i = obj.motor_channels + if ~isnan(i) + try + obj.channelsNET{i}.StopPolling(); % Stop polling device via .NET interface + obj.channelsNET{i}.DisableDevice(); % Disable device via .NET interface + catch + error(['Unable to disconnect device',obj.serialnumbers{i}]); + end + end + end + try obj.deviceNET.Disconnect(true) catch error(['Unable to disconnect device',obj.serialnumbers{i}]); @@ -164,10 +175,12 @@ function disconnect(obj) end function home(obj) - for i = 1 : 3 - workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout - obj.channelsNET{i}.Home(workDone); % Home device via .NET interface - obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + for i = obj.motor_channels + if ~isnan(i) + workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{i}.Home(workDone); % Home device via .NET interface + obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + end end obj.updatestatus; % Update status variables from device end @@ -178,10 +191,12 @@ function home(obj) % Error if the channel needs to be homed % Otherwise returns true tf = true; - for i = 1 : 3 - assert(~obj.channelsNET{i}.NeedsHoming,'Motor %f is not homed!', i) - assert(target_pos(i) <= max(obj.Travel) && target_pos(i) >= min(obj.Travel),... - 'Attempted to move motor %f to %f, but it is limited to %f, %f', i, target_pos, min(obj.Travel), max(obj.Travel)) + for i = obj.motor_channels + if ~isnan(i) + assert(~obj.channelsNET{i}.NeedsHoming,'Motor %f is not homed!', i) + assert(target_pos(i) <= max(obj.Travel) && target_pos(i) >= min(obj.Travel),... + 'Attempted to move motor %f to %f, but it is limited to %f, %f', i, target_pos, min(obj.Travel), max(obj.Travel)) + end end end @@ -189,13 +204,24 @@ function moveto(obj, target_pos) % Move to target position, target_pos := 1 * 3 array of double tf = obj.checkMove(target_pos); if tf - for i = 1 : 3 - try - workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout - obj.channelsNET{i}.MoveTo(target_pos(i), workDone); % Move device to position via .NET interface - obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish - catch - error(['Unable to Move channel ',obj.serialnumber{i},' to ',num2str(target_pos(i))]); + n = 1 + target_pos_channel = [NaN NaN NaN]; + for channelNo = obj.motor_channels + if isnan(channelNo) + target_pos_channel(n) = NaN; + else + target_pos_channel(n) = target_pos(channelNo) + n = n + 1; + end + for i = obj.motor_channels + if ~isnan(i) + try + workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{i}.MoveTo(target_pos(i), workDone); % Move device to position via .NET interface + obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + catch + error(['Unable to Move channel ',obj.serialnumber{i},' to ',num2str(target_pos(i))]); + end end end else @@ -249,33 +275,41 @@ function movecont(h, channelNo, varargin) % Set motor to move continuously end function stop(h, immediate) % Stop the motor moving (needed if set motor to continous) - for i = 1 : 3 - if nargin > 1 && immediate - h.channelsNET{i}.StopImmediate(); - else - h.channelsNET{channelNo}.Stop(h.TIMEOUTMOVE); % Stop motor movement via.NET interface + for i = obj.motor_channels + if ~isnan(i) + if nargin > 1 && immediate + h.channelsNET{i}.StopImmediate(); + else + h.channelsNET{channelNo}.Stop(h.TIMEOUTMOVE); % Stop motor movement via.NET interface + end end end obj.updatestatus % Update status variables from device end function pos = get.positions(obj) - pos = [0 0 0]; - for i = 1 : 3 - pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); + pos = [NaN NaN NaN]; + for i = obj.motor_channels + if ~isnan(i) + pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); + end end end function enable(obj) - for i = 1 : 3 - obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface + for i = obj.motor_channels + if ~isnan(i) + obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface + end end obj.updatestatus end function disable(obj) - for i = 1 : 3 - obj.channelsNET{i}.DisableDevice(); % Enable device via .NET interface + for i = obj.motor_channels + if ~isnan(i) + obj.channelsNET{i}.DisableDevice(); % Enable device via .NET interface + end end obj.updatestatus end diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index bbbc42a14..531d6d80c 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -12,14 +12,13 @@ end properties(SetObservable,AbortSet) availMotors = Drivers.Kinesis.KinesisBSC203.getAvailMotors; - controller % calibration = [500 500 500] Prefs.DoubleArray([2 2 2],'help_text','Calibration for the motor distance to true distance'); end properties(GetObservable, SetObservable, AbortSet) - x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set_function','help_text','Which channel in the controller controls the x direction motor'); - y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'help_text','Which channel in the controller controls the y direction motor'); - z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'help_text','Which channel in the controller controls the z direction motor'); + x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set',@set_x_motor,'help_text','Which channel in the controller controls the x direction motor'); + y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set',@set_y_motor,'help_text','Which channel in the controller controls the y direction motor'); + z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set',@set_z_motor,'help_text','Which channel in the controller controls the z direction motor'); end properties(SetAccess=private,SetObservable,AbortSet) % Default here will only matter if motors aren't set @@ -34,9 +33,9 @@ properties (Constant) % Currently used as placeholders to avoid the error as Abstract properties - xRange = [0 8]; - yRange = [0 8]; - zRange = [0 8]; + xRange = [-2 2] * 1000; + yRange = [-2 2] * 1000; + zRange = [-2 2] * 1000; end methods(Static) @@ -53,9 +52,6 @@ methods(Access=private) function obj = BSC203() obj.loadPrefs; - if ~isempty(obj.controller) - obj.controller = obj.availMotors{1}; - end end end % Callback functions for BSC203 Motor @@ -82,17 +78,29 @@ function delete(obj) end end function pos = get.position(obj) - % this function reads the current motor position - pos = obj.motors.positions; + % this function reads the current motor position + pos = [NaN NaN NaN]; + n = 1 + for channelNo = obj.motor_channels + positions = obj.motors.positions; + if isnan(channelNo) + pos(n) = NaN; + else + pos(n) = positions(channelNo) + n = n + 1; + end end function move(obj,x,y,z) pos = obj.position; + new_pos = [x,y,z]; % Allow for empty inputs in for loop below + for i = 1:length(new_pos) if isempty(new_pos(i)) || new_pos{i}==pos(i) || isnan(new_pos(i)) new_pos(i) = pos(i); end end + if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) obj.motors.moveto(new_pos) end @@ -126,44 +134,89 @@ function abort(obj,varargin) end drawnow; % Flush callback queue end - - % Motor construction callbacks - function setMotor(obj,val) - % Add new motor - disp(isstring(val)) - obj.motors = Drivers.Kinesis.KinesisBSC203.instance(val, [0 8], val); - % Listeners will follow lifecycle of their motor - addlistener(obj.motors,'isMoving','PostSet',@obj.movingCallback); - addlistener(obj.motors,'Homed','PostSet',@obj.homedCallback); - % Intialize values - obj.homedCallback; - obj.movingCallback; - end function set.availMotors(obj,val) % Validate that the serial number is the correct type for BSC203 if ~isempty(val) if strcmp(val(1:2), '70') try - obj.setMotor(val); + obj.set_controller(val); catch err rethrow(err) end else error('This device is not of the correct type; serial number should start with a 70') end - % set the controller using val end end - function val = set_x_motor(obj,val,~) - % check to make sure that no other motor is using this channel - % beforer setting + function val = set_x_motor(obj, channelNo, ~) + % check to make sure that no other motor is using this channel before setting x motor + if ~isnan(obj.motor_channels(1)) && obj.motor_channels(1) ~= channelNo + error(strcat('Channel ', num2str(channelNo), ' was already used for other motors.')) + else + val = channelNo; + + if isnumeric(channelNo) + obj.motor_channels(1) = channelNo; + + elseif strcmp(channelNo, '') + obj.motor_channels(1) = NaN; + else + error('x_motor assignment type was not recognised.') + end + end + obj.set_controller + end + + function val = set_y_motor(obj, channelNo, ~) + % check to make sure that no other motor is using this channel before setting y motor + if ~isnan(obj.motor_channels(2)) && obj.motor_channels(2) ~= channelNo + error(strcat('Channel ', num2str(channelNo), ' was already used for other motors.')) + else + val = channelNo; + + if isnumeric(channelNo) + obj.motor_channels(2) = channelNo; + + elseif strcmp(channelNo, '') + obj.motor_channels(2) = NaN; + else + error('x_motor assignment type was not recognised.') + end + end + obj.set_controller + end + + function val = set_z_motor(obj, channelNo, ~) + % check to make sure that no other motor is using this channel before setting z motor + if ~isnan(obj.motor_channels(3)) && obj.motor_channels(3) ~= channelNo + error(strcat('Channel ', num2str(channelNo), ' was already used for other motors.')) + else + val = channelNo; + + if isnumeric(channelNo) + obj.motor_channels(3) = channelNo; + + elseif strcmp(channelNo, '') + obj.motor_channels(3) = NaN; + else + error('x_motor assignment type was not recognised.') + end + end + obj.set_controller end - function set.controller(obj,val) - % take val, and instantiate the driver for the BSC203 - Drivers.Kinesis.BSC203 + function set_controller(obj, SerialNo) + % take val, and instantiate the driver for the BSC203 + + obj.motors = Drivers.Kinesis.KinesisBSC203.instance(SerialNo, [0 8], SerialNo, obj.motor_channels); + % Listeners will follow lifecycle of their motor + addlistener(obj.motors,'isMoving','PostSet',@obj.movingCallback); + addlistener(obj.motors,'Homed','PostSet',@obj.homedCallback); + % Intialize values + obj.homedCallback; + obj.movingCallback; end end end From ac28fc8c311fc9c207723d93dcb73d4eb3fd8bb7 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Tue, 17 Aug 2021 15:15:16 +0100 Subject: [PATCH 11/18] Tested on setup, still difficulties loading prefs --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 5 ++-- Modules/+Stages/BSC203.m | 36 +++++++++++++---------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index d89a47e1a..668667d14 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -204,14 +204,15 @@ function moveto(obj, target_pos) % Move to target position, target_pos := 1 * 3 array of double tf = obj.checkMove(target_pos); if tf - n = 1 + n = 1; target_pos_channel = [NaN NaN NaN]; for channelNo = obj.motor_channels if isnan(channelNo) target_pos_channel(n) = NaN; else - target_pos_channel(n) = target_pos(channelNo) + target_pos_channel(n) = target_pos(channelNo); n = n + 1; + end end for i = obj.motor_channels if ~isnan(i) diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index 531d6d80c..20c6c3f69 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -10,15 +10,12 @@ properties prefs = {'availMotors','x_motor','y_motor','z_motor'}; end - properties(SetObservable,AbortSet) - availMotors = Drivers.Kinesis.KinesisBSC203.getAvailMotors; -% calibration = [500 500 500] Prefs.DoubleArray([2 2 2],'help_text','Calibration for the motor distance to true distance'); - end properties(GetObservable, SetObservable, AbortSet) - x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set',@set_x_motor,'help_text','Which channel in the controller controls the x direction motor'); - y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set',@set_y_motor,'help_text','Which channel in the controller controls the y direction motor'); - z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set',@set_z_motor,'help_text','Which channel in the controller controls the z direction motor'); + availMotors = Drivers.Kinesis.KinesisBSC203.getAvailMotors; + x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set',@Stages.BSC203.set_x_motor,'help_text','Which channel in the controller controls the x direction motor'); + y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set',@Stages.BSC203.set_y_motor,'help_text','Which channel in the controller controls the y direction motor'); + z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set',@Stages.BSC203.set_z_motor,'help_text','Which channel in the controller controls the z direction motor'); end properties(SetAccess=private,SetObservable,AbortSet) % Default here will only matter if motors aren't set @@ -78,16 +75,20 @@ function delete(obj) end end function pos = get.position(obj) - % this function reads the current motor position + % this function reads the current motor position pos = [NaN NaN NaN]; - n = 1 - for channelNo = obj.motor_channels - positions = obj.motors.positions; - if isnan(channelNo) - pos(n) = NaN; - else - pos(n) = positions(channelNo) - n = n + 1; + if ~isempty(obj.motors) && isobject(obj.motors) && isvalid(obj.motors) + n = 1; + for channelNo = obj.motor_channels + positions = obj.motors.positions; + pos = positions; + if isnan(channelNo) + pos(n) = NaN; + else + pos(n) = positions(channelNo); + n = n + 1; + end + end end end function move(obj,x,y,z) @@ -138,6 +139,9 @@ function abort(obj,varargin) function set.availMotors(obj,val) % Validate that the serial number is the correct type for BSC203 if ~isempty(val) + if iscell(val) + val = val{:}; + end if strcmp(val(1:2), '70') try obj.set_controller(val); From 90d5d02069b98793e0729c4cffb8499647d7f234 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Tue, 17 Aug 2021 15:51:30 +0100 Subject: [PATCH 12/18] Change to the set function --- Modules/+Stages/BSC203.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index 20c6c3f69..1831bd2ed 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -13,9 +13,9 @@ properties(GetObservable, SetObservable, AbortSet) availMotors = Drivers.Kinesis.KinesisBSC203.getAvailMotors; - x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set',@Stages.BSC203.set_x_motor,'help_text','Which channel in the controller controls the x direction motor'); - y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set',@Stages.BSC203.set_y_motor,'help_text','Which channel in the controller controls the y direction motor'); - z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set',@Stages.BSC203.set_z_motor,'help_text','Which channel in the controller controls the z direction motor'); + x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set','set_x_motor','help_text','Which channel in the controller controls the x direction motor'); + y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set','set_y_motor','help_text','Which channel in the controller controls the y direction motor'); + z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set','set_z_motor','help_text','Which channel in the controller controls the z direction motor'); end properties(SetAccess=private,SetObservable,AbortSet) % Default here will only matter if motors aren't set From 9e4521d2d5a45302dddcb736cc55b2379cde7087 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Mon, 13 Sep 2021 17:25:25 -0400 Subject: [PATCH 13/18] Fixed bugs in KinesisBSC203 --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 29 ++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index 668667d14..1d77a3b82 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -26,6 +26,7 @@ isMoving = false; Travel = [-2 2] * 1000; + calibration = 1; end properties(Hidden) @@ -38,18 +39,19 @@ methods(Access=private) % Constructor - function obj = KinesisBSC203(serialNo, travel, name, motor_channels) % Instantiate the KinesisBSC203 motor object - Drivers.Kinesis.KinesisBSC203.loaddlls; % Load DLLs if not already loaded + function obj = KinesisBSC203(serialNo, travel, name, motor_channels, calibration) % Instantiate the KinesisBSC203 motor object + Drivers.Kinesis.KinesisBSC203.loaddlls; % Load DLLs if not already loaded + obj.motor_channels = motor_channels; + obj.calibration = calibration; obj.connect(serialNo); % Connect device obj.Travel = travel; - obj.name = name; - obj.motor_channels = motor_channels; + obj.name = name; end end methods(Static) % Use this to create/retrieve instance associated with serialNo - function obj = instance(serialNo, travel, name, motor_channels) + function obj = instance(serialNo, travel, name, motor_channels, calibration) mlock; if nargin < 2 name = serialNo; @@ -64,7 +66,7 @@ return end end - obj = Drivers.Kinesis.KinesisBSC203(serialNo, travel, name, motor_channels); % Create an instance + obj = Drivers.Kinesis.KinesisBSC203(serialNo, travel, name, motor_channels, calibration); % Create an instance obj.singleton_id = serialNo; % Define singleton ID Objects(end+1) = obj; % Add the instance to the object list end @@ -76,14 +78,16 @@ function connect(obj, serialNo) % serialNo := str obj.GetDevices; % Call this to build device list if not already done if ~obj.isconnected() % Connect and initialize device if not connected obj.deviceNET = Thorlabs.MotionControl.Benchtop.StepperMotorCLI.BenchtopStepperMotor.CreateBenchtopStepperMotor(serialNo); % Create an instance of .NET BenchtopStepperMotor + obj.deviceNET.Connect(serialNo); % Connect to device via .NET interface for i = obj.motor_channels if ~isnan(i) obj.channelsNET{i} = obj.deviceNET.GetChannel(i); % Get channel objects of the device obj.channelsNET{i}.ClearDeviceExceptions(); % Clear device exceptions via .NET interface + else + disp('Motor channel is empty') end end - obj.deviceNET.Connect(serialNo); % Connect to device via .NET interface obj.initialize(serialNo) % Initialize the device else % Device already connected error('Device is already connected') @@ -136,7 +140,7 @@ function updatestatus(obj) obj.acceleration{i}=System.Decimal.ToDouble(velocityparams{i}.Acceleration); % update acceleration parameter obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter - obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); % motor positions + obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position * obj.calibration); % motor positions homed(i) = ~obj.channelsNET{i}.NeedsHoming; if obj.channelsNET{i}.State == Thorlabs.MotionControl.GenericMotorCLI.MotorStates.Idle moving(i) = false; @@ -214,11 +218,13 @@ function moveto(obj, target_pos) n = n + 1; end end + target_pos_calib = target_pos / obj.calibration; for i = obj.motor_channels if ~isnan(i) try workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout - obj.channelsNET{i}.MoveTo(target_pos(i), workDone); % Move device to position via .NET interface + + obj.channelsNET{i}.MoveTo(target_pos_calib(i), workDone); % Move device to position via .NET interface obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish catch error(['Unable to Move channel ',obj.serialnumber{i},' to ',num2str(target_pos(i))]); @@ -244,7 +250,8 @@ function step(obj, channelNo, distance) % Calculate the position after the step step_pos = [0 0 0]; - obj.channelsNET{channelNo}.SetJogStepSize(abs(distance)) % Set the step size for jog + distance_calib = distance / obj.calibration; + obj.channelsNET{channelNo}.SetJogStepSize(abs(distance_calib)) % Set the step size for jog step_pos(channelNo) = obj.channelsNET{channelNo}.GetJogStepSize(); target_pos = obj.positions + step_pos; @@ -292,7 +299,7 @@ function stop(h, immediate) % Stop the motor moving (needed if set motor to cont pos = [NaN NaN NaN]; for i = obj.motor_channels if ~isnan(i) - pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position); + pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position) * obj.calibration; end end end From 258a16b56e00f825d604cc53026aa0bcc96c34b5 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Tue, 14 Sep 2021 13:36:37 -0400 Subject: [PATCH 14/18] Added set_controller method, fixed some typos, disconnect during delte --- Modules/+Stages/BSC203.m | 64 ++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index 1831bd2ed..137ea426c 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -12,7 +12,7 @@ end properties(GetObservable, SetObservable, AbortSet) - availMotors = Drivers.Kinesis.KinesisBSC203.getAvailMotors; + availMotors = Prefs.MultipleChoice('', 'choices', Drivers.Kinesis.KinesisBSC203.getAvailMotors, 'allow_empty', true, 'set', 'set_controller'); x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set','set_x_motor','help_text','Which channel in the controller controls the x direction motor'); y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set','set_y_motor','help_text','Which channel in the controller controls the y direction motor'); z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set','set_z_motor','help_text','Which channel in the controller controls the z direction motor'); @@ -71,6 +71,7 @@ function movingCallback(obj,varargin) methods function delete(obj) if isobject(obj.motors) + obj.motors.disconnect; delete(obj.motors); end end @@ -97,7 +98,7 @@ function move(obj,x,y,z) new_pos = [x,y,z]; % Allow for empty inputs in for loop below for i = 1:length(new_pos) - if isempty(new_pos(i)) || new_pos{i}==pos(i) || isnan(new_pos(i)) + if isempty(new_pos(i)) || new_pos(i)==pos(i) || isnan(new_pos(i)) new_pos(i) = pos(i); end end @@ -105,6 +106,7 @@ function move(obj,x,y,z) if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) obj.motors.moveto(new_pos) end + obj.position; % to update motor position end function enable(obj) @@ -136,23 +138,23 @@ function abort(obj,varargin) drawnow; % Flush callback queue end - function set.availMotors(obj,val) - % Validate that the serial number is the correct type for BSC203 - if ~isempty(val) - if iscell(val) - val = val{:}; - end - if strcmp(val(1:2), '70') - try - obj.set_controller(val); - catch err - rethrow(err) - end - else - error('This device is not of the correct type; serial number should start with a 70') - end - end - end +% function set.availMotors(obj,val) +% % Validate that the serial number is the correct type for BSC203 +% if ~isempty(val) +% if iscell(val) +% val = val{:}; +% end +% if strcmp(val(1:2), '70') +% try +% obj.set_controller(val); +% catch err +% rethrow(err) +% end +% else +% error('This device is not of the correct type; serial number should start with a 70') +% end +% end +% end function val = set_x_motor(obj, channelNo, ~) % check to make sure that no other motor is using this channel before setting x motor @@ -211,16 +213,20 @@ function abort(obj,varargin) obj.set_controller end - function set_controller(obj, SerialNo) - % take val, and instantiate the driver for the BSC203 - - obj.motors = Drivers.Kinesis.KinesisBSC203.instance(SerialNo, [0 8], SerialNo, obj.motor_channels); - % Listeners will follow lifecycle of their motor - addlistener(obj.motors,'isMoving','PostSet',@obj.movingCallback); - addlistener(obj.motors,'Homed','PostSet',@obj.homedCallback); - % Intialize values - obj.homedCallback; - obj.movingCallback; + function val = set_controller(obj, SerialNo, ~) + if ~isempty(serialNo) + % take val, and instantiate the driver for the BSC203 + val = SerialNo; + obj.motors = Drivers.Kinesis.KinesisBSC203.instance(SerialNo, [0 8], SerialNo, obj.motor_channels, 0.5); + % Listeners will follow lifecycle of their motor + addlistener(obj.motors,'isMoving','PostSet',@obj.movingCallback); + addlistener(obj.motors,'Homed','PostSet',@obj.homedCallback); + % Intialize values + obj.homedCallback; + obj.movingCallback; + else + obj.motors.disconnect; + end end end end From d9a8c7d3209ff357b2ee486565c645b37589c183 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Thu, 16 Sep 2021 15:25:28 -0400 Subject: [PATCH 15/18] Added generic set_motor function, delete motor when empty, some comments --- Modules/+Stages/BSC203.m | 116 +++++++++++++++------------------------ 1 file changed, 45 insertions(+), 71 deletions(-) diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index 137ea426c..8d73c42b3 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -50,6 +50,35 @@ function obj = BSC203() obj.loadPrefs; end + + function val = set_motor_generic(obj, channelNo, motor_index) + % Generic function that checks to make sure that no other motor + % is using this channel before setting motor connection + + not_motor_index = [1 2 3]; + not_motor_index = not_motor_index(not_motor_index ~= motor_index); + + if isempty(channelNo) + obj.motor_channels(motor_index) = NaN; + obj.motors.disconnect; + delete(obj.motors); + elseif channelNo ~= obj.motor_channels(motor_index) % Need to check to prevent initialising motor multiple times at startup + if any(obj.motor_channels(not_motor_index) == channelNo) % Check that not equal to an existing channel + error(strcat('Channel ', num2str(channelNo), ' was already used for other motors.')) + else + if ~isempty(obj.motors) && isobject(obj.motors) && isvalid(obj.motors) && obj.motors.isconnected + obj.motors.disconnect; + delete(obj.motors); + end + + obj.motor_channels(motor_index) = channelNo; + end + end + + obj.set_controller(obj.availMotors); + + val = channelNo; + end end % Callback functions for BSC203 Motor methods(Access=?KinesisBSC203) @@ -93,20 +122,21 @@ function delete(obj) end end function move(obj,x,y,z) - pos = obj.position; + % Method to move the motor to a given position [x, y, z] + pos = obj.position; % reads the current position - new_pos = [x,y,z]; % Allow for empty inputs in for loop below + new_pos = [x,y,z]; for i = 1:length(new_pos) + % Check whether the input target position for a certain axis is empty, if isempty(new_pos(i)) || new_pos(i)==pos(i) || isnan(new_pos(i)) - new_pos(i) = pos(i); + new_pos(i) = pos(i); % if empty, set the target position to the original position of that axis end end if ~isempty(obj.motors)&&isobject(obj.motors) && isvalid(obj.motors) - obj.motors.moveto(new_pos) + obj.motors.moveto(new_pos) % move to the new position end - obj.position; % to update motor position end function enable(obj) @@ -137,84 +167,24 @@ function abort(obj,varargin) end drawnow; % Flush callback queue end - -% function set.availMotors(obj,val) -% % Validate that the serial number is the correct type for BSC203 -% if ~isempty(val) -% if iscell(val) -% val = val{:}; -% end -% if strcmp(val(1:2), '70') -% try -% obj.set_controller(val); -% catch err -% rethrow(err) -% end -% else -% error('This device is not of the correct type; serial number should start with a 70') -% end -% end -% end function val = set_x_motor(obj, channelNo, ~) - % check to make sure that no other motor is using this channel before setting x motor - if ~isnan(obj.motor_channels(1)) && obj.motor_channels(1) ~= channelNo - error(strcat('Channel ', num2str(channelNo), ' was already used for other motors.')) - else - val = channelNo; - - if isnumeric(channelNo) - obj.motor_channels(1) = channelNo; - - elseif strcmp(channelNo, '') - obj.motor_channels(1) = NaN; - else - error('x_motor assignment type was not recognised.') - end - end - obj.set_controller + % Use generic function with appropriate index to check to make sure that no other motor is using this channel before setting x motor + val = obj.set_motor_generic(channelNo, 1); end function val = set_y_motor(obj, channelNo, ~) % check to make sure that no other motor is using this channel before setting y motor - if ~isnan(obj.motor_channels(2)) && obj.motor_channels(2) ~= channelNo - error(strcat('Channel ', num2str(channelNo), ' was already used for other motors.')) - else - val = channelNo; - - if isnumeric(channelNo) - obj.motor_channels(2) = channelNo; - - elseif strcmp(channelNo, '') - obj.motor_channels(2) = NaN; - else - error('x_motor assignment type was not recognised.') - end - end - obj.set_controller + val = obj.set_motor_generic(channelNo, 2); end function val = set_z_motor(obj, channelNo, ~) % check to make sure that no other motor is using this channel before setting z motor - if ~isnan(obj.motor_channels(3)) && obj.motor_channels(3) ~= channelNo - error(strcat('Channel ', num2str(channelNo), ' was already used for other motors.')) - else - val = channelNo; - - if isnumeric(channelNo) - obj.motor_channels(3) = channelNo; - - elseif strcmp(channelNo, '') - obj.motor_channels(3) = NaN; - else - error('x_motor assignment type was not recognised.') - end - end - obj.set_controller + val = obj.set_motor_generic(channelNo, 3); end function val = set_controller(obj, SerialNo, ~) - if ~isempty(serialNo) + if ~isempty(SerialNo) % take val, and instantiate the driver for the BSC203 val = SerialNo; obj.motors = Drivers.Kinesis.KinesisBSC203.instance(SerialNo, [0 8], SerialNo, obj.motor_channels, 0.5); @@ -225,7 +195,11 @@ function abort(obj,varargin) obj.homedCallback; obj.movingCallback; else - obj.motors.disconnect; + if ~isempty(obj.motors) && isobject(obj.motors) && isvalid(obj.motors) && obj.motors.isconnected + obj.motors.disconnect; + delete(obj.motors); + end + val = []; end end end From 238666ff47965b0a02704808b4b4a0ec0c32cdbc Mon Sep 17 00:00:00 2001 From: cassie-song Date: Thu, 16 Sep 2021 17:00:01 -0400 Subject: [PATCH 16/18] simplify moveto/updatestatus w.r.t. permuting channels; still buggy in certain channel configurations --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 107 ++++++++++------------ 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index 1d77a3b82..748a782be 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -130,19 +130,20 @@ function updatestatus(obj) obj.isconnected = obj.deviceNET.IsConnected(); % connection status homed = true(1, 3); moving = false(1, 3); - for i = obj.motor_channels - if ~isnan(i) - obj.serialnumbers{i}=char(obj.channelsNET{i}.DeviceID); % update serial number - obj.controllername{i}=char(obj.deviceInfoNET{i}.Name); % update controleller name - obj.controllerdescription{i}=char(obj.deviceInfoNET{i}.Description); % update controller description - obj.stagename{i}=char(obj.motorSettingsNET{i}.DeviceSettingsName); % update stagename - velocityparams{i}=obj.channelsNET{i}.GetVelocityParams(); % update velocity parameter - obj.acceleration{i}=System.Decimal.ToDouble(velocityparams{i}.Acceleration); % update acceleration parameter - obj.maxvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MaxVelocity); % update max velocit parameter - obj.minvelocity{i}=System.Decimal.ToDouble(velocityparams{i}.MinVelocity); % update Min velocity parameter - obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position * obj.calibration); % motor positions - homed(i) = ~obj.channelsNET{i}.NeedsHoming; - if obj.channelsNET{i}.State == Thorlabs.MotionControl.GenericMotorCLI.MotorStates.Idle + for i = 1:3 + + if ~isnan(obj.motor_channels(i)) + obj.serialnumbers{obj.motor_channels(i)}=char(obj.channelsNET{obj.motor_channels(i)}.DeviceID); % update serial number + obj.controllername{obj.motor_channels(i)}=char(obj.deviceInfoNET{obj.motor_channels(i)}.Name); % update controleller name + obj.controllerdescription{obj.motor_channels(i)}=char(obj.deviceInfoNET{obj.motor_channels(i)}.Description); % update controller description + obj.stagename{obj.motor_channels(i)}=char(obj.motorSettingsNET{obj.motor_channels(i)}.DeviceSettingsName); % update stagename + velocityparams{obj.motor_channels(i)}=obj.channelsNET{obj.motor_channels(i)}.GetVelocityParams(); % update velocity parameter + obj.acceleration{obj.motor_channels(i)}=System.Decimal.ToDouble(velocityparams{obj.motor_channels(i)}.Acceleration); % update acceleration parameter + obj.maxvelocity{obj.motor_channels(i)}=System.Decimal.ToDouble(velocityparams{obj.motor_channels(i)}.MaxVelocity); % update max velocit parameter + obj.minvelocity{obj.motor_channels(i)}=System.Decimal.ToDouble(velocityparams{obj.motor_channels(i)}.MinVelocity); % update Min velocity parameter + obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{obj.motor_channels(i)}.Position) * obj.calibration; % motor positions + homed(i) = ~obj.channelsNET{obj.motor_channels(i)}.NeedsHoming; + if obj.channelsNET{obj.motor_channels(i)}.State == Thorlabs.MotionControl.GenericMotorCLI.MotorStates.Idle moving(i) = false; else moving(i) = true; @@ -156,13 +157,13 @@ function updatestatus(obj) function disconnect(obj) obj.isconnected = obj.deviceNET.IsConnected(); % Read connection status if obj.isconnected % Disconnect device if connected - for i = obj.motor_channels - if ~isnan(i) + for i = 1:3 + if ~isnan(obj.motor_channels(i)) try - obj.channelsNET{i}.StopPolling(); % Stop polling device via .NET interface - obj.channelsNET{i}.DisableDevice(); % Disable device via .NET interface + obj.channelsNET{obj.motor_channels(i)}.StopPolling(); % Stop polling device via .NET interface + obj.channelsNET{obj.motor_channels(i)}.DisableDevice(); % Disable device via .NET interface catch - error(['Unable to disconnect device',obj.serialnumbers{i}]); + error(['Unable to disconnect device',obj.serialnumbers{obj.motor_channels(i)}]); end end @@ -170,7 +171,7 @@ function disconnect(obj) try obj.deviceNET.Disconnect(true) catch - error(['Unable to disconnect device',obj.serialnumbers{i}]); + error(['Unable to disconnect device',obj.serialnumbers{obj.motor_channels(i)}]); end obj.isconnected = obj.deviceNET.IsConnected(); else % Cannot disconnect because device not connected @@ -179,11 +180,11 @@ function disconnect(obj) end function home(obj) - for i = obj.motor_channels - if ~isnan(i) - workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout - obj.channelsNET{i}.Home(workDone); % Home device via .NET interface - obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + for i = 1:3 + if ~isnan(obj.motor_channels(i)) + workDone=obj.channelsNET{obj.motor_channels(i)}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{obj.motor_channels(i)}.Home(workDone); % Home device via .NET interface + obj.channelsNET{obj.motor_channels(i)}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish end end obj.updatestatus; % Update status variables from device @@ -195,11 +196,11 @@ function home(obj) % Error if the channel needs to be homed % Otherwise returns true tf = true; - for i = obj.motor_channels - if ~isnan(i) - assert(~obj.channelsNET{i}.NeedsHoming,'Motor %f is not homed!', i) + for i = 1:3 + if ~isnan(obj.motor_channels(i)) + assert(~obj.channelsNET{obj.motor_channels(i)}.NeedsHoming,'Motor %f is not homed!', i) assert(target_pos(i) <= max(obj.Travel) && target_pos(i) >= min(obj.Travel),... - 'Attempted to move motor %f to %f, but it is limited to %f, %f', i, target_pos, min(obj.Travel), max(obj.Travel)) + 'Attempted to move motor %f to %f, but it is limited to %f, %f', obj.motor_channels(i), target_pos, min(obj.Travel), max(obj.Travel)) end end end @@ -208,26 +209,16 @@ function moveto(obj, target_pos) % Move to target position, target_pos := 1 * 3 array of double tf = obj.checkMove(target_pos); if tf - n = 1; - target_pos_channel = [NaN NaN NaN]; - for channelNo = obj.motor_channels - if isnan(channelNo) - target_pos_channel(n) = NaN; - else - target_pos_channel(n) = target_pos(channelNo); - n = n + 1; - end - end target_pos_calib = target_pos / obj.calibration; - for i = obj.motor_channels - if ~isnan(i) + obj.isMoving = true; + for i = 1:3 + if ~isnan(obj.motor_channels(i)) try - workDone=obj.channelsNET{i}.InitializeWaitHandler(); % Initialise Waithandler for timeout - - obj.channelsNET{i}.MoveTo(target_pos_calib(i), workDone); % Move device to position via .NET interface - obj.channelsNET{i}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + workDone=obj.channelsNET{obj.motor_channels(i)}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{obj.motor_channels(i)}.MoveTo(target_pos_calib(i), workDone); % Move device to position via .NET interface + obj.channelsNET{obj.motor_channels(i)}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish catch - error(['Unable to Move channel ',obj.serialnumber{i},' to ',num2str(target_pos(i))]); + error(['Unable to Move channel ',obj.serialnumbers{obj.motor_channels(i)},' to ',num2str(target_pos(i))]); end end end @@ -283,12 +274,12 @@ function movecont(h, channelNo, varargin) % Set motor to move continuously end function stop(h, immediate) % Stop the motor moving (needed if set motor to continous) - for i = obj.motor_channels - if ~isnan(i) + for i = 1:3 + if ~isnan(obj.motor_channels(i)) if nargin > 1 && immediate - h.channelsNET{i}.StopImmediate(); + h.channelsNET{obj.motor_channels(i)}.StopImmediate(); else - h.channelsNET{channelNo}.Stop(h.TIMEOUTMOVE); % Stop motor movement via.NET interface + h.channelsNET{obj.motor_channels(i)}.Stop(h.TIMEOUTMOVE); % Stop motor movement via.NET interface end end end @@ -297,26 +288,26 @@ function stop(h, immediate) % Stop the motor moving (needed if set motor to cont function pos = get.positions(obj) pos = [NaN NaN NaN]; - for i = obj.motor_channels - if ~isnan(i) - pos(i) = System.Decimal.ToDouble(obj.channelsNET{i}.Position) * obj.calibration; + for i = 1:3 + if ~isnan(obj.motor_channels(i)) + pos(i) = System.Decimal.ToDouble(obj.channelsNET{obj.motor_channels(i)}.Position) * obj.calibration; end end end function enable(obj) - for i = obj.motor_channels - if ~isnan(i) - obj.channelsNET{i}.EnableDevice(); % Enable device via .NET interface + for i = 1:3 + if ~isnan(obj.motor_channels(i)) + obj.channelsNET{obj.motor_channels(i)}.EnableDevice(); % Enable device via .NET interface end end obj.updatestatus end function disable(obj) - for i = obj.motor_channels - if ~isnan(i) - obj.channelsNET{i}.DisableDevice(); % Enable device via .NET interface + for i = 1:3 + if ~isnan(obj.motor_channels(i)) + obj.channelsNET{obj.motor_channels(i)}.DisableDevice(); % Enable device via .NET interface end end obj.updatestatus From da06e01033753c9e47af3aedeabd998747e4a435 Mon Sep 17 00:00:00 2001 From: cassie-song Date: Mon, 20 Sep 2021 12:40:15 -0400 Subject: [PATCH 17/18] Made motor channels readonly pending fix of incorrect motors moving when x, y, z channels are not 1,2,3 --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 2 +- Modules/+Stages/BSC203.m | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index 748a782be..018f63575 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -1,5 +1,5 @@ classdef KinesisBSC203 < Drivers.Kinesis.Kinesis_invisible & Modules.Driver - + % Driver for kinesis stages. Currently only works with motor_channels = [1 2 3] (i.e. x y z motors in ascending order). Situations other than this will result in wrong motors moving, to be fixed. properties name motor_channels diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index 8d73c42b3..acacc3eb2 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -13,9 +13,9 @@ properties(GetObservable, SetObservable, AbortSet) availMotors = Prefs.MultipleChoice('', 'choices', Drivers.Kinesis.KinesisBSC203.getAvailMotors, 'allow_empty', true, 'set', 'set_controller'); - x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set','set_x_motor','help_text','Which channel in the controller controls the x direction motor'); - y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set','set_y_motor','help_text','Which channel in the controller controls the y direction motor'); - z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set','set_z_motor','help_text','Which channel in the controller controls the z direction motor'); + x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set','set_x_motor','help_text','Which channel in the controller controls the x direction motor','readonly',true); % Show only for now; need to fix driver to be able to change properly + y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set','set_y_motor','help_text','Which channel in the controller controls the y direction motor','readonly',true); + z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set','set_z_motor','help_text','Which channel in the controller controls the z direction motor','readonly',true); end properties(SetAccess=private,SetObservable,AbortSet) % Default here will only matter if motors aren't set From a5ff082d452b591159cbfca11b39e734b81e65af Mon Sep 17 00:00:00 2001 From: cassie-song Date: Tue, 21 Sep 2021 12:07:11 -0400 Subject: [PATCH 18/18] changed the factor to pref, added lines to rethrow error, shortened some lines --- Modules/+Drivers/+Kinesis/KinesisBSC203.m | 49 +++++++++++------------ Modules/+Stages/BSC203.m | 5 ++- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/Modules/+Drivers/+Kinesis/KinesisBSC203.m b/Modules/+Drivers/+Kinesis/KinesisBSC203.m index 018f63575..56f02aec8 100644 --- a/Modules/+Drivers/+Kinesis/KinesisBSC203.m +++ b/Modules/+Drivers/+Kinesis/KinesisBSC203.m @@ -25,8 +25,8 @@ Homed; isMoving = false; - Travel = [-2 2] * 1000; - calibration = 1; + Travel; + factor; end properties(Hidden) @@ -39,10 +39,10 @@ methods(Access=private) % Constructor - function obj = KinesisBSC203(serialNo, travel, name, motor_channels, calibration) % Instantiate the KinesisBSC203 motor object + function obj = KinesisBSC203(serialNo, travel, name, motor_channels, factor) % Instantiate the KinesisBSC203 motor object Drivers.Kinesis.KinesisBSC203.loaddlls; % Load DLLs if not already loaded obj.motor_channels = motor_channels; - obj.calibration = calibration; + obj.factor = factor; obj.connect(serialNo); % Connect device obj.Travel = travel; obj.name = name; @@ -51,7 +51,7 @@ methods(Static) % Use this to create/retrieve instance associated with serialNo - function obj = instance(serialNo, travel, name, motor_channels, calibration) + function obj = instance(serialNo, travel, name, motor_channels, factor) mlock; if nargin < 2 name = serialNo; @@ -66,7 +66,7 @@ return end end - obj = Drivers.Kinesis.KinesisBSC203(serialNo, travel, name, motor_channels, calibration); % Create an instance + obj = Drivers.Kinesis.KinesisBSC203(serialNo, travel, name, motor_channels, factor); % Create an instance obj.singleton_id = serialNo; % Define singleton ID Objects(end+1) = obj; % Add the instance to the object list end @@ -118,8 +118,9 @@ function initialize(obj, serialNo) % Initialize all three channels of the device % Initialize current motor settings obj.currentDeviceSettingsNET{i}=obj.channelsNET{i}.MotorDeviceSettings; obj.deviceInfoNET{i} = obj.channelsNET{i}.GetDeviceInfo(); % Get deviceInfo via .NET interface - catch - error(['Unable to initialize channel ', num2str(i)]); + catch err + disp(['Unable to initialize channel ', num2str(i)]); + rethrow err end end end @@ -141,7 +142,7 @@ function updatestatus(obj) obj.acceleration{obj.motor_channels(i)}=System.Decimal.ToDouble(velocityparams{obj.motor_channels(i)}.Acceleration); % update acceleration parameter obj.maxvelocity{obj.motor_channels(i)}=System.Decimal.ToDouble(velocityparams{obj.motor_channels(i)}.MaxVelocity); % update max velocit parameter obj.minvelocity{obj.motor_channels(i)}=System.Decimal.ToDouble(velocityparams{obj.motor_channels(i)}.MinVelocity); % update Min velocity parameter - obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{obj.motor_channels(i)}.Position) * obj.calibration; % motor positions + obj.positions(i) = System.Decimal.ToDouble(obj.channelsNET{obj.motor_channels(i)}.Position) * obj.factor; % motor positions homed(i) = ~obj.channelsNET{obj.motor_channels(i)}.NeedsHoming; if obj.channelsNET{obj.motor_channels(i)}.State == Thorlabs.MotionControl.GenericMotorCLI.MotorStates.Idle moving(i) = false; @@ -207,23 +208,19 @@ function home(obj) function moveto(obj, target_pos) % Move to target position, target_pos := 1 * 3 array of double - tf = obj.checkMove(target_pos); - if tf - target_pos_calib = target_pos / obj.calibration; - obj.isMoving = true; - for i = 1:3 - if ~isnan(obj.motor_channels(i)) - try - workDone=obj.channelsNET{obj.motor_channels(i)}.InitializeWaitHandler(); % Initialise Waithandler for timeout - obj.channelsNET{obj.motor_channels(i)}.MoveTo(target_pos_calib(i), workDone); % Move device to position via .NET interface - obj.channelsNET{obj.motor_channels(i)}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish - catch - error(['Unable to Move channel ',obj.serialnumbers{obj.motor_channels(i)},' to ',num2str(target_pos(i))]); - end + assert(obj.checkMove(target_pos),'Target position is out of range') + target_pos_calib = target_pos / obj.factor; + obj.isMoving = true; + for i = 1:3 + if ~isnan(obj.motor_channels(i)) + try + workDone=obj.channelsNET{obj.motor_channels(i)}.InitializeWaitHandler(); % Initialise Waithandler for timeout + obj.channelsNET{obj.motor_channels(i)}.MoveTo(target_pos_calib(i), workDone); % Move device to position via .NET interface + obj.channelsNET{obj.motor_channels(i)}.Wait(obj.TIMEOUTMOVE); % Wait for move to finish + catch + error(['Unable to Move channel ',obj.serialnumbers{obj.motor_channels(i)},' to ',num2str(target_pos(i))]); end end - else - error('Target position is out of range') end obj.updatestatus end @@ -241,7 +238,7 @@ function step(obj, channelNo, distance) % Calculate the position after the step step_pos = [0 0 0]; - distance_calib = distance / obj.calibration; + distance_calib = distance / obj.factor; obj.channelsNET{channelNo}.SetJogStepSize(abs(distance_calib)) % Set the step size for jog step_pos(channelNo) = obj.channelsNET{channelNo}.GetJogStepSize(); target_pos = obj.positions + step_pos; @@ -290,7 +287,7 @@ function stop(h, immediate) % Stop the motor moving (needed if set motor to cont pos = [NaN NaN NaN]; for i = 1:3 if ~isnan(obj.motor_channels(i)) - pos(i) = System.Decimal.ToDouble(obj.channelsNET{obj.motor_channels(i)}.Position) * obj.calibration; + pos(i) = System.Decimal.ToDouble(obj.channelsNET{obj.motor_channels(i)}.Position) * obj.factor; end end end diff --git a/Modules/+Stages/BSC203.m b/Modules/+Stages/BSC203.m index acacc3eb2..34ab4f477 100644 --- a/Modules/+Stages/BSC203.m +++ b/Modules/+Stages/BSC203.m @@ -8,7 +8,7 @@ % Home is -2,-2,-2 properties - prefs = {'availMotors','x_motor','y_motor','z_motor'}; + prefs = {'availMotors','x_motor','y_motor','z_motor', 'factor'}; end properties(GetObservable, SetObservable, AbortSet) @@ -16,6 +16,7 @@ x_motor = Prefs.MultipleChoice(1,'choices',{1,2,3},'allow_empty',true,'set','set_x_motor','help_text','Which channel in the controller controls the x direction motor','readonly',true); % Show only for now; need to fix driver to be able to change properly y_motor = Prefs.MultipleChoice(2,'choices',{1,2,3},'allow_empty',true,'set','set_y_motor','help_text','Which channel in the controller controls the y direction motor','readonly',true); z_motor = Prefs.MultipleChoice(3,'choices',{1,2,3},'allow_empty',true,'set','set_z_motor','help_text','Which channel in the controller controls the z direction motor','readonly',true); + factor = Prefs.Double(0.5, 'help_text', 'The factor between the actual distance moved and the distance read from Kinesis') end properties(SetAccess=private,SetObservable,AbortSet) % Default here will only matter if motors aren't set @@ -187,7 +188,7 @@ function abort(obj,varargin) if ~isempty(SerialNo) % take val, and instantiate the driver for the BSC203 val = SerialNo; - obj.motors = Drivers.Kinesis.KinesisBSC203.instance(SerialNo, [0 8], SerialNo, obj.motor_channels, 0.5); + obj.motors = Drivers.Kinesis.KinesisBSC203.instance(SerialNo, [0 8], SerialNo, obj.motor_channels, obj.factor); % Listeners will follow lifecycle of their motor addlistener(obj.motors,'isMoving','PostSet',@obj.movingCallback); addlistener(obj.motors,'Homed','PostSet',@obj.homedCallback);