From b153f6e0193f2fa4089a0de01889a3a0898f119e Mon Sep 17 00:00:00 2001 From: Stroud-Thorlabs Date: Wed, 24 Jun 2026 14:43:18 +0100 Subject: [PATCH 1/2] Add UMCx XA C++ example --- C++/XA/UMCx/UMCx.cpp | 141 +++++++++++++++++++++++++++++ C++/XA/UMCx/UMCx.vcxproj | 150 +++++++++++++++++++++++++++++++ C++/XA/UMCx/UMCx.vcxproj.filters | 22 +++++ C++/XA/UMCx/required_files.txt | 4 + 4 files changed, 317 insertions(+) create mode 100644 C++/XA/UMCx/UMCx.cpp create mode 100644 C++/XA/UMCx/UMCx.vcxproj create mode 100644 C++/XA/UMCx/UMCx.vcxproj.filters create mode 100644 C++/XA/UMCx/required_files.txt diff --git a/C++/XA/UMCx/UMCx.cpp b/C++/XA/UMCx/UMCx.cpp new file mode 100644 index 0000000..8060d9c --- /dev/null +++ b/C++/XA/UMCx/UMCx.cpp @@ -0,0 +1,141 @@ +/* +Title: UMCx +Created date: 24/06/2026 +Last Modified Date: 24/06/2026 +C++ version used: ISO C++ 14 +Thorlabs DLL version: 1.6.8.27431 +This example demonstrates how to set-up the communication for the Thorlabs +UMCx controllers with a brushless stage, home it, and move it by 5 mm or degrees. +*/ + +#include +#include +#include +#include +#include +#include + +int main() +{ + // Start up XA + TLMC_ResultCode startupResult = TLMC_Startup(nullptr); + if (startupResult != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "SDK Startup failed." << std::endl; + return 1; + } + + TLMC_DeviceHandle hDevice; + const char* deviceSerialNo = "121000020"; // Replace with your device's serial number + TLMC_ResultCode resultCode = TLMC_Open(deviceSerialNo, nullptr, TLMC_OperatingModes::TLMC_OperatingMode_Default, &hDevice); + if (resultCode != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "Failed to open device." << std::endl; + std::cerr << "Possible causes: Device is turned off, not connected, or channel serial number is incorrect." << std::endl; + TLMC_Shutdown(); + return 2; + } + std::cout << "Device opened successfully." << std::endl; + + // Create channel handle and assign channel serial number + TLMC_DeviceHandle hChannel; + std::string channelSerialNoStr = std::string(deviceSerialNo) + std::to_string(-1); + const char* channelSerialNo = channelSerialNoStr.c_str(); + + // Open the channel + resultCode = TLMC_Open(channelSerialNo, nullptr, TLMC_OperatingModes::TLMC_OperatingMode_Default, &hChannel); + if (resultCode != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "Failed to open device." << std::endl; + std::cerr << "Possible causes: Device is turned off, not connected, or channel serial number is incorrect." << std::endl; + TLMC_Shutdown(); + return 3; + } + std::cout << "Channel 1 opened successfully." << std::endl; + + // Enable channel 1 + resultCode = TLMC_SetEnableState(hChannel, TLMC_EnableState::TLMC_Enabled, 60000); + if (resultCode != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "Failed to enable channel 1." << std::endl; + TLMC_Close(hChannel); + TLMC_Shutdown(); + return 4; + } + std::cout << "Channel 1 enabled successfully." << std::endl; + + // Home channel 1 + resultCode = TLMC_Home(hChannel, TLMC_InfiniteWait); + if (resultCode != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "Homing failed." << std::endl; + TLMC_Close(hChannel); + TLMC_Shutdown(); + return 5; + } + std::cout << "Homing successful." << std::endl; + + // Convert 5 mm to device units (physical -> device) + double distanceMM = 5.0; + int64_t valueInDeviceUnits; + resultCode = TLMC_ConvertFromPhysicalToDevice(hChannel, TLMC_ScaleType::TLMC_ScaleType_Distance, TLMC_Unit_Type::TLMC_Unit_Millimetres, distanceMM, &valueInDeviceUnits); + if (resultCode != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "Conversion failed." << std::endl; + TLMC_Close(hChannel); + TLMC_Shutdown(); + return 6; + } + std::cout << "Conversion from 5 mm successful. Value in device units: " << valueInDeviceUnits << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + + // Absolute move to 5 mm + resultCode = TLMC_Move(hChannel, TLMC_MoveMode::TLMC_MoveMode_Absolute, valueInDeviceUnits, 60000); + if (resultCode != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "Move failed." << std::endl; + TLMC_Close(hChannel); + TLMC_Shutdown(); + return 7; + } + std::cout << "Absolute move successful." << std::endl; + + // Get the current position in device units + int32_t currentPosition; + resultCode = TLMC_GetPositionCounter(hChannel, ¤tPosition, 60000); + if (resultCode != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "Failed to get current position." << std::endl; + TLMC_Close(hChannel); + TLMC_Shutdown(); + return 8; + } + + // Convert the position to physical units and print both position values + double valueInPhysicalUnits; + TLMC_Unit_Type physicalUnit; + resultCode = TLMC_ConvertFromDeviceToPhysical(hChannel, TLMC_ScaleType::TLMC_ScaleType_Distance, currentPosition, &valueInPhysicalUnits, &physicalUnit); + if (resultCode != TLMC_ResultCodes::TLMC_Success) + { + std::cerr << "Conversion failed." << std::endl; + TLMC_Close(hChannel); + TLMC_Shutdown(); + return 9; + } + + std::cout << "New current position is:\nDevice Units: " << currentPosition << "\nPhysical Units: " << valueInPhysicalUnits << std::endl; + + // Close channel, disconnect and close device, shut down XA + if (TLMC_Close(hChannel) != TLMC_ResultCodes::TLMC_Success) + std::cerr << "Channel 1 close failed." << std::endl; + else + std::cout << "Channel 1 closed." << std::endl; + + if (TLMC_Close(hDevice) != TLMC_ResultCodes::TLMC_Success) + std::cerr << "Device close failed." << std::endl; + else + std::cout << "Device closed." << std::endl; + + TLMC_Shutdown(); +} \ No newline at end of file diff --git a/C++/XA/UMCx/UMCx.vcxproj b/C++/XA/UMCx/UMCx.vcxproj new file mode 100644 index 0000000..443390c --- /dev/null +++ b/C++/XA/UMCx/UMCx.vcxproj @@ -0,0 +1,150 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {36060bf3-e179-4261-a3a2-09b9ad994ef1} + UMCtesting + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + MultiByte + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + C:\Users\cstroud\source\repos\UMC testing\directory;$(IncludePath) + C:\Users\cstroud\source\repos\UMC testing\library;$(LibraryPath) + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + tlmc_xa_native.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/C++/XA/UMCx/UMCx.vcxproj.filters b/C++/XA/UMCx/UMCx.vcxproj.filters new file mode 100644 index 0000000..253087e --- /dev/null +++ b/C++/XA/UMCx/UMCx.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/C++/XA/UMCx/required_files.txt b/C++/XA/UMCx/required_files.txt new file mode 100644 index 0000000..4865c84 --- /dev/null +++ b/C++/XA/UMCx/required_files.txt @@ -0,0 +1,4 @@ +Copy the following required files to the project folder: +C:\Program Files\Thorlabs XA\SDK\Native (C, C++)\Libraries\x64\tlmc_xa_native.dll +C:\Program Files\Thorlabs XA\SDK\Native (C, C++)\Libraries\x64\tlmc_xa_native.lib +C:\Program Files\Thorlabs XA\SDK\Native (C, C++)\tlmc_xa_native_api.h \ No newline at end of file From 42d3e7c9c9d7520ce65a9408d86a48da7ac4b559 Mon Sep 17 00:00:00 2001 From: Stroud-Thorlabs Date: Wed, 24 Jun 2026 15:01:33 +0100 Subject: [PATCH 2/2] Add UMCx XA C# example --- C#/XA/UMCx/App.config | 6 + C#/XA/UMCx/Program.cs | 159 ++++++++++++++++++ C#/XA/UMCx/Properties/AssemblyInfo.cs | 36 ++++ C#/XA/UMCx/UMCx.csproj | 77 +++++++++ ...ework,Version=v4.7.2.AssemblyAttributes.cs | 4 + .../Debug/UMCx.csproj.FileListAbsolute.txt | 9 + C#/XA/UMCx/obj/Debug/UMCx.exe.config | 6 + 7 files changed, 297 insertions(+) create mode 100644 C#/XA/UMCx/App.config create mode 100644 C#/XA/UMCx/Program.cs create mode 100644 C#/XA/UMCx/Properties/AssemblyInfo.cs create mode 100644 C#/XA/UMCx/UMCx.csproj create mode 100644 C#/XA/UMCx/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs create mode 100644 C#/XA/UMCx/obj/Debug/UMCx.csproj.FileListAbsolute.txt create mode 100644 C#/XA/UMCx/obj/Debug/UMCx.exe.config diff --git a/C#/XA/UMCx/App.config b/C#/XA/UMCx/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/C#/XA/UMCx/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/C#/XA/UMCx/Program.cs b/C#/XA/UMCx/Program.cs new file mode 100644 index 0000000..a0e457c --- /dev/null +++ b/C#/XA/UMCx/Program.cs @@ -0,0 +1,159 @@ +// Title: UMCX +// Created Date: 23/06/2026 +// Last Modified Date: 23/06/2026 +// .NET Framework version: 4.7.2 +// Thorlabs DLL version: 1.6.8.27431 +// Example Description: +// This example demonstrates how to set-up the communication for the Thorlabs +// UMCX controllers with a brushless stage, home it, and move it by 1 mm or degrees. + +using System; +using System.Threading; +using Thorlabs.MotionControl.XA; +using Thorlabs.MotionControl.XA.Products; + +namespace UMC.net_testing +{ + class Program + { + //Change this line to match the serial number on your device (format: 121XXXXXX) + private static string _deviceId = "121000020"; + //Select the channel to control ("1", "2", or "3") + private static string _channelNumber = "1"; + + static void Main(string[] args) + { + SystemManager systemManager; + + //Start up XA + try + { + systemManager = SystemManager.Create(); + systemManager.Startup(); + } + catch (Exception ex) + { + Console.WriteLine("Exception: {0}", ex.Message); + return; + } + + + //Get the device list + System.Collections.Generic.IList devicelist = systemManager.GetDeviceList(); + + // Print all connected devices + Console.WriteLine("Connected devices: {0}", devicelist?.Count ?? 0); + if (devicelist != null && devicelist.Count > 0) + { + int index = 1; + + foreach (var d in devicelist) + { + Console.Write("Device {0}: ", index); + try + { + Console.WriteLine("{0}, Serial Number: {1}", d.DeviceType, d.Device); + + } + catch + { + Console.WriteLine(d?.ToString() ?? ""); + } + + index++; + } + } + else + { + Console.WriteLine("No devices found."); + } + + //Open the UMCx device + Umcx device; + bool ret = systemManager.TryOpenDevice(_deviceId, "", OperatingModes.Default, out device); + if (ret == false) + { + Console.WriteLine("\nFail to open base unit {0}", _deviceId); + systemManager.Shutdown(); + return; + } + else + { + Console.WriteLine("\nBase Unit {0} opened successfully", _deviceId); + } + + //Open the channel + UmcxBrushlessLogicalChannel channel; //Make sure to choose the correct stage type for the channel, i.e. UmcxBrushlessLogicalChannel or UmcxStepperLogicalChannel + string _channelId = _deviceId + "-" + _channelNumber; + ret = systemManager.TryOpenDevice(_channelId, "", OperatingModes.Default, out channel); + if (ret == false) + { + Console.WriteLine("Fail to open channel {0}", channel.DeviceId); + systemManager.Shutdown(); + return; + } + else + { + Console.WriteLine("Channel {0} opened successfully", channel.DeviceId); + } + + try + { + //Enable the device + channel.SetEnableState(EnableState.Enabled, TimeSpan.FromSeconds(1)); + + //Get the stage information + ConnectedProductInfo connectedProductInfo = channel.GetConnectedProductInfo(); + Console.WriteLine("Channel {0} Stage Name:{1}", channel.DeviceId, connectedProductInfo.ProductName); + + //Home the device + Console.WriteLine("Homing"); + channel.Home(TimeSpan.FromSeconds(60)); + Console.WriteLine("Home completed"); + + //Set the distance. Unit: millimeters or degrees depending on the actuator + double distance = 1; + + //Get the unit type of the actuator + Unit deviceUnit = connectedProductInfo.UnitType; + + //Convert the distance to device unit + long posInDeviceUnits = channel.FromPhysicalToDeviceUnit(ScaleType.Distance, deviceUnit, distance); + Thread.Sleep(500); + + //Move the device + Console.WriteLine("Moving to {0} {1}", distance, deviceUnit.ToString()); + channel.Move(MoveMode.Absolute, (int)posInDeviceUnits, TimeSpan.FromSeconds(60)); + Console.WriteLine("Move completed"); + + //Get the current position + int currentPosInDeviceUnits = channel.GetPositionCounter(TimeSpan.FromSeconds(1)); + + //Convert the device unit to physical unit + UnitConversionResult currentPos = channel.FromDeviceUnitToPhysical(ScaleType.Distance, currentPosInDeviceUnits); + Console.WriteLine("Current Position: {0} {1}", currentPos.Value, currentPos.UnitType); + } + catch (Exception ex) + { + Console.WriteLine("Exception: {0}", ex.Message); + } + finally + { + //Close the channel + if (channel != null) + channel.Close(); + + //Close the device + if (device != null) + { + device.Disconnect(); + device.Close(); + } + + //Shutdown XA + systemManager.Shutdown(); + + } + } + } +} diff --git a/C#/XA/UMCx/Properties/AssemblyInfo.cs b/C#/XA/UMCx/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0b43bcd --- /dev/null +++ b/C#/XA/UMCx/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UMC .net testing")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("UMC .net testing")] +[assembly: AssemblyCopyright("Copyright © 2026")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("12c10132-4d7d-4940-a749-b41cee252bb6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/C#/XA/UMCx/UMCx.csproj b/C#/XA/UMCx/UMCx.csproj new file mode 100644 index 0000000..49ffe10 --- /dev/null +++ b/C#/XA/UMCx/UMCx.csproj @@ -0,0 +1,77 @@ + + + + + Debug + AnyCPU + {12C10132-4D7D-4940-A749-B41CEE252BB6} + Exe + UMC.net_testing + UMC.net_testing + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + true + + + + + + + + + + + + False + bin\Debug\tlmc_xa_dotnet.dll + + + + + + + + + + + \ No newline at end of file diff --git a/C#/XA/UMCx/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs b/C#/XA/UMCx/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs new file mode 100644 index 0000000..3871b18 --- /dev/null +++ b/C#/XA/UMCx/obj/Debug/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] diff --git a/C#/XA/UMCx/obj/Debug/UMCx.csproj.FileListAbsolute.txt b/C#/XA/UMCx/obj/Debug/UMCx.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..9f799f8 --- /dev/null +++ b/C#/XA/UMCx/obj/Debug/UMCx.csproj.FileListAbsolute.txt @@ -0,0 +1,9 @@ +C:\Users\cstroud\source\repos\UMC .net testing\obj\x64\Debug\UMC .net testing.csproj.AssemblyReference.cache +C:\Users\cstroud\source\repos\UMC .net testing\obj\x64\Debug\UMC .net testing.csproj.CoreCompileInputs.cache +C:\Users\cstroud\source\repos\UMC .net testing\bin\x64\Debug\UMC.net_testing.exe.config +C:\Users\cstroud\source\repos\UMC .net testing\bin\x64\Debug\UMC.net_testing.exe +C:\Users\cstroud\source\repos\UMC .net testing\bin\x64\Debug\UMC.net_testing.pdb +C:\Users\cstroud\source\repos\UMC .net testing\bin\x64\Debug\tlmc_xa_dotnet.dll +C:\Users\cstroud\source\repos\UMC .net testing\obj\x64\Debug\UMC .net testing.csproj.CopyComplete +C:\Users\cstroud\source\repos\UMC .net testing\obj\x64\Debug\UMC.net_testing.exe +C:\Users\cstroud\source\repos\UMC .net testing\obj\x64\Debug\UMC.net_testing.pdb diff --git a/C#/XA/UMCx/obj/Debug/UMCx.exe.config b/C#/XA/UMCx/obj/Debug/UMCx.exe.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/C#/XA/UMCx/obj/Debug/UMCx.exe.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file