Hi,
I am having trouble handling an asynch stream over a serial port. I cannot use prefixed streams because the external device Tiny Spectrum Analyser (TSA) does not provide this.
The process I am using is:
1. Send a "scan" command to the SA over the serial port AStream.write
2. Using AStream_NewData I process the stream recursively until I have received all of the data.
3. I then repeat the process with different scan parameters until I have scanned all bands
The amount of data returned by the TSA varies considerably and so sometimes a single return to AStream_NewData will have the entire response at other times two or three buffers will need to be processed.
To prevent the sending of new scan commands before all the received data has been received and processed, I am waiting for a resumable sub to be completed which I run when the processing of the data has been finalised. The TSA is half duplex, you cannot sends a command until the TSA has set its data with a "cr> ". If you do send a message before completion of the return datathe TSA stalls and must be power cycled to restart.
The problem I am having is the program is not waiting for the resumable sub and also the resumable sub is run without any call to it. I had a look at the B4X Language manual and noticed on page 69 at the bottom that:
Notes:
- It is safer to use CallSubDelayed than CallSub. CallSub will fail if the second sub is never paused
(for example if the sleep is only called based on some condition).
- There is an assumption here that FirstSub will not be called again until it is completed.
In particular the second point about the first sub not being called until it is completed is relavant in my case as it is quite possible for AStream_NewData to interrupt its own processing as new data becomes available.
A flow chart of the process I am using is shown below:
The code is:
I guess one solution would be to use time-outs, however I wish to scan on a virtualy continuous basis with only a small "dead time" at the each of scan. As the amount end of the data stream can be determined, iI wish to use this as a flag for "all data received."
I am having trouble handling an asynch stream over a serial port. I cannot use prefixed streams because the external device Tiny Spectrum Analyser (TSA) does not provide this.
The process I am using is:
1. Send a "scan" command to the SA over the serial port AStream.write
2. Using AStream_NewData I process the stream recursively until I have received all of the data.
3. I then repeat the process with different scan parameters until I have scanned all bands
The amount of data returned by the TSA varies considerably and so sometimes a single return to AStream_NewData will have the entire response at other times two or three buffers will need to be processed.
To prevent the sending of new scan commands before all the received data has been received and processed, I am waiting for a resumable sub to be completed which I run when the processing of the data has been finalised. The TSA is half duplex, you cannot sends a command until the TSA has set its data with a "cr> ". If you do send a message before completion of the return datathe TSA stalls and must be power cycled to restart.
The problem I am having is the program is not waiting for the resumable sub and also the resumable sub is run without any call to it. I had a look at the B4X Language manual and noticed on page 69 at the bottom that:
Notes:
- It is safer to use CallSubDelayed than CallSub. CallSub will fail if the second sub is never paused
(for example if the sleep is only called based on some condition).
- There is an assumption here that FirstSub will not be called again until it is completed.
In particular the second point about the first sub not being called until it is completed is relavant in my case as it is quite possible for AStream_NewData to interrupt its own processing as new data becomes available.
A flow chart of the process I am using is shown below:
The code is:
scanAllBandsForThisSignalSet:
'--------------------------------------------------------------------------------------------
'Scan each bands sequentially. The scan of a single band yields as reply
'from the TSA for that band.
Public Sub scanAllBandsForThisSignalSet
Dim bList As List
bList.Initialize
Main.progBandScan.Progress = 0
Dim count As Int = 0
bandIndex = 0
Main.AllScansCompleteFlag = False
bList = Main.Band_Data.MbandList
For Each Record() As Object In bList
count = count + 1
'setBandSweepAndScan(band, start, stop, BandMargin, NoPoints, rbw
currentStart = Record(1) * 1e6
CurrentStop = Record(2) * 1e6
setBandSweepAndScan(Record(0), Record(1), Record(2), Record(11), Record(10), Main.Band_Data.SigSrcCurrentRecord.RBW_Khz)
' Wait for processing complete before sending another command
Wait For (saDataReceived) Complete(done As Boolean)
bandIndex = bandIndex + 1
Next
Main.AllScansCompleteFlag = True
CallSubDelayed(Me, "saAllScansComplete")
End Sub
setBandSweepAndScan:
'--------------------------------------------------------------------------------------------
'This sub sends out a sweep command to sweep the points in a band
' It is a resumable sub and uses completion functions to signal to
' other application modules
Public Sub setBandSweepAndScan(band As Int, start As Double, _
stop As Double, BandMargin As Double, NoPoints As Int, rbw As Int)
'Construct the frequency strings
Dim startStr As String
Dim stopStr As String
Main.scanCompleteFlag = False
scanDataStreamText = ""
noValidScanLines = 0
Main.noScanBytesTotalExpected = networkPars.NO_CHARS_IN_SCAN_RESPONSE_PER_LINE * NoPoints + networkPars.NO_CHARS_IN_SCAN_RESPONSE_TERMINATION
Main.noScanBytesReceived = 0
bandNo = band
start = start * 1e6
If start < 1e6 Then
startStr = NumberFormat(start, 1, 0) & "k"
else if start < 1000e6 Then
startStr = NumberFormat(start / 1000000, 1, 0) & "M"
else if start > = 1 Then
startStr = NumberFormat(start / 1e9, 1, 0) & "G"
End If
stop = stop * 1e6
If stop < 1e6 Then
stopStr = NumberFormat(stop, 1, 0) & "k"
else if stop < 1000e6 Then
stopStr = NumberFormat(stop / 1e6, 1, 0) & "M"
else if stop > = 1 Then
stopStr = NumberFormat(stop / 1e9, 1, 0) & "G"
End If
' Sweep the band
Main.dataReceivedFlag = False
chCount = False
dataStr = ""
' noPointsThisScan = NoPoints
cmd = "scan"
commandStr = cmd & " " & startStr & " " & stopStr & " " & NoPoints & " " & 3
noOfPackets = 0
Main.writeCmdSerial(commandStr)
Main.dataReceivedFlag = False
End Sub
AStream_NewData:
Public Sub writeCmdSerial(cmdStr As String)
Dim str As String
str = cmdStr & Chr(13)
byteBuffer = bc.StringToBytes(str, "UTF8")
astream.Write(byteBuffer)
End Sub
AStream_NewData:
'--------------------------------------------------------------------------------------------
Private Sub AStream_NewData (Buffer() As Byte)
Dim s As String = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
SaCom.processCmdRx(Buffer)
End Sub
Process command reply:
public Sub processCmdRx(Buffer() As Byte)
replyError = False
tempStr = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
Select cmd
Case "pause"
checkReplyForErrorsShortCmds("pause")
Main.dataReceivedFlag = True
Case "lna"
(...)
Case "scan"
cleanUpScanData(Buffer)
Main.dataReceivedFlag = True
End Select
End Sub
'--------------------------------------------------------------------------------------------
Private Sub cleanUpScanData(buffer() As Byte)
Log("Cleanup L429" & " " & DateTime.Time(DateTime.now))
Dim newDataStart As Int = sb.Length
sb.Append(BytesToString(buffer, 0, buffer.Length, charset))
Dim s As String = sb.ToString
Dim start As Int = 0
For i = newDataStart To s.Length - 1
noOfPackets = noOfPackets + 1
Dim c As Char = s.CharAt(i)
If i = 0 And c = Chr(10) Then '\n...
start = 1 'might be a broken end of line character
Continue
End If
If c = Chr(10) Then '\n
Log("Cleanup chr(10)")
CallSubDelayed2(Me, "checkAndProcessScanLine", s.SubString2(start, i))
' checkAndProcessScanLine(s.SubString2(start, i))
start = i + 1
Else If c = Chr(13) Then '\r
Log("Cleanup chr(13)")
CallSubDelayed2(Me, "checkAndProcessScanLine", s.SubString2(start, i))
' checkAndProcessScanLine(s.SubString2(start, i))
If i < s.Length - 1 And s.CharAt(i + 1) = Chr(10) Then '\r\n
i = i + 1
End If
start = i + 1
End If
Next
If start > 0 Then sb.Remove(0, start)
End Sub
checkAndProcessScanLine:
'--------------------------------------------------------------------------------------------
private Sub checkAndProcessScanLine(scanLineStr As String)
If scanLineStr.Contains("level") Or scanLineStr.Contains("scan") Then
Return
End If
scanDataStreamText = scanDataStreamText & Chr(10) & scanLineStr
noValidScanLines = noValidScanLines + 1
If noValidScanLines = Main.Band_Data.getBandNoPointsArray(bandIndex) Then
'Process the data
getScanListFromSaResult(scanDataStreamText)
CallSubDelayed(Me, "saDataReceived")
End If
Return
End Sub
I guess one solution would be to use time-outs, however I wish to scan on a virtualy continuous basis with only a small "dead time" at the each of scan. As the amount end of the data stream can be determined, iI wish to use this as a flag for "all data received."