czbiohub-sf/coPylot

Vortran Laser USB control

edyoshikun opened this issue · 2 comments

FEATURE:

The serial command through RS232 is slow and outdated.

Describe the solution you'd like
I would like to have this controlled through USB. The device appears as HID with vendor id 0x201A and product id 0x1001, and I can only read from it.
I don't have much documentation on the USB side, but I have the libusb.dll from the vendor.

We got the vendor's SDK from the website where they provide use with some C# precompiled .dll to talk to the lasers.

For simplicity, I tried using the pythonnet code tested with the latest commit in #157. I was able to instantiate the class StradusHUB and call its methods, but I don't seem to get responses from the lasers. I don't think I'm actually connected to them because if I run their program (img linked below) it runs and recognizes the lasers, while in theory, I have them opened in another port (python code).

This might be some useful peace of code since we can tell the buffer sizes, endpoints, and vendor ID Vid_201A & Pid_1001.

In their C# code, they use a finite state machine and a couple of muxes to coordinate sending/receiving data StradusDevice.cs starting at line 284.

#region USB READ BACKGROUND WORKER THREAD

        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            uint BytesWritten = 0;
            uint BytesRead = 0;
            byte responsePending = 0x00;
            byte responseReceived = 0x00;
            byte armGetResponse = 0x00;
            Mutex myMute;

            while (true)
            {
                if (readWriting)	//Do not try to use the read/write handles unless the USB device is attached and ready
                {
                    if ((WriteHandleToUSBDevice != null) && (ReadHandleToUSBDevice != null))
                    {
                        Thread.Sleep(20);
                        try
                        {
                            if (ReadFileManagedBuffer(ReadHandleToUSBDevice, INBuffer, 65, ref BytesRead, IntPtr.Zero))	//Blocking function, unless an "overlapped" structure is used	
                            {
                                if (BytesRead > 0)
                                {
                                    //INBuffer[0] is the report ID, which we don't care about.
                                    //INBuffer[1] is the request type
                                    //INBuffer[2] to INBuffer[63] contain the data

                                    Buffer.BlockCopy(INBuffer, 1, RequestIn, 0, 1); //src,dst

                                    //Parse the fields of the packet received
                                    switch (RequestIn[0])
                                    {
                                        case (byte)RequestTypes.GET_RESPONSE_STATUS:
                                            responsePending = Buffer.GetByte(INBuffer, 2);
                                            //updateRXText("\r\nRx: " + requestIn + " GET_RESPONSE_STATUS:" + System.Convert.ToString(responsePending));
                                            break;
                                        case (byte)RequestTypes.GET_RESPONSE:
                                            if (armGetResponse == 0x01) //Avoid stale data
                                            {
                                                armGetResponse = 0x00;
                                                responseReceived = 0x01;
                                                Buffer.BlockCopy(INBuffer, 2, MessageIn, 0, 63); //src,dst    
                                            }
                                            //updateRXText("\r\nRx: " + requestIn + " GET_RESPONSE: " + Encoding.Default.GetString(MessageIn));
                                            break;
                                        default:
                                            break;
                                    }
                                    //System.Console.WriteLine("ReqIn: "+RequestIn[0].ToString());
                                }
                            }

                            //System.Console.WriteLine("STATE: "+STATE);
                            if (STATE != PSTATE)
                            {
                                logger.Info("STATE: " + STATE);
                                PSTATE = STATE;
                            }

                            //Finite State Machine
                            switch (STATE)
                            {
                                case 0x00: //Idle state
                                    //Poll this status continually in the idle state
                                    SendRequest((byte)RequestTypes.GET_RESPONSE_STATUS, String.Empty);
                                    WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
                                    if (responsePending == 0x01) //The firmware has indicated that there is a response ready to retrieve
                                    {
                                        //Flag is reset by firmware
                                        STATE = 0x02;
                                    }
                                    else
                                        if (sendNow == 0x01)
                                        {
                                            sendNow = 0x00;
                                            //Send a command now...
                                            STATE = 0x03;
                                        }  
                                    break;
                                case 0x01: //Set Response Received
                                    SendRequest((byte)RequestTypes.SET_RESPONSE_RECEIVED, String.Empty);
                                    WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
                                    if (myParentObj != null)
                                        System.Console.WriteLine("SET_RESPONSE_RECEIVED VR Sent...");
                                    STATE = 0x05;
                                    break;
                                case 0x02: //Get response now
                                    //Send request to get the response
                                    SendRequest((byte)RequestTypes.GET_RESPONSE, String.Empty);
                                    WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
                                    armGetResponse = 0x01;
                                    if(myParentObj!=null)
                                        System.Console.WriteLine("GET_RESPONSE VR Sent...");
                                    STATE = 0x04;
                                    break;
                                case 0x03: //Send a new command to laser
                                    rwl.WaitOne();
                                    SendRequest((byte)RequestTypes.SET_CMD_QRY, stringToSend);                                   
                                    WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
                                    rwl.ReleaseMutex();
                                    if (myParentObj != null)
                                        System.Console.WriteLine("SET_COMMAND VR Sent... " + stringToSend);                                  
                                    STATE = 0x00;
                                    break;
                                case 0x04: //WAIT STATE (Wait for the response to actually come back)
                                    if (responseReceived == 0x01)
                                    {
                                        responseReceived = 0x00;
                                        if (myParentObj != null)
                                            System.Console.WriteLine("Response from firmware is:" + Encoding.Default.GetString(MessageIn)); 
                                        STATE = 0x01;
                                    }
                                    break;
                                case 0x05: //WAIT STATE (Wait for GET_RESPONSE_STATUS to return 0)
                                    SendRequest((byte)RequestTypes.GET_RESPONSE_STATUS, String.Empty);
                                    WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, IntPtr.Zero);
                                    if (responsePending == 0)
                                    {
                                        rwlReply.WaitOne();
                                        theReply = Encoding.Default.GetString(MessageIn);
                                        hasReply = true;
                                        rwlReply.ReleaseMutex();
                                        STATE = 0x00;
                                        System.Console.WriteLine("Command Transaction Complete");
                                    }
                                    break;
                                case 0x06:
                                    STATE = 0x06;
                                    break;
                            }
                            
                        }
                        catch
                        {
                            throw;
                            //Exceptions can occur during the read or write operations.  For example,
                            //exceptions may occur if for instance the USB device is physically unplugged
                            //from the host while the above read/write functions are executing.
                        }

                    }
                    else
                    {
                        Thread.Sleep(200);	//was 200, was 5ms Add a small delay.  Otherwise, this while(true) loop can execute very fast and cause 
                        //high CPU utilization, with no particular benefit to the application.
                    }



                } //end of while(true) loop
                //-------------------------------------------------------END CUT AND PASTE BLOCK-------------------------------------------------------------------------------------
                //-------------------------------------------------------------------------------------------------------------------------------------------------------------------
            }
        }
        #endregion

One thing to note is that the vendor's installation package just uses a libusub.sys or a LibUsbDotNet.dll to talk to the device and setup the USB drivers. Is there a better way to talk to these devices?

Screenshot 2023-03-27 at 2 14 31 PM

@gregcourville perhaps you have more experience with this? Any pointers would be appreciated. @ieivanov added this to today's copylot meeting.
Thank you both in advance.