Closed yvesauad closed 4 years ago
Please note that there is a non-proprietary version of NI VISA. Check this for windows and test everything on windows as well. Again, should work with no probs
I have tried a lot to make my laser work using ctypes. I had a few issues but i believe i am in the right track. For doc:
#include <iostream>
#include <stdio.h>
#include <crtdbg.h>
#include "SirahLaserObject.h"
void main(void)
{
LASER_OBJECT *p;
double wl;
!LaserCreate("Sirah Laser.ini", &p);
!LaserGet(p, LASER_FUND_WAVELENGTH, &wl);
//!LaserGoto(p, LASER_FUND_WAVELENGTH, wl, true);
!LaserDestroy(p);
printf("%f", wl);
}
This works using visual studio 2017. In order to make it work, we have included header .h in the same directory, as well as the available .dll. Note that this is not enough. Need to go to Project > Properties > Path Editors > General > Suplementary libraries and add the path of the sirah .lib archive
You also need to go to the tab below general (entrée) and add the name together with a bunch other such as kernel32.lib and shell32.lib.
Finally, you need to properly initialize object with Sirah Laser.ini. I've put in the same folder but doesn matter as long as you provide right path.
from ctypes import cdll
mylib = cdll.LoadLibrary('C:\\Users\Lumiere\Downloads\yves\\31_05_2020\\test_c\SirahLaserObject.dll')
I got an annoying error for a long time when trying to import my .dll using ctypes (did nothing with .dll..i was just trying to import it) Error: [WinError 193] %1 is not a valid Win32 application
The solution of this error was reinstalling python 3.8 using 32-bit version. Python 64 bit is simple not compatible or the available workarounds doesn't worth doing.
There is no error message. I will try tomorrow to implement ctypes and read laser wavelength inside python. I don't believe change everything to python 32 bit is a big deal. Let's hope for the best
Let's remember we have a second way of doing this by using pyserial module. I was already able to
import serial
ser = serial.Serial()
ser.baudrate=19200
ser.port='COM12'
ser.timeout=2
ser.open()
mes = [60, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62]
b= bytearray(mes)
check = 0
for i in range(len(mes)-1):
check = check + mes[i]
b[11] = check
ser.write(b)
s=ser.read(100)
print(s)
ser.close()
using ser.is_open
shows that we succesfully open and close our serial communication through COM12 (our laser). I have tryind to send these arrat of bytes but i didn't receive a response message. Message head is always 3c (60 in decimal) and ending is 3e (62 decimal). For STATUS kind of messages, only second byte defines instruction. In this case, second byte (x04) tells the stepper motor to stop and answers back with 0/1 depending if it was succesfull or not. Well, I am not super far away from this. CRC seems to be working as well (baud rate, parity, stop bits etc is eveyrhing by default well setted)
visit: https://pyserial.readthedocs.io/en/latest/pyserial_api.html for more info.
Hi Yves,
Yes, it is a 32 bit library. That's why I made a separate 32 bit program with a TCP/IP interface so that it can relay commands from a 64 bit program.
You can talk to this program instead, called "Credo.exe" if I remember well. I should send you the API.
The laser is quite old now, it is not probably worth to work a lot as new ones will not have this 32 bit problem.
Marcel.
Le 01/06/2020 à 20:08, yvesauad a écrit :
from ctypes import cdll mylib = cdll.LoadLibrary('C:\Users\Lumiere\Downloads\yves\31_05_2020\test_c\SirahLaserObject.dll') I got an annoying error for a long time when trying to import my .dll using ctypes (did nothing with .dll..i was just trying to import it) Error: [WinError 193] %1 is not a valid Win32 application
The solution of this error was reinstalling python 3.8 using 32-bit version. Python 64 bit is simple not compatible or the available workarounds doesn't worth doing.
There is no error message. I will try tomorrow to implement ctypes and read laser wavelength inside python. I don't believe change everything to python 32 bit is a big deal. Let's hope for the best
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/yvesauad/yvorsay-instrument/issues/12#issuecomment-637020329, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABGQQWKKWTS43SVQSFBQWTTRUPVAHANCNFSM4NPIQM2A.
@marceltence thank you so much. So i have tried with ctypes as you have seen but really looks like will be a complicated solution. Not only it is 32 bit but also .dll is a black box, we have a .lib and also the header .h (not saying the laser init) that we all need to in some way load using ctypes.
justo for me to to understand, do this credo.exe was compilled in C? So in fact you did a plain C program (which i was finally able to do as well) to read and set the wavelength and returned the output? I agree with you that this would be 200% easier. Going through all the process loading in python seems pretty crazy right?
Yves,
CTypes is great to use C dll. That's how the scan and cameras are implemented. You can have a look at orsaycamera.py or orsayscan.py. There is a tool that makes easier the definition of all function access.
credo.exe is program that I wrote in C# because it is easier to write TCP server code.
As you can see it also include the PowerMeter readout, that we made together.
/using System;// //using System.Globalization;// //using System.Runtime.InteropServices;// //using System.IO;// //using System.Net.Sockets;// //using System.Net;// //using System.Threading;// // //namespace Credo// //{// // class CredoServer// // {// // private const string sirah = "SirahLaserObject.dll";// // const short LASER_FUND_WAVELENGTH = 0;// // const short LASER_FUND_WAVENUMBER = 1;// // const short LASER_FUND_FREQUENCY = 2;// // // private Object locker = new Object();// // // CultureInfo ci;// // // [DllImport(sirah, EntryPoint = "LaserCreate", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]// // private static extern Int16 LaserCreate(string configurationfile, ref int laser);// // // [DllImport(sirah, EntryPoint = "LaserDestroy", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]// // private static extern Int16 LaserDestroy(int laser);// // // [DllImport(sirah, EntryPoint = "LaserInit", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]// // private static extern Int16 LaserInit(int laser);// // // [DllImport(sirah, EntryPoint = "LaserGet", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]// // private static extern Int16 LaserGet(int laser, short mode, ref double wl);// // // [DllImport(sirah, EntryPoint = "LaserGoto", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]// // private static extern Int16 LaserGoto(int laser, short mode, double wl);// // // [DllImport(sirah, EntryPoint = "LaserGetLastError", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]// // private static extern Int16 LaserGetLastError(int laser, string error);// // // private int laser = 0;// // private TcpListener listener;// // // public delegate void WaveLengthChangedDelegate(double wl);// // public WaveLengthChangedDelegate onWaveLengthChanged;// // // public PM100 pm100;// // // public CredoServer()// // {// // int port = 13000;// // IPAddress localAddr = IPAddress.Parse("127.0.0.1");// // Start(localAddr, port);// // ci = new CultureInfo("en-US");// // // pm100 = new PM100();// // }// // // ~CredoServer()// // {// // }// // // private double wavelength = 710.0;// // // public async void Start(IPAddress adr, int port)// // {// // // Connect to the laser first// // int laserok = LaserCreate("Sirah Laser.ini", ref laser);// // if ((laserok == 0) && (laser != 0))// // {// // GetWL(0, out wavelength);// // }// // listener = new TcpListener(adr, port);// // listener.Start();// // // while (true)// // {// // try// // {// // var tcpClient = await listener.AcceptTcpClientAsync();// // HandleConnectionAsync(tcpClient);// // }// // catch (Exception exp)// // {// //System.Diagnostics.Debug.WriteLine(exp.ToString());// // }// // // }// // // }// // private async void HandleConnectionAsync(TcpClient tcpClient)// // {// // string clientInfo = tcpClient.Client.RemoteEndPoint.ToString();// // Byte[] bytes = new Byte[256];// // // bool alive = true;// // // while (alive)// // {// // try// // {// // using (var networkStream = tcpClient.GetStream())// // using (var reader = new StreamReader(networkStream))// // using (var writer = new StreamWriter(networkStream))// // {// // writer.AutoFlush = true;// // while (true)// // {// // double wl;// // var dataFromServer = await reader.ReadLineAsync();// // if (string.IsNullOrEmpty(dataFromServer))// // {// // break;// // }// //System.Diagnostics.Debug.WriteLine(dataFromServer);// // if (dataFromServer.Substring(0, 3) == "?WL")// // {// // if (GetWL(0, out wl))// // {// // dataFromServer = "WL:" + wl.ToString(ci);// // }// // }// // else if (dataFromServer.Substring(0, 3) == "WL:")// // {// // try// // {// // wl = double.Parse(dataFromServer.Substring(3));// //SetWL(LASER_FUND_WAVELENGTH, wl);// // }// // catch (Exception ex)// // {// //System.Diagnostics.Debug.WriteLine("Failed to set wavelength: " + ex.ToString());// // }// // if (GetWL(LASER_FUND_WAVELENGTH, out wl))// // {// // dataFromServer = "WL:" + wl.ToString(ci);// // }// // }// // else if (dataFromServer.Substring(0, 3) == "?PW")// // {// // dataFromServer = "PW:" + pm100.Power.ToString(ci);// // }// // else if (dataFromServer.Substring(0, 3) == "?pw")// // {// // dataFromServer = "PW:" + pm100.MeasurePower().ToString(ci);// // }// // await writer.WriteLineAsync(dataFromServer);// // }// // }// // }// // catch (Exception exp)// // {// //System.Diagnostics.Debug.WriteLine(exp.Message);// // }// // finally// // {// //System.Diagnostics.Debug.WriteLine(string.Format("Closing the client connection - {0}",// // clientInfo));// // tcpClient.Close();// // alive = false;// // }// // // }// // }// // // public void Close()// // {// // if (laser != 0)// // {// // int res = LaserDestroy(laser);// // }// // }// // // public bool SetWL(short mode, double wl)// // {// // short res = LaserGoto(laser, mode, wl);// // wavelength = wl;// // if (pm100 != null)// // {// // pm100.SetWaveLength(wl);// // }// // return true;// // }// // // public bool GetWL(short mode, out double wl)// // {// // short res = LaserGet(laser, mode, ref wavelength);// // wl = wavelength;// // if (onWaveLengthChanged!= null)// // {// // onWaveLengthChanged(wl);// // }// // return true;// // }// // }// //}// // /
Basically you send a string through TCP to localhost if on the same computer on port 13000, and you read the answer.
"?WL" returns the wavelength
"WL:500" set the wavelength(don't remember which units to put here).
"?PW" returns the latest known power, periodically measured by credo itself.
"?pw" explicitly reads the power.
For DM, there is a mechanism to check whether we change the wavelength in credo itself. No such thing for TCP side.
This can be done, but makes the client(swift side) a bit more complicated.
This a mechanism quite simple to accommodate 64 applications, when 64 bit dll does not exist.
Marcel.
Le 02/06/2020 à 10:43, yvesauad a écrit :
@marceltence https://github.com/marceltence thank you so much. So i have tried with ctypes as you have seen but really looks like will be a complicated solution. Not only it is 32 bit but also .dll is a black box, we have a .lib and also the header .h (not saying the laser init) that we all need to in some way load using ctypes.
justo for me to to understand, do this credo.exe was compilled in C? So in fact you did a plain C program (which i was finally able to do as well) to read and set the wavelength and returned the output? I agree with you that this would be 200% easier. Going through all the process loading in python seems pretty crazy right?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/yvesauad/yvorsay-instrument/issues/12#issuecomment-637388531, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABGQQWLRWHXUQYVN6KHU4NTRUS3RRANCNFSM4NPIQM2A.
thank you so much Marcel, this is a beautiful workaround! I know you guys have sent me a lot of examples and its amazing that i have so much resource to study. Sorry that sometimes i question already answered questions...even when i look these codes most of the time i get it, like, 5%. This one you sent me sounded me surprinsily doable, maybe because i was lurking around you at the time you were doing the power meter part. thanks again!!! :+1:
i will report here another workaround that worked directly using python with no C. I finally got talking using directly the serial port. It is easier than i thought although they can't directly do all functions available in DLL.
The challenge here is send the train of strings correctly. Line terminator changes from machine to machine and in this case it seems to be NULL
.
Complete code and a few outputs:
import serial
import time
ser = serial.Serial()
ser.baudrate=19200
ser.port='COM12'
ser.timeout=1
print(ser)
ser.open()
mes = [60, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 62]
ba= bytearray(mes)
print("send :")
print(ba)
ser.write(ba) #WE WILL CHANGE HERE TO CHECK DIFFERENT OUTPUTS
print()
print("answer: ")
s=ser.read(14)
print(s)
ser.close()
If ser.write=('hello_world\n'.encode())
We have an answer like this:
b'[\x01\xc1\x02\nl\x13\x00\x1e\x00\x00\x00\x00]'
Following the manual, first bit is the error code, being the first bit an 01 means transmission error. Not surprising as we have sent gibberish information to the hardware.
if mes = [60, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62]
, our answer is:
b'[\x0f\xc1\x02\nl\x13\x00\x1e\x00\x00\x00\x00]'
first bit is 15, a checksum error. Before last pixel should be the sum of all bits excepting the last one so the correct message is mes = [60, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 62]
.
Good message doest not produce by default status message so nothing is printed at the terminal. You can change this setting using "auto-prompt enable".
Note that answer byte #06 tells us the position of the grating. I've checked using sirah control and error messages that they are consistent, altought i have no idea of precision. We have 2 bytes (255*255 = 65025 values) for describing something that must do a step of 0.001. I will check this right now and do a table.
If this solution is easy, we can stick to this ATM
Hi All
Yves, you have example of TCP/IP communication using sockets
in the MönchActuator. The case of the Mönch is a bit complicated, but in fact you can just use a simple socket
and it works like a charm
again, just for the sake of doc:
send_mes = [60, 7, 1, 106, 8, 19, 0, 0, 0, 0, 0, 201, 62]
bs = bytearray(send_mes)
ser.open()
ser.write(bs)
s = ser.read(14)
ser.close()
This three line code puts the laser at 600nm. I have tried moving it from several starting points and it works. good news: the absolute motor positions, given in this case by 106 (0x6A), 8 (0x08), 19 (0x13) and 0 (0x00) are very very consistent.
I was mistaken and we have 4 bytes (256 256 256 * 256) steps which puts precision <0.001 nm.
we can set a async communication in which packets go and back like crazy, but we know they are valid because of header and checksum.
if i understood correctly TCP/IP, that would be nice to implement like this. that would be full python :)
Regarding precision: When laser is at 600, bytes are [106, 08, 18, 0] (LSB -> MSB). When in 599.8: [22, 10, 19, 0]. When in 599.79 [44, 10, 19, 0] 22 steps in LSB between 0.01nm -> ~0.0004nm / bit pretty nice
check laser_example for a calibration (3 order polynomial using numpy) and a hello_hardware. Laser working with <pm resolution.
I was able pretty easily to read powermeter using PYVISA module. NI VISA is another module that allows us to acess national insturment VISA architecture.
Just so you dont forget how to do it:
This very simple program recovers and prints the power in the terminal. Last line tell us the setted wavelength. Implement this in nionswift shouldn't be an issue.
i) test how to do this (maybe a simple PIP in terminal, but check python version, as pyVISA runs in a pretty old python version (2.7+) ii) test laser. I know laser also uses NI-VISA architecture. Maybe these simple lines will work right away and everything is done
PS: You need to have permission in the USB. A simple chmod in
/home/@USER/dev
or something like this should do the trick. Test peripheric identification withlsusb
and test serial communication querying*IDN?
. If you have a response likeThorlabs,PM100USB,1907040,1.6.0
then communication is workingps2: In Linux, a simple
sudo python
is enough to temporary set super user permission so you dont need anything else. Handy to debugIf all this works, as it had worked with power meter, then its almost over :)