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.
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.
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.
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
ASKER
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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?
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.
ASKER
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.
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! =)
Glad you figured it out! =)
ASKER
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?
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?
...or are you going to use a Winsock Control Array?
ASKER
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.
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:
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
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.