Link to home
Start Free TrialLog in
Avatar of sherrele
sherrele

asked on

Synchronous Winsock?


Although my profile says I'm an expert, I'm really not (not sure how I received that classification).  I'm pretty much a newbie, having done some VB6 GPIB programming several years back.  So, I'm learning, or re-learning what I've done before.

I have a need to send queries to a spectrum analyzer, and to receive responses back using TCIP and winsock.  I've been somewhat successful, however occassionaly my little program hiccups, and because of the asynchronous nature of winsock, some of the returns are either overwritten or simply missed.  That may be due to analyzer task overload or LAN congestion.  The sequence goes something like this:

(-> indicates query from client to analyzer)
(<- indicates analyzer response to client)

What is the frequency? ->
<- The frequency is 1.12 GHz
What is the resolution bandwidth? ->
<- The resolution bandwidth is 100 kHz
What is the sweep? ->
<- The sweep is 893 ms

... and so on.


The issue is that I need to wait until I get a response from the analyzer before I send the next query.  Otherwise the response data is overwritten or somehow missed.  I have instituted a timing wait, but that seems somewhat clumbsy and if I create too long of a wait, the whole communication process just takes longer to complete.

So, my question is; is there a way to send a query, and as soon as the response is received, immediately send the next query?  I didn't seem to find anything in winsock that would permit that.  It seems that winsock was built to be asynchronous.
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Without seeing any code...

In the DataArrival() Event you get your response right?...after receiving a response you just send out the next request at the bottom of that event.

You'll have to keep track of what "step" you are in the communication process...a simple counter could work.
Avatar of js-profi
js-profi

winsock itself is synchronous. it is the API and event handling around what makes it asynchronous. so indeed you have to wait for the response before sending the next request. idle_mind made a suggestion. you also could set a global flag when the response has arrived and check the flag using a periodic timer. that is when the receiving function has not enough info for sending the next request.
Avatar of sherrele

ASKER

Idle_Mind made a point there that I'm going to try.  My program was modeled after a chat example and as such the GetData statement is in a sub of its own (see Code A).  That's most likely because they want a chat to be asynchronous (duh).

So I assume that if I include the code "sock1.GetData strData, vbString" within the same sub that my "btnSingleTrace_Click()" (see Code B) is located, then it should work?  (The code shown has been reduced to just two of the dozen or more commands that need to be sent to the instrument)

I'll give that a try.
Code A:
Public Sub sock1_DataArrival(ByVal bytesTotal As Long)
    sock1.GetData strData, vbString 
End Sub

Code B:
Public Sub btnSingleTrace_Click()

    sock1.RemoteHost = strIP
    sock1.RemotePort = strPort

    sock1.Connect strIP, strPort

    ' Retrieve the center frequency from the instrument.
    txtCommand = Chr$(2) & ":Frequency:Center?" & Chr$(3)
    sock1.SendData txtCommand
 
    sock1.GetData strData, vbString 
    strFrequency = strData

    ' Retrieve the reference level information from the instrument.
    txtStatus = Chr$(2) & ":Display:Amplitude:RLevel?" & Chr$(3)
    sock1.SendData txtStatus

    sock1.GetData strData, vbString 
    strRefLevel = strData

End Sub

Open in new window

Sorry.  The last two commands in the code have typos in them.  "txtStatus" should be replaced with "txtCommand".
you can't call sock1.GetData synchronously as it would retrieve nothing. the first sub is a callback which was called asynchronously when data had yet received. Put the sending of the second data to the first sub.
ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Idle_Mind,
I'm going to try your suggestion when I have some time later today.

js-profi,
I don't quite understand.   The sending of commands requesting data from the instrument is initiated by a button click.  How do I incorporate a button click into the data arrival sub?

it was your requirement that the second query should occur after data from first query arrived. if that is still true the second query would not be triggered by button click but by the arrival of data.
Idle_Mind,

This works well.  I had some problems with the fact that my VB6 didn't like some of the parenthesis, but that was easy to figure out.
Ha...sorry about that.  I was using my VB.Net 2008 IDE to write that and .Net puts parenthesis on all calls...

Glad you figured it out!  =)
Hi Idle_Mind,

Don't know if this is kosher (should this be another separate question?), but I am going to be querying a total of 5 spectrum analyzers using the code you supplied above.  Your code has been working great for a single analyzer.  However for the multiple analyzer task, this really needs to be in a subroutine that can be called as necessary.  So my question is, how would you make your code into a stand alone subroutine?

Depends...are you going to use a separate Winsock control for each analyzer?
...or are you going to use a Winsock Control Array?
Well, I plan on creating a simple CSV configuration file that would hold the IP addresses of the various analyzers, as well as some of the parameter settings for each analyzer.  My program would read that file and then sequentially send out a group of queries to each analyzer one by one.  It would then save the returned data in a pre-determined folder selected by a combobox.  So it would go like this:

1. a. Send queries to analyzer one (frequency, span, etc.).
    b. Receive data into the various variables (frequency, span, etc.).
    c. Save the variables in a CSV file that is named based on analyzer IP address, date & time.
2. a. Send queries to analyzer two (frequency, span, etc.).
    b. Receive data into the various variables (frequency, span, etc.).
    c. Save the variables in a CSV file that is named based on analyzer IP address, date & time.
3. a. Send queries to analyzer three (frequency, span, etc.).
    b. Receive data into the various variables (frequency, span, etc.).
    c. Save the variables in a CSV file that is named based on analyzer IP address, date & time.

... and so on.  Each numbered step above would be a call to the subroutine consisting of your code.

The naming and saving of the CSV file to be saved is already working using your code.  I can do all this manually right now by selecting a different IP address in a combobox and clicking on the "SingleTrace" button, but obviously I wanted to automate it more.

Hmmm...the single threaded nature of VB6 makes this a pain in the butt...

One possibility is to toggle a boolean flag when the last value has been received from the analyzer.  Then you could poll from a loop looking for it.

Something ROUGHLY like:
    Public StepNumber As Long
    Public strFrequency As String
    Public strRefLevel As String
    Public Finished As Boolean

    Public Sub btnSingleTrace_Click()
        btnSingleTrace.Enabled = False

        ' pseudo code for the loop!
        For Each Analyzer In CSV_File
            QueryAnalzyer Analyzer.IP, Analyzer.Port
            While Not Finished
                DoEvents
            End While

            ' do something with strFrequency, strRefLevel, etc...

        Next

        btnSingleTrace.Enabled = True
        MsgBox "Done!"
    End Sub

    Public Sub QueryAnalzyer(ByVal strIP As String, ByVal strPort As String)
        StepNumber = 1
        Finished = False
        sock1.RemoteHost = strIP
        sock1.RemotePort = strPort
        sock1.Connect strIP, strPort
    End Sub

    Public Sub sock1_Connect()
        ' Retrieve the center frequency from the instrument.
        txtCommand = Chr$(2) & ":Frequency:Center?" & Chr$(3)
        sock1.SendData txtCommand
        ' when a response comes back, the DataArrival event below will fire...
    End Sub

    Public Sub sock1_DataArrival(ByVal bytesTotal As Long)
        Select Case StepNumber
            Case 1
                sock1.GetData strData, vbString
                strFrequency = strData

                ' Retrieve the reference level information from the instrument.
                txtStatus = Chr$(2) & ":Display:Amplitude:RLevel?" & Chr$(3)
                sock1.SendData txtStatus

            Case 2
                sock1.GetData strData, vbString
                strRefLevel = strData

                ' after the last "step", close the connection and toggle "Finished"
                sock1.Close
                Finished = True

        End Select
        StepNumber = StepNumber + 1
    End Sub

Open in new window